Use of unsafe package detected; ensure it’s necessary, justified in comments, and bounds-checked to avoid memory corruption
data := make([]byte, unsafe.Sizeof(hdr))
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.45/*6Package elf implements access to ELF object files.78# Security910This 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 elf1718import (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)3334// TODO: error reporting detail3536/*37 * Internal ELF representation38 */3940// 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}5253// 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}6364// 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 uint647677 // 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}8384// A Section represents a single section in an ELF file.85type Section struct {86 SectionHeader8788 // 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.SectionReader100101 compressionType CompressionType102 compressionOffset int64103}104105// 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}113114// 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}122123// 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 }133134 var zrd func(io.Reader) (io.ReadCloser, error)135 if s.Flags&SHF_COMPRESSED == 0 {136137 if !strings.HasPrefix(s.Name, ".zdebug") {138 return io.NewSectionReader(s.sr, 0, 1<<63-1)139 }140141 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 }146147 s.compressionOffset = 12148 s.compressionType = COMPRESS_ZLIB149 s.Size = binary.BigEndian.Uint64(b[4:12])150 zrd = zlib.NewReader151152 } 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 }156157 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 }165166 if zrd == nil {167 return errorReader{&FormatError{int64(s.Offset), "unknown compression type", s.compressionType}}168 }169170 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}178179// 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}190191// A Prog represents a single ELF program header in an ELF binary.192type Prog struct {193 ProgHeader194195 // 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}204205// 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) }207208// A Symbol represents an entry in an ELF symbol table section.209type Symbol struct {210 Name string211 Info, Other byte212213 // 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 VersionIndex220221 Section SectionIndex222 Value, Size uint64223224 // These fields are present only for the dynamic symbol table.225 Version string226 Library string227}228229/*230 * ELF reader231 */232233type FormatError struct {234 off int64235 msg string236 val any237}238239func (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}247248// 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}262263// 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}274275// 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}285286// 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, &FormatError{0, "cannot read ELF identifier", err}294 }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 }298299 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 }308309 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 = bo320321 f.Version = Version(ident[EI_VERSION])322 if f.Version != EV_CURRENT {323 return nil, &FormatError{0, "unknown ELF version", f.Version}324 }325326 f.OSABI = OSABI(ident[EI_OSABI])327 f.ABIVersion = ident[EI_ABIVERSION]328329 // 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 }374375 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 }381382 if shoff == 0 && shnum != 0 {383 return nil, &FormatError{0, "invalid ELF shnum for shoff=0", shnum}384 }385386 if shnum > 0 && shstrndx >= shnum {387 return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}388 }389390 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 }402403 // If the number of sections is greater than or equal to SHN_LORESERVE404 // (0xff00), shnum has the value zero and the actual number of section405 // header table entries is contained in the sh_size field of the section406 // header at index 0.407 //408 // If the number of segments is greater than or equal to 0xffff,409 // phnum has the value 0xffff, and the actual number of segments410 // is contained in the sh_info field of the section header at411 // index 0.412 const pnXnum = 0xffff413 if shoff > 0 && (shnum == 0 || phnum == pnXnum) {414 var typ, link, info uint32415 var size uint64416 sr.Seek(shoff, io.SeekStart)417 switch f.Class {418 case ELFCLASS32:419 sh := new(Section32)420 if err := binary.Read(sr, bo, sh); err != nil {421 return nil, err422 }423 size = uint64(sh.Size)424 typ = sh.Type425 link = sh.Link426 info = sh.Info427 case ELFCLASS64:428 sh := new(Section64)429 if err := binary.Read(sr, bo, sh); err != nil {430 return nil, err431 }432 size = sh.Size433 typ = sh.Type434 link = sh.Link435 info = sh.Info436 }437438 if SectionType(typ) != SHT_NULL {439 return nil, &FormatError{shoff, "invalid type of the initial section", SectionType(typ)}440 }441442 if shnum == 0 {443 if size < uint64(SHN_LORESERVE) {444 return nil, &FormatError{shoff, "invalid ELF shnum contained in sh_size", shnum}445 }446 shnum = int(size)447 }448449 if phnum == pnXnum {450 if info < 0xffff {451 return nil, &FormatError{shoff, "invalid ELF phnum contained in sh_info", info}452 }453 phnum = int(info)454 }455456 // If the section name string table section index is greater than or457 // equal to SHN_LORESERVE (0xff00), this member has the value458 // SHN_XINDEX (0xffff) and the actual index of the section name459 // string table section is contained in the sh_link field of the460 // section header at index 0.461 if shstrndx == int(SHN_XINDEX) {462 shstrndx = int(link)463 if shstrndx < int(SHN_LORESERVE) || shstrndx >= shnum {464 return nil, &FormatError{shoff, "invalid ELF shstrndx contained in sh_link", shstrndx}465 }466 }467 }468469 // Read program headers470 c := saferio.SliceCap[*Prog](uint64(phnum))471 if c < 0 {472 return nil, &FormatError{0, "too many segments", phnum}473 }474 if phnum > 0 && ((1<<64)-1)/uint64(phnum) < uint64(phentsize) {475 return nil, &FormatError{0, "segment header overflow", phnum}476 }477 f.Progs = make([]*Prog, 0, c)478 phdata, err := saferio.ReadDataAt(sr, uint64(phnum)*uint64(phentsize), phoff)479 if err != nil {480 return nil, err481 }482 for i := 0; i < phnum; i++ {483 off := uintptr(i) * uintptr(phentsize)484 p := new(Prog)485 switch f.Class {486 case ELFCLASS32:487 var ph Prog32488 p.ProgHeader = ProgHeader{489 Type: ProgType(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Type):])),490 Flags: ProgFlag(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Flags):])),491 Off: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Off):])),492 Vaddr: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Vaddr):])),493 Paddr: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Paddr):])),494 Filesz: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Filesz):])),495 Memsz: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Memsz):])),496 Align: uint64(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Align):])),497 }498 case ELFCLASS64:499 var ph Prog64500 p.ProgHeader = ProgHeader{501 Type: ProgType(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Type):])),502 Flags: ProgFlag(bo.Uint32(phdata[off+unsafe.Offsetof(ph.Flags):])),503 Off: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Off):]),504 Vaddr: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Vaddr):]),505 Paddr: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Paddr):]),506 Filesz: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Filesz):]),507 Memsz: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Memsz):]),508 Align: bo.Uint64(phdata[off+unsafe.Offsetof(ph.Align):]),509 }510 }511 if int64(p.Off) < 0 {512 return nil, &FormatError{phoff + int64(off), "invalid program header offset", p.Off}513 }514 if int64(p.Filesz) < 0 {515 return nil, &FormatError{phoff + int64(off), "invalid program header file size", p.Filesz}516 }517 p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))518 p.ReaderAt = p.sr519 f.Progs = append(f.Progs, p)520 }521522 if shnum > 0 && shentsize < wantShentsize {523 return nil, &FormatError{0, "invalid ELF shentsize", shentsize}524 }525526 // Read section headers527 c = saferio.SliceCap[Section](uint64(shnum))528 if c < 0 {529 return nil, &FormatError{0, "too many sections", shnum}530 }531 if shnum > 0 && ((1<<64)-1)/uint64(shnum) < uint64(shentsize) {532 return nil, &FormatError{0, "section header overflow", shnum}533 }534 f.Sections = make([]*Section, 0, c)535 names := make([]uint32, 0, c)536 shdata, err := saferio.ReadDataAt(sr, uint64(shnum)*uint64(shentsize), shoff)537 if err != nil {538 return nil, err539 }540 for i := 0; i < shnum; i++ {541 off := uintptr(i) * uintptr(shentsize)542 s := new(Section)543 switch f.Class {544 case ELFCLASS32:545 var sh Section32546 names = append(names, bo.Uint32(shdata[off+unsafe.Offsetof(sh.Name):]))547 s.SectionHeader = SectionHeader{548 Type: SectionType(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Type):])),549 Flags: SectionFlag(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Flags):])),550 Addr: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Addr):])),551 Offset: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Off):])),552 FileSize: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Size):])),553 Link: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Link):]),554 Info: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Info):]),555 Addralign: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Addralign):])),556 Entsize: uint64(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Entsize):])),557 }558 case ELFCLASS64:559 var sh Section64560 names = append(names, bo.Uint32(shdata[off+unsafe.Offsetof(sh.Name):]))561 s.SectionHeader = SectionHeader{562 Type: SectionType(bo.Uint32(shdata[off+unsafe.Offsetof(sh.Type):])),563 Flags: SectionFlag(bo.Uint64(shdata[off+unsafe.Offsetof(sh.Flags):])),564 Offset: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Off):]),565 FileSize: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Size):]),566 Addr: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Addr):]),567 Link: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Link):]),568 Info: bo.Uint32(shdata[off+unsafe.Offsetof(sh.Info):]),569 Addralign: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Addralign):]),570 Entsize: bo.Uint64(shdata[off+unsafe.Offsetof(sh.Entsize):]),571 }572 }573 if int64(s.Offset) < 0 {574 return nil, &FormatError{shoff + int64(off), "invalid section offset", int64(s.Offset)}575 }576 if int64(s.FileSize) < 0 {577 return nil, &FormatError{shoff + int64(off), "invalid section size", int64(s.FileSize)}578 }579 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))580581 if s.Flags&SHF_COMPRESSED == 0 {582 s.ReaderAt = s.sr583 s.Size = s.FileSize584 } else {585 // Read the compression header.586 switch f.Class {587 case ELFCLASS32:588 var ch Chdr32589 chdata := make([]byte, unsafe.Sizeof(ch))590 if _, err := s.sr.ReadAt(chdata, 0); err != nil {591 return nil, err592 }593 s.compressionType = CompressionType(bo.Uint32(chdata[unsafe.Offsetof(ch.Type):]))594 s.Size = uint64(bo.Uint32(chdata[unsafe.Offsetof(ch.Size):]))595 s.Addralign = uint64(bo.Uint32(chdata[unsafe.Offsetof(ch.Addralign):]))596 s.compressionOffset = int64(unsafe.Sizeof(ch))597 case ELFCLASS64:598 var ch Chdr64599 chdata := make([]byte, unsafe.Sizeof(ch))600 if _, err := s.sr.ReadAt(chdata, 0); err != nil {601 return nil, err602 }603 s.compressionType = CompressionType(bo.Uint32(chdata[unsafe.Offsetof(ch.Type):]))604 s.Size = bo.Uint64(chdata[unsafe.Offsetof(ch.Size):])605 s.Addralign = bo.Uint64(chdata[unsafe.Offsetof(ch.Addralign):])606 s.compressionOffset = int64(unsafe.Sizeof(ch))607 }608 }609610 f.Sections = append(f.Sections, s)611 }612613 if len(f.Sections) == 0 {614 return f, nil615 }616617 // Load section header string table.618 if shstrndx == 0 {619 // If the file has no section name string table,620 // shstrndx holds the value SHN_UNDEF (0).621 return f, nil622 }623 shstr := f.Sections[shstrndx]624 if shstr.Type != SHT_STRTAB {625 return nil, &FormatError{shoff + int64(shstrndx*shentsize), "invalid ELF section name string table type", shstr.Type}626 }627 shstrtab, err := shstr.Data()628 if err != nil {629 return nil, err630 }631 for i, s := range f.Sections {632 var ok bool633 s.Name, ok = getString(shstrtab, int(names[i]))634 if !ok {635 return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]}636 }637 }638639 return f, nil640}641642// getSymbols returns a slice of Symbols from parsing the symbol table643// with the given type, along with the associated string table.644func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) {645 switch f.Class {646 case ELFCLASS64:647 return f.getSymbols64(typ)648649 case ELFCLASS32:650 return f.getSymbols32(typ)651 }652653 return nil, nil, errors.New("not implemented")654}655656// ErrNoSymbols is returned by [File.Symbols] and [File.DynamicSymbols]657// if there is no such section in the File.658var ErrNoSymbols = errors.New("no symbol section")659660func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {661 symtabSection := f.SectionByType(typ)662 if symtabSection == nil {663 return nil, nil, ErrNoSymbols664 }665666 data, err := symtabSection.Data()667 if err != nil {668 return nil, nil, fmt.Errorf("cannot load symbol section: %w", err)669 }670 if len(data) == 0 {671 return nil, nil, ErrNoSymbols672 }673 if len(data)%Sym32Size != 0 {674 return nil, nil, errors.New("length of symbol section is not a multiple of SymSize")675 }676677 strdata, err := f.stringTable(symtabSection.Link)678 if err != nil {679 return nil, nil, fmt.Errorf("cannot load string table section: %w", err)680 }681682 // The first entry is all zeros.683 data = data[Sym32Size:]684685 symbols := make([]Symbol, len(data)/Sym32Size)686687 i := 0688 var sym Sym32689 for len(data) > 0 {690 sym.Name = f.ByteOrder.Uint32(data[0:4])691 sym.Value = f.ByteOrder.Uint32(data[4:8])692 sym.Size = f.ByteOrder.Uint32(data[8:12])693 sym.Info = data[12]694 sym.Other = data[13]695 sym.Shndx = f.ByteOrder.Uint16(data[14:16])696 str, _ := getString(strdata, int(sym.Name))697 symbols[i].Name = str698 symbols[i].Info = sym.Info699 symbols[i].Other = sym.Other700 symbols[i].Section = SectionIndex(sym.Shndx)701 symbols[i].Value = uint64(sym.Value)702 symbols[i].Size = uint64(sym.Size)703 i++704 data = data[Sym32Size:]705 }706707 return symbols, strdata, nil708}709710func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {711 symtabSection := f.SectionByType(typ)712 if symtabSection == nil {713 return nil, nil, ErrNoSymbols714 }715716 data, err := symtabSection.Data()717 if err != nil {718 return nil, nil, fmt.Errorf("cannot load symbol section: %w", err)719 }720 if len(data) == 0 {721 return nil, nil, ErrNoSymbols722 }723 if len(data)%Sym64Size != 0 {724 return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")725 }726727 strdata, err := f.stringTable(symtabSection.Link)728 if err != nil {729 return nil, nil, fmt.Errorf("cannot load string table section: %w", err)730 }731732 // The first entry is all zeros.733 data = data[Sym64Size:]734735 symbols := make([]Symbol, len(data)/Sym64Size)736737 i := 0738 var sym Sym64739 for len(data) > 0 {740 sym.Name = f.ByteOrder.Uint32(data[0:4])741 sym.Info = data[4]742 sym.Other = data[5]743 sym.Shndx = f.ByteOrder.Uint16(data[6:8])744 sym.Value = f.ByteOrder.Uint64(data[8:16])745 sym.Size = f.ByteOrder.Uint64(data[16:24])746 str, _ := getString(strdata, int(sym.Name))747 symbols[i].Name = str748 symbols[i].Info = sym.Info749 symbols[i].Other = sym.Other750 symbols[i].Section = SectionIndex(sym.Shndx)751 symbols[i].Value = sym.Value752 symbols[i].Size = sym.Size753 i++754 data = data[Sym64Size:]755 }756757 return symbols, strdata, nil758}759760// getString extracts a string from an ELF string table.761func getString(section []byte, start int) (string, bool) {762 if start < 0 || start >= len(section) {763 return "", false764 }765766 end := bytes.IndexByte(section[start:], 0)767 if end < 0 {768 return "", false769 }770 return string(section[start : start+end]), true771}772773// Section returns a section with the given name, or nil if no such774// section exists.775func (f *File) Section(name string) *Section {776 for _, s := range f.Sections {777 if s.Name == name {778 return s779 }780 }781 return nil782}783784// applyRelocations applies relocations to dst. rels is a relocations section785// in REL or RELA format.786func (f *File) applyRelocations(dst []byte, rels []byte) error {787 switch {788 case f.Class == ELFCLASS64 && f.Machine == EM_X86_64:789 return f.applyRelocationsAMD64(dst, rels)790 case f.Class == ELFCLASS32 && f.Machine == EM_386:791 return f.applyRelocations386(dst, rels)792 case f.Class == ELFCLASS32 && f.Machine == EM_ARM:793 return f.applyRelocationsARM(dst, rels)794 case f.Class == ELFCLASS64 && f.Machine == EM_AARCH64:795 return f.applyRelocationsARM64(dst, rels)796 case f.Class == ELFCLASS32 && f.Machine == EM_PPC:797 return f.applyRelocationsPPC(dst, rels)798 case f.Class == ELFCLASS64 && f.Machine == EM_PPC64:799 return f.applyRelocationsPPC64(dst, rels)800 case f.Class == ELFCLASS32 && f.Machine == EM_MIPS:801 return f.applyRelocationsMIPS(dst, rels)802 case f.Class == ELFCLASS64 && f.Machine == EM_MIPS:803 return f.applyRelocationsMIPS64(dst, rels)804 case f.Class == ELFCLASS64 && f.Machine == EM_LOONGARCH:805 return f.applyRelocationsLOONG64(dst, rels)806 case f.Class == ELFCLASS64 && f.Machine == EM_RISCV:807 return f.applyRelocationsRISCV64(dst, rels)808 case f.Class == ELFCLASS64 && f.Machine == EM_S390:809 return f.applyRelocationss390x(dst, rels)810 case f.Class == ELFCLASS64 && f.Machine == EM_SPARCV9:811 return f.applyRelocationsSPARC64(dst, rels)812 default:813 return errors.New("applyRelocations: not implemented")814 }815}816817// canApplyRelocation reports whether we should try to apply a818// relocation to a DWARF data section, given a pointer to the symbol819// targeted by the relocation.820// Most relocations in DWARF data tend to be section-relative, but821// some target non-section symbols (for example, low_PC attrs on822// subprogram or compilation unit DIEs that target function symbols).823func canApplyRelocation(sym *Symbol) bool {824 return sym.Section != SHN_UNDEF && sym.Section < SHN_LORESERVE825}826827func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {828 // 24 is the size of Rela64.829 if len(rels)%24 != 0 {830 return errors.New("length of relocation section is not a multiple of 24")831 }832833 symbols, _, err := f.getSymbols(SHT_SYMTAB)834 if err != nil {835 return err836 }837838 b := bytes.NewReader(rels)839 var rela Rela64840841 for b.Len() > 0 {842 binary.Read(b, f.ByteOrder, &rela)843 symNo := rela.Info >> 32844 t := R_X86_64(rela.Info & 0xffff)845846 if symNo == 0 || symNo > uint64(len(symbols)) {847 continue848 }849 sym := &symbols[symNo-1]850 if !canApplyRelocation(sym) {851 continue852 }853854 // There are relocations, so this must be a normal855 // object file. The code below handles only basic relocations856 // of the form S + A (symbol plus addend).857858 switch t {859 case R_X86_64_64:860 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)861 case R_X86_64_32:862 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)863 }864 }865866 return nil867}868869func (f *File) applyRelocations386(dst []byte, rels []byte) error {870 // 8 is the size of Rel32.871 if len(rels)%8 != 0 {872 return errors.New("length of relocation section is not a multiple of 8")873 }874875 symbols, _, err := f.getSymbols(SHT_SYMTAB)876 if err != nil {877 return err878 }879880 b := bytes.NewReader(rels)881 var rel Rel32882883 for b.Len() > 0 {884 binary.Read(b, f.ByteOrder, &rel)885 symNo := rel.Info >> 8886 t := R_386(rel.Info & 0xff)887888 if symNo == 0 || symNo > uint32(len(symbols)) {889 continue890 }891 sym := &symbols[symNo-1]892893 if t == R_386_32 {894 putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)895 }896 }897898 return nil899}900901func (f *File) applyRelocationsARM(dst []byte, rels []byte) error {902 // 8 is the size of Rel32.903 if len(rels)%8 != 0 {904 return errors.New("length of relocation section is not a multiple of 8")905 }906907 symbols, _, err := f.getSymbols(SHT_SYMTAB)908 if err != nil {909 return err910 }911912 b := bytes.NewReader(rels)913 var rel Rel32914915 for b.Len() > 0 {916 binary.Read(b, f.ByteOrder, &rel)917 symNo := rel.Info >> 8918 t := R_ARM(rel.Info & 0xff)919920 if symNo == 0 || symNo > uint32(len(symbols)) {921 continue922 }923 sym := &symbols[symNo-1]924925 switch t {926 case R_ARM_ABS32:927 putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)928 }929 }930931 return nil932}933934func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {935 // 24 is the size of Rela64.936 if len(rels)%24 != 0 {937 return errors.New("length of relocation section is not a multiple of 24")938 }939940 symbols, _, err := f.getSymbols(SHT_SYMTAB)941 if err != nil {942 return err943 }944945 b := bytes.NewReader(rels)946 var rela Rela64947948 for b.Len() > 0 {949 binary.Read(b, f.ByteOrder, &rela)950 symNo := rela.Info >> 32951 t := R_AARCH64(rela.Info & 0xffff)952953 if symNo == 0 || symNo > uint64(len(symbols)) {954 continue955 }956 sym := &symbols[symNo-1]957 if !canApplyRelocation(sym) {958 continue959 }960961 // There are relocations, so this must be a normal962 // object file. The code below handles only basic relocations963 // of the form S + A (symbol plus addend).964965 switch t {966 case R_AARCH64_ABS64:967 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)968 case R_AARCH64_ABS32:969 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)970 }971 }972973 return nil974}975976func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {977 // 12 is the size of Rela32.978 if len(rels)%12 != 0 {979 return errors.New("length of relocation section is not a multiple of 12")980 }981982 symbols, _, err := f.getSymbols(SHT_SYMTAB)983 if err != nil {984 return err985 }986987 b := bytes.NewReader(rels)988 var rela Rela32989990 for b.Len() > 0 {991 binary.Read(b, f.ByteOrder, &rela)992 symNo := rela.Info >> 8993 t := R_PPC(rela.Info & 0xff)994995 if symNo == 0 || symNo > uint32(len(symbols)) {996 continue997 }998 sym := &symbols[symNo-1]999 if !canApplyRelocation(sym) {1000 continue1001 }10021003 switch t {1004 case R_PPC_ADDR32:1005 putUint(f.ByteOrder, dst, uint64(rela.Off), 4, sym.Value, 0, false)1006 }1007 }10081009 return nil1010}10111012func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {1013 // 24 is the size of Rela64.1014 if len(rels)%24 != 0 {1015 return errors.New("length of relocation section is not a multiple of 24")1016 }10171018 symbols, _, err := f.getSymbols(SHT_SYMTAB)1019 if err != nil {1020 return err1021 }10221023 b := bytes.NewReader(rels)1024 var rela Rela6410251026 for b.Len() > 0 {1027 binary.Read(b, f.ByteOrder, &rela)1028 symNo := rela.Info >> 321029 t := R_PPC64(rela.Info & 0xffff)10301031 if symNo == 0 || symNo > uint64(len(symbols)) {1032 continue1033 }1034 sym := &symbols[symNo-1]1035 if !canApplyRelocation(sym) {1036 continue1037 }10381039 switch t {1040 case R_PPC64_ADDR64:1041 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1042 case R_PPC64_ADDR32:1043 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1044 }1045 }10461047 return nil1048}10491050func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error {1051 // 8 is the size of Rel32.1052 if len(rels)%8 != 0 {1053 return errors.New("length of relocation section is not a multiple of 8")1054 }10551056 symbols, _, err := f.getSymbols(SHT_SYMTAB)1057 if err != nil {1058 return err1059 }10601061 b := bytes.NewReader(rels)1062 var rel Rel3210631064 for b.Len() > 0 {1065 binary.Read(b, f.ByteOrder, &rel)1066 symNo := rel.Info >> 81067 t := R_MIPS(rel.Info & 0xff)10681069 if symNo == 0 || symNo > uint32(len(symbols)) {1070 continue1071 }1072 sym := &symbols[symNo-1]10731074 switch t {1075 case R_MIPS_32:1076 putUint(f.ByteOrder, dst, uint64(rel.Off), 4, sym.Value, 0, true)1077 }1078 }10791080 return nil1081}10821083func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {1084 // 24 is the size of Rela64.1085 if len(rels)%24 != 0 {1086 return errors.New("length of relocation section is not a multiple of 24")1087 }10881089 symbols, _, err := f.getSymbols(SHT_SYMTAB)1090 if err != nil {1091 return err1092 }10931094 b := bytes.NewReader(rels)1095 var rela Rela6410961097 for b.Len() > 0 {1098 binary.Read(b, f.ByteOrder, &rela)1099 var symNo uint641100 var t R_MIPS1101 if f.ByteOrder == binary.BigEndian {1102 symNo = rela.Info >> 321103 t = R_MIPS(rela.Info & 0xff)1104 } else {1105 symNo = rela.Info & 0xffffffff1106 t = R_MIPS(rela.Info >> 56)1107 }11081109 if symNo == 0 || symNo > uint64(len(symbols)) {1110 continue1111 }1112 sym := &symbols[symNo-1]1113 if !canApplyRelocation(sym) {1114 continue1115 }11161117 switch t {1118 case R_MIPS_64:1119 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1120 case R_MIPS_32:1121 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1122 }1123 }11241125 return nil1126}11271128func (f *File) applyRelocationsLOONG64(dst []byte, rels []byte) error {1129 // 24 is the size of Rela64.1130 if len(rels)%24 != 0 {1131 return errors.New("length of relocation section is not a multiple of 24")1132 }11331134 symbols, _, err := f.getSymbols(SHT_SYMTAB)1135 if err != nil {1136 return err1137 }11381139 b := bytes.NewReader(rels)1140 var rela Rela6411411142 for b.Len() > 0 {1143 binary.Read(b, f.ByteOrder, &rela)1144 var symNo uint641145 var t R_LARCH1146 symNo = rela.Info >> 321147 t = R_LARCH(rela.Info & 0xffff)11481149 if symNo == 0 || symNo > uint64(len(symbols)) {1150 continue1151 }1152 sym := &symbols[symNo-1]1153 if !canApplyRelocation(sym) {1154 continue1155 }11561157 switch t {1158 case R_LARCH_64:1159 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1160 case R_LARCH_32:1161 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1162 }1163 }11641165 return nil1166}11671168func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {1169 // 24 is the size of Rela64.1170 if len(rels)%24 != 0 {1171 return errors.New("length of relocation section is not a multiple of 24")1172 }11731174 symbols, _, err := f.getSymbols(SHT_SYMTAB)1175 if err != nil {1176 return err1177 }11781179 b := bytes.NewReader(rels)1180 var rela Rela6411811182 for b.Len() > 0 {1183 binary.Read(b, f.ByteOrder, &rela)1184 symNo := rela.Info >> 321185 t := R_RISCV(rela.Info & 0xffff)11861187 if symNo == 0 || symNo > uint64(len(symbols)) {1188 continue1189 }1190 sym := &symbols[symNo-1]1191 if !canApplyRelocation(sym) {1192 continue1193 }11941195 switch t {1196 case R_RISCV_64:1197 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1198 case R_RISCV_32:1199 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1200 }1201 }12021203 return nil1204}12051206func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {1207 // 24 is the size of Rela64.1208 if len(rels)%24 != 0 {1209 return errors.New("length of relocation section is not a multiple of 24")1210 }12111212 symbols, _, err := f.getSymbols(SHT_SYMTAB)1213 if err != nil {1214 return err1215 }12161217 b := bytes.NewReader(rels)1218 var rela Rela6412191220 for b.Len() > 0 {1221 binary.Read(b, f.ByteOrder, &rela)1222 symNo := rela.Info >> 321223 t := R_390(rela.Info & 0xffff)12241225 if symNo == 0 || symNo > uint64(len(symbols)) {1226 continue1227 }1228 sym := &symbols[symNo-1]1229 if !canApplyRelocation(sym) {1230 continue1231 }12321233 switch t {1234 case R_390_64:1235 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)1236 case R_390_32:1237 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1238 }1239 }12401241 return nil1242}12431244func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {1245 // 24 is the size of Rela64.1246 if len(rels)%24 != 0 {1247 return errors.New("length of relocation section is not a multiple of 24")1248 }12491250 symbols, _, err := f.getSymbols(SHT_SYMTAB)1251 if err != nil {1252 return err1253 }12541255 b := bytes.NewReader(rels)1256 var rela Rela6412571258 for b.Len() > 0 {1259 binary.Read(b, f.ByteOrder, &rela)1260 symNo := rela.Info >> 321261 t := R_SPARC(rela.Info & 0xff)12621263 if symNo == 0 || symNo > uint64(len(symbols)) {1264 continue1265 }1266 sym := &symbols[symNo-1]1267 if !canApplyRelocation(sym) {1268 continue1269 }12701271 switch t {1272 case R_SPARC_64, R_SPARC_UA64:1273 putUint(f.ByteOrder, dst, rela.Off, 8, sym.Value, rela.Addend, false)12741275 case R_SPARC_32, R_SPARC_UA32:1276 putUint(f.ByteOrder, dst, rela.Off, 4, sym.Value, rela.Addend, false)1277 }1278 }12791280 return nil1281}12821283func (f *File) DWARF() (*dwarf.Data, error) {1284 dwarfSuffix := func(s *Section) string {1285 switch {1286 case strings.HasPrefix(s.Name, ".debug_"):1287 return s.Name[7:]1288 case strings.HasPrefix(s.Name, ".zdebug_"):1289 return s.Name[8:]1290 default:1291 return ""1292 }12931294 }1295 // sectionData gets the data for s, checks its size, and1296 // applies any applicable relations.1297 sectionData := func(i int, s *Section) ([]byte, error) {1298 b, err := s.Data()1299 if err != nil && uint64(len(b)) < s.Size {1300 return nil, err1301 }13021303 if f.Type == ET_EXEC {1304 // Do not apply relocations to DWARF sections for ET_EXEC binaries.1305 // Relocations should already be applied, and .rela sections may1306 // contain incorrect data.1307 return b, nil1308 }13091310 for _, r := range f.Sections {1311 if r.Type != SHT_RELA && r.Type != SHT_REL {1312 continue1313 }1314 if int(r.Info) != i {1315 continue1316 }1317 rd, err := r.Data()1318 if err != nil {1319 return nil, err1320 }1321 err = f.applyRelocations(b, rd)1322 if err != nil {1323 return nil, err1324 }1325 }1326 return b, nil1327 }13281329 // There are many DWARF sections, but these are the ones1330 // the debug/dwarf package started with.1331 var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}1332 for i, s := range f.Sections {1333 suffix := dwarfSuffix(s)1334 if suffix == "" {1335 continue1336 }1337 if _, ok := dat[suffix]; !ok {1338 continue1339 }1340 b, err := sectionData(i, s)1341 if err != nil {1342 return nil, err1343 }1344 dat[suffix] = b1345 }13461347 d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])1348 if err != nil {1349 return nil, err1350 }13511352 // Look for DWARF4 .debug_types sections and DWARF5 sections.1353 for i, s := range f.Sections {1354 suffix := dwarfSuffix(s)1355 if suffix == "" {1356 continue1357 }1358 if _, ok := dat[suffix]; ok {1359 // Already handled.1360 continue1361 }13621363 b, err := sectionData(i, s)1364 if err != nil {1365 return nil, err1366 }13671368 if suffix == "types" {1369 if err := d.AddTypes(fmt.Sprintf("types-%d", i), b); err != nil {1370 return nil, err1371 }1372 } else {1373 if err := d.AddSection(".debug_"+suffix, b); err != nil {1374 return nil, err1375 }1376 }1377 }13781379 return d, nil1380}13811382// Symbols returns the symbol table for f. The symbols will be listed in the order1383// they appear in f.1384//1385// For compatibility with Go 1.0, Symbols omits the null symbol at index 0.1386// After retrieving the symbols as symtab, an externally supplied index x1387// corresponds to symtab[x-1], not symtab[x].1388func (f *File) Symbols() ([]Symbol, error) {1389 sym, _, err := f.getSymbols(SHT_SYMTAB)1390 return sym, err1391}13921393// DynamicSymbols returns the dynamic symbol table for f. The symbols1394// will be listed in the order they appear in f.1395//1396// If f has a symbol version table, the returned [File.Symbols] will have1397// initialized Version and Library fields.1398//1399// For compatibility with [File.Symbols], [File.DynamicSymbols] omits the null symbol at index 0.1400// After retrieving the symbols as symtab, an externally supplied index x1401// corresponds to symtab[x-1], not symtab[x].1402func (f *File) DynamicSymbols() ([]Symbol, error) {1403 sym, str, err := f.getSymbols(SHT_DYNSYM)1404 if err != nil {1405 return nil, err1406 }1407 hasVersions, err := f.gnuVersionInit(str)1408 if err != nil {1409 return nil, err1410 }1411 if hasVersions {1412 for i := range sym {1413 sym[i].HasVersion, sym[i].VersionIndex, sym[i].Version, sym[i].Library = f.gnuVersion(i)1414 }1415 }1416 return sym, nil1417}14181419type ImportedSymbol struct {1420 Name string1421 Version string1422 Library string1423}14241425// ImportedSymbols returns the names of all symbols1426// referred to by the binary f that are expected to be1427// satisfied by other libraries at dynamic load time.1428// It does not return weak symbols.1429func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {1430 sym, str, err := f.getSymbols(SHT_DYNSYM)1431 if err != nil {1432 return nil, err1433 }1434 if _, err := f.gnuVersionInit(str); err != nil {1435 return nil, err1436 }1437 var all []ImportedSymbol1438 for i, s := range sym {1439 if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {1440 all = append(all, ImportedSymbol{Name: s.Name})1441 sym := &all[len(all)-1]1442 _, _, sym.Version, sym.Library = f.gnuVersion(i)1443 }1444 }1445 return all, nil1446}14471448// VersionIndex is the type of a [Symbol] version index.1449type VersionIndex uint1614501451// IsHidden reports whether the symbol is hidden within the version.1452// This means that the symbol can only be seen by specifying the exact version.1453func (vi VersionIndex) IsHidden() bool {1454 return vi&0x8000 != 01455}14561457// Index returns the version index.1458// If this is the value 0, it means that the symbol is local,1459// and is not visible externally.1460// If this is the value 1, it means that the symbol is in the base version,1461// and has no specific version; it may or may not match a1462// [DynamicVersion.Index] in the slice returned by [File.DynamicVersions].1463// Other values will match either [DynamicVersion.Index]1464// in the slice returned by [File.DynamicVersions],1465// or [DynamicVersionDep.Index] in the Needs field1466// of the elements of the slice returned by [File.DynamicVersionNeeds].1467// In general, a defined symbol will have an index referring1468// to DynamicVersions, and an undefined symbol will have an index1469// referring to some version in DynamicVersionNeeds.1470func (vi VersionIndex) Index() uint16 {1471 return uint16(vi & 0x7fff)1472}14731474// DynamicVersion is a version defined by a dynamic object.1475// This describes entries in the ELF SHT_GNU_verdef section.1476// We assume that the vd_version field is 1.1477// Note that the name of the version appears here;1478// it is not in the first Deps entry as it is in the ELF file.1479type DynamicVersion struct {1480 Name string // Name of version defined by this index.1481 Index uint16 // Version index.1482 Flags DynamicVersionFlag1483 Deps []string // Names of versions that this version depends upon.1484}14851486// DynamicVersionNeed describes a shared library needed by a dynamic object,1487// with a list of the versions needed from that shared library.1488// This describes entries in the ELF SHT_GNU_verneed section.1489// We assume that the vn_version field is 1.1490type DynamicVersionNeed struct {1491 Name string // Shared library name.1492 Needs []DynamicVersionDep // Dependencies.1493}14941495// DynamicVersionDep is a version needed from some shared library.1496type DynamicVersionDep struct {1497 Flags DynamicVersionFlag1498 Index uint16 // Version index.1499 Dep string // Name of required version.1500}15011502// dynamicVersions returns version information for a dynamic object.1503func (f *File) dynamicVersions(str []byte) error {1504 if f.dynVers != nil {1505 // Already initialized.1506 return nil1507 }15081509 // Accumulate verdef information.1510 vd := f.SectionByType(SHT_GNU_VERDEF)1511 if vd == nil {1512 return nil1513 }1514 d, _ := vd.Data()15151516 var dynVers []DynamicVersion1517 i := 01518 for {1519 if i+20 > len(d) {1520 break1521 }1522 version := f.ByteOrder.Uint16(d[i : i+2])1523 if version != 1 {1524 return &FormatError{int64(vd.Offset + uint64(i)), "unexpected dynamic version", version}1525 }1526 flags := DynamicVersionFlag(f.ByteOrder.Uint16(d[i+2 : i+4]))1527 ndx := f.ByteOrder.Uint16(d[i+4 : i+6])1528 cnt := f.ByteOrder.Uint16(d[i+6 : i+8])1529 aux := f.ByteOrder.Uint32(d[i+12 : i+16])1530 next := f.ByteOrder.Uint32(d[i+16 : i+20])15311532 if cnt == 0 {1533 return &FormatError{int64(vd.Offset + uint64(i)), "dynamic version has no name", nil}1534 }15351536 var name string1537 var depName string1538 var deps []string1539 j := i + int(aux)1540 for c := 0; c < int(cnt); c++ {1541 if j+8 > len(d) {1542 break1543 }1544 vname := f.ByteOrder.Uint32(d[j : j+4])1545 vnext := f.ByteOrder.Uint32(d[j+4 : j+8])1546 depName, _ = getString(str, int(vname))15471548 if c == 0 {1549 name = depName1550 } else {1551 deps = append(deps, depName)1552 }15531554 if vnext == 0 {1555 break1556 }1557 j += int(vnext)1558 }15591560 dynVers = append(dynVers, DynamicVersion{1561 Name: name,1562 Index: ndx,1563 Flags: flags,1564 Deps: deps,1565 })15661567 if next == 0 {1568 break1569 }1570 i += int(next)1571 }15721573 f.dynVers = dynVers15741575 return nil1576}15771578// DynamicVersions returns version information for a dynamic object.1579func (f *File) DynamicVersions() ([]DynamicVersion, error) {1580 if f.dynVers == nil {1581 _, str, err := f.getSymbols(SHT_DYNSYM)1582 if err != nil {1583 return nil, err1584 }1585 hasVersions, err := f.gnuVersionInit(str)1586 if err != nil {1587 return nil, err1588 }1589 if !hasVersions {1590 return nil, errors.New("DynamicVersions: missing version table")1591 }1592 }15931594 return f.dynVers, nil1595}15961597// dynamicVersionNeeds returns version dependencies for a dynamic object.1598func (f *File) dynamicVersionNeeds(str []byte) error {1599 if f.dynVerNeeds != nil {1600 // Already initialized.1601 return nil1602 }16031604 // Accumulate verneed information.1605 vn := f.SectionByType(SHT_GNU_VERNEED)1606 if vn == nil {1607 return nil1608 }1609 d, _ := vn.Data()16101611 var dynVerNeeds []DynamicVersionNeed1612 i := 01613 for {1614 if i+16 > len(d) {1615 break1616 }1617 vers := f.ByteOrder.Uint16(d[i : i+2])1618 if vers != 1 {1619 return &FormatError{int64(vn.Offset + uint64(i)), "unexpected dynamic need version", vers}1620 }1621 cnt := f.ByteOrder.Uint16(d[i+2 : i+4])1622 fileoff := f.ByteOrder.Uint32(d[i+4 : i+8])1623 aux := f.ByteOrder.Uint32(d[i+8 : i+12])1624 next := f.ByteOrder.Uint32(d[i+12 : i+16])1625 file, _ := getString(str, int(fileoff))16261627 var deps []DynamicVersionDep1628 j := i + int(aux)1629 for c := 0; c < int(cnt); c++ {1630 if j+16 > len(d) {1631 break1632 }1633 flags := DynamicVersionFlag(f.ByteOrder.Uint16(d[j+4 : j+6]))1634 index := f.ByteOrder.Uint16(d[j+6 : j+8])1635 nameoff := f.ByteOrder.Uint32(d[j+8 : j+12])1636 next := f.ByteOrder.Uint32(d[j+12 : j+16])1637 depName, _ := getString(str, int(nameoff))16381639 deps = append(deps, DynamicVersionDep{1640 Flags: flags,1641 Index: index,1642 Dep: depName,1643 })16441645 if next == 0 {1646 break1647 }1648 j += int(next)1649 }16501651 dynVerNeeds = append(dynVerNeeds, DynamicVersionNeed{1652 Name: file,1653 Needs: deps,1654 })16551656 if next == 0 {1657 break1658 }1659 i += int(next)1660 }16611662 f.dynVerNeeds = dynVerNeeds16631664 return nil1665}16661667// DynamicVersionNeeds returns version dependencies for a dynamic object.1668func (f *File) DynamicVersionNeeds() ([]DynamicVersionNeed, error) {1669 if f.dynVerNeeds == nil {1670 _, str, err := f.getSymbols(SHT_DYNSYM)1671 if err != nil {1672 return nil, err1673 }1674 hasVersions, err := f.gnuVersionInit(str)1675 if err != nil {1676 return nil, err1677 }1678 if !hasVersions {1679 return nil, errors.New("DynamicVersionNeeds: missing version table")1680 }1681 }16821683 return f.dynVerNeeds, nil1684}16851686// gnuVersionInit parses the GNU version tables1687// for use by calls to gnuVersion.1688// It reports whether any version tables were found.1689func (f *File) gnuVersionInit(str []byte) (bool, error) {1690 // Versym parallels symbol table, indexing into verneed.1691 vs := f.SectionByType(SHT_GNU_VERSYM)1692 if vs == nil {1693 return false, nil1694 }1695 d, _ := vs.Data()16961697 f.gnuVersym = d1698 if err := f.dynamicVersions(str); err != nil {1699 return false, err1700 }1701 if err := f.dynamicVersionNeeds(str); err != nil {1702 return false, err1703 }1704 return true, nil1705}17061707// gnuVersion adds Library and Version information to sym,1708// which came from offset i of the symbol table.1709func (f *File) gnuVersion(i int) (hasVersion bool, versionIndex VersionIndex, version string, library string) {1710 // Each entry is two bytes; skip undef entry at beginning.1711 i = (i + 1) * 21712 if i >= len(f.gnuVersym) {1713 return false, 0, "", ""1714 }1715 s := f.gnuVersym[i:]1716 if len(s) < 2 {1717 return false, 0, "", ""1718 }1719 vi := VersionIndex(f.ByteOrder.Uint16(s))1720 ndx := vi.Index()17211722 if ndx == 0 || ndx == 1 {1723 return true, vi, "", ""1724 }17251726 for _, v := range f.dynVerNeeds {1727 for _, n := range v.Needs {1728 if ndx == n.Index {1729 return true, vi, n.Dep, v.Name1730 }1731 }1732 }17331734 for _, v := range f.dynVers {1735 if ndx == v.Index {1736 return true, vi, v.Name, ""1737 }1738 }17391740 return false, 0, "", ""1741}17421743// ImportedLibraries returns the names of all libraries1744// referred to by the binary f that are expected to be1745// linked with the binary at dynamic link time.1746func (f *File) ImportedLibraries() ([]string, error) {1747 return f.DynString(DT_NEEDED)1748}17491750// DynString returns the strings listed for the given tag in the file's dynamic1751// section.1752//1753// The tag must be one that takes string values: [DT_NEEDED], [DT_SONAME], [DT_RPATH], or1754// [DT_RUNPATH].1755func (f *File) DynString(tag DynTag) ([]string, error) {1756 switch tag {1757 case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH:1758 default:1759 return nil, fmt.Errorf("non-string-valued tag %v", tag)1760 }1761 ds := f.SectionByType(SHT_DYNAMIC)1762 if ds == nil {1763 // not dynamic, so no libraries1764 return nil, nil1765 }1766 d, err := ds.Data()1767 if err != nil {1768 return nil, err1769 }17701771 dynSize := 81772 if f.Class == ELFCLASS64 {1773 dynSize = 161774 }1775 if len(d)%dynSize != 0 {1776 return nil, errors.New("length of dynamic section is not a multiple of dynamic entry size")1777 }17781779 str, err := f.stringTable(ds.Link)1780 if err != nil {1781 return nil, err1782 }1783 var all []string1784 for len(d) > 0 {1785 var t DynTag1786 var v uint641787 switch f.Class {1788 case ELFCLASS32:1789 t = DynTag(f.ByteOrder.Uint32(d[0:4]))1790 v = uint64(f.ByteOrder.Uint32(d[4:8]))1791 d = d[8:]1792 case ELFCLASS64:1793 t = DynTag(f.ByteOrder.Uint64(d[0:8]))1794 v = f.ByteOrder.Uint64(d[8:16])1795 d = d[16:]1796 }1797 if t == tag {1798 s, ok := getString(str, int(v))1799 if ok {1800 all = append(all, s)1801 }1802 }1803 }1804 return all, nil1805}18061807// DynValue returns the values listed for the given tag in the file's dynamic1808// section.1809func (f *File) DynValue(tag DynTag) ([]uint64, error) {1810 ds := f.SectionByType(SHT_DYNAMIC)1811 if ds == nil {1812 return nil, nil1813 }1814 d, err := ds.Data()1815 if err != nil {1816 return nil, err1817 }18181819 dynSize := 81820 if f.Class == ELFCLASS64 {1821 dynSize = 161822 }1823 if len(d)%dynSize != 0 {1824 return nil, errors.New("length of dynamic section is not a multiple of dynamic entry size")1825 }18261827 // Parse the .dynamic section as a string of bytes.1828 var vals []uint641829 for len(d) > 0 {1830 var t DynTag1831 var v uint641832 switch f.Class {1833 case ELFCLASS32:1834 t = DynTag(f.ByteOrder.Uint32(d[0:4]))1835 v = uint64(f.ByteOrder.Uint32(d[4:8]))1836 d = d[8:]1837 case ELFCLASS64:1838 t = DynTag(f.ByteOrder.Uint64(d[0:8]))1839 v = f.ByteOrder.Uint64(d[8:16])1840 d = d[16:]1841 }1842 if t == tag {1843 vals = append(vals, v)1844 }1845 }1846 return vals, nil1847}18481849type nobitsSectionReader struct{}18501851func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) {1852 return 0, errors.New("unexpected read from SHT_NOBITS section")1853}18541855// putUint writes a relocation to slice1856// at offset start of length length (4 or 8 bytes),1857// adding sym+addend to the existing value if readUint is true,1858// or just writing sym+addend if readUint is false.1859// If the write would extend beyond the end of slice, putUint does nothing.1860// If the addend is negative, putUint does nothing.1861// If the addition would overflow, putUint does nothing.1862func putUint(byteOrder binary.ByteOrder, slice []byte, start, length, sym uint64, addend int64, readUint bool) {1863 if start+length > uint64(len(slice)) || math.MaxUint64-start < length {1864 return1865 }1866 if addend < 0 {1867 return1868 }18691870 s := slice[start : start+length]18711872 switch length {1873 case 4:1874 ae := uint32(addend)1875 if readUint {1876 ae += byteOrder.Uint32(s)1877 }1878 byteOrder.PutUint32(s, uint32(sym)+ae)1879 case 8:1880 ae := uint64(addend)1881 if readUint {1882 ae += byteOrder.Uint64(s)1883 }1884 byteOrder.PutUint64(s, sym+ae)1885 default:1886 panic("can't happen")1887 }1888}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.