1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package elf implements access to ELF object files.
18 // TODO: error reporting detail
21 * Internal ELF representation
24 // A FileHeader represents an ELF file header.
25 type FileHeader struct {
31 ByteOrder binary.ByteOrder
37 // A File represents an open ELF file.
47 // A SectionHeader represents a single ELF section header.
48 type SectionHeader struct {
61 // A Section represents a single section in an ELF file.
65 // Embed ReaderAt for ReadAt method.
66 // Do not embed SectionReader directly
67 // to avoid having Read and Seek.
68 // If a client wants Read and Seek it must use
69 // Open() to avoid fighting over the seek offset
70 // with other clients.
75 // Data reads and returns the contents of the ELF section.
76 func (s *Section) Data() ([]byte, error) {
77 dat := make([]byte, s.sr.Size())
78 n, err := s.sr.ReadAt(dat, 0)
82 // stringTable reads and returns the string table given by the
83 // specified link value.
84 func (f *File) stringTable(link uint32) ([]byte, error) {
85 if link <= 0 || link >= uint32(len(f.Sections)) {
86 return nil, errors.New("section has invalid string table link")
88 return f.Sections[link].Data()
91 // Open returns a new ReadSeeker reading the ELF section.
92 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
94 // A ProgHeader represents a single ELF program header.
95 type ProgHeader struct {
106 // A Prog represents a single ELF program header in an ELF binary.
110 // Embed ReaderAt for ReadAt method.
111 // Do not embed SectionReader directly
112 // to avoid having Read and Seek.
113 // If a client wants Read and Seek it must use
114 // Open() to avoid fighting over the seek offset
115 // with other clients.
120 // Open returns a new ReadSeeker reading the ELF program body.
121 func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) }
123 // A Symbol represents an entry in an ELF symbol table section.
135 type FormatError struct {
141 func (e *FormatError) Error() string {
144 msg += fmt.Sprintf(" '%v' ", e.val)
146 msg += fmt.Sprintf("in record at byte %#x", e.off)
150 // Open opens the named file using os.Open and prepares it for use as an ELF binary.
151 func Open(name string) (*File, error) {
152 f, err := os.Open(name)
156 ff, err := NewFile(f)
165 // Close closes the File.
166 // If the File was created using NewFile directly instead of Open,
167 // Close has no effect.
168 func (f *File) Close() error {
171 err = f.closer.Close()
177 // SectionByType returns the first section in f with the
178 // given type, or nil if there is no such section.
179 func (f *File) SectionByType(typ SectionType) *Section {
180 for _, s := range f.Sections {
188 // NewFile creates a new File for accessing an ELF binary in an underlying reader.
189 // The ELF binary is expected to start at position 0 in the ReaderAt.
190 func NewFile(r io.ReaderAt) (*File, error) {
191 sr := io.NewSectionReader(r, 0, 1<<63-1)
192 // Read and decode ELF identifier
194 if _, err := r.ReadAt(ident[0:], 0); err != nil {
197 if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' {
198 return nil, &FormatError{0, "bad magic number", ident[0:4]}
202 f.Class = Class(ident[EI_CLASS])
208 return nil, &FormatError{0, "unknown ELF class", f.Class}
211 f.Data = Data(ident[EI_DATA])
214 f.ByteOrder = binary.LittleEndian
216 f.ByteOrder = binary.BigEndian
218 return nil, &FormatError{0, "unknown ELF data encoding", f.Data}
221 f.Version = Version(ident[EI_VERSION])
222 if f.Version != EV_CURRENT {
223 return nil, &FormatError{0, "unknown ELF version", f.Version}
226 f.OSABI = OSABI(ident[EI_OSABI])
227 f.ABIVersion = ident[EI_ABIVERSION]
229 // Read ELF file header
231 var phentsize, phnum int
233 var shentsize, shnum, shstrndx int
238 sr.Seek(0, os.SEEK_SET)
239 if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
242 f.Type = Type(hdr.Type)
243 f.Machine = Machine(hdr.Machine)
244 f.Entry = uint64(hdr.Entry)
245 if v := Version(hdr.Version); v != f.Version {
246 return nil, &FormatError{0, "mismatched ELF version", v}
248 phoff = int64(hdr.Phoff)
249 phentsize = int(hdr.Phentsize)
250 phnum = int(hdr.Phnum)
251 shoff = int64(hdr.Shoff)
252 shentsize = int(hdr.Shentsize)
253 shnum = int(hdr.Shnum)
254 shstrndx = int(hdr.Shstrndx)
257 sr.Seek(0, os.SEEK_SET)
258 if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
261 f.Type = Type(hdr.Type)
262 f.Machine = Machine(hdr.Machine)
263 f.Entry = uint64(hdr.Entry)
264 if v := Version(hdr.Version); v != f.Version {
265 return nil, &FormatError{0, "mismatched ELF version", v}
267 phoff = int64(hdr.Phoff)
268 phentsize = int(hdr.Phentsize)
269 phnum = int(hdr.Phnum)
270 shoff = int64(hdr.Shoff)
271 shentsize = int(hdr.Shentsize)
272 shnum = int(hdr.Shnum)
273 shstrndx = int(hdr.Shstrndx)
276 if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) {
277 return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}
280 // Read program headers
281 f.Progs = make([]*Prog, phnum)
282 for i := 0; i < phnum; i++ {
283 off := phoff + int64(i)*int64(phentsize)
284 sr.Seek(off, os.SEEK_SET)
289 if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
292 p.ProgHeader = ProgHeader{
293 Type: ProgType(ph.Type),
294 Flags: ProgFlag(ph.Flags),
296 Vaddr: uint64(ph.Vaddr),
297 Paddr: uint64(ph.Paddr),
298 Filesz: uint64(ph.Filesz),
299 Memsz: uint64(ph.Memsz),
300 Align: uint64(ph.Align),
304 if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
307 p.ProgHeader = ProgHeader{
308 Type: ProgType(ph.Type),
309 Flags: ProgFlag(ph.Flags),
311 Vaddr: uint64(ph.Vaddr),
312 Paddr: uint64(ph.Paddr),
313 Filesz: uint64(ph.Filesz),
314 Memsz: uint64(ph.Memsz),
315 Align: uint64(ph.Align),
318 p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
323 // Read section headers
324 f.Sections = make([]*Section, shnum)
325 names := make([]uint32, shnum)
326 for i := 0; i < shnum; i++ {
327 off := shoff + int64(i)*int64(shentsize)
328 sr.Seek(off, os.SEEK_SET)
333 if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
337 s.SectionHeader = SectionHeader{
338 Type: SectionType(sh.Type),
339 Flags: SectionFlag(sh.Flags),
340 Addr: uint64(sh.Addr),
341 Offset: uint64(sh.Off),
342 Size: uint64(sh.Size),
343 Link: uint32(sh.Link),
344 Info: uint32(sh.Info),
345 Addralign: uint64(sh.Addralign),
346 Entsize: uint64(sh.Entsize),
350 if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
354 s.SectionHeader = SectionHeader{
355 Type: SectionType(sh.Type),
356 Flags: SectionFlag(sh.Flags),
357 Offset: uint64(sh.Off),
358 Size: uint64(sh.Size),
359 Addr: uint64(sh.Addr),
360 Link: uint32(sh.Link),
361 Info: uint32(sh.Info),
362 Addralign: uint64(sh.Addralign),
363 Entsize: uint64(sh.Entsize),
366 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size))
371 if len(f.Sections) == 0 {
375 // Load section header string table.
376 shstrtab, err := f.Sections[shstrndx].Data()
380 for i, s := range f.Sections {
382 s.Name, ok = getString(shstrtab, int(names[i]))
384 return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]}
391 // getSymbols returns a slice of Symbols from parsing the symbol table
392 // with the given type, along with the associated string table.
393 func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) {
396 return f.getSymbols64(typ)
399 return f.getSymbols32(typ)
402 return nil, nil, errors.New("not implemented")
405 func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {
406 symtabSection := f.SectionByType(typ)
407 if symtabSection == nil {
408 return nil, nil, errors.New("no symbol section")
411 data, err := symtabSection.Data()
413 return nil, nil, errors.New("cannot load symbol section")
415 symtab := bytes.NewBuffer(data)
416 if symtab.Len()%Sym32Size != 0 {
417 return nil, nil, errors.New("length of symbol section is not a multiple of SymSize")
420 strdata, err := f.stringTable(symtabSection.Link)
422 return nil, nil, errors.New("cannot load string table section")
425 symbols := make([]Symbol, symtab.Len()/Sym32Size)
429 for symtab.Len() > 0 {
430 binary.Read(symtab, f.ByteOrder, &sym)
431 str, _ := getString(strdata, int(sym.Name))
432 symbols[i].Name = str
433 symbols[i].Info = sym.Info
434 symbols[i].Other = sym.Other
435 symbols[i].Section = SectionIndex(sym.Shndx)
436 symbols[i].Value = uint64(sym.Value)
437 symbols[i].Size = uint64(sym.Size)
441 return symbols, strdata, nil
444 func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
445 symtabSection := f.SectionByType(typ)
446 if symtabSection == nil {
447 return nil, nil, errors.New("no symbol section")
450 data, err := symtabSection.Data()
452 return nil, nil, errors.New("cannot load symbol section")
454 symtab := bytes.NewBuffer(data)
455 if symtab.Len()%Sym64Size != 0 {
456 return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
459 strdata, err := f.stringTable(symtabSection.Link)
461 return nil, nil, errors.New("cannot load string table section")
464 symbols := make([]Symbol, symtab.Len()/Sym64Size)
468 for symtab.Len() > 0 {
469 binary.Read(symtab, f.ByteOrder, &sym)
470 str, _ := getString(strdata, int(sym.Name))
471 symbols[i].Name = str
472 symbols[i].Info = sym.Info
473 symbols[i].Other = sym.Other
474 symbols[i].Section = SectionIndex(sym.Shndx)
475 symbols[i].Value = sym.Value
476 symbols[i].Size = sym.Size
480 return symbols, strdata, nil
483 // getString extracts a string from an ELF string table.
484 func getString(section []byte, start int) (string, bool) {
485 if start < 0 || start >= len(section) {
489 for end := start; end < len(section); end++ {
490 if section[end] == 0 {
491 return string(section[start:end]), true
497 // Section returns a section with the given name, or nil if no such
499 func (f *File) Section(name string) *Section {
500 for _, s := range f.Sections {
508 // applyRelocations applies relocations to dst. rels is a relocations section
510 func (f *File) applyRelocations(dst []byte, rels []byte) error {
511 if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 {
512 return f.applyRelocationsAMD64(dst, rels)
515 return errors.New("not implemented")
518 func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
519 if len(rels)%Sym64Size != 0 {
520 return errors.New("length of relocation section is not a multiple of Sym64Size")
523 symbols, _, err := f.getSymbols(SHT_SYMTAB)
528 b := bytes.NewBuffer(rels)
532 binary.Read(b, f.ByteOrder, &rela)
533 symNo := rela.Info >> 32
534 t := R_X86_64(rela.Info & 0xffff)
536 if symNo >= uint64(len(symbols)) {
539 sym := &symbols[symNo]
540 if SymType(sym.Info&0xf) != STT_SECTION {
541 // We don't handle non-section relocations for now.
547 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
550 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
552 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
555 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
562 func (f *File) DWARF() (*dwarf.Data, error) {
563 // There are many other DWARF sections, but these
564 // are the required ones, and the debug/dwarf package
565 // does not use the others, so don't bother loading them.
566 var names = [...]string{"abbrev", "info", "line", "ranges", "str"}
567 var dat [len(names)][]byte
568 for i, name := range names {
569 name = ".debug_" + name
575 if err != nil && uint64(len(b)) < s.Size {
581 // If there's a relocation table for .debug_info, we have to process it
582 // now otherwise the data in .debug_info is invalid for x86-64 objects.
583 rela := f.Section(".rela.debug_info")
584 if rela != nil && rela.Type == SHT_RELA && f.Machine == EM_X86_64 {
585 data, err := rela.Data()
589 err = f.applyRelocations(dat[1], data)
595 abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
596 return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
599 // Symbols returns the symbol table for f.
600 func (f *File) Symbols() ([]Symbol, error) {
601 sym, _, err := f.getSymbols(SHT_SYMTAB)
605 type ImportedSymbol struct {
611 // ImportedSymbols returns the names of all symbols
612 // referred to by the binary f that are expected to be
613 // satisfied by other libraries at dynamic load time.
614 // It does not return weak symbols.
615 func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
616 sym, str, err := f.getSymbols(SHT_DYNSYM)
620 f.gnuVersionInit(str)
621 var all []ImportedSymbol
622 for i, s := range sym {
623 if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {
624 all = append(all, ImportedSymbol{Name: s.Name})
625 f.gnuVersion(i, &all[len(all)-1])
631 type verneed struct {
636 // gnuVersionInit parses the GNU version tables
637 // for use by calls to gnuVersion.
638 func (f *File) gnuVersionInit(str []byte) {
639 // Accumulate verneed information.
640 vn := f.SectionByType(SHT_GNU_VERNEED)
652 vers := f.ByteOrder.Uint16(d[i : i+2])
656 cnt := f.ByteOrder.Uint16(d[i+2 : i+4])
657 fileoff := f.ByteOrder.Uint32(d[i+4 : i+8])
658 aux := f.ByteOrder.Uint32(d[i+8 : i+12])
659 next := f.ByteOrder.Uint32(d[i+12 : i+16])
660 file, _ := getString(str, int(fileoff))
664 for c := 0; c < int(cnt); c++ {
668 // hash := f.ByteOrder.Uint32(d[j:j+4])
669 // flags := f.ByteOrder.Uint16(d[j+4:j+6])
670 other := f.ByteOrder.Uint16(d[j+6 : j+8])
671 nameoff := f.ByteOrder.Uint32(d[j+8 : j+12])
672 next := f.ByteOrder.Uint32(d[j+12 : j+16])
673 name, _ = getString(str, int(nameoff))
675 if ndx >= len(need) {
676 a := make([]verneed, 2*(ndx+1))
681 need[ndx] = verneed{file, name}
694 // Versym parallels symbol table, indexing into verneed.
695 vs := f.SectionByType(SHT_GNU_VERSYM)
705 // gnuVersion adds Library and Version information to sym,
706 // which came from offset i of the symbol table.
707 func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
708 // Each entry is two bytes.
710 if i >= len(f.gnuVersym) {
713 j := int(f.ByteOrder.Uint16(f.gnuVersym[i:]))
714 if j < 2 || j >= len(f.gnuNeed) {
722 // ImportedLibraries returns the names of all libraries
723 // referred to by the binary f that are expected to be
724 // linked with the binary at dynamic link time.
725 func (f *File) ImportedLibraries() ([]string, error) {
726 return f.DynString(DT_NEEDED)
729 // DynString returns the strings listed for the given tag in the file's dynamic
732 // The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or
734 func (f *File) DynString(tag DynTag) ([]string, error) {
736 case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH:
738 return nil, fmt.Errorf("non-string-valued tag %v", tag)
740 ds := f.SectionByType(SHT_DYNAMIC)
742 // not dynamic, so no libraries
749 str, err := f.stringTable(ds.Link)
759 t = DynTag(f.ByteOrder.Uint32(d[0:4]))
760 v = uint64(f.ByteOrder.Uint32(d[4:8]))
763 t = DynTag(f.ByteOrder.Uint64(d[0:8]))
764 v = f.ByteOrder.Uint64(d[8:16])
768 s, ok := getString(str, int(v))