// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package elf implements access to ELF object files.
package elf import ( ) // seekStart, seekCurrent, seekEnd are copies of // io.SeekStart, io.SeekCurrent, and io.SeekEnd. // We can't use the ones from package io because // we want this code to build with Go 1.4 during // cmd/dist bootstrap. const ( seekStart int = 0 seekCurrent int = 1 seekEnd int = 2 ) // TODO: error reporting detail /* * Internal ELF representation */ // A FileHeader represents an ELF file header. type FileHeader struct { Class Class Data Data Version Version OSABI OSABI ABIVersion uint8 ByteOrder binary.ByteOrder Type Type Machine Machine Entry uint64 } // A File represents an open ELF file. type File struct { FileHeader Sections []*Section Progs []*Prog closer io.Closer gnuNeed []verneed gnuVersym []byte } // A SectionHeader represents a single ELF section header. type SectionHeader struct { Name string Type SectionType Flags SectionFlag Addr uint64 Offset uint64 Size uint64 Link uint32 Info uint32 Addralign uint64 Entsize uint64 // FileSize is the size of this section in the file in bytes. // If a section is compressed, FileSize is the size of the // compressed data, while Size (above) is the size of the // uncompressed data. FileSize uint64 } // A Section represents a single section in an ELF file. type Section struct { SectionHeader // Embed ReaderAt for ReadAt method. // Do not embed SectionReader directly // to avoid having Read and Seek. // If a client wants Read and Seek it must use // Open() to avoid fighting over the seek offset // with other clients. // // ReaderAt may be nil if the section is not easily available // in a random-access form. For example, a compressed section // may have a nil ReaderAt. io.ReaderAt sr *io.SectionReader compressionType CompressionType compressionOffset int64 } // Data reads and returns the contents of the ELF section. // Even if the section is stored compressed in the ELF file, // Data returns uncompressed data. func ( *Section) () ([]byte, error) { := make([]byte, .Size) , := io.ReadFull(.Open(), ) return [0:], } // stringTable reads and returns the string table given by the // specified link value. func ( *File) ( uint32) ([]byte, error) { if <= 0 || >= uint32(len(.Sections)) { return nil, errors.New("section has invalid string table link") } return .Sections[].Data() } // Open returns a new ReadSeeker reading the ELF section. // Even if the section is stored compressed in the ELF file, // the ReadSeeker reads uncompressed data. func ( *Section) () io.ReadSeeker { if .Flags&SHF_COMPRESSED == 0 { return io.NewSectionReader(.sr, 0, 1<<63-1) } if .compressionType == COMPRESS_ZLIB { return &readSeekerFromReader{ reset: func() (io.Reader, error) { := io.NewSectionReader(.sr, .compressionOffset, int64(.FileSize)-.compressionOffset) return zlib.NewReader() }, size: int64(.Size), } } := &FormatError{int64(.Offset), "unknown compression type", .compressionType} return errorReader{} } // A ProgHeader represents a single ELF program header. type ProgHeader struct { Type ProgType Flags ProgFlag Off uint64 Vaddr uint64 Paddr uint64 Filesz uint64 Memsz uint64 Align uint64 } // A Prog represents a single ELF program header in an ELF binary. type Prog struct { ProgHeader // Embed ReaderAt for ReadAt method. // Do not embed SectionReader directly // to avoid having Read and Seek. // If a client wants Read and Seek it must use // Open() to avoid fighting over the seek offset // with other clients. io.ReaderAt sr *io.SectionReader } // Open returns a new ReadSeeker reading the ELF program body. func ( *Prog) () io.ReadSeeker { return io.NewSectionReader(.sr, 0, 1<<63-1) } // A Symbol represents an entry in an ELF symbol table section. type Symbol struct { Name string Info, Other byte Section SectionIndex Value, Size uint64 // Version and Library are present only for the dynamic symbol // table. Version string Library string } /* * ELF reader */ type FormatError struct { off int64 msg string val interface{} } func ( *FormatError) () string { := .msg if .val != nil { += fmt.Sprintf(" '%v' ", .val) } += fmt.Sprintf("in record at byte %#x", .off) return } // Open opens the named file using os.Open and prepares it for use as an ELF binary. func ( string) (*File, error) { , := os.Open() if != nil { return nil, } , := NewFile() if != nil { .Close() return nil, } .closer = return , nil } // Close closes the File. // If the File was created using NewFile directly instead of Open, // Close has no effect. func ( *File) () error { var error if .closer != nil { = .closer.Close() .closer = nil } return } // SectionByType returns the first section in f with the // given type, or nil if there is no such section. func ( *File) ( SectionType) *Section { for , := range .Sections { if .Type == { return } } return nil } // NewFile creates a new File for accessing an ELF binary in an underlying reader. // The ELF binary is expected to start at position 0 in the ReaderAt. func ( io.ReaderAt) (*File, error) { := io.NewSectionReader(, 0, 1<<63-1) // Read and decode ELF identifier var [16]uint8 if , := .ReadAt([0:], 0); != nil { return nil, } if [0] != '\x7f' || [1] != 'E' || [2] != 'L' || [3] != 'F' { return nil, &FormatError{0, "bad magic number", [0:4]} } := new(File) .Class = Class([EI_CLASS]) switch .Class { case ELFCLASS32: case ELFCLASS64: // ok default: return nil, &FormatError{0, "unknown ELF class", .Class} } .Data = Data([EI_DATA]) switch .Data { case ELFDATA2LSB: .ByteOrder = binary.LittleEndian case ELFDATA2MSB: .ByteOrder = binary.BigEndian default: return nil, &FormatError{0, "unknown ELF data encoding", .Data} } .Version = Version([EI_VERSION]) if .Version != EV_CURRENT { return nil, &FormatError{0, "unknown ELF version", .Version} } .OSABI = OSABI([EI_OSABI]) .ABIVersion = [EI_ABIVERSION] // Read ELF file header var int64 var , int var int64 var , , int switch .Class { case ELFCLASS32: := new(Header32) .Seek(0, seekStart) if := binary.Read(, .ByteOrder, ); != nil { return nil, } .Type = Type(.Type) .Machine = Machine(.Machine) .Entry = uint64(.Entry) if := Version(.Version); != .Version { return nil, &FormatError{0, "mismatched ELF version", } } = int64(.Phoff) = int(.Phentsize) = int(.Phnum) = int64(.Shoff) = int(.Shentsize) = int(.Shnum) = int(.Shstrndx) case ELFCLASS64: := new(Header64) .Seek(0, seekStart) if := binary.Read(, .ByteOrder, ); != nil { return nil, } .Type = Type(.Type) .Machine = Machine(.Machine) .Entry = .Entry if := Version(.Version); != .Version { return nil, &FormatError{0, "mismatched ELF version", } } = int64(.Phoff) = int(.Phentsize) = int(.Phnum) = int64(.Shoff) = int(.Shentsize) = int(.Shnum) = int(.Shstrndx) } if == 0 && != 0 { return nil, &FormatError{0, "invalid ELF shnum for shoff=0", } } if > 0 && >= { return nil, &FormatError{0, "invalid ELF shstrndx", } } // Read program headers .Progs = make([]*Prog, ) for := 0; < ; ++ { := + int64()*int64() .Seek(, seekStart) := new(Prog) switch .Class { case ELFCLASS32: := new(Prog32) if := binary.Read(, .ByteOrder, ); != nil { return nil, } .ProgHeader = ProgHeader{ Type: ProgType(.Type), Flags: ProgFlag(.Flags), Off: uint64(.Off), Vaddr: uint64(.Vaddr), Paddr: uint64(.Paddr), Filesz: uint64(.Filesz), Memsz: uint64(.Memsz), Align: uint64(.Align), } case ELFCLASS64: := new(Prog64) if := binary.Read(, .ByteOrder, ); != nil { return nil, } .ProgHeader = ProgHeader{ Type: ProgType(.Type), Flags: ProgFlag(.Flags), Off: .Off, Vaddr: .Vaddr, Paddr: .Paddr, Filesz: .Filesz, Memsz: .Memsz, Align: .Align, } } .sr = io.NewSectionReader(, int64(.Off), int64(.Filesz)) .ReaderAt = .sr .Progs[] = } // Read section headers .Sections = make([]*Section, ) := make([]uint32, ) for := 0; < ; ++ { := + int64()*int64() .Seek(, seekStart) := new(Section) switch .Class { case ELFCLASS32: := new(Section32) if := binary.Read(, .ByteOrder, ); != nil { return nil, } [] = .Name .SectionHeader = SectionHeader{ Type: SectionType(.Type), Flags: SectionFlag(.Flags), Addr: uint64(.Addr), Offset: uint64(.Off), FileSize: uint64(.Size), Link: .Link, Info: .Info, Addralign: uint64(.Addralign), Entsize: uint64(.Entsize), } case ELFCLASS64: := new(Section64) if := binary.Read(, .ByteOrder, ); != nil { return nil, } [] = .Name .SectionHeader = SectionHeader{ Type: SectionType(.Type), Flags: SectionFlag(.Flags), Offset: .Off, FileSize: .Size, Addr: .Addr, Link: .Link, Info: .Info, Addralign: .Addralign, Entsize: .Entsize, } } .sr = io.NewSectionReader(, int64(.Offset), int64(.FileSize)) if .Flags&SHF_COMPRESSED == 0 { .ReaderAt = .sr .Size = .FileSize } else { // Read the compression header. switch .Class { case ELFCLASS32: := new(Chdr32) if := binary.Read(.sr, .ByteOrder, ); != nil { return nil, } .compressionType = CompressionType(.Type) .Size = uint64(.Size) .Addralign = uint64(.Addralign) .compressionOffset = int64(binary.Size()) case ELFCLASS64: := new(Chdr64) if := binary.Read(.sr, .ByteOrder, ); != nil { return nil, } .compressionType = CompressionType(.Type) .Size = .Size .Addralign = .Addralign .compressionOffset = int64(binary.Size()) } } .Sections[] = } if len(.Sections) == 0 { return , nil } // Load section header string table. , := .Sections[].Data() if != nil { return nil, } for , := range .Sections { var bool .Name, = getString(, int([])) if ! { return nil, &FormatError{ + int64(*), "bad section name index", []} } } return , nil } // getSymbols returns a slice of Symbols from parsing the symbol table // with the given type, along with the associated string table. func ( *File) ( SectionType) ([]Symbol, []byte, error) { switch .Class { case ELFCLASS64: return .getSymbols64() case ELFCLASS32: return .getSymbols32() } return nil, nil, errors.New("not implemented") } // ErrNoSymbols is returned by File.Symbols and File.DynamicSymbols // if there is no such section in the File. var ErrNoSymbols = errors.New("no symbol section") func ( *File) ( SectionType) ([]Symbol, []byte, error) { := .SectionByType() if == nil { return nil, nil, ErrNoSymbols } , := .Data() if != nil { return nil, nil, errors.New("cannot load symbol section") } := bytes.NewReader() if .Len()%Sym32Size != 0 { return nil, nil, errors.New("length of symbol section is not a multiple of SymSize") } , := .stringTable(.Link) if != nil { return nil, nil, errors.New("cannot load string table section") } // The first entry is all zeros. var [Sym32Size]byte .Read([:]) := make([]Symbol, .Len()/Sym32Size) := 0 var Sym32 for .Len() > 0 { binary.Read(, .ByteOrder, &) , := getString(, int(.Name)) [].Name = [].Info = .Info [].Other = .Other [].Section = SectionIndex(.Shndx) [].Value = uint64(.Value) [].Size = uint64(.Size) ++ } return , , nil } func ( *File) ( SectionType) ([]Symbol, []byte, error) { := .SectionByType() if == nil { return nil, nil, ErrNoSymbols } , := .Data() if != nil { return nil, nil, errors.New("cannot load symbol section") } := bytes.NewReader() if .Len()%Sym64Size != 0 { return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size") } , := .stringTable(.Link) if != nil { return nil, nil, errors.New("cannot load string table section") } // The first entry is all zeros. var [Sym64Size]byte .Read([:]) := make([]Symbol, .Len()/Sym64Size) := 0 var Sym64 for .Len() > 0 { binary.Read(, .ByteOrder, &) , := getString(, int(.Name)) [].Name = [].Info = .Info [].Other = .Other [].Section = SectionIndex(.Shndx) [].Value = .Value [].Size = .Size ++ } return , , nil } // getString extracts a string from an ELF string table. func getString( []byte, int) (string, bool) { if < 0 || >= len() { return "", false } for := ; < len(); ++ { if [] == 0 { return string([:]), true } } return "", false } // Section returns a section with the given name, or nil if no such // section exists. func ( *File) ( string) *Section { for , := range .Sections { if .Name == { return } } return nil } // applyRelocations applies relocations to dst. rels is a relocations section // in REL or RELA format. func ( *File) ( []byte, []byte) error { switch { case .Class == ELFCLASS64 && .Machine == EM_X86_64: return .applyRelocationsAMD64(, ) case .Class == ELFCLASS32 && .Machine == EM_386: return .applyRelocations386(, ) case .Class == ELFCLASS32 && .Machine == EM_ARM: return .applyRelocationsARM(, ) case .Class == ELFCLASS64 && .Machine == EM_AARCH64: return .applyRelocationsARM64(, ) case .Class == ELFCLASS32 && .Machine == EM_PPC: return .applyRelocationsPPC(, ) case .Class == ELFCLASS64 && .Machine == EM_PPC64: return .applyRelocationsPPC64(, ) case .Class == ELFCLASS32 && .Machine == EM_MIPS: return .applyRelocationsMIPS(, ) case .Class == ELFCLASS64 && .Machine == EM_MIPS: return .applyRelocationsMIPS64(, ) case .Class == ELFCLASS64 && .Machine == EM_RISCV: return .applyRelocationsRISCV64(, ) case .Class == ELFCLASS64 && .Machine == EM_S390: return .applyRelocationss390x(, ) case .Class == ELFCLASS64 && .Machine == EM_SPARCV9: return .applyRelocationsSPARC64(, ) default: return errors.New("applyRelocations: not implemented") } } // canApplyRelocation reports whether we should try to apply a // relocation to a DWARF data section, given a pointer to the symbol // targeted by the relocation. // Most relocations in DWARF data tend to be section-relative, but // some target non-section symbols (for example, low_PC attrs on // subprogram or compilation unit DIEs that target function symbols). func canApplyRelocation( *Symbol) bool { return .Section != SHN_UNDEF && .Section < SHN_LORESERVE } func ( *File) ( []byte, []byte) error { // 24 is the size of Rela64. if len()%24 != 0 { return errors.New("length of relocation section is not a multiple of 24") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rela64 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 32 := R_X86_64(.Info & 0xffff) if == 0 || > uint64(len()) { continue } := &[-1] if !canApplyRelocation() { continue } // There are relocations, so this must be a normal // object file. The code below handles only basic relocations // of the form S + A (symbol plus addend). switch { case R_X86_64_64: if .Off+8 >= uint64(len()) || .Addend < 0 { continue } := .Value + uint64(.Addend) .ByteOrder.PutUint64([.Off:.Off+8], ) case R_X86_64_32: if .Off+4 >= uint64(len()) || .Addend < 0 { continue } := uint32(.Value) + uint32(.Addend) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 8 is the size of Rel32. if len()%8 != 0 { return errors.New("length of relocation section is not a multiple of 8") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rel32 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 8 := R_386(.Info & 0xff) if == 0 || > uint32(len()) { continue } := &[-1] if == R_386_32 { if .Off+4 >= uint32(len()) { continue } := .ByteOrder.Uint32([.Off : .Off+4]) += uint32(.Value) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 8 is the size of Rel32. if len()%8 != 0 { return errors.New("length of relocation section is not a multiple of 8") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rel32 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 8 := R_ARM(.Info & 0xff) if == 0 || > uint32(len()) { continue } := &[-1] switch { case R_ARM_ABS32: if .Off+4 >= uint32(len()) { continue } := .ByteOrder.Uint32([.Off : .Off+4]) += uint32(.Value) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 24 is the size of Rela64. if len()%24 != 0 { return errors.New("length of relocation section is not a multiple of 24") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rela64 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 32 := R_AARCH64(.Info & 0xffff) if == 0 || > uint64(len()) { continue } := &[-1] if !canApplyRelocation() { continue } // There are relocations, so this must be a normal // object file. The code below handles only basic relocations // of the form S + A (symbol plus addend). switch { case R_AARCH64_ABS64: if .Off+8 >= uint64(len()) || .Addend < 0 { continue } := .Value + uint64(.Addend) .ByteOrder.PutUint64([.Off:.Off+8], ) case R_AARCH64_ABS32: if .Off+4 >= uint64(len()) || .Addend < 0 { continue } := uint32(.Value) + uint32(.Addend) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 12 is the size of Rela32. if len()%12 != 0 { return errors.New("length of relocation section is not a multiple of 12") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rela32 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 8 := R_PPC(.Info & 0xff) if == 0 || > uint32(len()) { continue } := &[-1] if !canApplyRelocation() { continue } switch { case R_PPC_ADDR32: if .Off+4 >= uint32(len()) || .Addend < 0 { continue } := uint32(.Value) + uint32(.Addend) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 24 is the size of Rela64. if len()%24 != 0 { return errors.New("length of relocation section is not a multiple of 24") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rela64 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 32 := R_PPC64(.Info & 0xffff) if == 0 || > uint64(len()) { continue } := &[-1] if !canApplyRelocation() { continue } switch { case R_PPC64_ADDR64: if .Off+8 >= uint64(len()) || .Addend < 0 { continue } := .Value + uint64(.Addend) .ByteOrder.PutUint64([.Off:.Off+8], ) case R_PPC64_ADDR32: if .Off+4 >= uint64(len()) || .Addend < 0 { continue } := uint32(.Value) + uint32(.Addend) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 8 is the size of Rel32. if len()%8 != 0 { return errors.New("length of relocation section is not a multiple of 8") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rel32 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 8 := R_MIPS(.Info & 0xff) if == 0 || > uint32(len()) { continue } := &[-1] switch { case R_MIPS_32: if .Off+4 >= uint32(len()) { continue } := .ByteOrder.Uint32([.Off : .Off+4]) += uint32(.Value) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 24 is the size of Rela64. if len()%24 != 0 { return errors.New("length of relocation section is not a multiple of 24") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rela64 for .Len() > 0 { binary.Read(, .ByteOrder, &) var uint64 var R_MIPS if .ByteOrder == binary.BigEndian { = .Info >> 32 = R_MIPS(.Info & 0xff) } else { = .Info & 0xffffffff = R_MIPS(.Info >> 56) } if == 0 || > uint64(len()) { continue } := &[-1] if !canApplyRelocation() { continue } switch { case R_MIPS_64: if .Off+8 >= uint64(len()) || .Addend < 0 { continue } := .Value + uint64(.Addend) .ByteOrder.PutUint64([.Off:.Off+8], ) case R_MIPS_32: if .Off+4 >= uint64(len()) || .Addend < 0 { continue } := uint32(.Value) + uint32(.Addend) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 24 is the size of Rela64. if len()%24 != 0 { return errors.New("length of relocation section is not a multiple of 24") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rela64 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 32 := R_RISCV(.Info & 0xffff) if == 0 || > uint64(len()) { continue } := &[-1] if !canApplyRelocation() { continue } switch { case R_RISCV_64: if .Off+8 >= uint64(len()) || .Addend < 0 { continue } := .Value + uint64(.Addend) .ByteOrder.PutUint64([.Off:.Off+8], ) case R_RISCV_32: if .Off+4 >= uint64(len()) || .Addend < 0 { continue } := uint32(.Value) + uint32(.Addend) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 24 is the size of Rela64. if len()%24 != 0 { return errors.New("length of relocation section is not a multiple of 24") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rela64 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 32 := R_390(.Info & 0xffff) if == 0 || > uint64(len()) { continue } := &[-1] if !canApplyRelocation() { continue } switch { case R_390_64: if .Off+8 >= uint64(len()) || .Addend < 0 { continue } := .Value + uint64(.Addend) .ByteOrder.PutUint64([.Off:.Off+8], ) case R_390_32: if .Off+4 >= uint64(len()) || .Addend < 0 { continue } := uint32(.Value) + uint32(.Addend) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) ( []byte, []byte) error { // 24 is the size of Rela64. if len()%24 != 0 { return errors.New("length of relocation section is not a multiple of 24") } , , := .getSymbols(SHT_SYMTAB) if != nil { return } := bytes.NewReader() var Rela64 for .Len() > 0 { binary.Read(, .ByteOrder, &) := .Info >> 32 := R_SPARC(.Info & 0xff) if == 0 || > uint64(len()) { continue } := &[-1] if !canApplyRelocation() { continue } switch { case R_SPARC_64, R_SPARC_UA64: if .Off+8 >= uint64(len()) || .Addend < 0 { continue } := .Value + uint64(.Addend) .ByteOrder.PutUint64([.Off:.Off+8], ) case R_SPARC_32, R_SPARC_UA32: if .Off+4 >= uint64(len()) || .Addend < 0 { continue } := uint32(.Value) + uint32(.Addend) .ByteOrder.PutUint32([.Off:.Off+4], ) } } return nil } func ( *File) () (*dwarf.Data, error) { := func( *Section) string { switch { case strings.HasPrefix(.Name, ".debug_"): return .Name[7:] case strings.HasPrefix(.Name, ".zdebug_"): return .Name[8:] default: return "" } } // sectionData gets the data for s, checks its size, and // applies any applicable relations. := func( int, *Section) ([]byte, error) { , := .Data() if != nil && uint64(len()) < .Size { return nil, } if len() >= 12 && string([:4]) == "ZLIB" { := binary.BigEndian.Uint64([4:12]) := make([]byte, ) , := zlib.NewReader(bytes.NewBuffer([12:])) if != nil { return nil, } if , := io.ReadFull(, ); != nil { return nil, } if := .Close(); != nil { return nil, } = } for , := range .Sections { if .Type != SHT_RELA && .Type != SHT_REL { continue } if int(.Info) != { continue } , := .Data() if != nil { return nil, } = .applyRelocations(, ) if != nil { return nil, } } return , nil } // There are many DWARf sections, but these are the ones // the debug/dwarf package started with. var = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} for , := range .Sections { := () if == "" { continue } if , := []; ! { continue } , := (, ) if != nil { return nil, } [] = } , := dwarf.New(["abbrev"], nil, nil, ["info"], ["line"], nil, ["ranges"], ["str"]) if != nil { return nil, } // Look for DWARF4 .debug_types sections and DWARF5 sections. for , := range .Sections { := () if == "" { continue } if , := []; { // Already handled. continue } , := (, ) if != nil { return nil, } if == "types" { if := .AddTypes(fmt.Sprintf("types-%d", ), ); != nil { return nil, } } else { if := .AddSection(".debug_"+, ); != nil { return nil, } } } return , nil } // Symbols returns the symbol table for f. The symbols will be listed in the order // they appear in f. // // For compatibility with Go 1.0, Symbols omits the null symbol at index 0. // After retrieving the symbols as symtab, an externally supplied index x // corresponds to symtab[x-1], not symtab[x]. func ( *File) () ([]Symbol, error) { , , := .getSymbols(SHT_SYMTAB) return , } // DynamicSymbols returns the dynamic symbol table for f. The symbols // will be listed in the order they appear in f. // // If f has a symbol version table, the returned Symbols will have // initialized Version and Library fields. // // For compatibility with Symbols, DynamicSymbols omits the null symbol at index 0. // After retrieving the symbols as symtab, an externally supplied index x // corresponds to symtab[x-1], not symtab[x]. func ( *File) () ([]Symbol, error) { , , := .getSymbols(SHT_DYNSYM) if != nil { return nil, } if .gnuVersionInit() { for := range { [].Library, [].Version = .gnuVersion() } } return , nil } type ImportedSymbol struct { Name string Version string Library string } // ImportedSymbols returns the names of all symbols // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. // It does not return weak symbols. func ( *File) () ([]ImportedSymbol, error) { , , := .getSymbols(SHT_DYNSYM) if != nil { return nil, } .gnuVersionInit() var []ImportedSymbol for , := range { if ST_BIND(.Info) == STB_GLOBAL && .Section == SHN_UNDEF { = append(, ImportedSymbol{Name: .Name}) := &[len()-1] .Library, .Version = .gnuVersion() } } return , nil } type verneed struct { File string Name string } // gnuVersionInit parses the GNU version tables // for use by calls to gnuVersion. func ( *File) ( []byte) bool { if .gnuNeed != nil { // Already initialized return true } // Accumulate verneed information. := .SectionByType(SHT_GNU_VERNEED) if == nil { return false } , := .Data() var []verneed := 0 for { if +16 > len() { break } := .ByteOrder.Uint16([ : +2]) if != 1 { break } := .ByteOrder.Uint16([+2 : +4]) := .ByteOrder.Uint32([+4 : +8]) := .ByteOrder.Uint32([+8 : +12]) := .ByteOrder.Uint32([+12 : +16]) , := getString(, int()) var string := + int() for := 0; < int(); ++ { if +16 > len() { break } // hash := f.ByteOrder.Uint32(d[j:j+4]) // flags := f.ByteOrder.Uint16(d[j+4:j+6]) := .ByteOrder.Uint16([+6 : +8]) := .ByteOrder.Uint32([+8 : +12]) := .ByteOrder.Uint32([+12 : +16]) , _ = getString(, int()) := int() if >= len() { := make([]verneed, 2*(+1)) copy(, ) = } [] = verneed{, } if == 0 { break } += int() } if == 0 { break } += int() } // Versym parallels symbol table, indexing into verneed. := .SectionByType(SHT_GNU_VERSYM) if == nil { return false } , _ = .Data() .gnuNeed = .gnuVersym = return true } // gnuVersion adds Library and Version information to sym, // which came from offset i of the symbol table. func ( *File) ( int) ( string, string) { // Each entry is two bytes. = ( + 1) * 2 if >= len(.gnuVersym) { return } := int(.ByteOrder.Uint16(.gnuVersym[:])) if < 2 || >= len(.gnuNeed) { return } := &.gnuNeed[] return .File, .Name } // ImportedLibraries returns the names of all libraries // referred to by the binary f that are expected to be // linked with the binary at dynamic link time. func ( *File) () ([]string, error) { return .DynString(DT_NEEDED) } // DynString returns the strings listed for the given tag in the file's dynamic // section. // // The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or // DT_RUNPATH. func ( *File) ( DynTag) ([]string, error) { switch { case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH: default: return nil, fmt.Errorf("non-string-valued tag %v", ) } := .SectionByType(SHT_DYNAMIC) if == nil { // not dynamic, so no libraries return nil, nil } , := .Data() if != nil { return nil, } , := .stringTable(.Link) if != nil { return nil, } var []string for len() > 0 { var DynTag var uint64 switch .Class { case ELFCLASS32: = DynTag(.ByteOrder.Uint32([0:4])) = uint64(.ByteOrder.Uint32([4:8])) = [8:] case ELFCLASS64: = DynTag(.ByteOrder.Uint64([0:8])) = .ByteOrder.Uint64([8:16]) = [16:] } if == { , := getString(, int()) if { = append(, ) } } } return , nil }