// 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. # Security This package is not designed to be hardened against adversarial inputs, and is outside the scope of https://go.dev/security/policy. In particular, only basic validation is done when parsing object files. As such, care should be taken when parsing untrusted inputs, as parsing malformed files may consume significant resources, or cause panics. */
package elf import ( ) // 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. // // For an [SHT_NOBITS] section, Data always returns a non-nil error. func ( *Section) () ([]byte, error) { return saferio.ReadData(.Open(), .Size) } // 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. // // For an [SHT_NOBITS] section, all calls to the opened reader // will return a non-nil error. func ( *Section) () io.ReadSeeker { if .Type == SHT_NOBITS { return io.NewSectionReader(&nobitsSectionReader{}, 0, int64(.Size)) } var func(io.Reader) (io.ReadCloser, error) if .Flags&SHF_COMPRESSED == 0 { if !strings.HasPrefix(.Name, ".zdebug") { return io.NewSectionReader(.sr, 0, 1<<63-1) } := make([]byte, 12) , := .sr.ReadAt(, 0) if != 12 || string([:4]) != "ZLIB" { return io.NewSectionReader(.sr, 0, 1<<63-1) } .compressionOffset = 12 .compressionType = COMPRESS_ZLIB .Size = binary.BigEndian.Uint64([4:12]) = zlib.NewReader } else if .Flags&SHF_ALLOC != 0 { return errorReader{&FormatError{int64(.Offset), "SHF_COMPRESSED applies only to non-allocable sections", .compressionType}} } switch .compressionType { case COMPRESS_ZLIB: = zlib.NewReader case COMPRESS_ZSTD: = func( io.Reader) (io.ReadCloser, error) { return io.NopCloser(zstd.NewReader()), nil } } if == nil { return errorReader{&FormatError{int64(.Offset), "unknown compression type", .compressionType}} } return &readSeekerFromReader{ reset: func() (io.Reader, error) { := io.NewSectionReader(.sr, .compressionOffset, int64(.FileSize)-.compressionOffset) return () }, size: int64(.Size), } } // 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 any } 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, io.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, io.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 { return nil, &FormatError{0, "invalid shoff", } } if < 0 { return nil, &FormatError{0, "invalid phoff", } } if == 0 && != 0 { return nil, &FormatError{0, "invalid ELF shnum for shoff=0", } } if > 0 && >= { return nil, &FormatError{0, "invalid ELF shstrndx", } } var , int switch .Class { case ELFCLASS32: = 8 * 4 = 10 * 4 case ELFCLASS64: = 2*4 + 6*8 = 4*4 + 6*8 } if > 0 && < { return nil, &FormatError{0, "invalid ELF phentsize", } } // Read program headers .Progs = make([]*Prog, ) for := 0; < ; ++ { := + int64()*int64() .Seek(, io.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, } } if int64(.Off) < 0 { return nil, &FormatError{, "invalid program header offset", .Off} } if int64(.Filesz) < 0 { return nil, &FormatError{, "invalid program header file size", .Filesz} } .sr = io.NewSectionReader(, int64(.Off), int64(.Filesz)) .ReaderAt = .sr .Progs[] = } // If the number of sections is greater than or equal to SHN_LORESERVE // (0xff00), shnum has the value zero and the actual number of section // header table entries is contained in the sh_size field of the section // header at index 0. if > 0 && == 0 { var , uint32 .Seek(, io.SeekStart) switch .Class { case ELFCLASS32: := new(Section32) if := binary.Read(, .ByteOrder, ); != nil { return nil, } = int(.Size) = .Type = .Link case ELFCLASS64: := new(Section64) if := binary.Read(, .ByteOrder, ); != nil { return nil, } = int(.Size) = .Type = .Link } if SectionType() != SHT_NULL { return nil, &FormatError{, "invalid type of the initial section", SectionType()} } if < int(SHN_LORESERVE) { return nil, &FormatError{, "invalid ELF shnum contained in sh_size", } } // If the section name string table section index is greater than or // equal to SHN_LORESERVE (0xff00), this member has the value // SHN_XINDEX (0xffff) and the actual index of the section name // string table section is contained in the sh_link field of the // section header at index 0. if == int(SHN_XINDEX) { = int() if < int(SHN_LORESERVE) { return nil, &FormatError{, "invalid ELF shstrndx contained in sh_link", } } } } if > 0 && < { return nil, &FormatError{0, "invalid ELF shentsize", } } // Read section headers := saferio.SliceCap[Section](uint64()) if < 0 { return nil, &FormatError{0, "too many sections", } } .Sections = make([]*Section, 0, ) := make([]uint32, 0, ) for := 0; < ; ++ { := + int64()*int64() .Seek(, io.SeekStart) := new(Section) switch .Class { case ELFCLASS32: := new(Section32) if := binary.Read(, .ByteOrder, ); != nil { return nil, } = append(, .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, } = append(, .Name) .SectionHeader = SectionHeader{ Type: SectionType(.Type), Flags: SectionFlag(.Flags), Offset: .Off, FileSize: .Size, Addr: .Addr, Link: .Link, Info: .Info, Addralign: .Addralign, Entsize: .Entsize, } } if int64(.Offset) < 0 { return nil, &FormatError{, "invalid section offset", int64(.Offset)} } if int64(.FileSize) < 0 { return nil, &FormatError{, "invalid section size", int64(.FileSize)} } .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 = append(.Sections, ) } if len(.Sections) == 0 { return , nil } // Load section header string table. if == 0 { // If the file has no section name string table, // shstrndx holds the value SHN_UNDEF (0). return , nil } := .Sections[] if .Type != SHT_STRTAB { return nil, &FormatError{ + int64(*), "invalid ELF section name string table type", .Type} } , := .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, fmt.Errorf("cannot load symbol section: %w", ) } if len() == 0 { return nil, nil, errors.New("symbol section is empty") } 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, fmt.Errorf("cannot load string table section: %w", ) } // The first entry is all zeros. = [Sym32Size:] := make([]Symbol, len()/Sym32Size) := 0 var Sym32 for len() > 0 { .Name = .ByteOrder.Uint32([0:4]) .Value = .ByteOrder.Uint32([4:8]) .Size = .ByteOrder.Uint32([8:12]) .Info = [12] .Other = [13] .Shndx = .ByteOrder.Uint16([14:16]) , := getString(, int(.Name)) [].Name = [].Info = .Info [].Other = .Other [].Section = SectionIndex(.Shndx) [].Value = uint64(.Value) [].Size = uint64(.Size) ++ = [Sym32Size:] } return , , nil } func ( *File) ( SectionType) ([]Symbol, []byte, error) { := .SectionByType() if == nil { return nil, nil, ErrNoSymbols } , := .Data() if != nil { return nil, nil, fmt.Errorf("cannot load symbol section: %w", ) } 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, fmt.Errorf("cannot load string table section: %w", ) } // The first entry is all zeros. = [Sym64Size:] := make([]Symbol, len()/Sym64Size) := 0 var Sym64 for len() > 0 { .Name = .ByteOrder.Uint32([0:4]) .Info = [4] .Other = [5] .Shndx = .ByteOrder.Uint16([6:8]) .Value = .ByteOrder.Uint64([8:16]) .Size = .ByteOrder.Uint64([16:24]) , := getString(, int(.Name)) [].Name = [].Info = .Info [].Other = .Other [].Section = SectionIndex(.Shndx) [].Value = .Value [].Size = .Size ++ = [Sym64Size:] } 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_LOONGARCH: return .applyRelocationsLOONG64(, ) 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, &) var uint64 var R_LARCH = .Info >> 32 = R_LARCH(.Info & 0xffff) if == 0 || > uint64(len()) { continue } := &[-1] if !canApplyRelocation() { continue } switch { case R_LARCH_64: if .Off+8 >= uint64(len()) || .Addend < 0 { continue } := .Value + uint64(.Addend) .ByteOrder.PutUint64([.Off:.Off+8], ) case R_LARCH_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 .Type == ET_EXEC { // Do not apply relocations to DWARF sections for ET_EXEC binaries. // Relocations should already be applied, and .rela sections may // contain incorrect data. 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 [File.Symbols] will have // initialized [Version] and Library fields. // // For compatibility with [File.Symbols], [File.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; skip undef entry at beginning. = ( + 1) * 2 if >= len(.gnuVersym) { return } := .gnuVersym[:] if len() < 2 { return } := int(.ByteOrder.Uint16()) 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, } := 8 if .Class == ELFCLASS64 { = 16 } if len()% != 0 { return nil, errors.New("length of dynamic section is not a multiple of dynamic entry size") } , := .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 } // DynValue returns the values listed for the given tag in the file's dynamic // section. func ( *File) ( DynTag) ([]uint64, error) { := .SectionByType(SHT_DYNAMIC) if == nil { return nil, nil } , := .Data() if != nil { return nil, } := 8 if .Class == ELFCLASS64 { = 16 } if len()% != 0 { return nil, errors.New("length of dynamic section is not a multiple of dynamic entry size") } // Parse the .dynamic section as a string of bytes. var []uint64 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 == { = append(, ) } } return , nil } type nobitsSectionReader struct{} func (*nobitsSectionReader) ( []byte, int64) ( int, error) { return 0, errors.New("unexpected read from SHT_NOBITS section") }