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.
4
5// Package elf implements access to ELF object files.
6package elf
7
8import (
9	"bytes"
10	"debug/dwarf"
11	"encoding/binary"
12	"errors"
13	"fmt"
14	"io"
15	"os"
16)
17
18// TODO: error reporting detail
19
20/*
21 * Internal ELF representation
22 */
23
24// A FileHeader represents an ELF file header.
25type 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
35}
36
37// A File represents an open ELF file.
38type File struct {
39	FileHeader
40	Sections  []*Section
41	Progs     []*Prog
42	closer    io.Closer
43	gnuNeed   []verneed
44	gnuVersym []byte
45}
46
47// A SectionHeader represents a single ELF section header.
48type 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
59}
60
61// A Section represents a single section in an ELF file.
62type Section struct {
63	SectionHeader
64
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
73}
74
75// Data reads and returns the contents of the ELF section.
76func (s *Section) Data() ([]byte, error) {
77	dat := make([]byte, s.sr.Size())
78	n, err := s.sr.ReadAt(dat, 0)
79	return dat[0:n], err
80}
81
82// stringTable reads and returns the string table given by the
83// specified link value.
84func (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")
87	}
88	return f.Sections[link].Data()
89}
90
91// Open returns a new ReadSeeker reading the ELF section.
92func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
93
94// A ProgHeader represents a single ELF program header.
95type ProgHeader struct {
96	Type   ProgType
97	Flags  ProgFlag
98	Off    uint64
99	Vaddr  uint64
100	Paddr  uint64
101	Filesz uint64
102	Memsz  uint64
103	Align  uint64
104}
105
106// A Prog represents a single ELF program header in an ELF binary.
107type Prog struct {
108	ProgHeader
109
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.
116	io.ReaderAt
117	sr *io.SectionReader
118}
119
120// Open returns a new ReadSeeker reading the ELF program body.
121func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) }
122
123// A Symbol represents an entry in an ELF symbol table section.
124type Symbol struct {
125	Name        string
126	Info, Other byte
127	Section     SectionIndex
128	Value, Size uint64
129}
130
131/*
132 * ELF reader
133 */
134
135type FormatError struct {
136	off int64
137	msg string
138	val interface{}
139}
140
141func (e *FormatError) Error() string {
142	msg := e.msg
143	if e.val != nil {
144		msg += fmt.Sprintf(" '%v' ", e.val)
145	}
146	msg += fmt.Sprintf("in record at byte %#x", e.off)
147	return msg
148}
149
150// Open opens the named file using os.Open and prepares it for use as an ELF binary.
151func Open(name string) (*File, error) {
152	f, err := os.Open(name)
153	if err != nil {
154		return nil, err
155	}
156	ff, err := NewFile(f)
157	if err != nil {
158		f.Close()
159		return nil, err
160	}
161	ff.closer = f
162	return ff, nil
163}
164
165// Close closes the File.
166// If the File was created using NewFile directly instead of Open,
167// Close has no effect.
168func (f *File) Close() error {
169	var err error
170	if f.closer != nil {
171		err = f.closer.Close()
172		f.closer = nil
173	}
174	return err
175}
176
177// SectionByType returns the first section in f with the
178// given type, or nil if there is no such section.
179func (f *File) SectionByType(typ SectionType) *Section {
180	for _, s := range f.Sections {
181		if s.Type == typ {
182			return s
183		}
184	}
185	return nil
186}
187
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.
190func NewFile(r io.ReaderAt) (*File, error) {
191	sr := io.NewSectionReader(r, 0, 1<<63-1)
192	// Read and decode ELF identifier
193	var ident [16]uint8
194	if _, err := r.ReadAt(ident[0:], 0); err != nil {
195		return nil, err
196	}
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]}
199	}
200
201	f := new(File)
202	f.Class = Class(ident[EI_CLASS])
203	switch f.Class {
204	case ELFCLASS32:
205	case ELFCLASS64:
206		// ok
207	default:
208		return nil, &FormatError{0, "unknown ELF class", f.Class}
209	}
210
211	f.Data = Data(ident[EI_DATA])
212	switch f.Data {
213	case ELFDATA2LSB:
214		f.ByteOrder = binary.LittleEndian
215	case ELFDATA2MSB:
216		f.ByteOrder = binary.BigEndian
217	default:
218		return nil, &FormatError{0, "unknown ELF data encoding", f.Data}
219	}
220
221	f.Version = Version(ident[EI_VERSION])
222	if f.Version != EV_CURRENT {
223		return nil, &FormatError{0, "unknown ELF version", f.Version}
224	}
225
226	f.OSABI = OSABI(ident[EI_OSABI])
227	f.ABIVersion = ident[EI_ABIVERSION]
228
229	// Read ELF file header
230	var phoff int64
231	var phentsize, phnum int
232	var shoff int64
233	var shentsize, shnum, shstrndx int
234	shstrndx = -1
235	switch f.Class {
236	case ELFCLASS32:
237		hdr := new(Header32)
238		sr.Seek(0, os.SEEK_SET)
239		if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
240			return nil, err
241		}
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}
247		}
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)
255	case ELFCLASS64:
256		hdr := new(Header64)
257		sr.Seek(0, os.SEEK_SET)
258		if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
259			return nil, err
260		}
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}
266		}
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)
274	}
275
276	if shnum > 0 && shoff > 0 && (shstrndx < 0 || shstrndx >= shnum) {
277		return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}
278	}
279
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)
285		p := new(Prog)
286		switch f.Class {
287		case ELFCLASS32:
288			ph := new(Prog32)
289			if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
290				return nil, err
291			}
292			p.ProgHeader = ProgHeader{
293				Type:   ProgType(ph.Type),
294				Flags:  ProgFlag(ph.Flags),
295				Off:    uint64(ph.Off),
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),
301			}
302		case ELFCLASS64:
303			ph := new(Prog64)
304			if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
305				return nil, err
306			}
307			p.ProgHeader = ProgHeader{
308				Type:   ProgType(ph.Type),
309				Flags:  ProgFlag(ph.Flags),
310				Off:    uint64(ph.Off),
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),
316			}
317		}
318		p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
319		p.ReaderAt = p.sr
320		f.Progs[i] = p
321	}
322
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)
329		s := new(Section)
330		switch f.Class {
331		case ELFCLASS32:
332			sh := new(Section32)
333			if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
334				return nil, err
335			}
336			names[i] = sh.Name
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),
347			}
348		case ELFCLASS64:
349			sh := new(Section64)
350			if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
351				return nil, err
352			}
353			names[i] = sh.Name
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),
364			}
365		}
366		s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size))
367		s.ReaderAt = s.sr
368		f.Sections[i] = s
369	}
370
371	if len(f.Sections) == 0 {
372		return f, nil
373	}
374
375	// Load section header string table.
376	shstrtab, err := f.Sections[shstrndx].Data()
377	if err != nil {
378		return nil, err
379	}
380	for i, s := range f.Sections {
381		var ok bool
382		s.Name, ok = getString(shstrtab, int(names[i]))
383		if !ok {
384			return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]}
385		}
386	}
387
388	return f, nil
389}
390
391// getSymbols returns a slice of Symbols from parsing the symbol table
392// with the given type, along with the associated string table.
393func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) {
394	switch f.Class {
395	case ELFCLASS64:
396		return f.getSymbols64(typ)
397
398	case ELFCLASS32:
399		return f.getSymbols32(typ)
400	}
401
402	return nil, nil, errors.New("not implemented")
403}
404
405func (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")
409	}
410
411	data, err := symtabSection.Data()
412	if err != nil {
413		return nil, nil, errors.New("cannot load symbol section")
414	}
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")
418	}
419
420	strdata, err := f.stringTable(symtabSection.Link)
421	if err != nil {
422		return nil, nil, errors.New("cannot load string table section")
423	}
424
425	// The first entry is all zeros.
426	var skip [Sym32Size]byte
427	symtab.Read(skip[:])
428
429	symbols := make([]Symbol, symtab.Len()/Sym32Size)
430
431	i := 0
432	var sym Sym32
433	for symtab.Len() > 0 {
434		binary.Read(symtab, f.ByteOrder, &sym)
435		str, _ := getString(strdata, int(sym.Name))
436		symbols[i].Name = str
437		symbols[i].Info = sym.Info
438		symbols[i].Other = sym.Other
439		symbols[i].Section = SectionIndex(sym.Shndx)
440		symbols[i].Value = uint64(sym.Value)
441		symbols[i].Size = uint64(sym.Size)
442		i++
443	}
444
445	return symbols, strdata, nil
446}
447
448func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
449	symtabSection := f.SectionByType(typ)
450	if symtabSection == nil {
451		return nil, nil, errors.New("no symbol section")
452	}
453
454	data, err := symtabSection.Data()
455	if err != nil {
456		return nil, nil, errors.New("cannot load symbol section")
457	}
458	symtab := bytes.NewBuffer(data)
459	if symtab.Len()%Sym64Size != 0 {
460		return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
461	}
462
463	strdata, err := f.stringTable(symtabSection.Link)
464	if err != nil {
465		return nil, nil, errors.New("cannot load string table section")
466	}
467
468	// The first entry is all zeros.
469	var skip [Sym64Size]byte
470	symtab.Read(skip[:])
471
472	symbols := make([]Symbol, symtab.Len()/Sym64Size)
473
474	i := 0
475	var sym Sym64
476	for symtab.Len() > 0 {
477		binary.Read(symtab, f.ByteOrder, &sym)
478		str, _ := getString(strdata, int(sym.Name))
479		symbols[i].Name = str
480		symbols[i].Info = sym.Info
481		symbols[i].Other = sym.Other
482		symbols[i].Section = SectionIndex(sym.Shndx)
483		symbols[i].Value = sym.Value
484		symbols[i].Size = sym.Size
485		i++
486	}
487
488	return symbols, strdata, nil
489}
490
491// getString extracts a string from an ELF string table.
492func getString(section []byte, start int) (string, bool) {
493	if start < 0 || start >= len(section) {
494		return "", false
495	}
496
497	for end := start; end < len(section); end++ {
498		if section[end] == 0 {
499			return string(section[start:end]), true
500		}
501	}
502	return "", false
503}
504
505// Section returns a section with the given name, or nil if no such
506// section exists.
507func (f *File) Section(name string) *Section {
508	for _, s := range f.Sections {
509		if s.Name == name {
510			return s
511		}
512	}
513	return nil
514}
515
516// applyRelocations applies relocations to dst. rels is a relocations section
517// in RELA format.
518func (f *File) applyRelocations(dst []byte, rels []byte) error {
519	if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 {
520		return f.applyRelocationsAMD64(dst, rels)
521	}
522	if f.Class == ELFCLASS64 && f.Machine == EM_AARCH64 {
523		return f.applyRelocationsARM64(dst, rels)
524	}
525
526	return errors.New("not implemented")
527}
528
529func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
530	if len(rels)%Sym64Size != 0 {
531		return errors.New("length of relocation section is not a multiple of Sym64Size")
532	}
533
534	symbols, _, err := f.getSymbols(SHT_SYMTAB)
535	if err != nil {
536		return err
537	}
538
539	b := bytes.NewBuffer(rels)
540	var rela Rela64
541
542	for b.Len() > 0 {
543		binary.Read(b, f.ByteOrder, &rela)
544		symNo := rela.Info >> 32
545		t := R_X86_64(rela.Info & 0xffff)
546
547		if symNo == 0 || symNo > uint64(len(symbols)) {
548			continue
549		}
550		sym := &symbols[symNo-1]
551		if SymType(sym.Info&0xf) != STT_SECTION {
552			// We don't handle non-section relocations for now.
553			continue
554		}
555
556		switch t {
557		case R_X86_64_64:
558			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
559				continue
560			}
561			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
562		case R_X86_64_32:
563			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
564				continue
565			}
566			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
567		}
568	}
569
570	return nil
571}
572
573func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
574	// 24 is the size of Rela64.
575	if len(rels)%24 != 0 {
576		return errors.New("length of relocation section is not a multiple of 24")
577	}
578
579	symbols, _, err := f.getSymbols(SHT_SYMTAB)
580	if err != nil {
581		return err
582	}
583
584	b := bytes.NewReader(rels)
585	var rela Rela64
586
587	for b.Len() > 0 {
588		binary.Read(b, f.ByteOrder, &rela)
589		symNo := rela.Info >> 32
590		t := R_AARCH64(rela.Info & 0xffff)
591
592		if symNo == 0 || symNo > uint64(len(symbols)) {
593			continue
594		}
595		sym := &symbols[symNo-1]
596		if SymType(sym.Info&0xf) != STT_SECTION {
597			// We don't handle non-section relocations for now.
598			continue
599		}
600
601		switch t {
602		case R_AARCH64_ABS64:
603			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
604				continue
605			}
606			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
607		case R_AARCH64_ABS32:
608			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
609				continue
610			}
611			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
612		}
613	}
614
615	return nil
616}
617
618func (f *File) DWARF() (*dwarf.Data, error) {
619	// There are many other DWARF sections, but these
620	// are the required ones, and the debug/dwarf package
621	// does not use the others, so don't bother loading them.
622	var names = [...]string{"abbrev", "info", "line", "ranges", "str"}
623	var dat [len(names)][]byte
624	for i, name := range names {
625		name = ".debug_" + name
626		s := f.Section(name)
627		if s == nil {
628			continue
629		}
630		b, err := s.Data()
631		if err != nil && uint64(len(b)) < s.Size {
632			return nil, err
633		}
634		dat[i] = b
635	}
636
637	// If there's a relocation table for .debug_info, we have to process it
638	// now otherwise the data in .debug_info is invalid for x86-64 objects.
639	rela := f.Section(".rela.debug_info")
640	if rela != nil && rela.Type == SHT_RELA && (f.Machine == EM_X86_64 || f.Machine == EM_AARCH64) {
641		data, err := rela.Data()
642		if err != nil {
643			return nil, err
644		}
645		err = f.applyRelocations(dat[1], data)
646		if err != nil {
647			return nil, err
648		}
649	}
650
651	abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
652	return dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
653}
654
655// Symbols returns the symbol table for f.
656//
657// For compatibility with Go 1.0, Symbols omits the null symbol at index 0.
658// After retrieving the symbols as symtab, an externally supplied index x
659// corresponds to symtab[x-1], not symtab[x].
660func (f *File) Symbols() ([]Symbol, error) {
661	sym, _, err := f.getSymbols(SHT_SYMTAB)
662	return sym, err
663}
664
665type ImportedSymbol struct {
666	Name    string
667	Version string
668	Library string
669}
670
671// ImportedSymbols returns the names of all symbols
672// referred to by the binary f that are expected to be
673// satisfied by other libraries at dynamic load time.
674// It does not return weak symbols.
675func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
676	sym, str, err := f.getSymbols(SHT_DYNSYM)
677	if err != nil {
678		return nil, err
679	}
680	f.gnuVersionInit(str)
681	var all []ImportedSymbol
682	for i, s := range sym {
683		if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {
684			all = append(all, ImportedSymbol{Name: s.Name})
685			f.gnuVersion(i, &all[len(all)-1])
686		}
687	}
688	return all, nil
689}
690
691type verneed struct {
692	File string
693	Name string
694}
695
696// gnuVersionInit parses the GNU version tables
697// for use by calls to gnuVersion.
698func (f *File) gnuVersionInit(str []byte) {
699	// Accumulate verneed information.
700	vn := f.SectionByType(SHT_GNU_VERNEED)
701	if vn == nil {
702		return
703	}
704	d, _ := vn.Data()
705
706	var need []verneed
707	i := 0
708	for {
709		if i+16 > len(d) {
710			break
711		}
712		vers := f.ByteOrder.Uint16(d[i : i+2])
713		if vers != 1 {
714			break
715		}
716		cnt := f.ByteOrder.Uint16(d[i+2 : i+4])
717		fileoff := f.ByteOrder.Uint32(d[i+4 : i+8])
718		aux := f.ByteOrder.Uint32(d[i+8 : i+12])
719		next := f.ByteOrder.Uint32(d[i+12 : i+16])
720		file, _ := getString(str, int(fileoff))
721
722		var name string
723		j := i + int(aux)
724		for c := 0; c < int(cnt); c++ {
725			if j+16 > len(d) {
726				break
727			}
728			// hash := f.ByteOrder.Uint32(d[j:j+4])
729			// flags := f.ByteOrder.Uint16(d[j+4:j+6])
730			other := f.ByteOrder.Uint16(d[j+6 : j+8])
731			nameoff := f.ByteOrder.Uint32(d[j+8 : j+12])
732			next := f.ByteOrder.Uint32(d[j+12 : j+16])
733			name, _ = getString(str, int(nameoff))
734			ndx := int(other)
735			if ndx >= len(need) {
736				a := make([]verneed, 2*(ndx+1))
737				copy(a, need)
738				need = a
739			}
740
741			need[ndx] = verneed{file, name}
742			if next == 0 {
743				break
744			}
745			j += int(next)
746		}
747
748		if next == 0 {
749			break
750		}
751		i += int(next)
752	}
753
754	// Versym parallels symbol table, indexing into verneed.
755	vs := f.SectionByType(SHT_GNU_VERSYM)
756	if vs == nil {
757		return
758	}
759	d, _ = vs.Data()
760
761	f.gnuNeed = need
762	f.gnuVersym = d
763}
764
765// gnuVersion adds Library and Version information to sym,
766// which came from offset i of the symbol table.
767func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
768	// Each entry is two bytes.
769	i = (i + 1) * 2
770	if i >= len(f.gnuVersym) {
771		return
772	}
773	j := int(f.ByteOrder.Uint16(f.gnuVersym[i:]))
774	if j < 2 || j >= len(f.gnuNeed) {
775		return
776	}
777	n := &f.gnuNeed[j]
778	sym.Library = n.File
779	sym.Version = n.Name
780}
781
782// ImportedLibraries returns the names of all libraries
783// referred to by the binary f that are expected to be
784// linked with the binary at dynamic link time.
785func (f *File) ImportedLibraries() ([]string, error) {
786	return f.DynString(DT_NEEDED)
787}
788
789// DynString returns the strings listed for the given tag in the file's dynamic
790// section.
791//
792// The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or
793// DT_RUNPATH.
794func (f *File) DynString(tag DynTag) ([]string, error) {
795	switch tag {
796	case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH:
797	default:
798		return nil, fmt.Errorf("non-string-valued tag %v", tag)
799	}
800	ds := f.SectionByType(SHT_DYNAMIC)
801	if ds == nil {
802		// not dynamic, so no libraries
803		return nil, nil
804	}
805	d, err := ds.Data()
806	if err != nil {
807		return nil, err
808	}
809	str, err := f.stringTable(ds.Link)
810	if err != nil {
811		return nil, err
812	}
813	var all []string
814	for len(d) > 0 {
815		var t DynTag
816		var v uint64
817		switch f.Class {
818		case ELFCLASS32:
819			t = DynTag(f.ByteOrder.Uint32(d[0:4]))
820			v = uint64(f.ByteOrder.Uint32(d[4:8]))
821			d = d[8:]
822		case ELFCLASS64:
823			t = DynTag(f.ByteOrder.Uint64(d[0:8]))
824			v = f.ByteOrder.Uint64(d[8:16])
825			d = d[16:]
826		}
827		if t == tag {
828			s, ok := getString(str, int(v))
829			if ok {
830				all = append(all, s)
831			}
832		}
833	}
834	return all, nil
835}
836