mirror of
https://github.com/golang/go
synced 2024-11-21 18:24:46 -07:00
debug/elf, debug/macho: add ImportedLibraries, ImportedSymbols
R=r, iant CC=golang-dev https://golang.org/cl/3470044
This commit is contained in:
parent
f16c280fe5
commit
37499ebcfd
@ -1356,9 +1356,12 @@ type Sym32 struct {
|
||||
|
||||
const Sym32Size = 16
|
||||
|
||||
func ST_BIND(info uint8) SymBind { return SymBind(info >> 4) }
|
||||
func ST_TYPE(bind SymBind, typ SymType) uint8 { return uint8(bind)<<4 | uint8(typ)&0xf }
|
||||
func ST_VISIBILITY(other uint8) SymVis { return SymVis(other & 3) }
|
||||
func ST_BIND(info uint8) SymBind { return SymBind(info >> 4) }
|
||||
func ST_TYPE(info uint8) SymType { return SymType(info & 0xF) }
|
||||
func ST_INFO(bind SymBind, typ SymType) uint8 {
|
||||
return uint8(bind)<<4 | uint8(typ)&0xf
|
||||
}
|
||||
func ST_VISIBILITY(other uint8) SymVis { return SymVis(other & 3) }
|
||||
|
||||
/*
|
||||
* ELF64
|
||||
|
@ -75,6 +75,15 @@ func (s *Section) Data() ([]byte, os.Error) {
|
||||
return dat[0:n], err
|
||||
}
|
||||
|
||||
// stringTable reads and returns the string table given by the
|
||||
// specified link value.
|
||||
func (f *File) stringTable(link uint32) ([]byte, os.Error) {
|
||||
if link <= 0 || link >= uint32(len(f.Sections)) {
|
||||
return nil, os.ErrorString("section has invalid string table link")
|
||||
}
|
||||
return f.Sections[link].Data()
|
||||
}
|
||||
|
||||
// Open returns a new ReadSeeker reading the ELF section.
|
||||
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
|
||||
|
||||
@ -108,9 +117,9 @@ func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-
|
||||
|
||||
// A Symbol represents an entry in an ELF symbol table section.
|
||||
type Symbol struct {
|
||||
Name uint32
|
||||
Name string
|
||||
Info, Other byte
|
||||
Section uint32
|
||||
Section SectionIndex
|
||||
Value, Size uint64
|
||||
}
|
||||
|
||||
@ -160,6 +169,17 @@ func (f *File) Close() os.Error {
|
||||
return err
|
||||
}
|
||||
|
||||
// SectionByType returns the first section in f with the
|
||||
// given type, or nil if there is no such section.
|
||||
func (f *File) SectionByType(typ SectionType) *Section {
|
||||
for _, s := range f.Sections {
|
||||
if s.Type == typ {
|
||||
return s
|
||||
}
|
||||
}
|
||||
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 NewFile(r io.ReaderAt) (*File, os.Error) {
|
||||
@ -293,9 +313,8 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
|
||||
}
|
||||
|
||||
// Load section header string table.
|
||||
s := f.Sections[shstrndx]
|
||||
shstrtab := make([]byte, s.Size)
|
||||
if _, err := r.ReadAt(shstrtab, int64(s.Offset)); err != nil {
|
||||
shstrtab, err := f.Sections[shstrndx].Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, s := range f.Sections {
|
||||
@ -309,25 +328,65 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *File) getSymbols() ([]Symbol, os.Error) {
|
||||
// getSymbols returns a slice of Symbols from parsing the symbol table
|
||||
// with the given type.
|
||||
func (f *File) getSymbols(typ SectionType) ([]Symbol, os.Error) {
|
||||
switch f.Class {
|
||||
case ELFCLASS64:
|
||||
return f.getSymbols64()
|
||||
return f.getSymbols64(typ)
|
||||
|
||||
case ELFCLASS32:
|
||||
return f.getSymbols32(typ)
|
||||
}
|
||||
|
||||
return nil, os.ErrorString("not implemented")
|
||||
}
|
||||
|
||||
// GetSymbols returns a slice of Symbols from parsing the symbol table.
|
||||
func (f *File) getSymbols64() ([]Symbol, os.Error) {
|
||||
var symtabSection *Section
|
||||
for _, section := range f.Sections {
|
||||
if section.Type == SHT_SYMTAB {
|
||||
symtabSection = section
|
||||
break
|
||||
}
|
||||
func (f *File) getSymbols32(typ SectionType) ([]Symbol, os.Error) {
|
||||
symtabSection := f.SectionByType(typ)
|
||||
if symtabSection == nil {
|
||||
return nil, os.ErrorString("no symbol section")
|
||||
}
|
||||
|
||||
data, err := symtabSection.Data()
|
||||
if err != nil {
|
||||
return nil, os.ErrorString("cannot load symbol section")
|
||||
}
|
||||
symtab := bytes.NewBuffer(data)
|
||||
if symtab.Len()%Sym32Size != 0 {
|
||||
return nil, os.ErrorString("length of symbol section is not a multiple of SymSize")
|
||||
}
|
||||
|
||||
strdata, err := f.stringTable(symtabSection.Link)
|
||||
if err != nil {
|
||||
return nil, os.ErrorString("cannot load string table section")
|
||||
}
|
||||
|
||||
// The first entry is all zeros.
|
||||
var skip [Sym32Size]byte
|
||||
symtab.Read(skip[0:])
|
||||
|
||||
symbols := make([]Symbol, symtab.Len()/Sym32Size)
|
||||
|
||||
i := 0
|
||||
var sym Sym32
|
||||
for symtab.Len() > 0 {
|
||||
binary.Read(symtab, f.ByteOrder, &sym)
|
||||
str, _ := getString(strdata, int(sym.Name))
|
||||
symbols[i].Name = str
|
||||
symbols[i].Info = sym.Info
|
||||
symbols[i].Other = sym.Other
|
||||
symbols[i].Section = SectionIndex(sym.Shndx)
|
||||
symbols[i].Value = uint64(sym.Value)
|
||||
symbols[i].Size = uint64(sym.Size)
|
||||
i++
|
||||
}
|
||||
|
||||
return symbols, nil
|
||||
}
|
||||
|
||||
func (f *File) getSymbols64(typ SectionType) ([]Symbol, os.Error) {
|
||||
symtabSection := f.SectionByType(typ)
|
||||
if symtabSection == nil {
|
||||
return nil, os.ErrorString("no symbol section")
|
||||
}
|
||||
@ -341,6 +400,11 @@ func (f *File) getSymbols64() ([]Symbol, os.Error) {
|
||||
return nil, os.ErrorString("length of symbol section is not a multiple of Sym64Size")
|
||||
}
|
||||
|
||||
strdata, err := f.stringTable(symtabSection.Link)
|
||||
if err != nil {
|
||||
return nil, os.ErrorString("cannot load string table section")
|
||||
}
|
||||
|
||||
// The first entry is all zeros.
|
||||
var skip [Sym64Size]byte
|
||||
symtab.Read(skip[0:])
|
||||
@ -351,10 +415,11 @@ func (f *File) getSymbols64() ([]Symbol, os.Error) {
|
||||
var sym Sym64
|
||||
for symtab.Len() > 0 {
|
||||
binary.Read(symtab, f.ByteOrder, &sym)
|
||||
symbols[i].Name = sym.Name
|
||||
str, _ := getString(strdata, int(sym.Name))
|
||||
symbols[i].Name = str
|
||||
symbols[i].Info = sym.Info
|
||||
symbols[i].Other = sym.Other
|
||||
symbols[i].Section = uint32(sym.Shndx)
|
||||
symbols[i].Section = SectionIndex(sym.Shndx)
|
||||
symbols[i].Value = sym.Value
|
||||
symbols[i].Size = sym.Size
|
||||
i++
|
||||
@ -403,7 +468,7 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) os.Error {
|
||||
return os.ErrorString("length of relocation section is not a multiple of Sym64Size")
|
||||
}
|
||||
|
||||
symbols, err := f.getSymbols()
|
||||
symbols, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -478,3 +543,63 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) {
|
||||
abbrev, info, str := dat[0], dat[1], dat[2]
|
||||
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
|
||||
}
|
||||
|
||||
// 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 (f *File) ImportedSymbols() ([]string, os.Error) {
|
||||
sym, err := f.getSymbols(SHT_DYNSYM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var all []string
|
||||
for _, s := range sym {
|
||||
if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {
|
||||
all = append(all, s.Name)
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// 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 (f *File) ImportedLibraries() ([]string, os.Error) {
|
||||
ds := f.SectionByType(SHT_DYNAMIC)
|
||||
if ds == nil {
|
||||
// not dynamic, so no libraries
|
||||
return nil, nil
|
||||
}
|
||||
d, err := ds.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
str, err := f.stringTable(ds.Link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var all []string
|
||||
for len(d) > 0 {
|
||||
var tag DynTag
|
||||
var value uint64
|
||||
switch f.Class {
|
||||
case ELFCLASS32:
|
||||
tag = DynTag(f.ByteOrder.Uint32(d[0:4]))
|
||||
value = uint64(f.ByteOrder.Uint32(d[4:8]))
|
||||
d = d[8:]
|
||||
case ELFCLASS64:
|
||||
tag = DynTag(f.ByteOrder.Uint64(d[0:8]))
|
||||
value = f.ByteOrder.Uint64(d[8:16])
|
||||
d = d[16:]
|
||||
}
|
||||
if tag == DT_NEEDED {
|
||||
s, ok := getString(str, int(value))
|
||||
if ok {
|
||||
all = append(all, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ type File struct {
|
||||
Loads []Load
|
||||
Sections []*Section
|
||||
|
||||
Symtab *Symtab
|
||||
Dysymtab *Dysymtab
|
||||
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
@ -112,6 +115,28 @@ func (s *Section) Data() ([]byte, os.Error) {
|
||||
// Open returns a new ReadSeeker reading the Mach-O section.
|
||||
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
|
||||
|
||||
// A Dylib represents a Mach-O load dynamic library command.
|
||||
type Dylib struct {
|
||||
LoadBytes
|
||||
Name string
|
||||
Time uint32
|
||||
CurrentVersion uint32
|
||||
CompatVersion uint32
|
||||
}
|
||||
|
||||
// A Symtab represents a Mach-O symbol table command.
|
||||
type Symtab struct {
|
||||
LoadBytes
|
||||
SymtabCmd
|
||||
Syms []Symbol
|
||||
}
|
||||
|
||||
// A Dysymtab represents a Mach-O dynamic symbol table command.
|
||||
type Dysymtab struct {
|
||||
LoadBytes
|
||||
DysymtabCmd
|
||||
IndirectSyms []uint32 // indices into Symtab.Syms
|
||||
}
|
||||
|
||||
/*
|
||||
* Mach-O reader
|
||||
@ -217,6 +242,71 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
|
||||
default:
|
||||
f.Loads[i] = LoadBytes(cmddat)
|
||||
|
||||
case LoadCmdDylib:
|
||||
var hdr DylibCmd
|
||||
b := bytes.NewBuffer(cmddat)
|
||||
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := new(Dylib)
|
||||
if hdr.Name >= uint32(len(cmddat)) {
|
||||
return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
|
||||
}
|
||||
l.Name = cstring(cmddat[hdr.Name:])
|
||||
l.Time = hdr.Time
|
||||
l.CurrentVersion = hdr.CurrentVersion
|
||||
l.CompatVersion = hdr.CompatVersion
|
||||
l.LoadBytes = LoadBytes(cmddat)
|
||||
f.Loads[i] = l
|
||||
|
||||
case LoadCmdSymtab:
|
||||
var hdr SymtabCmd
|
||||
b := bytes.NewBuffer(cmddat)
|
||||
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
strtab := make([]byte, hdr.Strsize)
|
||||
if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var symsz int
|
||||
if f.Magic == Magic64 {
|
||||
symsz = 16
|
||||
} else {
|
||||
symsz = 12
|
||||
}
|
||||
symdat := make([]byte, int(hdr.Nsyms)*symsz)
|
||||
if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Loads[i] = st
|
||||
f.Symtab = st
|
||||
|
||||
case LoadCmdDysymtab:
|
||||
var hdr DysymtabCmd
|
||||
b := bytes.NewBuffer(cmddat)
|
||||
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dat := make([]byte, hdr.Nindirectsyms*4)
|
||||
if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := make([]uint32, hdr.Nindirectsyms)
|
||||
if err := binary.Read(bytes.NewBuffer(dat), bo, x); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st := new(Dysymtab)
|
||||
st.LoadBytes = LoadBytes(cmddat)
|
||||
st.DysymtabCmd = hdr
|
||||
st.IndirectSyms = x
|
||||
f.Loads[i] = st
|
||||
f.Dysymtab = st
|
||||
|
||||
case LoadCmdSegment:
|
||||
var seg32 Segment32
|
||||
b := bytes.NewBuffer(cmddat)
|
||||
@ -301,6 +391,43 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, os.Error) {
|
||||
bo := f.ByteOrder
|
||||
symtab := make([]Symbol, hdr.Nsyms)
|
||||
b := bytes.NewBuffer(symdat)
|
||||
for i := range symtab {
|
||||
var n Nlist64
|
||||
if f.Magic == Magic64 {
|
||||
if err := binary.Read(b, bo, &n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
var n32 Nlist32
|
||||
if err := binary.Read(b, bo, &n32); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.Name = n32.Name
|
||||
n.Type = n32.Type
|
||||
n.Sect = n32.Sect
|
||||
n.Desc = n32.Desc
|
||||
n.Value = uint64(n32.Value)
|
||||
}
|
||||
sym := &symtab[i]
|
||||
if n.Name >= uint32(len(strtab)) {
|
||||
return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
|
||||
}
|
||||
sym.Name = cstring(strtab[n.Name:])
|
||||
sym.Type = n.Type
|
||||
sym.Sect = n.Sect
|
||||
sym.Desc = n.Desc
|
||||
sym.Value = n.Value
|
||||
}
|
||||
st := new(Symtab)
|
||||
st.LoadBytes = LoadBytes(cmddat)
|
||||
st.Syms = symtab
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func (f *File) pushSection(sh *Section, r io.ReaderAt) {
|
||||
f.Sections = append(f.Sections, sh)
|
||||
sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
|
||||
@ -358,3 +485,33 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) {
|
||||
abbrev, info, str := dat[0], dat[1], dat[2]
|
||||
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (f *File) ImportedSymbols() ([]string, os.Error) {
|
||||
if f.Dysymtab == nil || f.Symtab == nil {
|
||||
return nil, &FormatError{0, "missing symbol table", nil}
|
||||
}
|
||||
|
||||
st := f.Symtab
|
||||
dt := f.Dysymtab
|
||||
var all []string
|
||||
for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
|
||||
all = append(all, s.Name)
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// ImportedLibraries returns the paths of all libraries
|
||||
// referred to by the binary f that are expected to be
|
||||
// linked with the binary at dynamic link time.
|
||||
func (f *File) ImportedLibraries() ([]string, os.Error) {
|
||||
var all []string
|
||||
for _, l := range f.Loads {
|
||||
if lib, ok := l.(*Dylib); ok {
|
||||
all = append(all, lib.Name)
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
@ -59,16 +59,21 @@ type LoadCmd uint32
|
||||
|
||||
const (
|
||||
LoadCmdSegment LoadCmd = 1
|
||||
LoadCmdSegment64 LoadCmd = 25
|
||||
LoadCmdSymtab LoadCmd = 2
|
||||
LoadCmdThread LoadCmd = 4
|
||||
LoadCmdUnixThread LoadCmd = 5 // thread+stack
|
||||
LoadCmdDysymtab LoadCmd = 11
|
||||
LoadCmdDylib LoadCmd = 12
|
||||
LoadCmdDylinker LoadCmd = 15
|
||||
LoadCmdSegment64 LoadCmd = 25
|
||||
)
|
||||
|
||||
var cmdStrings = []intName{
|
||||
{uint32(LoadCmdSegment), "LoadCmdSegment"},
|
||||
{uint32(LoadCmdSegment64), "LoadCmdSegment64"},
|
||||
{uint32(LoadCmdThread), "LoadCmdThread"},
|
||||
{uint32(LoadCmdUnixThread), "LoadCmdUnixThread"},
|
||||
{uint32(LoadCmdDylib), "LoadCmdDylib"},
|
||||
{uint32(LoadCmdSegment64), "LoadCmdSegment64"},
|
||||
}
|
||||
|
||||
func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) }
|
||||
@ -104,6 +109,16 @@ type Segment32 struct {
|
||||
Flag uint32
|
||||
}
|
||||
|
||||
// A DylibCmd is a Mach-O load dynamic library command.
|
||||
type DylibCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Name uint32
|
||||
Time uint32
|
||||
CurrentVersion uint32
|
||||
CompatVersion uint32
|
||||
}
|
||||
|
||||
// A Section32 is a 32-bit Mach-O section header.
|
||||
type Section32 struct {
|
||||
Name [16]byte
|
||||
@ -135,6 +150,67 @@ type Section64 struct {
|
||||
Reserve3 uint32
|
||||
}
|
||||
|
||||
// A SymtabCmd is a Mach-O symbol table command.
|
||||
type SymtabCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Symoff uint32
|
||||
Nsyms uint32
|
||||
Stroff uint32
|
||||
Strsize uint32
|
||||
}
|
||||
|
||||
// A DysymtabCmd is a Mach-O dynamic symbol table command.
|
||||
type DysymtabCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Ilocalsym uint32
|
||||
Nlocalsym uint32
|
||||
Iextdefsym uint32
|
||||
Nextdefsym uint32
|
||||
Iundefsym uint32
|
||||
Nundefsym uint32
|
||||
Tocoffset uint32
|
||||
Ntoc uint32
|
||||
Modtaboff uint32
|
||||
Nmodtab uint32
|
||||
Extrefsymoff uint32
|
||||
Nextrefsyms uint32
|
||||
Indirectsymoff uint32
|
||||
Nindirectsyms uint32
|
||||
Extreloff uint32
|
||||
Nextrel uint32
|
||||
Locreloff uint32
|
||||
Nlocrel uint32
|
||||
}
|
||||
|
||||
// An Nlist32 is a Mach-O 32-bit symbol table entry.
|
||||
type Nlist32 struct {
|
||||
Name uint32
|
||||
Type uint8
|
||||
Sect uint8
|
||||
Desc uint16
|
||||
Value uint32
|
||||
}
|
||||
|
||||
// An Nlist64 is a Mach-O 64-bit symbol table entry.
|
||||
type Nlist64 struct {
|
||||
Name uint32
|
||||
Type uint8
|
||||
Sect uint8
|
||||
Desc uint16
|
||||
Value uint64
|
||||
}
|
||||
|
||||
// A Symbol is a Mach-O 32-bit or 64-bit symbol table entry.
|
||||
type Symbol struct {
|
||||
Name string
|
||||
Type uint8
|
||||
Sect uint8
|
||||
Desc uint16
|
||||
Value uint64
|
||||
}
|
||||
|
||||
// A Thread is a Mach-O thread state command.
|
||||
type Thread struct {
|
||||
Cmd LoadCmd
|
||||
|
Loading…
Reference in New Issue
Block a user