1
0
mirror of https://github.com/golang/go synced 2024-11-25 02:07:58 -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:
Russ Cox 2010-12-08 13:53:19 -05:00
parent f16c280fe5
commit 37499ebcfd
4 changed files with 384 additions and 23 deletions

View File

@ -1356,9 +1356,12 @@ type Sym32 struct {
const Sym32Size = 16 const Sym32Size = 16
func ST_BIND(info uint8) SymBind { return SymBind(info >> 4) } 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_TYPE(info uint8) SymType { return SymType(info & 0xF) }
func ST_VISIBILITY(other uint8) SymVis { return SymVis(other & 3) } 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 * ELF64

View File

@ -75,6 +75,15 @@ func (s *Section) Data() ([]byte, os.Error) {
return dat[0:n], err 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. // Open returns a new ReadSeeker reading the ELF section.
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) } 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. // A Symbol represents an entry in an ELF symbol table section.
type Symbol struct { type Symbol struct {
Name uint32 Name string
Info, Other byte Info, Other byte
Section uint32 Section SectionIndex
Value, Size uint64 Value, Size uint64
} }
@ -160,6 +169,17 @@ func (f *File) Close() os.Error {
return err 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. // 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. // The ELF binary is expected to start at position 0 in the ReaderAt.
func NewFile(r io.ReaderAt) (*File, os.Error) { 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. // Load section header string table.
s := f.Sections[shstrndx] shstrtab, err := f.Sections[shstrndx].Data()
shstrtab := make([]byte, s.Size) if err != nil {
if _, err := r.ReadAt(shstrtab, int64(s.Offset)); err != nil {
return nil, err return nil, err
} }
for i, s := range f.Sections { for i, s := range f.Sections {
@ -309,25 +328,65 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
return f, nil 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 { switch f.Class {
case ELFCLASS64: case ELFCLASS64:
return f.getSymbols64() return f.getSymbols64(typ)
case ELFCLASS32:
return f.getSymbols32(typ)
} }
return nil, os.ErrorString("not implemented") return nil, os.ErrorString("not implemented")
} }
// GetSymbols returns a slice of Symbols from parsing the symbol table. func (f *File) getSymbols32(typ SectionType) ([]Symbol, os.Error) {
func (f *File) getSymbols64() ([]Symbol, os.Error) { symtabSection := f.SectionByType(typ)
var symtabSection *Section if symtabSection == nil {
for _, section := range f.Sections { return nil, os.ErrorString("no symbol section")
if section.Type == SHT_SYMTAB {
symtabSection = section
break
}
} }
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 { if symtabSection == nil {
return nil, os.ErrorString("no symbol section") 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") 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. // The first entry is all zeros.
var skip [Sym64Size]byte var skip [Sym64Size]byte
symtab.Read(skip[0:]) symtab.Read(skip[0:])
@ -351,10 +415,11 @@ func (f *File) getSymbols64() ([]Symbol, os.Error) {
var sym Sym64 var sym Sym64
for symtab.Len() > 0 { for symtab.Len() > 0 {
binary.Read(symtab, f.ByteOrder, &sym) 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].Info = sym.Info
symbols[i].Other = sym.Other symbols[i].Other = sym.Other
symbols[i].Section = uint32(sym.Shndx) symbols[i].Section = SectionIndex(sym.Shndx)
symbols[i].Value = sym.Value symbols[i].Value = sym.Value
symbols[i].Size = sym.Size symbols[i].Size = sym.Size
i++ 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") 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 { if err != nil {
return err return err
} }
@ -478,3 +543,63 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) {
abbrev, info, str := dat[0], dat[1], dat[2] abbrev, info, str := dat[0], dat[1], dat[2]
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) 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
}

View File

@ -24,6 +24,9 @@ type File struct {
Loads []Load Loads []Load
Sections []*Section Sections []*Section
Symtab *Symtab
Dysymtab *Dysymtab
closer io.Closer closer io.Closer
} }
@ -112,6 +115,28 @@ func (s *Section) Data() ([]byte, os.Error) {
// Open returns a new ReadSeeker reading the Mach-O section. // 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) } 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 * Mach-O reader
@ -217,6 +242,71 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
default: default:
f.Loads[i] = LoadBytes(cmddat) 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: case LoadCmdSegment:
var seg32 Segment32 var seg32 Segment32
b := bytes.NewBuffer(cmddat) b := bytes.NewBuffer(cmddat)
@ -301,6 +391,43 @@ func NewFile(r io.ReaderAt) (*File, os.Error) {
return f, nil 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) { func (f *File) pushSection(sh *Section, r io.ReaderAt) {
f.Sections = append(f.Sections, sh) f.Sections = append(f.Sections, sh)
sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size)) 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] abbrev, info, str := dat[0], dat[1], dat[2]
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) 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
}

View File

@ -59,16 +59,21 @@ type LoadCmd uint32
const ( const (
LoadCmdSegment LoadCmd = 1 LoadCmdSegment LoadCmd = 1
LoadCmdSegment64 LoadCmd = 25 LoadCmdSymtab LoadCmd = 2
LoadCmdThread LoadCmd = 4 LoadCmdThread LoadCmd = 4
LoadCmdUnixThread LoadCmd = 5 // thread+stack LoadCmdUnixThread LoadCmd = 5 // thread+stack
LoadCmdDysymtab LoadCmd = 11
LoadCmdDylib LoadCmd = 12
LoadCmdDylinker LoadCmd = 15
LoadCmdSegment64 LoadCmd = 25
) )
var cmdStrings = []intName{ var cmdStrings = []intName{
{uint32(LoadCmdSegment), "LoadCmdSegment"}, {uint32(LoadCmdSegment), "LoadCmdSegment"},
{uint32(LoadCmdSegment64), "LoadCmdSegment64"},
{uint32(LoadCmdThread), "LoadCmdThread"}, {uint32(LoadCmdThread), "LoadCmdThread"},
{uint32(LoadCmdUnixThread), "LoadCmdUnixThread"}, {uint32(LoadCmdUnixThread), "LoadCmdUnixThread"},
{uint32(LoadCmdDylib), "LoadCmdDylib"},
{uint32(LoadCmdSegment64), "LoadCmdSegment64"},
} }
func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) } func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) }
@ -104,6 +109,16 @@ type Segment32 struct {
Flag uint32 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. // A Section32 is a 32-bit Mach-O section header.
type Section32 struct { type Section32 struct {
Name [16]byte Name [16]byte
@ -135,6 +150,67 @@ type Section64 struct {
Reserve3 uint32 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. // A Thread is a Mach-O thread state command.
type Thread struct { type Thread struct {
Cmd LoadCmd Cmd LoadCmd