codekingpro/portable-devtools
114k
1// Copyright 2009 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4 5/*6Package elf implements access to ELF object files.7 8# Security9 10This package is not designed to be hardened against adversarial inputs, and is11outside the scope of https://go.dev/security/policy. In particular, only basic12validation is done when parsing object files. As such, care should be taken when13parsing untrusted inputs, as parsing malformed files may consume significant14resources, or cause panics.15*/16package elf17 18import (19 "bytes"20 "compress/zlib"21 "debug/dwarf"22 "encoding/binary"23 "errors"24 "fmt"25 "internal/saferio"26 "internal/zstd"27 "io"28 "math"29 "os"30 "strings"31 "unsafe"32)33 34// TODO: error reporting detail35 36/*37 * Internal ELF representation38 */39 40// A FileHeader represents an ELF file header.41type FileHeader struct {42 Class Class43 Data Data44 Version Version45 OSABI OSABI46 ABIVersion uint847 ByteOrder binary.ByteOrder48 Type Type49 Machine Machine50 Entry uint6451}52 53// A File represents an open ELF file.54type File struct {55 FileHeader56 Sections []*Section57 Progs []*Prog58 closer io.Closer59 dynVers []DynamicVersion60 dynVerNeeds []DynamicVersionNeed61 gnuVersym []byte62}63 64// A SectionHeader represents a single ELF section header.65type SectionHeader struct {66 Name string67 Type SectionType68 Flags SectionFlag69 Addr uint6470 Offset uint6471 Size uint6472 Link uint3273 Info uint3274 Addralign uint6475 Entsize uint6476 77 // FileSize is the size of this section in the file in bytes.78 // If a section is compressed, FileSize is the size of the79 // compressed data, while Size (above) is the size of the80 // uncompressed data.81 FileSize uint6482}83 84// A Section represents a single section in an ELF file.85type Section struct {86 SectionHeader87 88 // Embed ReaderAt for ReadAt method.89 // Do not embed SectionReader directly90 // to avoid having Read and Seek.91 // If a client wants Read and Seek it must use92 // Open() to avoid fighting over the seek offset93 // with other clients.94 //95 // ReaderAt may be nil if the section is not easily available96 // in a random-access form. For example, a compressed section97 // may have a nil ReaderAt.98 io.ReaderAt99 sr *io.SectionReader100 101 compressionType CompressionType102 compressionOffset int64103}104 105// Data reads and returns the contents of the ELF section.106// Even if the section is stored compressed in the ELF file,107// Data returns uncompressed data.108//109// For an [SHT_NOBITS] section, Data always returns a non-nil error.110func (s *Section) Data() ([]byte, error) {111 return saferio.ReadData(s.Open(), s.Size)112}113 114// stringTable reads and returns the string table given by the115// specified link value.116func (f *File) stringTable(link uint32) ([]byte, error) {117 if link <= 0 || link >= uint32(len(f.Sections)) {118 return nil, errors.New("section has invalid string table link")119 }120 return f.Sections[link].Data()121}122 123// Open returns a new ReadSeeker reading the ELF section.124// Even if the section is stored compressed in the ELF file,125// the ReadSeeker reads uncompressed data.126//127// For an [SHT_NOBITS] section, all calls to the opened reader128// will return a non-nil error.129func (s *Section) Open() io.ReadSeeker {130 if s.Type == SHT_NOBITS {131 return io.NewSectionReader(&nobitsSectionReader{}, 0, int64(s.Size))132 }133 134 var zrd func(io.Reader) (io.ReadCloser, error)135 if s.Flags&SHF_COMPRESSED == 0 {136 137 if !strings.HasPrefix(s.Name, ".zdebug") {138 return io.NewSectionReader(s.sr, 0, 1<<63-1)139 }140 141 b := make([]byte, 12)142 n, _ := s.sr.ReadAt(b, 0)143 if n != 12 || string(b[:4]) != "ZLIB" {144 return io.NewSectionReader(s.sr, 0, 1<<63-1)145 }146 147 s.compressionOffset = 12148 s.compressionType = COMPRESS_ZLIB149 s.Size = binary.BigEndian.Uint64(b[4:12])150 zrd = zlib.NewReader151 152 } else if s.Flags&SHF_ALLOC != 0 {153 return errorReader{&FormatError{int64(s.Offset),154 "SHF_COMPRESSED applies only to non-allocable sections", s.compressionType}}155 }156 157 switch s.compressionType {158 case COMPRESS_ZLIB:159 zrd = zlib.NewReader160 case COMPRESS_ZSTD:161 zrd = func(r io.Reader) (io.ReadCloser, error) {162 return io.NopCloser(zstd.NewReader(r)), nil163 }164 }165 166 if zrd == nil {167 return errorReader{&FormatError{int64(s.Offset), "unknown compression type", s.compressionType}}168 }169 170 return &readSeekerFromReader{171 reset: func() (io.Reader, error) {172 fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.FileSize)-s.compressionOffset)173 return zrd(fr)174 },175 size: int64(s.Size),176 }177}178 179// A ProgHeader represents a single ELF program header.180type ProgHeader struct {181 Type ProgType182 Flags ProgFlag183 Off uint64184 Vaddr uint64185 Paddr uint64186 Filesz uint64187 Memsz uint64188 Align uint64189}190 191// A Prog represents a single ELF program header in an ELF binary.192type Prog struct {193 ProgHeader194 195 // Embed ReaderAt for ReadAt method.196 // Do not embed SectionReader directly197 // to avoid having Read and Seek.198 // If a client wants Read and Seek it must use199 // Open() to avoid fighting over the seek offset200 // with other clients.201 io.ReaderAt202 sr *io.SectionReader203}204 205// Open returns a new ReadSeeker reading the ELF program body.206func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) }207 208// A Symbol represents an entry in an ELF symbol table section.209type Symbol struct {210 Name string211 Info, Other byte212 213 // HasVersion reports whether the symbol has any version information.214 // This will only be true for the dynamic symbol table.215 HasVersion bool216 // VersionIndex is the symbol's version index.217 // Use the methods of the [VersionIndex] type to access it.218 // This field is only meaningful if HasVersion is true.219 VersionIndex VersionIndex220 221 Section SectionIndex222 Value, Size uint64223 224 // These fields are present only for the dynamic symbol table.225 Version string226 Library string227}228 229/*230 * ELF reader231 */232 233type FormatError struct {234 off int64235 msg string236 val any237}238 239func (e *FormatError) Error() string {240 msg := e.msg241 if e.val != nil {242 msg += fmt.Sprintf(" '%v' ", e.val)243 }244 msg += fmt.Sprintf("in record at byte %#x", e.off)245 return msg246}247 248// Open opens the named file using [os.Open] and prepares it for use as an ELF binary.249func Open(name string) (*File, error) {250 f, err := os.Open(name)251 if err != nil {252 return nil, err253 }254 ff, err := NewFile(f)255 if err != nil {256 f.Close()257 return nil, err258 }259 ff.closer = f260 return ff, nil261}262 263// Close closes the [File].264// If the [File] was created using [NewFile] directly instead of [Open],265// Close has no effect.266func (f *File) Close() error {267 var err error268 if f.closer != nil {269 err = f.closer.Close()270 f.closer = nil271 }272 return err273}274 275// SectionByType returns the first section in f with the276// given type, or nil if there is no such section.277func (f *File) SectionByType(typ SectionType) *Section {278 for _, s := range f.Sections {279 if s.Type == typ {280 return s281 }282 }283 return nil284}285 286// NewFile creates a new [File] for accessing an ELF binary in an underlying reader.287// The ELF binary is expected to start at position 0 in the ReaderAt.288func NewFile(r io.ReaderAt) (*File, error) {289 sr := io.NewSectionReader(r, 0, 1<<63-1)290 // Read and decode ELF identifier291 var ident [16]uint8292 if _, err := r.ReadAt(ident[0:], 0); err != nil {293 return nil, err294 }295 if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' {296 return nil, &FormatError{0, "bad magic number", ident[0:4]}297 }298 299 f := new(File)300 f.Class = Class(ident[EI_CLASS])301 switch f.Class {302 case ELFCLASS32:303 case ELFCLASS64:304 // ok305 default:306 return nil, &FormatError{0, "unknown ELF class", f.Class}307 }308 309 f.Data = Data(ident[EI_DATA])310 var bo binary.ByteOrder311 switch f.Data {312 case ELFDATA2LSB:313 bo = binary.LittleEndian314 case ELFDATA2MSB:315 bo = binary.BigEndian316 default:317 return nil, &FormatError{0, "unknown ELF data encoding", f.Data}318 }319 f.ByteOrder = bo320 321 f.Version = Version(ident[EI_VERSION])322 if f.Version != EV_CURRENT {323 return nil, &FormatError{0, "unknown ELF version", f.Version}324 }325 326 f.OSABI = OSABI(ident[EI_OSABI])327 f.ABIVersion = ident[EI_ABIVERSION]328 329 // Read ELF file header330 var phoff int64331 var phentsize, phnum int332 var shoff int64333 var shentsize, shnum, shstrndx int334 switch f.Class {335 case ELFCLASS32:336 var hdr Header32337 data := make([]byte, unsafe.Sizeof(hdr))338 if _, err := sr.ReadAt(data, 0); err != nil {339 return nil, err340 }341 f.Type = Type(bo.Uint16(data[unsafe.Offsetof(hdr.Type):]))342 f.Machine = Machine(bo.Uint16(data[unsafe.Offsetof(hdr.Machine):]))343 f.Entry = uint64(bo.Uint32(data[unsafe.Offsetof(hdr.Entry):]))344 if v := Version(bo.Uint32(data[unsafe.Offsetof(hdr.Version):])); v != f.Version {345 return nil, &FormatError{0, "mismatched ELF version", v}346 }347 phoff = int64(bo.Uint32(data[unsafe.Offsetof(hdr.Phoff):]))348 phentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phentsize):]))349 phnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phnum):]))350 shoff = int64(bo.Uint32(data[unsafe.Offsetof(hdr.Shoff):]))351 shentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shentsize):]))352 shnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shnum):]))353 shstrndx = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shstrndx):]))354 case ELFCLASS64:355 var hdr Header64356 data := make([]byte, unsafe.Sizeof(hdr))357 if _, err := sr.ReadAt(data, 0); err != nil {358 return nil, err359 }360 f.Type = Type(bo.Uint16(data[unsafe.Offsetof(hdr.Type):]))361 f.Machine = Machine(bo.Uint16(data[unsafe.Offsetof(hdr.Machine):]))362 f.Entry = bo.Uint64(data[unsafe.Offsetof(hdr.Entry):])363 if v := Version(bo.Uint32(data[unsafe.Offsetof(hdr.Version):])); v != f.Version {364 return nil, &FormatError{0, "mismatched ELF version", v}365 }366 phoff = int64(bo.Uint64(data[unsafe.Offsetof(hdr.Phoff):]))367 phentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phentsize):]))368 phnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Phnum):]))369 shoff = int64(bo.Uint64(data[unsafe.Offsetof(hdr.Shoff):]))370 shentsize = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shentsize):]))371 shnum = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shnum):]))372 shstrndx = int(bo.Uint16(data[unsafe.Offsetof(hdr.Shstrndx):]))373 }374 375 if shoff < 0 {376 return nil, &FormatError{0, "invalid shoff", shoff}377 }378 if phoff < 0 {379 return nil, &FormatError{0, "invalid phoff", phoff}380 }381 382 if shoff == 0 && shnum != 0 {383 return nil, &FormatError{0, "invalid ELF shnum for shoff=0", shnum}384 }385 386 if shnum > 0 && shstrndx >= shnum {387 return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}388 }389 390 var wantPhentsize, wantShentsize int391 switch f.Class {392 case ELFCLASS32:393 wantPhentsize = 8 * 4394 wantShentsize = 10 * 4395 case ELFCLASS64:396 wantPhentsize = 2*4 + 6*8397 wantShentsize = 4*4 + 6*8398 }399 if phnum > 0 && phentsize < wantPhentsize {400 return nil, &FormatError{0, "invalid ELF phentsize", phentsize}401 }402 403 // Read program headers404 f.Progs = make([]*Prog, phnum)405 phdata, err := saferio.ReadDataAt(sr, uint64(phnum)*uint64(phentsize), phoff)406 if err != nil {407 return nil, err408 }409 for i := 0; i < phnum; i++ {410 off := uintptr(i) * uintptr(phentsize)411 p := new(Prog)412 switch f.Class {413 case ELFCLASS32:414 var ph Prog32415 p.ProgHeader = ProgHeader{416 Type: ProgType(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Type):])),417 Flags: ProgFlag(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Flags):])),418 Off: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Off):])),419 Vaddr: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Vaddr):])),420 Paddr: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Paddr):])),421 Filesz: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Filesz):])),422 Memsz: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Memsz):])),423 Align: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Align):])),424 }425 case ELFCLASS64:426 var ph Prog64427 p.ProgHeader = ProgHeader{428 Type: ProgType(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Type):])),429 Flags: ProgFlag(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Flags):])),430 Off: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Off):]),431 Vaddr: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Vaddr):]),432 Paddr: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Paddr):]),433 Filesz: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Filesz):]),434 Memsz: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Memsz):]),435 Align: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Align):]),436 }437 }438 if int64(p.Off) < 0 {439 return nil, &FormatError{phoff + int64(off), "invalid program header offset", p.Off}440 }441 if int64(p.Filesz) < 0 {442 return nil, &FormatError{phoff + int64(off), "invalid program header file size", p.Filesz}443 }444 p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))445 p.ReaderAt = p.sr446 f.Progs[i] = p447 }448 449 // If the number of sections is greater than or equal to SHN_LORESERVE450 // (0xff00), shnum has the value zero and the actual number of section451 // header table entries is contained in the sh_size field of the section452 // header at index 0.453 if shoff > 0 && shnum == 0 {454 var typ, link uint32455 sr.Seek(shoff, io.SeekStart)456 switch f.Class {457 case ELFCLASS32:458 sh := new(Section32)459 if err := binary.Read(sr, bo, sh); err != nil {460 return nil, err461 }462 shnum = int(sh.Size)463 typ = sh.Type464 link = sh.Link465 case ELFCLASS64:466 sh := new(Section64)467 if err := binary.Read(sr, bo, sh); err != nil {468 return nil, err469 }470 shnum = int(sh.Size)471 typ = sh.Type472 link = sh.Link473 }474 if SectionType(typ) != SHT_NULL {475 return nil, &FormatError{shoff, "invalid type of the initial section", SectionType(typ)}476 }477 478 if shnum < int(SHN_LORESERVE) {479 return nil, &FormatError{shoff, "invalid ELF shnum contained in sh_size", shnum}480 }481 482 // If the section name string table section index is greater than or483 // equal to SHN_LORESERVE (0xff00), this member has the value484 // SHN_XINDEX (0xffff) and the actual index of the section name485 // string table section is contained in the sh_link field of the486 // section header at index 0.487 if shstrndx == int(SHN_XINDEX) {488 shstrndx = int(link)489 if shstrndx < int(SHN_LORESERVE) {490 return nil, &FormatError{shoff, "invalid ELF shstrndx contained in sh_link", shstrndx}491 }492 }493 }494 495 if shnum > 0 && shentsize < wantShentsize {496 return nil, &FormatError{0, "invalid ELF shentsize", shentsize}497 }498 499 // Read section headers500 c := saferio.SliceCap[Section](uint64(shnum))501 if c < 0 {502 return nil, &FormatError{0, "too many sections", shnum}503 }504 if shnum > 0 && ((1<<64)-1)/uint64(shnum) < uint64(shentsize) {505 return nil, &FormatError{0, "section header overflow", shnum}506 }507 f.Sections = make([]*Section, 0, c)508 names := make([]uint32, 0, c)509 shdata, err := saferio.ReadDataAt(sr, uint64(shnum)*uint64(shentsize), shoff)510 if err != nil {511 return nil, err512 }513 for i := 0; i < shnum; i++ {514 off := uintptr(i) * uintptr(shentsize)515 s := new(Section)516 switch f.Class {517 case ELFCLASS32:518 var sh Section32519 names = append(names, bo.Uint32(shdata[off+unsafe.Offsetof(sh.Name):]))520 s.SectionHeader = SectionHeader{521 Type: SectionType(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Type):])),522 Flags: SectionFlag(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Flags):])),523 Addr: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Addr):])),524 Offset: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Off):])),525 FileSize: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Size):])),526 Link: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Link):]),527 Info: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Info):]),528 Addralign: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Addralign):])),529 Entsize: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Entsize):])),530 }531 case ELFCLASS64:532 var sh Section64533 names = append(names, bo.Uint32(shdata[off+unsafe.Offsetof(sh.Name):]))534 s.SectionHeader = SectionHeader{535 Type: SectionType(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Type):])),536 Flags: SectionFlag(bo.Uint64(shdata[off+unsafe.Offsetof(sh.Flags):])),537 Offset: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Off):]),538 FileSize: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Size):]),539 Addr: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Addr):]),540 Link: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Link):]),541 Info: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Info):]),542 Addralign: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Addralign):]),543 Entsize: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Entsize):]),544 }545 }546 if int64(s.Offset) < 0 {547 return nil, &FormatError{shoff + int64(off), "invalid section offset", int64(s.Offset)}548 }549 if int64(s.FileSize) < 0 {550 return nil, &FormatError{shoff + int64(off), "invalid section size", int64(s.FileSize)}551 }552 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))553 554 if s.Flags&SHF_COMPRESSED == 0 {555 s.ReaderAt = s.sr556 s.Size = s.FileSize557 } else {558 // Read the compression header.559 switch f.Class {560 case ELFCLASS32:561 var ch Chdr32562 chdata := make([]byte, unsafe.Sizeof(ch))563 if _, err := s.sr.ReadAt(chdata, 0); err != nil {564 return nil, err565 }566 s.compressionType = CompressionType(bo.Uint32(chdata[unsafe.Offsetof(ch.Type):]))567 s.Size = uint64(bo.Uint32(chdata[unsafe.Offsetof(ch.Size):]))568 s.Addralign = uint64(bo.Uint32(chdata[unsafe.Offsetof(ch.Addralign):]))569 s.compressionOffset = int64(unsafe.Sizeof(ch))570 case ELFCLASS64:571 var ch Chdr64572 chdata := make([]byte, unsafe.Sizeof(ch))573 if _, err := s.sr.ReadAt(chdata, 0); err != nil {574 return nil, err575 }576 s.compressionType = CompressionType(bo.Uint32(chdata[unsafe.Offsetof(ch.Type):]))577 s.Size = bo.Uint64(chdata[unsafe.Offsetof(ch.Size):])578 s.Addralign = bo.Uint64(chdata[unsafe.Offsetof(ch.Addralign):])579 s.compressionOffset = int64(unsafe.Sizeof(ch))580 }581 }582 583 f.Sections = append(f.Sections, s)584 }585 586 if len(f.Sections) == 0 {587 return f, nil588 }589 590 // Load section header string table.591 if shstrndx == 0 {592 // If the file has no section name string table,593 // shstrndx holds the value SHN_UNDEF (0).594 return f, nil595 }596 shstr := f.Sections[shstrndx]597 if shstr.Type != SHT_STRTAB {598 return nil, &FormatError{shoff + int64(shstrndx*shentsize), "invalid ELF section name string table type", shstr.Type}599 }600 shstrtab, err := shstr.Data()601 if err != nil {602 return nil, err603 }604 for i, s := range f.Sections {605 var ok bool606 s.Name, ok = getString(shstrtab, int(names[i]))607 if !ok {608 return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]}609 }610 }611 612 return f, nil613}614 615// getSymbols returns a slice of Symbols from parsing the symbol table616// with the given type, along with the associated string table.617func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) {618 switch f.Class {619 case ELFCLASS64:620 return f.getSymbols64(typ)621 622 case ELFCLASS32:623 return f.getSymbols32(typ)624 }625 626 return nil, nil, errors.New("not implemented")627}628 629// ErrNoSymbols is returned by [File.Symbols] and [File.DynamicSymbols]630// if there is no such section in the File.631var ErrNoSymbols = errors.New("no symbol section")632 633func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {634 symtabSection := f.SectionByType(typ)635 if symtabSection == nil {636 return nil, nil, ErrNoSymbols637 }638 639 data, err := symtabSection.Data()640 if err != nil {641 return nil, nil, fmt.Errorf("cannot load symbol section: %w", err)642 }643 if len(data) == 0 {644 return nil, nil, ErrNoSymbols645 }646 if len(data)%Sym32Size != 0 {647 return nil, nil, errors.New("length of symbol section is not a multiple of SymSize")648 }649 650 strdata, err := f.stringTable(symtabSection.Link)651 if err != nil {652 return nil, nil, fmt.Errorf("cannot load string table section: %w", err)653 }654 655 // The first entry is all zeros.656 data = data[Sym32Size:]657 658 symbols := make([]Symbol, len(data)/Sym32Size)659 660 i := 0661 var sym Sym32662 for len(data) > 0 {663 sym.Name = f.ByteOrder.Uint32(data[0:4])664 sym.Value = f.ByteOrder.Uint32(data[4:8])665 sym.Size = f.ByteOrder.Uint32(data[8:12])666 sym.Info = data[12]667 sym.Other = data[13]668 sym.Shndx = f.ByteOrder.Uint16(data[14:16])669 str, _ := getString(strdata, int(sym.Name))670 symbols[i].Name = str671 symbols[i].Info = sym.Info672 symbols[i].Other = sym.Other673 symbols[i].Section = SectionIndex(sym.Shndx)674 symbols[i].Value = uint64(sym.Value)675 symbols[i].Size = uint64(sym.Size)676 i++677 data = data[Sym32Size:]678 }679 680 return symbols, strdata, nil681}682 683func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {684 symtabSection := f.SectionByType(typ)685 if symtabSection == nil {686 return nil, nil, ErrNoSymbols687 }688 689 data, err := symtabSection.Data()690 if err != nil {691 return nil, nil, fmt.Errorf("cannot load symbol section: %w", err)692 }693 if len(data) == 0 {694 return nil, nil, ErrNoSymbols695 }696 if len(data)%Sym64Size != 0 {697 return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")698 }699 700 strdata, err := f.stringTable(symtabSection.Link)701 if err != nil {702 return nil, nil, fmt.Errorf("cannot load string table section: %w", err)703 }704 705 // The first entry is all zeros.706 data = data[Sym64Size:]707 708 symbols := make([]Symbol, len(data)/Sym64Size)709 710 i := 0711 var sym Sym64712 for len(data) > 0 {713 sym.Name = f.ByteOrder.Uint32(data[0:4])714 sym.Info = data[4]715 sym.Other = data[5]716 sym.Shndx = f.ByteOrder.Uint16(data[6:8])717 sym.Value = f.ByteOrder.Uint64(data[8:16])718 sym.Size = f.ByteOrder.Uint64(data[16:24])719 str, _ := getString(strdata, int(sym.Name))720 symbols[i].Name = str721 symbols[i].Info = sym.Info722 symbols[i].Other = sym.Other723 symbols[i].Section = SectionIndex(sym.Shndx)724 symbols[i].Value = sym.Value725 symbols[i].Size = sym.Size726 i++727 data = data[Sym64Size:]728 }729 730 return symbols, strdata, nil731}732 733// getString extracts a string from an ELF string table.734func getString(section []byte, start int) (string, bool) {735 if start < 0 || start >= len(section) {736 return "", false737 }738 739 for end := start; end < len(section); end++ {740 if section[end] == 0 {741 return string(section[start:end]), true742 }743 }744 return "", false745}746 747// Section returns a section with the given name, or nil if no such748// section exists.749func (f *File) Section(name string) *Section {750 for _, s := range f.Sections {751 if s.Name == name {752 return s753 }754 }755 return nil756}757 758// applyRelocations applies relocations to dst. rels is a relocations section759// in REL or RELA format.760func (f *File) applyRelocations(dst []byte, rels []byte) error {761 switch {762 case f.Class == ELFCLASS64 && f.Machine == EM_X86_64:763 return f.applyRelocationsAMD64(dst, rels)764 case f.Class == ELFCLASS32 && f.Machine == EM_386:765 return f.applyRelocations386(dst, rels)766 case f.Class == ELFCLASS32 && f.Machine == EM_ARM:767 return f.applyRelocationsARM(dst, rels)768 case f.Class == ELFCLASS64 && f.Machine == EM_AARCH64:769 return f.applyRelocationsARM64(dst, rels)770 case f.Class == ELFCLASS32 && f.Machine == EM_PPC:771 return f.applyRelocationsPPC(dst, rels)772 case f.Class == ELFCLASS64 && f.Machine == EM_PPC64:773 return f.applyRelocationsPPC64(dst, rels)774 case f.Class == ELFCLASS32 && f.Machine == EM_MIPS:775 return f.applyRelocationsMIPS(dst, rels)776 case f.Class == ELFCLASS64 && f.Machine == EM_MIPS:777 return f.applyRelocationsMIPS64(dst, rels)778 case f.Class == ELFCLASS64 && f.Machine == EM_LOONGARCH:779 return f.applyRelocationsLOONG64(dst, rels)780 case f.Class == ELFCLASS64 && f.Machine == EM_RISCV:781 return f.applyRelocationsRISCV64(dst, rels)782 case f.Class == ELFCLASS64 && f.Machine == EM_S390:783 return f.applyRelocationss390x(dst, rels)784 case f.Class == ELFCLASS64 && f.Machine == EM_SPARCV9:785 return f.applyRelocationsSPARC64(dst, rels)786 default:787 return errors.New("applyRelocations: not implemented")788 }789}790 791// canApplyRelocation reports whether we should try to apply a792// relocation to a DWARF data section, given a pointer to the symbol793// targeted by the relocation.794// Most relocations in DWARF data tend to be section-relative, but795// some target non-section symbols (for example, low_PC attrs on796// subprogram or compilation unit DIEs that target function symbols).797func canApplyRelocation(sym *Symbol) bool {798 return sym.Section != SHN_UNDEF && sym.Section < SHN_LORESERVE799}800 801func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {802 // 24 is the size of Rela64.803 if len(rels)%24 != 0 {804 return errors.New("length of relocation section is not a multiple of 24")805 }806 807 symbols, _, err := f.getSymbols(SHT_SYMTAB)808 if err != nil {809 return err810 }811 812 b := bytes.NewReader(rels)813 var rela Rela64814 815 for b.Len() > 0 {816 binary.Read(b, f.ByteOrder, &rela)817 symNo := rela.Info >> 32818 t := R_X86_64(rela.Info & 0xffff)819 820 if symNo == 0 || symNo > uint64(len(symbols)) {821 continue822 }823 sym := &symbols[symNo-1]824 if !canApplyRelocation(sym) {825 continue826 }827 828 // There are relocations, so this must be a normal829 // object file. The code below handles only basic relocations830 // of the form S + A (symbol plus addend).831 832 switch t {833 case R_X86_64_64:834 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)835 case R_X86_64_32:836 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)837 }838 }839 840 return nil841}842 843func (f *File) applyRelocations386(dst []byte, rels []byte) error {844 // 8 is the size of Rel32.845 if len(rels)%8 != 0 {846 return errors.New("length of relocation section is not a multiple of 8")847 }848 849 symbols, _, err := f.getSymbols(SHT_SYMTAB)850 if err != nil {851 return err852 }853 854 b := bytes.NewReader(rels)855 var rel Rel32856 857 for b.Len() > 0 {858 binary.Read(b, f.ByteOrder, &rel)859 symNo := rel.Info >> 8860 t := R_386(rel.Info & 0xff)861 862 if symNo == 0 || symNo > uint32(len(symbols)) {863 continue864 }865 sym := &symbols[symNo-1]866 867 if t == R_386_32 {868 putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)869 }870 }871 872 return nil873}874 875func (f *File) applyRelocationsARM(dst []byte, rels []byte) error {876 // 8 is the size of Rel32.877 if len(rels)%8 != 0 {878 return errors.New("length of relocation section is not a multiple of 8")879 }880 881 symbols, _, err := f.getSymbols(SHT_SYMTAB)882 if err != nil {883 return err884 }885 886 b := bytes.NewReader(rels)887 var rel Rel32888 889 for b.Len() > 0 {890 binary.Read(b, f.ByteOrder, &rel)891 symNo := rel.Info >> 8892 t := R_ARM(rel.Info & 0xff)893 894 if symNo == 0 || symNo > uint32(len(symbols)) {895 continue896 }897 sym := &symbols[symNo-1]898 899 switch t {900 case R_ARM_ABS32:901 putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)902 }903 }904 905 return nil906}907 908func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {909 // 24 is the size of Rela64.910 if len(rels)%24 != 0 {911 return errors.New("length of relocation section is not a multiple of 24")912 }913 914 symbols, _, err := f.getSymbols(SHT_SYMTAB)915 if err != nil {916 return err917 }918 919 b := bytes.NewReader(rels)920 var rela Rela64921 922 for b.Len() > 0 {923 binary.Read(b, f.ByteOrder, &rela)924 symNo := rela.Info >> 32925 t := R_AARCH64(rela.Info & 0xffff)926 927 if symNo == 0 || symNo > uint64(len(symbols)) {928 continue929 }930 sym := &symbols[symNo-1]931 if !canApplyRelocation(sym) {932 continue933 }934 935 // There are relocations, so this must be a normal936 // object file. The code below handles only basic relocations937 // of the form S + A (symbol plus addend).938 939 switch t {940 case R_AARCH64_ABS64:941 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)942 case R_AARCH64_ABS32:943 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)944 }945 }946 947 return nil948}949 950func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {951 // 12 is the size of Rela32.952 if len(rels)%12 != 0 {953 return errors.New("length of relocation section is not a multiple of 12")954 }955 956 symbols, _, err := f.getSymbols(SHT_SYMTAB)957 if err != nil {958 return err959 }960 961 b := bytes.NewReader(rels)962 var rela Rela32963 964 for b.Len() > 0 {965 binary.Read(b, f.ByteOrder, &rela)966 symNo := rela.Info >> 8967 t := R_PPC(rela.Info & 0xff)968 969 if symNo == 0 || symNo > uint32(len(symbols)) {970 continue971 }972 sym := &symbols[symNo-1]973 if !canApplyRelocation(sym) {974 continue975 }976 977 switch t {978 case R_PPC_ADDR32:979 putUint(f.ByteOrder, dst, uint64(rela.Off), 4, sym.Value, 0, false)980 }981 }982 983 return nil984}985 986func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {987 // 24 is the size of Rela64.988 if len(rels)%24 != 0 {989 return errors.New("length of relocation section is not a multiple of 24")990 }991 992 symbols, _, err := f.getSymbols(SHT_SYMTAB)993 if err != nil {994 return err995 }996 997 b := bytes.NewReader(rels)998 var rela Rela64999 1000 for b.Len() > 0 {1001 binary.Read(b, f.ByteOrder, &rela)1002 symNo := rela.Info >> 321003 t := R_PPC64(rela.Info & 0xffff)1004 1005 if symNo == 0 || symNo > uint64(len(symbols)) {1006 continue1007 }1008 sym := &symbols[symNo-1]1009 if !canApplyRelocation(sym) {1010 continue1011 }1012 1013 switch t {1014 case R_PPC64_ADDR64:1015 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1016 case R_PPC64_ADDR32:1017 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1018 }1019 }1020 1021 return nil1022}1023 1024func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error {1025 // 8 is the size of Rel32.1026 if len(rels)%8 != 0 {1027 return errors.New("length of relocation section is not a multiple of 8")1028 }1029 1030 symbols, _, err := f.getSymbols(SHT_SYMTAB)1031 if err != nil {1032 return err1033 }1034 1035 b := bytes.NewReader(rels)1036 var rel Rel321037 1038 for b.Len() > 0 {1039 binary.Read(b, f.ByteOrder, &rel)1040 symNo := rel.Info >> 81041 t := R_MIPS(rel.Info & 0xff)1042 1043 if symNo == 0 || symNo > uint32(len(symbols)) {1044 continue1045 }1046 sym := &symbols[symNo-1]1047 1048 switch t {1049 case R_MIPS_32:1050 putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)1051 }1052 }1053 1054 return nil1055}1056 1057func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {1058 // 24 is the size of Rela64.1059 if len(rels)%24 != 0 {1060 return errors.New("length of relocation section is not a multiple of 24")1061 }1062 1063 symbols, _, err := f.getSymbols(SHT_SYMTAB)1064 if err != nil {1065 return err1066 }1067 1068 b := bytes.NewReader(rels)1069 var rela Rela641070 1071 for b.Len() > 0 {1072 binary.Read(b, f.ByteOrder, &rela)1073 var symNo uint641074 var t R_MIPS1075 if f.ByteOrder == binary.BigEndian {1076 symNo = rela.Info >> 321077 t = R_MIPS(rela.Info & 0xff)1078 } else {1079 symNo = rela.Info & 0xffffffff1080 t = R_MIPS(rela.Info >> 56)1081 }1082 1083 if symNo == 0 || symNo > uint64(len(symbols)) {1084 continue1085 }1086 sym := &symbols[symNo-1]1087 if !canApplyRelocation(sym) {1088 continue1089 }1090 1091 switch t {1092 case R_MIPS_64:1093 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1094 case R_MIPS_32:1095 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1096 }1097 }1098 1099 return nil1100}1101 1102func (f *File) applyRelocationsLOONG64(dst []byte, rels []byte) error {1103 // 24 is the size of Rela64.1104 if len(rels)%24 != 0 {1105 return errors.New("length of relocation section is not a multiple of 24")1106 }1107 1108 symbols, _, err := f.getSymbols(SHT_SYMTAB)1109 if err != nil {1110 return err1111 }1112 1113 b := bytes.NewReader(rels)1114 var rela Rela641115 1116 for b.Len() > 0 {1117 binary.Read(b, f.ByteOrder, &rela)1118 var symNo uint641119 var t R_LARCH1120 symNo = rela.Info >> 321121 t = R_LARCH(rela.Info & 0xffff)1122 1123 if symNo == 0 || symNo > uint64(len(symbols)) {1124 continue1125 }1126 sym := &symbols[symNo-1]1127 if !canApplyRelocation(sym) {1128 continue1129 }1130 1131 switch t {1132 case R_LARCH_64:1133 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1134 case R_LARCH_32:1135 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1136 }1137 }1138 1139 return nil1140}1141 1142func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {1143 // 24 is the size of Rela64.1144 if len(rels)%24 != 0 {1145 return errors.New("length of relocation section is not a multiple of 24")1146 }1147 1148 symbols, _, err := f.getSymbols(SHT_SYMTAB)1149 if err != nil {1150 return err1151 }1152 1153 b := bytes.NewReader(rels)1154 var rela Rela641155 1156 for b.Len() > 0 {1157 binary.Read(b, f.ByteOrder, &rela)1158 symNo := rela.Info >> 321159 t := R_RISCV(rela.Info & 0xffff)1160 1161 if symNo == 0 || symNo > uint64(len(symbols)) {1162 continue1163 }1164 sym := &symbols[symNo-1]1165 if !canApplyRelocation(sym) {1166 continue1167 }1168 1169 switch t {1170 case R_RISCV_64:1171 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1172 case R_RISCV_32:1173 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1174 }1175 }1176 1177 return nil1178}1179 1180func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {1181 // 24 is the size of Rela64.1182 if len(rels)%24 != 0 {1183 return errors.New("length of relocation section is not a multiple of 24")1184 }1185 1186 symbols, _, err := f.getSymbols(SHT_SYMTAB)1187 if err != nil {1188 return err1189 }1190 1191 b := bytes.NewReader(rels)1192 var rela Rela641193 1194 for b.Len() > 0 {1195 binary.Read(b, f.ByteOrder, &rela)1196 symNo := rela.Info >> 321197 t := R_390(rela.Info & 0xffff)1198 1199 if symNo == 0 || symNo > uint64(len(symbols)) {1200 continue