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