Commit 09092a78 authored by Russ Cox's avatar Russ Cox

cgo: handle versioned ELF symbols

Fixes #1397.

R=iant
CC=golang-dev
https://golang.org/cl/4444064
parent 70b0de8e
......@@ -156,6 +156,7 @@ struct Sym
char* file;
char* dynimpname;
char* dynimplib;
char* dynimpvers;
// STEXT
Auto* autom;
......
......@@ -95,6 +95,8 @@ enum {
ElfStrStrtab,
ElfStrRelaPlt,
ElfStrPlt,
ElfStrGnuVersion,
ElfStrGnuVersionR,
NElfStr
};
......@@ -436,6 +438,7 @@ adddynsym(Sym *s)
s->dynid = nelfsym++;
d = lookup(".dynsym", 0);
name = s->dynimpname;
if(name == nil)
name = s->name;
......@@ -586,6 +589,8 @@ doelf(void)
elfstr[ElfStrRela] = addstring(shstrtab, ".rela");
elfstr[ElfStrRelaPlt] = addstring(shstrtab, ".rela.plt");
elfstr[ElfStrPlt] = addstring(shstrtab, ".plt");
elfstr[ElfStrGnuVersion] = addstring(shstrtab, ".gnu.version");
elfstr[ElfStrGnuVersionR] = addstring(shstrtab, ".gnu.version_r");
/* dynamic symbol table - first entry all zeros */
s = lookup(".dynsym", 0);
......@@ -629,6 +634,14 @@ doelf(void)
s = lookup(".rela.plt", 0);
s->reachable = 1;
s->type = SELFDATA;
s = lookup(".gnu.version", 0);
s->reachable = 1;
s->type = SELFDATA;
s = lookup(".gnu.version_r", 0);
s->reachable = 1;
s->type = SELFDATA;
/* define dynamic elf table */
s = lookup(".dynamic", 0);
......@@ -653,7 +666,8 @@ doelf(void)
elfwritedynent(s, DT_PLTREL, DT_RELA);
elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rela.plt", 0));
elfwritedynentsym(s, DT_JMPREL, lookup(".rela.plt", 0));
elfwritedynent(s, DT_NULL, 0);
// Do not write DT_NULL. elfdynhash will finish it.
}
}
......@@ -735,8 +749,11 @@ asmb(void)
/* index of elf text section; needed by asmelfsym, double-checked below */
/* !debug['d'] causes extra sections before the .text section */
elftextsh = 1;
if(!debug['d'])
if(!debug['d']) {
elftextsh += 10;
if(elfverneed)
elftextsh += 2;
}
break;
case Hwindows:
break;
......@@ -920,6 +937,24 @@ asmb(void)
sh->addralign = 1;
shsym(sh, lookup(".dynstr", 0));
if(elfverneed) {
sh = newElfShdr(elfstr[ElfStrGnuVersion]);
sh->type = SHT_GNU_VERSYM;
sh->flags = SHF_ALLOC;
sh->addralign = 2;
sh->link = dynsym;
sh->entsize = 2;
shsym(sh, lookup(".gnu.version", 0));
sh = newElfShdr(elfstr[ElfStrGnuVersionR]);
sh->type = SHT_GNU_VERNEED;
sh->flags = SHF_ALLOC;
sh->addralign = 8;
sh->info = elfverneed;
sh->link = dynsym+1; // dynstr
shsym(sh, lookup(".gnu.version_r", 0));
}
sh = newElfShdr(elfstr[ElfStrRelaPlt]);
sh->type = SHT_RELA;
sh->flags = SHF_ALLOC;
......
......@@ -148,6 +148,7 @@ struct Sym
char* file;
char* dynimpname;
char* dynimplib;
char* dynimpvers;
// STEXT
Auto* autom;
......
......@@ -91,6 +91,8 @@ enum {
ElfStrStrtab,
ElfStrRelPlt,
ElfStrPlt,
ElfStrGnuVersion,
ElfStrGnuVersionR,
NElfStr
};
......@@ -420,7 +422,7 @@ adddynsym(Sym *s)
s->dynid = nelfsym++;
d = lookup(".dynsym", 0);
/* name */
name = s->dynimpname;
if(name == nil)
......@@ -545,6 +547,8 @@ doelf(void)
elfstr[ElfStrRel] = addstring(shstrtab, ".rel");
elfstr[ElfStrRelPlt] = addstring(shstrtab, ".rel.plt");
elfstr[ElfStrPlt] = addstring(shstrtab, ".plt");
elfstr[ElfStrGnuVersion] = addstring(shstrtab, ".gnu.version");
elfstr[ElfStrGnuVersionR] = addstring(shstrtab, ".gnu.version_r");
/* interpreter string */
s = lookup(".interp", 0);
......@@ -592,6 +596,14 @@ doelf(void)
s = lookup(".rel.plt", 0);
s->reachable = 1;
s->type = SELFDATA;
s = lookup(".gnu.version", 0);
s->reachable = 1;
s->type = SELFDATA;
s = lookup(".gnu.version_r", 0);
s->reachable = 1;
s->type = SELFDATA;
elfsetupplt();
......@@ -617,7 +629,8 @@ doelf(void)
elfwritedynent(s, DT_PLTREL, DT_REL);
elfwritedynentsymsize(s, DT_PLTRELSZ, lookup(".rel.plt", 0));
elfwritedynentsym(s, DT_JMPREL, lookup(".rel.plt", 0));
elfwritedynent(s, DT_NULL, 0);
// Do not write DT_NULL. elfdynhash will finish it.
}
}
......@@ -681,8 +694,11 @@ asmb(void)
/* index of elf text section; needed by asmelfsym, double-checked below */
/* !debug['d'] causes extra sections before the .text section */
elftextsh = 1;
if(!debug['d'])
if(!debug['d']) {
elftextsh += 10;
if(elfverneed)
elftextsh += 2;
}
}
symsize = 0;
......@@ -966,6 +982,24 @@ asmb(void)
sh->addralign = 1;
shsym(sh, lookup(".dynstr", 0));
if(elfverneed) {
sh = newElfShdr(elfstr[ElfStrGnuVersion]);
sh->type = SHT_GNU_VERSYM;
sh->flags = SHF_ALLOC;
sh->addralign = 2;
sh->link = dynsym;
sh->entsize = 2;
shsym(sh, lookup(".gnu.version", 0));
sh = newElfShdr(elfstr[ElfStrGnuVersionR]);
sh->type = SHT_GNU_VERNEED;
sh->flags = SHF_ALLOC;
sh->addralign = 4;
sh->info = elfverneed;
sh->link = dynsym+1; // dynstr
shsym(sh, lookup(".gnu.version_r", 0));
}
sh = newElfShdr(elfstr[ElfStrRelPlt]);
sh->type = SHT_REL;
sh->flags = SHF_ALLOC;
......
......@@ -147,6 +147,7 @@ struct Sym
char* file;
char* dynimpname;
char* dynimplib;
char* dynimpvers;
// STEXT
Auto* autom;
......
......@@ -534,6 +534,32 @@ out:
print("%s incomplete\n", s->name);
}
Sym*
getimpsym(void)
{
int c;
char *cp;
c = getnsc();
if(isspace(c) || c == '"') {
unget(c);
return S;
}
for(cp = symb;;) {
if(cp <= symb+NSYMB-4)
*cp++ = c;
c = getc();
if(c > 0 && !isspace(c) && c != '"')
continue;
unget(c);
break;
}
*cp = 0;
if(cp > symb+NSYMB-4)
yyerror("symbol too large: %s", symb);
return lookup();
}
void
pragdynimport(void)
{
......@@ -541,11 +567,11 @@ pragdynimport(void)
char *path;
Dynimp *f;
local = getsym();
local = getimpsym();
if(local == nil)
goto err;
remote = getsym();
remote = getimpsym();
if(remote == nil)
goto err;
......
......@@ -63,7 +63,7 @@ getsym(void)
if(cp <= symb+NSYMB-4)
*cp++ = c;
c = getc();
if(isalnum(c) || c == '_' || c >= 0x80 || c == '$')
if(isalnum(c) || c == '_' || c >= 0x80)
continue;
unget(c);
break;
......
......@@ -20,7 +20,6 @@ import (
"os"
"reflect"
"strings"
"runtime"
)
// A Package collects information about the package we're going to write.
......@@ -135,20 +134,7 @@ func main() {
// instead of needing to make the linkers duplicate all the
// specialized knowledge gcc has about where to look for imported
// symbols and which ones to use.
syms, imports := dynimport(*dynobj)
if runtime.GOOS == "windows" {
for _, sym := range syms {
ss := strings.Split(sym, ":", -1)
fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1]))
}
return
}
for _, sym := range syms {
fmt.Printf("#pragma dynimport %s %s %q\n", sym, sym, "")
}
for _, p := range imports {
fmt.Printf("#pragma dynimport %s %s %q\n", "_", "_", p)
}
dynimport(*dynobj)
return
}
......
......@@ -95,42 +95,63 @@ func (p *Package) writeDefs() {
fc.Close()
}
func dynimport(obj string) (syms, imports []string) {
var f interface {
ImportedLibraries() ([]string, os.Error)
ImportedSymbols() ([]string, os.Error)
}
var isMacho bool
var err1, err2, err3 os.Error
if f, err1 = elf.Open(obj); err1 != nil {
if f, err2 = pe.Open(obj); err2 != nil {
if f, err3 = macho.Open(obj); err3 != nil {
fatalf("cannot parse %s as ELF (%v) or PE (%v) or Mach-O (%v)", obj, err1, err2, err3)
func dynimport(obj string) {
if f, err := elf.Open(obj); err == nil {
sym, err := f.ImportedSymbols()
if err != nil {
fatalf("cannot load imported symbols from ELF file %s: %v", obj, err)
}
for _, s := range sym {
targ := s.Name
if s.Version != "" {
targ += "@" + s.Version
}
isMacho = true
fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library)
}
lib, err := f.ImportedLibraries()
if err != nil {
fatalf("cannot load imported libraries from ELF file %s: %v", obj, err)
}
for _, l := range lib {
fmt.Printf("#pragma dynimport _ _ %q\n", l)
}
return
}
var err os.Error
syms, err = f.ImportedSymbols()
if err != nil {
fatalf("cannot load dynamic symbols: %v", err)
}
if isMacho {
// remove leading _ that OS X insists on
for i, s := range syms {
if len(s) >= 2 && s[0] == '_' {
syms[i] = s[1:]
if f, err := macho.Open(obj); err == nil {
sym, err := f.ImportedSymbols()
if err != nil {
fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err)
}
for _, s := range sym {
if len(s) > 0 && s[0] == '_' {
s = s[1:]
}
fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "")
}
lib, err := f.ImportedLibraries()
if err != nil {
fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err)
}
for _, l := range lib {
fmt.Printf("#pragma dynimport _ _ %q\n", l)
}
return
}
imports, err = f.ImportedLibraries()
if err != nil {
fatalf("cannot load dynamic imports: %v", err)
if f, err := pe.Open(obj); err == nil {
sym, err := f.ImportedSymbols()
if err != nil {
fatalf("cannot load imported symbols from PE file %s: v", obj, err)
}
for _, s := range sym {
ss := strings.Split(s, ":", -1)
fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1]))
}
return
}
return
fatalf("cannot parse %s as ELF, Mach-O or PE", obj)
}
// Construct a gcc struct matching the 6c argument frame.
......
......@@ -331,17 +331,62 @@ elfinterp(ElfShdr *sh, uint64 startva, char *p)
}
extern int nelfsym;
int elfverneed;
typedef struct Elfaux Elfaux;
typedef struct Elflib Elflib;
struct Elflib
{
Elflib *next;
Elfaux *aux;
char *file;
};
struct Elfaux
{
Elfaux *next;
int num;
char *vers;
};
Elfaux*
addelflib(Elflib **list, char *file, char *vers)
{
Elflib *lib;
Elfaux *aux;
for(lib=*list; lib; lib=lib->next)
if(strcmp(lib->file, file) == 0)
goto havelib;
lib = mal(sizeof *lib);
lib->next = *list;
lib->file = file;
*list = lib;
havelib:
for(aux=lib->aux; aux; aux=aux->next)
if(strcmp(aux->vers, vers) == 0)
goto haveaux;
aux = mal(sizeof *aux);
aux->next = lib->aux;
aux->vers = vers;
lib->aux = aux;
haveaux:
return aux;
}
void
elfdynhash(void)
{
Sym *s, *sy;
int i, nbucket, b;
uchar *pc;
uint32 hc, g;
uint32 *chain, *buckets;
Sym *s, *sy, *dynstr;
int i, j, nbucket, b, nfile;
uint32 hc, *chain, *buckets;
int nsym;
char *name;
Elfaux **need;
Elflib *needlib;
Elflib *l;
Elfaux *x;
if(!iself)
return;
......@@ -358,29 +403,29 @@ elfdynhash(void)
i >>= 1;
}
chain = malloc(nsym * sizeof(uint32));
buckets = malloc(nbucket * sizeof(uint32));
if(chain == nil || buckets == nil) {
needlib = nil;
need = malloc(nsym * sizeof need[0]);
chain = malloc(nsym * sizeof chain[0]);
buckets = malloc(nbucket * sizeof buckets[0]);
if(need == nil || chain == nil || buckets == nil) {
cursym = nil;
diag("out of memory");
errorexit();
}
memset(chain, 0, nsym * sizeof(uint32));
memset(buckets, 0, nbucket * sizeof(uint32));
memset(need, 0, nsym * sizeof need[0]);
memset(chain, 0, nsym * sizeof chain[0]);
memset(buckets, 0, nbucket * sizeof buckets[0]);
for(sy=allsym; sy!=S; sy=sy->allsym) {
if (sy->dynid <= 0)
continue;
hc = 0;
if(sy->dynimpvers)
need[sy->dynid] = addelflib(&needlib, sy->dynimplib, sy->dynimpvers);
name = sy->dynimpname;
if(name == nil)
name = sy->name;
for(pc = (uchar*)name; *pc; pc++) {
hc = (hc<<4) + *pc;
g = hc & 0xf0000000;
hc ^= g >> 24;
hc &= ~g;
}
hc = elfhash((uchar*)name);
b = hc % nbucket;
chain[sy->dynid] = buckets[b];
......@@ -396,8 +441,62 @@ elfdynhash(void)
free(chain);
free(buckets);
// version symbols
dynstr = lookup(".dynstr", 0);
s = lookup(".gnu.version_r", 0);
i = 2;
nfile = 0;
for(l=needlib; l; l=l->next) {
nfile++;
// header
adduint16(s, 1); // table version
j = 0;
for(x=l->aux; x; x=x->next)
j++;
adduint16(s, j); // aux count
adduint32(s, addstring(dynstr, l->file)); // file string offset
adduint32(s, 16); // offset from header to first aux
if(l->next)
adduint32(s, 16+j*16); // offset from this header to next
else
adduint32(s, 0);
for(x=l->aux; x; x=x->next) {
x->num = i++;
// aux struct
adduint32(s, elfhash((uchar*)x->vers)); // hash
adduint16(s, 0); // flags
adduint16(s, x->num); // other - index we refer to this by
adduint32(s, addstring(dynstr, x->vers)); // version string offset
if(x->next)
adduint32(s, 16); // offset from this aux to next
else
adduint32(s, 0);
}
}
// version references
s = lookup(".gnu.version", 0);
for(i=0; i<nsym; i++) {
if(i == 0)
adduint16(s, 0); // first entry - no symbol
else if(need[i] == nil)
adduint16(s, 1); // global
else
adduint16(s, need[i]->num);
}
elfwritedynent(lookup(".dynamic", 0), DT_NULL, 0);
free(need);
s = lookup(".dynamic", 0);
elfverneed = nfile;
if(elfverneed) {
elfwritedynentsym(s, DT_VERNEED, lookup(".gnu.version_r", 0));
elfwritedynent(s, DT_VERNEEDNUM, nfile);
elfwritedynentsym(s, DT_VERSYM, lookup(".gnu.version", 0));
}
elfwritedynent(s, DT_NULL, 0);
}
ElfPhdr*
......
......@@ -216,6 +216,9 @@ typedef struct {
#define SHT_SYMTAB_SHNDX 18 /* Section indexes (see SHN_XINDEX). */
#define SHT_LOOS 0x60000000 /* First of OS specific semantics */
#define SHT_HIOS 0x6fffffff /* Last of OS specific semantics */
#define SHT_GNU_VERDEF 0x6ffffffd
#define SHT_GNU_VERNEED 0x6ffffffe
#define SHT_GNU_VERSYM 0x6fffffff
#define SHT_LOPROC 0x70000000 /* reserved range for processor */
#define SHT_HIPROC 0x7fffffff /* specific section header types */
#define SHT_LOUSER 0x80000000 /* reserved range for application */
......@@ -311,6 +314,10 @@ typedef struct {
#define DT_LOPROC 0x70000000 /* First processor-specific type. */
#define DT_HIPROC 0x7fffffff /* Last processor-specific type. */
#define DT_VERNEED 0x6ffffffe
#define DT_VERNEEDNUM 0x6fffffff
#define DT_VERSYM 0x6ffffff0
/* Values for DT_FLAGS */
#define DF_ORIGIN 0x0001 /* Indicates that the object being loaded may
make reference to the $ORIGIN substitution
......@@ -962,12 +969,14 @@ uint64 endelf(void);
extern int numelfphdr;
extern int numelfshdr;
extern int iself;
extern int elfverneed;
int elfwriteinterp(void);
void elfinterp(ElfShdr*, uint64, char*);
void elfdynhash(void);
ElfPhdr* elfphload(Segment*);
ElfShdr* elfshbits(Section*);
void elfsetstring(char*, int);
void elfaddverneed(Sym*);
/*
* Total amount of space to reserve at the start of the file
......
......@@ -412,7 +412,7 @@ parsemethod(char **pp, char *ep, char **methp)
static void
loaddynimport(char *file, char *pkg, char *p, int n)
{
char *pend, *next, *name, *def, *p0, *lib;
char *pend, *next, *name, *def, *p0, *lib, *q;
Sym *s;
pend = p + n;
......@@ -459,10 +459,14 @@ loaddynimport(char *file, char *pkg, char *p, int n)
}
name = expandpkg(name, pkg);
q = strchr(def, '@');
if(q)
*q++ = '\0';
s = lookup(name, 0);
if(s->type == 0 || s->type == SXREF) {
s->dynimplib = lib;
s->dynimpname = def;
s->dynimpvers = q;
s->type = SDYNIMPORT;
}
}
......
......@@ -330,29 +330,35 @@ func (i SectionIndex) GoString() string { return stringName(uint32(i), shnString
type SectionType uint32
const (
SHT_NULL SectionType = 0 /* inactive */
SHT_PROGBITS SectionType = 1 /* program defined information */
SHT_SYMTAB SectionType = 2 /* symbol table section */
SHT_STRTAB SectionType = 3 /* string table section */
SHT_RELA SectionType = 4 /* relocation section with addends */
SHT_HASH SectionType = 5 /* symbol hash table section */
SHT_DYNAMIC SectionType = 6 /* dynamic section */
SHT_NOTE SectionType = 7 /* note section */
SHT_NOBITS SectionType = 8 /* no space section */
SHT_REL SectionType = 9 /* relocation section - no addends */
SHT_SHLIB SectionType = 10 /* reserved - purpose unknown */
SHT_DYNSYM SectionType = 11 /* dynamic symbol table section */
SHT_INIT_ARRAY SectionType = 14 /* Initialization function pointers. */
SHT_FINI_ARRAY SectionType = 15 /* Termination function pointers. */
SHT_PREINIT_ARRAY SectionType = 16 /* Pre-initialization function ptrs. */
SHT_GROUP SectionType = 17 /* Section group. */
SHT_SYMTAB_SHNDX SectionType = 18 /* Section indexes (see SHN_XINDEX). */
SHT_LOOS SectionType = 0x60000000 /* First of OS specific semantics */
SHT_HIOS SectionType = 0x6fffffff /* Last of OS specific semantics */
SHT_LOPROC SectionType = 0x70000000 /* reserved range for processor */
SHT_HIPROC SectionType = 0x7fffffff /* specific section header types */
SHT_LOUSER SectionType = 0x80000000 /* reserved range for application */
SHT_HIUSER SectionType = 0xffffffff /* specific indexes */
SHT_NULL SectionType = 0 /* inactive */
SHT_PROGBITS SectionType = 1 /* program defined information */
SHT_SYMTAB SectionType = 2 /* symbol table section */
SHT_STRTAB SectionType = 3 /* string table section */
SHT_RELA SectionType = 4 /* relocation section with addends */
SHT_HASH SectionType = 5 /* symbol hash table section */
SHT_DYNAMIC SectionType = 6 /* dynamic section */
SHT_NOTE SectionType = 7 /* note section */
SHT_NOBITS SectionType = 8 /* no space section */
SHT_REL SectionType = 9 /* relocation section - no addends */
SHT_SHLIB SectionType = 10 /* reserved - purpose unknown */
SHT_DYNSYM SectionType = 11 /* dynamic symbol table section */
SHT_INIT_ARRAY SectionType = 14 /* Initialization function pointers. */
SHT_FINI_ARRAY SectionType = 15 /* Termination function pointers. */
SHT_PREINIT_ARRAY SectionType = 16 /* Pre-initialization function ptrs. */
SHT_GROUP SectionType = 17 /* Section group. */
SHT_SYMTAB_SHNDX SectionType = 18 /* Section indexes (see SHN_XINDEX). */
SHT_LOOS SectionType = 0x60000000 /* First of OS specific semantics */
SHT_GNU_ATTRIBUTES SectionType = 0x6ffffff5 /* GNU object attributes */
SHT_GNU_HASH SectionType = 0x6ffffff6 /* GNU hash table */
SHT_GNU_LIBLIST SectionType = 0x6ffffff7 /* GNU prelink library list */
SHT_GNU_VERDEF SectionType = 0x6ffffffd /* GNU version definition section */
SHT_GNU_VERNEED SectionType = 0x6ffffffe /* GNU version needs section */
SHT_GNU_VERSYM SectionType = 0x6fffffff /* GNU version symbol table */
SHT_HIOS SectionType = 0x6fffffff /* Last of OS specific semantics */
SHT_LOPROC SectionType = 0x70000000 /* reserved range for processor */
SHT_HIPROC SectionType = 0x7fffffff /* specific section header types */
SHT_LOUSER SectionType = 0x80000000 /* reserved range for application */
SHT_HIUSER SectionType = 0xffffffff /* specific indexes */
)
var shtStrings = []intName{
......@@ -374,7 +380,12 @@ var shtStrings = []intName{
{17, "SHT_GROUP"},
{18, "SHT_SYMTAB_SHNDX"},
{0x60000000, "SHT_LOOS"},
{0x6fffffff, "SHT_HIOS"},
{0x6ffffff5, "SHT_GNU_ATTRIBUTES"},
{0x6ffffff6, "SHT_GNU_HASH"},
{0x6ffffff7, "SHT_GNU_LIBLIST"},
{0x6ffffffd, "SHT_GNU_VERDEF"},
{0x6ffffffe, "SHT_GNU_VERNEED"},
{0x6fffffff, "SHT_GNU_VERSYM"},
{0x70000000, "SHT_LOPROC"},
{0x7fffffff, "SHT_HIPROC"},
{0x80000000, "SHT_LOUSER"},
......@@ -518,6 +529,9 @@ const (
DT_PREINIT_ARRAYSZ DynTag = 33 /* Size in bytes of the array of pre-initialization functions. */
DT_LOOS DynTag = 0x6000000d /* First OS-specific */
DT_HIOS DynTag = 0x6ffff000 /* Last OS-specific */
DT_VERSYM DynTag = 0x6ffffff0
DT_VERNEED DynTag = 0x6ffffffe
DT_VERNEEDNUM DynTag = 0x6fffffff
DT_LOPROC DynTag = 0x70000000 /* First processor-specific type. */
DT_HIPROC DynTag = 0x7fffffff /* Last processor-specific type. */
)
......@@ -559,6 +573,9 @@ var dtStrings = []intName{
{33, "DT_PREINIT_ARRAYSZ"},
{0x6000000d, "DT_LOOS"},
{0x6ffff000, "DT_HIOS"},
{0x6ffffff0, "DT_VERSYM"},
{0x6ffffffe, "DT_VERNEED"},
{0x6fffffff, "DT_VERNEEDNUM"},
{0x70000000, "DT_LOPROC"},
{0x7fffffff, "DT_HIPROC"},
}
......
......@@ -35,9 +35,11 @@ type FileHeader struct {
// A File represents an open ELF file.
type File struct {
FileHeader
Sections []*Section
Progs []*Prog
closer io.Closer
Sections []*Section
Progs []*Prog
closer io.Closer
gnuNeed []verneed
gnuVersym []byte
}
// A SectionHeader represents a single ELF section header.
......@@ -329,8 +331,8 @@ func NewFile(r io.ReaderAt) (*File, 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) {
// with the given type, along with the associated string table.
func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, os.Error) {
switch f.Class {
case ELFCLASS64:
return f.getSymbols64(typ)
......@@ -339,27 +341,27 @@ func (f *File) getSymbols(typ SectionType) ([]Symbol, os.Error) {
return f.getSymbols32(typ)
}
return nil, os.ErrorString("not implemented")
return nil, nil, os.ErrorString("not implemented")
}
func (f *File) getSymbols32(typ SectionType) ([]Symbol, os.Error) {
func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, os.Error) {
symtabSection := f.SectionByType(typ)
if symtabSection == nil {
return nil, os.ErrorString("no symbol section")
return nil, nil, os.ErrorString("no symbol section")
}
data, err := symtabSection.Data()
if err != nil {
return nil, os.ErrorString("cannot load symbol section")
return nil, 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")
return nil, 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")
return nil, nil, os.ErrorString("cannot load string table section")
}
// The first entry is all zeros.
......@@ -382,27 +384,27 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, os.Error) {
i++
}
return symbols, nil
return symbols, strdata, nil
}
func (f *File) getSymbols64(typ SectionType) ([]Symbol, os.Error) {
func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, os.Error) {
symtabSection := f.SectionByType(typ)
if symtabSection == nil {
return nil, os.ErrorString("no symbol section")
return nil, nil, os.ErrorString("no symbol section")
}
data, err := symtabSection.Data()
if err != nil {
return nil, os.ErrorString("cannot load symbol section")
return nil, nil, os.ErrorString("cannot load symbol section")
}
symtab := bytes.NewBuffer(data)
if symtab.Len()%Sym64Size != 0 {
return nil, os.ErrorString("length of symbol section is not a multiple of Sym64Size")
return nil, 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")
return nil, nil, os.ErrorString("cannot load string table section")
}
// The first entry is all zeros.
......@@ -425,7 +427,7 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, os.Error) {
i++
}
return symbols, nil
return symbols, strdata, nil
}
// getString extracts a string from an ELF string table.
......@@ -468,7 +470,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(SHT_SYMTAB)
symbols, _, err := f.getSymbols(SHT_SYMTAB)
if err != nil {
return err
}
......@@ -544,24 +546,123 @@ func (f *File) DWARF() (*dwarf.Data, os.Error) {
return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
}
type ImportedSymbol struct {
Name string
Version string
Library string
}
// 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)
func (f *File) ImportedSymbols() ([]ImportedSymbol, os.Error) {
sym, str, err := f.getSymbols(SHT_DYNSYM)
if err != nil {
return nil, err
}
var all []string
for _, s := range sym {
f.gnuVersionInit(str)
var all []ImportedSymbol
for i, s := range sym {
if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {
all = append(all, s.Name)
all = append(all, ImportedSymbol{Name: s.Name})
f.gnuVersion(i, &all[len(all)-1])
}
}
return all, nil
}
type verneed struct {
File string
Name string
}
// gnuVersionInit parses the GNU version tables
// for use by calls to gnuVersion.
func (f *File) gnuVersionInit(str []byte) {
// Accumulate verneed information.
vn := f.SectionByType(SHT_GNU_VERNEED)
if vn == nil {
return
}
d, _ := vn.Data()
var need []verneed
i := 0
for {
if i+16 > len(d) {
break
}
vers := f.ByteOrder.Uint16(d[i : i+2])
if vers != 1 {
break
}
cnt := f.ByteOrder.Uint16(d[i+2 : i+4])
fileoff := f.ByteOrder.Uint32(d[i+4 : i+8])
aux := f.ByteOrder.Uint32(d[i+8 : i+12])
next := f.ByteOrder.Uint32(d[i+12 : i+16])
file, _ := getString(str, int(fileoff))
var name string
j := i + int(aux)
for c := 0; c < int(cnt); c++ {
if j+16 > len(d) {
break
}
// hash := f.ByteOrder.Uint32(d[j:j+4])
// flags := f.ByteOrder.Uint16(d[j+4:j+6])
other := f.ByteOrder.Uint16(d[j+6 : j+8])
nameoff := f.ByteOrder.Uint32(d[j+8 : j+12])
next := f.ByteOrder.Uint32(d[j+12 : j+16])
name, _ = getString(str, int(nameoff))
ndx := int(other)
if ndx >= len(need) {
a := make([]verneed, 2*(ndx+1))
copy(a, need)
need = a
}
need[ndx] = verneed{file, name}
if next == 0 {
break
}
j += int(next)
}
if next == 0 {
break
}
i += int(next)
}
// Versym parallels symbol table, indexing into verneed.
vs := f.SectionByType(SHT_GNU_VERSYM)
if vs == nil {
return
}
d, _ = vs.Data()
f.gnuNeed = need
f.gnuVersym = d
}
// gnuVersion adds Library and Version information to sym,
// which came from offset i of the symbol table.
func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
// Each entry is two bytes; skip undef entry at beginning.
i = (i + 1) * 2
if i >= len(f.gnuVersym) {
return
}
j := int(f.ByteOrder.Uint16(f.gnuVersym[i:]))
if j < 2 || j >= len(f.gnuNeed) {
return
}
n := &f.gnuNeed[j]
sym.Library = n.File
sym.Version = n.Name
}
// 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.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment