summaryrefslogtreecommitdiff
path: root/libdynld/dynld.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdynld/dynld.c')
-rw-r--r--libdynld/dynld.c258
1 files changed, 258 insertions, 0 deletions
diff --git a/libdynld/dynld.c b/libdynld/dynld.c
new file mode 100644
index 00000000..43bef93c
--- /dev/null
+++ b/libdynld/dynld.c
@@ -0,0 +1,258 @@
+#include "lib9.h"
+#include <a.out.h>
+#include <dynld.h>
+
+static ulong
+get2(uchar *b)
+{
+ return (b[0] << 8) | b[1];
+}
+
+static ulong
+get4(uchar *b)
+{
+ return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+}
+
+static ulong
+lgetbe(ulong l)
+{
+ union {
+ ulong l;
+ uchar c[4];
+ } u;
+ u.l = l;
+ return get4(u.c);
+}
+
+Dynsym*
+dynfindsym(char *s, Dynsym *tab, int ntab)
+{
+ int n, n2, d;
+ Dynsym *t, *m;
+
+ t = tab;
+ n = ntab;
+ while(n > 0){
+ n2 = n>>1;
+ m = t+n2;
+ d = strcmp(s, m->name);
+ if(d < 0){
+ n = n2;
+ continue;
+ }
+ if(d > 0){
+ t = m+1;
+ n -= n2+1;
+ continue;
+ }
+ return m;
+ }
+ return nil;
+}
+
+void*
+dynimport(Dynobj *o, char *name, ulong sig)
+{
+ Dynsym *t;
+
+ t = dynfindsym(name, o->export, o->nexport);
+ if(t == nil || sig != 0 && t->sig != 0 && t->sig != sig)
+ return nil;
+ return (void*)t->addr;
+}
+
+int
+dyntabsize(Dynsym *t)
+{
+ int n;
+
+ for(n = 0; t->name != nil; t++)
+ n++;
+ return n;
+}
+
+void
+dynobjfree(Dynobj *o)
+{
+ if(o != nil){
+ free(o->base);
+ free(o->import);
+ free(o);
+ }
+}
+
+void
+dynfreeimport(Dynobj *o)
+{
+ free(o->import);
+ o->import = nil;
+ o->nimport = 0;
+}
+
+static char Ereloc[] = "error reading object file";
+
+Dynobj*
+dynloadgen(void *file, long (*rd)(void*,void*,long), vlong (*sk)(void*,vlong,int), void (*werr)(char*), Dynsym *tab, int ntab, ulong maxsize)
+{
+ int i, m, n, ni, nr, relsize;
+ ulong syms, entry, sig, p, a;
+ uchar *base;
+ Exec e;
+ Dynsym *t;
+ Dynobj *l;
+ char *s, *err, buf[64];
+ uchar *reldata, *rp, *ep;
+ vlong off;
+
+ err = Ereloc; /* default */
+ off = (*sk)(file, 0, 1);
+ l = mallocz(sizeof(Dynobj), 1);
+ if(l == nil){
+ err = "can't allocate Dynobj";
+ goto Error;
+ }
+ if((*rd)(file, &e, sizeof(Exec)) != sizeof(Exec))
+ goto Error;
+ if(lgetbe(e.magic) != dynmagic()){
+ err = "not dynamic object file or wrong platform";
+ goto Error;
+ }
+ l->text = lgetbe(e.text);
+ l->data = lgetbe(e.data);
+ l->bss = lgetbe(e.bss);
+ syms = lgetbe(e.syms)+lgetbe(e.spsz)+lgetbe(e.pcsz);
+ entry = lgetbe(e.entry);
+ l->size = l->text + l->data + l->bss;
+ if(entry < 0 || entry >= l->size || entry & 3){
+ err = "invalid export table pointer (entry point)";
+ goto Error;
+ }
+ if(maxsize && l->size >= maxsize){
+ snprint(buf, sizeof(buf), "%lud: object too big", l->size);
+ err = buf;
+ goto Error;
+ }
+
+ l->base = base = malloc(l->size);
+ if(base == nil){
+ err = "out of memory: loading object file";
+ goto Error;
+ }
+ l->export = (Dynsym*)(base+entry);
+ if((*rd)(file, base, l->text+l->data) != l->text+l->data)
+ goto Error;
+ memset(base+l->text+l->data, 0, l->bss);
+ if((*sk)(file, syms, 1) < 0)
+ goto Error;
+ if((*rd)(file, buf, 4) != 4)
+ goto Error;
+ relsize = get4((uchar*)buf); /* always contains at least an import count (might be zero) */
+ if(relsize < 4)
+ goto Error;
+ reldata = malloc(relsize);
+ if(reldata == nil){
+ err = "out of memory: relocation data";
+ goto Error;
+ }
+ if((*rd)(file, reldata, relsize) != relsize)
+ goto Error;
+ rp = reldata;
+ ep = reldata+relsize;
+ ni = get4(rp);
+ rp += 4;
+ if(ni < 0 || ni > 8000)
+ goto Error; /* implausible size */
+ l->nimport = ni;
+ l->import = malloc(ni*sizeof(Dynsym*));
+ if(l->import == nil){
+ err = "out of memory: symbol table";
+ goto Error;
+ }
+ for(i = 0; i < ni; i++){
+ if(rp+5 > ep)
+ goto Error;
+ sig = get4(rp);
+ rp += 4;
+ s = (char*)rp;
+ while(*rp++)
+ if(rp >= ep)
+ goto Error;
+ t = dynfindsym(s, tab, ntab);
+ if(t == nil){
+ snprint(buf, sizeof(buf), "undefined symbol: %s", s);
+ err = buf;
+ goto Error;
+ }
+ if(sig != 0 && t->sig != 0 && t->sig != sig){
+ snprint(buf, sizeof(buf), "signature mismatch: %s (%lux != %lux)", s, sig, t->sig);
+ err = buf;
+ goto Error;
+ }
+ l->import[i] = t;
+ }
+
+ a = 0;
+ if(rp+4 > ep)
+ goto Error;
+ nr = get4(rp);
+ rp += 4;
+ for(i = 0; i < nr; i++){
+ if(rp >= ep)
+ goto Error;
+ m = *rp++;
+ n = m>>6;
+ if(rp+(1<<n) > ep)
+ goto Error;
+ switch(n){
+ case 0:
+ p = *rp++;
+ break;
+ case 1:
+ p = get2(rp);
+ rp += 2;
+ break;
+ case 2:
+ p = get4(rp);
+ rp += 4;
+ break;
+ default:
+ goto Error;
+ }
+ a += p;
+ err = dynreloc(base, a, m&0xf, l->import, ni);
+ if(err != nil){
+ snprint(buf, sizeof(buf), "dynamic object: %s", err);
+ err = buf;
+ goto Error;
+ }
+ }
+ free(reldata);
+
+ /* could check relocated export table here */
+ l->nexport = dyntabsize(l->export);
+
+ segflush(base, l->text);
+
+ return l;
+
+Error:
+ if(off >= 0)
+ (*sk)(file, off, 0); /* restore original file offset */
+ (*werr)(err);
+ dynobjfree(l);
+ return nil;
+}
+
+int
+dynloadable(void* file, long (*rd)(void*,void*,long), vlong (*sk)(void*,vlong,int))
+{
+ long magic;
+
+ if((*rd)(file, &magic, sizeof(magic)) != sizeof(magic)){
+ (*sk)(file, -(signed int)sizeof(magic), 1);
+ return 0;
+ }
+ (*sk)(file, -(signed int)sizeof(magic), 1);
+ return lgetbe(magic) == dynmagic();
+}