aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/ld/elf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/ld/elf.c')
-rw-r--r--src/cmd/ld/elf.c135
1 files changed, 117 insertions, 18 deletions
diff --git a/src/cmd/ld/elf.c b/src/cmd/ld/elf.c
index b0cce4985d..fc917b203b 100644
--- a/src/cmd/ld/elf.c
+++ b/src/cmd/ld/elf.c
@@ -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*