libgo: Update to Go 1.3 release.
[official-gcc.git] / libgo / go / debug / elf / file.go
blob38269aaf4ab34598645ee753687ef772d94cd0a7
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.
6 package elf
8 import (
9 "bytes"
10 "debug/dwarf"
11 "encoding/binary"
12 "errors"
13 "fmt"
14 "io"
15 "os"
18 // TODO: error reporting detail
21 * Internal ELF representation
24 // A FileHeader represents an ELF file header.
25 type FileHeader struct {
26 Class Class
27 Data Data
28 Version Version
29 OSABI OSABI
30 ABIVersion uint8
31 ByteOrder binary.ByteOrder
32 Type Type
33 Machine Machine
34 Entry uint64
37 // A File represents an open ELF file.
38 type File struct {
39 FileHeader
40 Sections []*Section
41 Progs []*Prog
42 closer io.Closer
43 gnuNeed []verneed
44 gnuVersym []byte
47 // A SectionHeader represents a single ELF section header.
48 type SectionHeader struct {
49 Name string
50 Type SectionType
51 Flags SectionFlag
52 Addr uint64
53 Offset uint64
54 Size uint64
55 Link uint32
56 Info uint32
57 Addralign uint64
58 Entsize uint64
61 // A Section represents a single section in an ELF file.
62 type Section struct {
63 SectionHeader
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.
71 io.ReaderAt
72 sr *io.SectionReader
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)
79 if n == len(dat) {
80 err = nil
82 return dat[0:n], err
85 // stringTable reads and returns the string table given by the
86 // specified link value.
87 func (f *File) stringTable(link uint32) ([]byte, error) {
88 if link <= 0 || link >= uint32(len(f.Sections)) {
89 return nil, errors.New("section has invalid string table link")
91 return f.Sections[link].Data()
94 // Open returns a new ReadSeeker reading the ELF section.
95 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
97 // A ProgHeader represents a single ELF program header.
98 type ProgHeader struct {
99 Type ProgType
100 Flags ProgFlag
101 Off uint64
102 Vaddr uint64
103 Paddr uint64
104 Filesz uint64
105 Memsz uint64
106 Align uint64
109 // A Prog represents a single ELF program header in an ELF binary.
110 type Prog struct {
111 ProgHeader
113 // Embed ReaderAt for ReadAt method.
114 // Do not embed SectionReader directly
115 // to avoid having Read and Seek.
116 // If a client wants Read and Seek it must use
117 // Open() to avoid fighting over the seek offset
118 // with other clients.
119 io.ReaderAt
120 sr *io.SectionReader
123 // Open returns a new ReadSeeker reading the ELF program body.
124 func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) }
126 // A Symbol represents an entry in an ELF symbol table section.
127 type Symbol struct {
128 Name string
129 Info, Other byte
130 Section SectionIndex
131 Value, Size uint64
135 * ELF reader
138 type FormatError struct {
139 off int64
140 msg string
141 val interface{}
144 func (e *FormatError) Error() string {
145 msg := e.msg
146 if e.val != nil {
147 msg += fmt.Sprintf(" '%v' ", e.val)
149 msg += fmt.Sprintf("in record at byte %#x", e.off)
150 return msg
153 // Open opens the named file using os.Open and prepares it for use as an ELF binary.
154 func Open(name string) (*File, error) {
155 f, err := os.Open(name)
156 if err != nil {
157 return nil, err
159 ff, err := NewFile(f)
160 if err != nil {
161 f.Close()
162 return nil, err
164 ff.closer = f
165 return ff, nil
168 // Close closes the File.
169 // If the File was created using NewFile directly instead of Open,
170 // Close has no effect.
171 func (f *File) Close() error {
172 var err error
173 if f.closer != nil {
174 err = f.closer.Close()
175 f.closer = nil
177 return err
180 // SectionByType returns the first section in f with the
181 // given type, or nil if there is no such section.
182 func (f *File) SectionByType(typ SectionType) *Section {
183 for _, s := range f.Sections {
184 if s.Type == typ {
185 return s
188 return nil
191 // NewFile creates a new File for accessing an ELF binary in an underlying reader.
192 // The ELF binary is expected to start at position 0 in the ReaderAt.
193 func NewFile(r io.ReaderAt) (*File, error) {
194 sr := io.NewSectionReader(r, 0, 1<<63-1)
195 // Read and decode ELF identifier
196 var ident [16]uint8
197 if _, err := r.ReadAt(ident[0:], 0); err != nil {
198 return nil, err
200 if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' {
201 return nil, &FormatError{0, "bad magic number", ident[0:4]}
204 f := new(File)
205 f.Class = Class(ident[EI_CLASS])
206 switch f.Class {
207 case ELFCLASS32:
208 case ELFCLASS64:
209 // ok
210 default:
211 return nil, &FormatError{0, "unknown ELF class", f.Class}
214 f.Data = Data(ident[EI_DATA])
215 switch f.Data {
216 case ELFDATA2LSB:
217 f.ByteOrder = binary.LittleEndian
218 case ELFDATA2MSB:
219 f.ByteOrder = binary.BigEndian
220 default:
221 return nil, &FormatError{0, "unknown ELF data encoding", f.Data}
224 f.Version = Version(ident[EI_VERSION])
225 if f.Version != EV_CURRENT {
226 return nil, &FormatError{0, "unknown ELF version", f.Version}
229 f.OSABI = OSABI(ident[EI_OSABI])
230 f.ABIVersion = ident[EI_ABIVERSION]
232 // Read ELF file header
233 var phoff int64
234 var phentsize, phnum int
235 var shoff int64
236 var shentsize, shnum, shstrndx int
237 shstrndx = -1
238 switch f.Class {
239 case ELFCLASS32:
240 hdr := new(Header32)
241 sr.Seek(0, os.SEEK_SET)
242 if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
243 return nil, err
245 f.Type = Type(hdr.Type)
246 f.Machine = Machine(hdr.Machine)
247 f.Entry = uint64(hdr.Entry)
248 if v := Version(hdr.Version); v != f.Version {
249 return nil, &FormatError{0, "mismatched ELF version", v}
251 phoff = int64(hdr.Phoff)
252 phentsize = int(hdr.Phentsize)
253 phnum = int(hdr.Phnum)
254 shoff = int64(hdr.Shoff)
255 shentsize = int(hdr.Shentsize)
256 shnum = int(hdr.Shnum)
257 shstrndx = int(hdr.Shstrndx)
258 case ELFCLASS64:
259 hdr := new(Header64)
260 sr.Seek(0, os.SEEK_SET)
261 if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
262 return nil, err
264 f.Type = Type(hdr.Type)
265 f.Machine = Machine(hdr.Machine)
266 f.Entry = uint64(hdr.Entry)
267 if v := Version(hdr.Version); v != f.Version {
268 return nil, &FormatError{0, "mismatched ELF version", v}
270 phoff = int64(hdr.Phoff)
271 phentsize = int(hdr.Phentsize)
272 phnum = int(hdr.Phnum)
273 shoff = int64(hdr.Shoff)
274 shentsize = int(hdr.Shentsize)
275 shnum = int(hdr.Shnum)
276 shstrndx = int(hdr.Shstrndx)
279 if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) {
280 return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}
283 // Read program headers
284 f.Progs = make([]*Prog, phnum)
285 for i := 0; i < phnum; i++ {
286 off := phoff + int64(i)*int64(phentsize)
287 sr.Seek(off, os.SEEK_SET)
288 p := new(Prog)
289 switch f.Class {
290 case ELFCLASS32:
291 ph := new(Prog32)
292 if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
293 return nil, err
295 p.ProgHeader = ProgHeader{
296 Type: ProgType(ph.Type),
297 Flags: ProgFlag(ph.Flags),
298 Off: uint64(ph.Off),
299 Vaddr: uint64(ph.Vaddr),
300 Paddr: uint64(ph.Paddr),
301 Filesz: uint64(ph.Filesz),
302 Memsz: uint64(ph.Memsz),
303 Align: uint64(ph.Align),
305 case ELFCLASS64:
306 ph := new(Prog64)
307 if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
308 return nil, err
310 p.ProgHeader = ProgHeader{
311 Type: ProgType(ph.Type),
312 Flags: ProgFlag(ph.Flags),
313 Off: uint64(ph.Off),
314 Vaddr: uint64(ph.Vaddr),
315 Paddr: uint64(ph.Paddr),
316 Filesz: uint64(ph.Filesz),
317 Memsz: uint64(ph.Memsz),
318 Align: uint64(ph.Align),
321 p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
322 p.ReaderAt = p.sr
323 f.Progs[i] = p
326 // Read section headers
327 f.Sections = make([]*Section, shnum)
328 names := make([]uint32, shnum)
329 for i := 0; i < shnum; i++ {
330 off := shoff + int64(i)*int64(shentsize)
331 sr.Seek(off, os.SEEK_SET)
332 s := new(Section)
333 switch f.Class {
334 case ELFCLASS32:
335 sh := new(Section32)
336 if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
337 return nil, err
339 names[i] = sh.Name
340 s.SectionHeader = SectionHeader{
341 Type: SectionType(sh.Type),
342 Flags: SectionFlag(sh.Flags),
343 Addr: uint64(sh.Addr),
344 Offset: uint64(sh.Off),
345 Size: uint64(sh.Size),
346 Link: uint32(sh.Link),
347 Info: uint32(sh.Info),
348 Addralign: uint64(sh.Addralign),
349 Entsize: uint64(sh.Entsize),
351 case ELFCLASS64:
352 sh := new(Section64)
353 if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
354 return nil, err
356 names[i] = sh.Name
357 s.SectionHeader = SectionHeader{
358 Type: SectionType(sh.Type),
359 Flags: SectionFlag(sh.Flags),
360 Offset: uint64(sh.Off),
361 Size: uint64(sh.Size),
362 Addr: uint64(sh.Addr),
363 Link: uint32(sh.Link),
364 Info: uint32(sh.Info),
365 Addralign: uint64(sh.Addralign),
366 Entsize: uint64(sh.Entsize),
369 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size))
370 s.ReaderAt = s.sr
371 f.Sections[i] = s
374 if len(f.Sections) == 0 {
375 return f, nil
378 // Load section header string table.
379 shstrtab, err := f.Sections[shstrndx].Data()
380 if err != nil {
381 return nil, err
383 for i, s := range f.Sections {
384 var ok bool
385 s.Name, ok = getString(shstrtab, int(names[i]))
386 if !ok {
387 return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]}
391 return f, nil
394 // getSymbols returns a slice of Symbols from parsing the symbol table
395 // with the given type, along with the associated string table.
396 func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) {
397 switch f.Class {
398 case ELFCLASS64:
399 return f.getSymbols64(typ)
401 case ELFCLASS32:
402 return f.getSymbols32(typ)
405 return nil, nil, errors.New("not implemented")
408 func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {
409 symtabSection := f.SectionByType(typ)
410 if symtabSection == nil {
411 return nil, nil, errors.New("no symbol section")
414 data, err := symtabSection.Data()
415 if err != nil {
416 return nil, nil, errors.New("cannot load symbol section")
418 symtab := bytes.NewReader(data)
419 if symtab.Len()%Sym32Size != 0 {
420 return nil, nil, errors.New("length of symbol section is not a multiple of SymSize")
423 strdata, err := f.stringTable(symtabSection.Link)
424 if err != nil {
425 return nil, nil, errors.New("cannot load string table section")
428 // The first entry is all zeros.
429 var skip [Sym32Size]byte
430 symtab.Read(skip[:])
432 symbols := make([]Symbol, symtab.Len()/Sym32Size)
434 i := 0
435 var sym Sym32
436 for symtab.Len() > 0 {
437 binary.Read(symtab, f.ByteOrder, &sym)
438 str, _ := getString(strdata, int(sym.Name))
439 symbols[i].Name = str
440 symbols[i].Info = sym.Info
441 symbols[i].Other = sym.Other
442 symbols[i].Section = SectionIndex(sym.Shndx)
443 symbols[i].Value = uint64(sym.Value)
444 symbols[i].Size = uint64(sym.Size)
448 return symbols, strdata, nil
451 func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
452 symtabSection := f.SectionByType(typ)
453 if symtabSection == nil {
454 return nil, nil, errors.New("no symbol section")
457 data, err := symtabSection.Data()
458 if err != nil {
459 return nil, nil, errors.New("cannot load symbol section")
461 symtab := bytes.NewReader(data)
462 if symtab.Len()%Sym64Size != 0 {
463 return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
466 strdata, err := f.stringTable(symtabSection.Link)
467 if err != nil {
468 return nil, nil, errors.New("cannot load string table section")
471 // The first entry is all zeros.
472 var skip [Sym64Size]byte
473 symtab.Read(skip[:])
475 symbols := make([]Symbol, symtab.Len()/Sym64Size)
477 i := 0
478 var sym Sym64
479 for symtab.Len() > 0 {
480 binary.Read(symtab, f.ByteOrder, &sym)
481 str, _ := getString(strdata, int(sym.Name))
482 symbols[i].Name = str
483 symbols[i].Info = sym.Info
484 symbols[i].Other = sym.Other
485 symbols[i].Section = SectionIndex(sym.Shndx)
486 symbols[i].Value = sym.Value
487 symbols[i].Size = sym.Size
491 return symbols, strdata, nil
494 // getString extracts a string from an ELF string table.
495 func getString(section []byte, start int) (string, bool) {
496 if start < 0 || start >= len(section) {
497 return "", false
500 for end := start; end < len(section); end++ {
501 if section[end] == 0 {
502 return string(section[start:end]), true
505 return "", false
508 // Section returns a section with the given name, or nil if no such
509 // section exists.
510 func (f *File) Section(name string) *Section {
511 for _, s := range f.Sections {
512 if s.Name == name {
513 return s
516 return nil
519 // applyRelocations applies relocations to dst. rels is a relocations section
520 // in RELA format.
521 func (f *File) applyRelocations(dst []byte, rels []byte) error {
522 if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 {
523 return f.applyRelocationsAMD64(dst, rels)
525 if f.Class == ELFCLASS32 && f.Machine == EM_386 {
526 return f.applyRelocations386(dst, rels)
529 return errors.New("not implemented")
532 func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
533 // 24 is the size of Rela64.
534 if len(rels)%24 != 0 {
535 return errors.New("length of relocation section is not a multiple of 24")
538 symbols, _, err := f.getSymbols(SHT_SYMTAB)
539 if err != nil {
540 return err
543 b := bytes.NewReader(rels)
544 var rela Rela64
546 for b.Len() > 0 {
547 binary.Read(b, f.ByteOrder, &rela)
548 symNo := rela.Info >> 32
549 t := R_X86_64(rela.Info & 0xffff)
551 if symNo == 0 || symNo > uint64(len(symbols)) {
552 continue
554 sym := &symbols[symNo-1]
555 if SymType(sym.Info&0xf) != STT_SECTION {
556 // We don't handle non-section relocations for now.
557 continue
560 switch t {
561 case R_X86_64_64:
562 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
563 continue
565 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
566 case R_X86_64_32:
567 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
568 continue
570 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
574 return nil
577 func (f *File) applyRelocations386(dst []byte, rels []byte) error {
578 // 8 is the size of Rel32.
579 if len(rels)%8 != 0 {
580 return errors.New("length of relocation section is not a multiple of 8")
583 symbols, _, err := f.getSymbols(SHT_SYMTAB)
584 if err != nil {
585 return err
588 b := bytes.NewReader(rels)
589 var rel Rel32
591 for b.Len() > 0 {
592 binary.Read(b, f.ByteOrder, &rel)
593 symNo := rel.Info >> 8
594 t := R_386(rel.Info & 0xff)
596 if symNo == 0 || symNo > uint32(len(symbols)) {
597 continue
599 sym := &symbols[symNo-1]
601 if t == R_386_32 {
602 if rel.Off+4 >= uint32(len(dst)) {
603 continue
605 val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
606 val += uint32(sym.Value)
607 f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
611 return nil
614 func (f *File) DWARF() (*dwarf.Data, error) {
615 // There are many other DWARF sections, but these
616 // are the required ones, and the debug/dwarf package
617 // does not use the others, so don't bother loading them.
618 var names = [...]string{"abbrev", "info", "line", "ranges", "str"}
619 var dat [len(names)][]byte
620 for i, name := range names {
621 name = ".debug_" + name
622 s := f.Section(name)
623 if s == nil {
624 continue
626 b, err := s.Data()
627 if err != nil && uint64(len(b)) < s.Size {
628 return nil, err
630 dat[i] = b
633 // If there's a relocation table for .debug_info, we have to process it
634 // now otherwise the data in .debug_info is invalid for x86-64 objects.
635 rela := f.Section(".rela.debug_info")
636 if rela != nil && rela.Type == SHT_RELA && f.Machine == EM_X86_64 {
637 data, err := rela.Data()
638 if err != nil {
639 return nil, err
641 err = f.applyRelocations(dat[1], data)
642 if err != nil {
643 return nil, err
647 // When using clang we need to process relocations even for 386.
648 rel := f.Section(".rel.debug_info")
649 if rel != nil && rel.Type == SHT_REL && f.Machine == EM_386 {
650 data, err := rel.Data()
651 if err != nil {
652 return nil, err
654 err = f.applyRelocations(dat[1], data)
655 if err != nil {
656 return nil, err
660 abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
661 d, err := dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
662 if err != nil {
663 return nil, err
666 // Look for DWARF4 .debug_types sections.
667 for i, s := range f.Sections {
668 if s.Name == ".debug_types" {
669 b, err := s.Data()
670 if err != nil && uint64(len(b)) < s.Size {
671 return nil, err
674 for _, r := range f.Sections {
675 if r.Type != SHT_RELA && r.Type != SHT_REL {
676 continue
678 if int(r.Info) != i {
679 continue
681 rd, err := r.Data()
682 if err != nil {
683 return nil, err
685 err = f.applyRelocations(b, rd)
686 if err != nil {
687 return nil, err
691 err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
692 if err != nil {
693 return nil, err
698 return d, nil
701 // Symbols returns the symbol table for f.
703 // For compatibility with Go 1.0, Symbols omits the null symbol at index 0.
704 // After retrieving the symbols as symtab, an externally supplied index x
705 // corresponds to symtab[x-1], not symtab[x].
706 func (f *File) Symbols() ([]Symbol, error) {
707 sym, _, err := f.getSymbols(SHT_SYMTAB)
708 return sym, err
711 type ImportedSymbol struct {
712 Name string
713 Version string
714 Library string
717 // ImportedSymbols returns the names of all symbols
718 // referred to by the binary f that are expected to be
719 // satisfied by other libraries at dynamic load time.
720 // It does not return weak symbols.
721 func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
722 sym, str, err := f.getSymbols(SHT_DYNSYM)
723 if err != nil {
724 return nil, err
726 f.gnuVersionInit(str)
727 var all []ImportedSymbol
728 for i, s := range sym {
729 if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {
730 all = append(all, ImportedSymbol{Name: s.Name})
731 f.gnuVersion(i, &all[len(all)-1])
734 return all, nil
737 type verneed struct {
738 File string
739 Name string
742 // gnuVersionInit parses the GNU version tables
743 // for use by calls to gnuVersion.
744 func (f *File) gnuVersionInit(str []byte) {
745 // Accumulate verneed information.
746 vn := f.SectionByType(SHT_GNU_VERNEED)
747 if vn == nil {
748 return
750 d, _ := vn.Data()
752 var need []verneed
753 i := 0
754 for {
755 if i+16 > len(d) {
756 break
758 vers := f.ByteOrder.Uint16(d[i : i+2])
759 if vers != 1 {
760 break
762 cnt := f.ByteOrder.Uint16(d[i+2 : i+4])
763 fileoff := f.ByteOrder.Uint32(d[i+4 : i+8])
764 aux := f.ByteOrder.Uint32(d[i+8 : i+12])
765 next := f.ByteOrder.Uint32(d[i+12 : i+16])
766 file, _ := getString(str, int(fileoff))
768 var name string
769 j := i + int(aux)
770 for c := 0; c < int(cnt); c++ {
771 if j+16 > len(d) {
772 break
774 // hash := f.ByteOrder.Uint32(d[j:j+4])
775 // flags := f.ByteOrder.Uint16(d[j+4:j+6])
776 other := f.ByteOrder.Uint16(d[j+6 : j+8])
777 nameoff := f.ByteOrder.Uint32(d[j+8 : j+12])
778 next := f.ByteOrder.Uint32(d[j+12 : j+16])
779 name, _ = getString(str, int(nameoff))
780 ndx := int(other)
781 if ndx >= len(need) {
782 a := make([]verneed, 2*(ndx+1))
783 copy(a, need)
784 need = a
787 need[ndx] = verneed{file, name}
788 if next == 0 {
789 break
791 j += int(next)
794 if next == 0 {
795 break
797 i += int(next)
800 // Versym parallels symbol table, indexing into verneed.
801 vs := f.SectionByType(SHT_GNU_VERSYM)
802 if vs == nil {
803 return
805 d, _ = vs.Data()
807 f.gnuNeed = need
808 f.gnuVersym = d
811 // gnuVersion adds Library and Version information to sym,
812 // which came from offset i of the symbol table.
813 func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
814 // Each entry is two bytes.
815 i = (i + 1) * 2
816 if i >= len(f.gnuVersym) {
817 return
819 j := int(f.ByteOrder.Uint16(f.gnuVersym[i:]))
820 if j < 2 || j >= len(f.gnuNeed) {
821 return
823 n := &f.gnuNeed[j]
824 sym.Library = n.File
825 sym.Version = n.Name
828 // ImportedLibraries returns the names of all libraries
829 // referred to by the binary f that are expected to be
830 // linked with the binary at dynamic link time.
831 func (f *File) ImportedLibraries() ([]string, error) {
832 return f.DynString(DT_NEEDED)
835 // DynString returns the strings listed for the given tag in the file's dynamic
836 // section.
838 // The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or
839 // DT_RUNPATH.
840 func (f *File) DynString(tag DynTag) ([]string, error) {
841 switch tag {
842 case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH:
843 default:
844 return nil, fmt.Errorf("non-string-valued tag %v", tag)
846 ds := f.SectionByType(SHT_DYNAMIC)
847 if ds == nil {
848 // not dynamic, so no libraries
849 return nil, nil
851 d, err := ds.Data()
852 if err != nil {
853 return nil, err
855 str, err := f.stringTable(ds.Link)
856 if err != nil {
857 return nil, err
859 var all []string
860 for len(d) > 0 {
861 var t DynTag
862 var v uint64
863 switch f.Class {
864 case ELFCLASS32:
865 t = DynTag(f.ByteOrder.Uint32(d[0:4]))
866 v = uint64(f.ByteOrder.Uint32(d[4:8]))
867 d = d[8:]
868 case ELFCLASS64:
869 t = DynTag(f.ByteOrder.Uint64(d[0:8]))
870 v = f.ByteOrder.Uint64(d[8:16])
871 d = d[16:]
873 if t == tag {
874 s, ok := getString(str, int(v))
875 if ok {
876 all = append(all, s)
880 return all, nil