]> Cypherpunks repositories - gostls13.git/commitdiff
Add support for #pragma dynexport.
authorIan Lance Taylor <iant@golang.org>
Tue, 23 Mar 2010 13:46:30 +0000 (06:46 -0700)
committerIan Lance Taylor <iant@golang.org>
Tue, 23 Mar 2010 13:46:30 +0000 (06:46 -0700)
R=rsc
CC=golang-dev
https://golang.org/cl/661043

16 files changed:
src/cmd/5c/swt.c
src/cmd/5l/asm.c
src/cmd/5l/l.h
src/cmd/6c/swt.c
src/cmd/6l/asm.c
src/cmd/6l/l.h
src/cmd/8c/swt.c
src/cmd/8l/asm.c
src/cmd/8l/l.h
src/cmd/cc/cc.h
src/cmd/cc/dpchk.c
src/cmd/cc/lexbody
src/cmd/cc/macbody
src/cmd/ld/elf.c
src/cmd/ld/elf.h
src/cmd/ld/go.c

index 3aaf748a458ddf131b721f808210e3dc55dc5faf..75f768ddaeaa4d3c54cc34d10a6e94eef11dead5 100644 (file)
@@ -374,7 +374,7 @@ outcode(void)
        }
 
        Bprint(&outbuf, "%s\n", thestring);
-       if(ndynimp > 0) {
+       if(ndynimp > 0 || ndynexp > 0) {
                int i;
 
                Bprint(&outbuf, "\n");
@@ -383,7 +383,10 @@ outcode(void)
                Bprint(&outbuf, "$$  // dynimport\n", thestring);
                for(i=0; i<ndynimp; i++)
                        Bprint(&outbuf, "dynimport %s %s %s\n", dynimp[i].local, dynimp[i].remote, dynimp[i].path);
-               Bprint(&outbuf, "$$\n\n");
+               Bprint(&outbuf, "\n$$  // dynexport\n", thestring);
+               for(i=0; i<ndynexp; i++)
+                       Bprint(&outbuf, "dynexport %s %s\n", dynexp[i].local, dynexp[i].remote);
+               Bprint(&outbuf, "\n$$\n\n");
        }
        Bprint(&outbuf, "!\n");
 
index 306d828b249925ac56c8311f8211be46adec7048..f7cbccec57a496c533467b3a8c5fa0db3e4b0805 100644 (file)
@@ -192,11 +192,27 @@ enum {
 
 vlong elfstr[NElfStr];
 
+static int
+needlib(char *name)
+{
+       char *p;
+       Sym *s;
+
+       /* reuse hash code in symbol table */
+       p = smprint(".dynlib.%s", name);
+       s = lookup(p, 0);
+       if(s->type == 0) {
+               s->type = 100;  // avoid SDATA, etc.
+               return 1;
+       }
+       return 0;
+}
+
 void
 doelf(void)
 {
-       Sym *s, *shstrtab, *dynamic, *dynstr;
-       int h, nsym;
+       Sym *s, *shstrtab, *dynamic, *dynstr, *d;
+       int h, nsym, t;
 
        if(!iself)
                return;
@@ -267,48 +283,62 @@ doelf(void)
                        for(s=hash[h]; s!=S; s=s->link) {
                                if(!s->reachable || (s->type != SDATA && s->type != SBSS) || s->dynimpname == nil)
                                        continue;
-                       #if 0
-                               d = lookup(".rel", 0);
-                               addaddr(d, s);
-                               adduint32(d, ELF32_R_INFO(nsym, R_386_32));
+
+                               if(!s->dynexport) {
+                                       d = lookup(".rel", 0);
+                                       addaddr(d, s);
+                                       adduint32(d, ELF32_R_INFO(nsym, R_ARM_ABS32));
+                               }
+
                                nsym++;
 
                                d = lookup(".dynsym", 0);
                                adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname));
-                               adduint32(d, 0);        /* value */
-                               adduint32(d, 0);        /* size of object */
+                               /* value */
+                               if(!s->dynexport)
+                                       adduint32(d, 0);
+                               else
+                                       addaddr(d, s);
+
+                               /* size of object */
+                               adduint32(d, 0);
+
+                               /* type */
                                t = STB_GLOBAL << 4;
-                               t |= STT_OBJECT;        // works for func too, empirically
+                               if(s->dynexport && s->type == STEXT)
+                                       t |= STT_FUNC;
+                               else
+                                       t |= STT_OBJECT;
                                adduint8(d, t);
-                               adduint8(d, 0); /* reserved */
-                               adduint16(d, SHN_UNDEF);        /* section where symbol is defined */
 
-                               if(needlib(s->dynimplib))
+                               /* reserved */
+                               adduint8(d, 0);
+
+                               /* section where symbol is defined */
+                               if(!s->dynexport)
+                                       adduint16(d, SHN_UNDEF);
+                               else {
+                                       switch(s->type) {
+                                       default:
+                                       case STEXT:
+                                               t = 9;
+                                               break;
+                                       case SDATA:
+                                               t = 10;
+                                               break;
+                                       case SBSS:
+                                               t = 11;
+                                               break;
+                                       }
+                                       adduint16(d, t);
+                               }
+
+                               if(!s->dynexport && needlib(s->dynimplib))
                                        elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib));
-                       #endif
                        }
                }
 
-               /*
-                * hash table.
-                * only entries that other objects need to find when
-                * linking us need to be in the table.  right now that is
-                * no entries.
-                *
-                * freebsd insists on having chains enough for all
-                * the local symbols, though.  for now, we just lay
-                * down a trivial hash table with 1 bucket and a long chain,
-                * because no one is actually looking for our symbols.
-                */
-               s = lookup(".hash", 0);
-               s->type = SDATA;        // TODO: rodata
-               s->reachable = 1;
-               adduint32(s, 1);        // nbucket
-               adduint32(s, nsym);     // nchain
-               adduint32(s, nsym-1);   // bucket 0
-               adduint32(s, 0);        // chain 0
-               for(h=1; h<nsym; h++)   // chain nsym-1 -> nsym-2 -> ... -> 2 -> 1 -> 0
-                       adduint32(s, h-1);
+               elfdynhash(nsym);
 
                /*
                 * .dynamic table
index 45ddd616f1e007f3731c28ef16f04a9a3cca95e4..44bd923a9c34fc412c7d60038ad2f048f5d17ee9 100644 (file)
@@ -125,6 +125,7 @@ struct      Sym
        uchar   subtype;
        uchar   dupok;
        uchar   reachable;
+       uchar   dynexport;
        int32   value;
        int32   sig;
        int32   size;
index cdb948f890e259745567a5607c65ad0eeae8ceb6..0c83704688c0daeb5b556fc5d6997bc4e7ad0bc6 100644 (file)
@@ -232,7 +232,7 @@ outcode(void)
        Binit(&b, f, OWRITE);
 
        Bprint(&b, "%s\n", thestring);
-       if(ndynimp > 0) {
+       if(ndynimp > 0 || ndynexp > 0) {
                int i;
 
                Bprint(&b, "\n");
@@ -241,7 +241,10 @@ outcode(void)
                Bprint(&b, "$$  // dynimport\n", thestring);
                for(i=0; i<ndynimp; i++)
                        Bprint(&b, "dynimport %s %s %s\n", dynimp[i].local, dynimp[i].remote, dynimp[i].path);
-               Bprint(&b, "$$\n\n");
+               Bprint(&b, "\n$$  // dynexport\n", thestring);
+               for(i=0; i<ndynexp; i++)
+                       Bprint(&b, "dynexport %s %s\n", dynexp[i].local, dynexp[i].remote);
+               Bprint(&b, "\n$$\n\n");
        }
        Bprint(&b, "!\n");
 
index 1fc3db98de463972ed167bfaafe8c7cf98274ec6..af00f559446b9f1f1e41b8c14a02471237519a90 100644 (file)
@@ -351,50 +351,65 @@ doelf(void)
                nsym = 1;       // sym 0 is reserved
                for(h=0; h<NHASH; h++) {
                        for(s=hash[h]; s!=S; s=s->link) {
-                               if(!s->reachable || (s->type != SDATA && s->type != SBSS) || s->dynimpname == nil)
+                               if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil)
                                        continue;
 
-                               d = lookup(".rela", 0);
-                               addaddr(d, s);
-                               adduint64(d, ELF64_R_INFO(nsym, R_X86_64_64));
-                               adduint64(d, 0);
+                               if(!s->dynexport) {
+                                       d = lookup(".rela", 0);
+                                       addaddr(d, s);
+                                       adduint64(d, ELF64_R_INFO(nsym, R_X86_64_64));
+                                       adduint64(d, 0);
+                               }
+
                                nsym++;
 
                                d = lookup(".dynsym", 0);
                                adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname));
+                               /* type */
                                t = STB_GLOBAL << 4;
-                               t |= STT_OBJECT;        // works for func too, empirically
+                               if(s->dynexport && s->type == STEXT)
+                                       t |= STT_FUNC;
+                               else
+                                       t |= STT_OBJECT;
                                adduint8(d, t);
-                               adduint8(d, 0); /* reserved */
-                               adduint16(d, SHN_UNDEF);        /* section where symbol is defined */
-                               adduint64(d, 0);        /* value */
-                               adduint64(d, 0);        /* size of object */
 
-                               if(needlib(s->dynimplib))
+                               /* reserved */
+                               adduint8(d, 0);
+
+                               /* section where symbol is defined */
+                               if(!s->dynexport)
+                                       adduint16(d, SHN_UNDEF);
+                               else {
+                                       switch(s->type) {
+                                       default:
+                                       case STEXT:
+                                               t = 9;
+                                               break;
+                                       case SDATA:
+                                               t = 10;
+                                               break;
+                                       case SBSS:
+                                               t = 11;
+                                               break;
+                                       }
+                                       adduint16(d, t);
+                               }
+
+                               /* value */
+                               if(!s->dynexport)
+                                       adduint64(d, 0);
+                               else
+                                       addaddr(d, s);
+
+                               /* size of object */
+                               adduint64(d, 0);
+
+                               if(!s->dynexport && needlib(s->dynimplib))
                                        elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib));
                        }
                }
 
-               /*
-                * hash table.
-                * only entries that other objects need to find when
-                * linking us need to be in the table.  right now that is
-                * no entries.
-                *
-                * freebsd insists on having chains enough for all
-                * the local symbols, though.  for now, we just lay
-                * down a trivial hash table with 1 bucket and a long chain,
-                * because no one is actually looking for our symbols.
-                */
-               s = lookup(".hash", 0);
-               s->type = SDATA;        // TODO: rodata
-               s->reachable = 1;
-               adduint32(s, 1);        // nbucket
-               adduint32(s, nsym);     // nchain
-               adduint32(s, nsym-1);   // bucket 0
-               adduint32(s, 0);        // chain 0
-               for(h=1; h<nsym; h++)   // chain nsym-1 -> nsym-2 -> ... -> 2 -> 1 -> 0
-                       adduint32(s, h-1);
+               elfdynhash(nsym);
 
                /*
                 * .dynamic table
index 4b911ff69862d61ed5b4a93f6526052fe26b2946..5f99e9a51220f2687cb2ccc24633446ff5784401 100644 (file)
@@ -123,6 +123,7 @@ struct      Sym
        uchar   subtype;
        uchar   dupok;
        uchar   reachable;
+       uchar   dynexport;
        vlong   value;
        vlong   size;
        int32   sig;
index 035b8e9c3e4b9975c66166bbf8056fdd25aef649..72cebc00c81519bb763dc8d9b8b1555287aabe3f 100644 (file)
@@ -231,7 +231,7 @@ outcode(void)
        Binit(&b, f, OWRITE);
 
        Bprint(&b, "%s\n", thestring);
-       if(ndynimp > 0) {
+       if(ndynimp > 0 || ndynexp > 0) {
                int i;
 
                Bprint(&b, "\n");
@@ -240,7 +240,10 @@ outcode(void)
                Bprint(&b, "$$  // dynimport\n", thestring);
                for(i=0; i<ndynimp; i++)
                        Bprint(&b, "dynimport %s %s %s\n", dynimp[i].local, dynimp[i].remote, dynimp[i].path);
-               Bprint(&b, "$$\n\n");
+               Bprint(&b, "\n$$  // dynexport\n", thestring);
+               for(i=0; i<ndynexp; i++)
+                       Bprint(&b, "dynexport %s %s\n", dynexp[i].local, dynexp[i].remote);
+               Bprint(&b, "\n$$\n\n");
        }
        Bprint(&b, "!\n");
 
index a4d8adc31227a8c45dfccd4b802d3a63f36f350d..eddf6617b18c7ad57c3d93aa65e098980f42e374 100644 (file)
@@ -342,49 +342,64 @@ doelf(void)
                nsym = 1;       // sym 0 is reserved
                for(h=0; h<NHASH; h++) {
                        for(s=hash[h]; s!=S; s=s->link) {
-                               if(!s->reachable || (s->type != SDATA && s->type != SBSS) || s->dynimpname == nil)
+                               if(!s->reachable || (s->type != STEXT && s->type != SDATA && s->type != SBSS) || s->dynimpname == nil)
                                        continue;
 
-                               d = lookup(".rel", 0);
-                               addaddr(d, s);
-                               adduint32(d, ELF32_R_INFO(nsym, R_386_32));
+                               if(!s->dynexport) {
+                                       d = lookup(".rel", 0);
+                                       addaddr(d, s);
+                                       adduint32(d, ELF32_R_INFO(nsym, R_386_32));
+                               }
+
                                nsym++;
 
                                d = lookup(".dynsym", 0);
                                adduint32(d, addstring(lookup(".dynstr", 0), s->dynimpname));
-                               adduint32(d, 0);        /* value */
-                               adduint32(d, 0);        /* size of object */
+                               /* value */
+                               if(!s->dynexport)
+                                       adduint32(d, 0);
+                               else
+                                       addaddr(d, s);
+
+                               /* size of object */
+                               adduint32(d, 0);
+
+                               /* type */
                                t = STB_GLOBAL << 4;
-                               t |= STT_OBJECT;        // works for func too, empirically
+                               if(s->dynexport && s->type == STEXT)
+                                       t |= STT_FUNC;
+                               else
+                                       t |= STT_OBJECT;
                                adduint8(d, t);
-                               adduint8(d, 0); /* reserved */
-                               adduint16(d, SHN_UNDEF);        /* section where symbol is defined */
 
-                               if(needlib(s->dynimplib))
+                               /* reserved */
+                               adduint8(d, 0);
+
+                               /* section where symbol is defined */
+                               if(!s->dynexport)
+                                       adduint16(d, SHN_UNDEF);
+                               else {
+                                       switch(s->type) {
+                                       default:
+                                       case STEXT:
+                                               t = 9;
+                                               break;
+                                       case SDATA:
+                                               t = 10;
+                                               break;
+                                       case SBSS:
+                                               t = 11;
+                                               break;
+                                       }
+                                       adduint16(d, t);
+                               }
+
+                               if(!s->dynexport && needlib(s->dynimplib))
                                        elfwritedynent(dynamic, DT_NEEDED, addstring(dynstr, s->dynimplib));
                        }
                }
 
-               /*
-                * hash table.
-                * only entries that other objects need to find when
-                * linking us need to be in the table.  right now that is
-                * no entries.
-                *
-                * freebsd insists on having chains enough for all
-                * the local symbols, though.  for now, we just lay
-                * down a trivial hash table with 1 bucket and a long chain,
-                * because no one is actually looking for our symbols.
-                */
-               s = lookup(".hash", 0);
-               s->type = SDATA;        // TODO: rodata
-               s->reachable = 1;
-               adduint32(s, 1);        // nbucket
-               adduint32(s, nsym);     // nchain
-               adduint32(s, nsym-1);   // bucket 0
-               adduint32(s, 0);        // chain 0
-               for(h=1; h<nsym; h++)   // chain nsym-1 -> nsym-2 -> ... -> 2 -> 1 -> 0
-                       adduint32(s, h-1);
+               elfdynhash(nsym);
 
                /*
                 * .dynamic table
index 04f6c0e2706438f2a2ebdcd5882e32d571b9b4f0..8f02bdefda0a614fa2e01e5c0ca0993d5d9ab865 100644 (file)
@@ -123,6 +123,7 @@ struct      Sym
        uchar   subtype;
        uchar   dupok;
        uchar   reachable;
+       uchar   dynexport;
        int32   value;
        int32   size;
        int32   sig;
index 4241c18f73e6317790e9624018889fa42bab05c3..725a3cb0898168166e30e8fb4423d7b387ee41dc 100644 (file)
@@ -57,6 +57,7 @@ typedef       struct  Term    Term;
 typedef        struct  Init    Init;
 typedef        struct  Bits    Bits;
 typedef        struct  Dynimp  Dynimp;
+typedef        struct  Dynexp  Dynexp;
 
 #define        NHUNK           50000L
 #define        BUFSIZ          8192
@@ -454,6 +455,15 @@ struct     Dynimp
 EXTERN Dynimp  *dynimp;
 EXTERN int     ndynimp;
 
+struct Dynexp
+{
+       char*   local;
+       char*   remote;
+};
+
+EXTERN Dynexp  *dynexp;
+EXTERN int     ndynexp;
+
 EXTERN struct
 {
        Type*   tenum;          /* type of entire enum */
@@ -761,6 +771,7 @@ void        pragfpround(void);
 void   pragtextflag(void);
 void   pragincomplete(void);
 void   pragdynimport(void);
+void   pragdynexporg(void);
 
 /*
  * calls to machine depend part
index 99d8c05f15f11c5ef9b09a7f5a599e1d84ff15e1..046c0e4da9b8744f17f46905ee6ae6c1551b2ed1 100644 (file)
@@ -566,3 +566,32 @@ out:
        while(getnsc() != '\n')
                ;
 }
+
+void
+pragdynexport(void)
+{
+       Sym *local, *remote;
+       Dynexp *f;
+
+       local = getsym();
+       if(local == nil)
+               goto err;
+
+       remote = getsym();
+       if(remote == nil)
+               goto err;
+
+       if(ndynexp%32 == 0)
+               dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
+       f = &dynexp[ndynexp++];
+       f->local = local->name;
+       f->remote = remote->name;
+       goto out;
+
+err:
+       yyerror("usage: #pragma dynexport local remote");
+
+out:
+       while(getnsc() != '\n')
+               ;
+}
index 743e6e52dceac5db490b871aefcc0bead6248c5c..0bccc173358a9cc6ea7596cd2d9bb09a67042e9e 100644 (file)
@@ -53,6 +53,13 @@ pragdynimport(void)
                ;
 }
 
+void
+pragdynexport(void)
+{
+       while(getnsc() != '\n')
+               ;
+}
+
 void
 pragfpround(void)
 {
index 37ddc81c93b75312da8b34acbf6f54cdd9492932..ca8a54c0bce90e81d6ec81823516f581807bd7a0 100644 (file)
@@ -743,6 +743,10 @@ macprag(void)
                pragdynimport();
                return;
        }
+       if(s && strcmp(s->name, "dynexport") == 0) {
+               pragdynexport();
+               return;
+       }
        while(getnsc() != '\n')
                ;
        return;
index d2adca16ce64d5225c985c28da780052e2908184..a0bcba35a66de2673a0d22f23bbbbbb0151afa8f 100644 (file)
@@ -309,3 +309,59 @@ elfinterp(ElfShdr *sh, uint64 startva, char *p)
        sh->off = ELFRESERVE - n;
        sh->size = n;
 }
+
+void
+elfdynhash(int nsym)
+{
+       Sym *s, *sy;
+       int i, h, nbucket, b;
+       uchar *pc;
+       uint32 hc, g;
+       uint32 *chain, *buckets;
+
+       s = lookup(".hash", 0);
+       s->type = SDATA;        // TODO: rodata
+       s->reachable = 1;
+
+       i = nsym;
+       nbucket = 1;
+       while(i > 0) {
+               ++nbucket;
+               i >>= 1;
+       }
+
+       chain = malloc(nsym * sizeof(uint32));
+       memset(chain, 0, nsym * sizeof(uint32));
+       buckets = malloc(nbucket * sizeof(uint32));
+       memset(buckets, 0, nbucket * sizeof(uint32));
+       i = 1;
+       for(h = 0; h<NHASH; h++) {
+               for(sy=hash[h]; sy!=S; sy=sy->link) {
+                       if (!sy->reachable || (sy->type != STEXT && sy->type != SDATA && sy->type != SBSS) || sy->dynimpname == nil)
+                               continue;
+
+                       hc = 0;
+                       for(pc = (uchar*)sy->dynimpname; *pc; pc++) {
+                               hc = (hc<<4) + *pc;
+                               g = hc & 0xf0000000;
+                               hc ^= g >> 24;
+                               hc &= ~g;
+                       }
+
+                       b = hc % nbucket;
+                       chain[i] = buckets[b];
+                       buckets[b] = i;
+                       i++;
+               }
+       }
+
+       adduint32(s, nbucket);
+       adduint32(s, nsym);
+       for(i = 0; i<nbucket; i++)
+               adduint32(s, buckets[i]);
+       for(i = 0; i<nsym; i++)
+               adduint32(s, chain[i]);
+
+       free(chain);
+       free(buckets);
+}
index cd78f2d9ccecbe8e601de6af29241749401ba7cf..9b5fdb17e9baeafdae317e99eef6f49678d237a6 100644 (file)
@@ -964,6 +964,7 @@ extern      int     numelfshdr;
 extern int     iself;
 int    elfwriteinterp(void);
 void   elfinterp(ElfShdr*, uint64, char*);
+void   elfdynhash(int);
 
 /*
  * Total amount of space to reserve at the start of the file
index 4a3b4725bef9fdd496c961c8538f63a92a6db466..b5e0def7bef52bbc7564abce8d473ee8cad6fec1 100644 (file)
@@ -67,9 +67,13 @@ ilookup(char *name)
 
 static void loadpkgdata(char*, char*, char*, int);
 static void loaddynimport(char*, char*, int);
+static void loaddynexport(char*, char*, char*, int);
 static int parsemethod(char**, char*, char**);
 static int parsepkgdata(char*, char*, char**, char*, char**, char**, char**);
 
+static int ndynexp;
+static Sym **dynexp;
+
 void
 ldpkg(Biobuf *f, char *pkg, int64 len, char *filename)
 {
@@ -156,7 +160,25 @@ ldpkg(Biobuf *f, char *pkg, int64 len, char *filename)
                        fprint(2, "%s: cannot find end of // dynimport section in %s\n", argv0, filename);
                        return;
                }
-               loaddynimport(filename, p0 + 1, p1 - p0);
+               loaddynimport(filename, p0 + 1, p1 - (p0+1));
+       }
+
+       // look for dynexp section
+       p0 = strstr(p1, "\n$$  // dynexport");
+       if(p0 != nil) {
+               p0 = strchr(p0+1, '\n');
+               if(p0 == nil) {
+                       fprint(2, "%s: found $$ // dynexporg but no newline in %s\n", argv0, filename);
+                       return;
+               }
+               p1 = strstr(p0, "\n$$");
+               if(p1 == nil)
+                       p1 = strstr(p0, "\n!\n");
+               if(p1 == nil) {
+                       fprint(2, "%s: cannot find end of // dynexporg section in %s\n", argv0, filename);
+                       return;
+               }
+               loaddynexport(filename, pkg, p0 + 1, p1 - (p0+1));
        }
 }
 
@@ -339,13 +361,12 @@ parsemethod(char **pp, char *ep, char **methp)
 static void
 loaddynimport(char *file, char *p, int n)
 {
-       char *next, *name, *def, *p0, *lib;
+       char *pend, *next, *name, *def, *p0, *lib;
        Sym *s;
 
-       p[n] = '\0';
-
+       pend = p + n;
        p0 = p;
-       for(; *p; p=next) {
+       for(; p<pend; p=next) {
                next = strchr(p, '\n');
                if(next == nil)
                        next = "";
@@ -384,6 +405,59 @@ err:
        nerrors++;
 }
 
+static void
+loaddynexport(char *file, char *pkg, char *p, int n)
+{
+       char *pend, *next, *local, *elocal, *remote, *p0;
+       Sym *s;
+
+       pend = p + n;
+       p0 = p;
+       for(; p<pend; p=next) {
+               next = strchr(p, '\n');
+               if(next == nil)
+                       next = "";
+               else
+                       *next++ = '\0';
+               p0 = p;
+               if(strncmp(p, "dynexport ", 10) != 0)
+                       goto err;
+               p += 10;
+               local = p;
+               p = strchr(local, ' ');
+               if(p == nil)
+                       goto err;
+               while(*p == ' ')
+                       p++;
+               remote = p;
+
+               // successful parse: now can edit the line
+               *strchr(local, ' ') = 0;
+
+               elocal = expandpkg(local, pkg);
+
+               s = lookup(elocal, 0);
+               if(s->dynimplib != nil) {
+                       fprint(2, "%s: symbol is both dynimport and dynexport %s\n", argv0, local);
+                       nerrors++;
+               }
+               s->dynimpname = remote;
+               s->dynexport = 1;
+
+               if(ndynexp%32 == 0)
+                       dynexp = realloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
+               dynexp[ndynexp++] = s;
+
+               if (elocal != local)
+                       free(elocal);
+       }
+       return;
+
+err:
+       fprint(2, "%s: invalid dynexport line: %s\n", argv0, p0);
+       nerrors++;
+}
+
 static int markdepth;
 
 static void
@@ -502,6 +576,9 @@ deadcode(void)
        for(i=0; i<nelem(morename); i++)
                mark(lookup(morename[i], 0));
 
+       for(i=0; i<ndynexp; i++)
+               mark(dynexp[i]);
+
        // remove dead data
        sweeplist(&datap, &edatap);
 }