diff --git a/sys/include/ape/dynld.h b/sys/include/ape/dynld.h
new file mode 100644
index 0000000..03261d3
--- /dev/null
+++ b/sys/include/ape/dynld.h
@@ -0,0 +1,49 @@
+#ifndef __DYNLD_H
+#define __DYNLD_H
+#if !defined(_PLAN9_SOURCE)
+ This header file is an extension to ANSI/POSIX
+#endif
+
+#pragma src "/sys/src/ape/lib/dynld"
+#pragma lib "/$M/lib/ape/libdynld.a"
+
+typedef struct Dynobj Dynobj;
+typedef struct Dynsym Dynsym;
+
+struct Dynobj
+{
+ unsigned long size; /* total size in bytes */
+ unsigned long text; /* bytes of text */
+ unsigned long data; /* bytes of data */
+ unsigned long bss; /* bytes of bss */
+ unsigned char* base; /* start of text, data, bss */
+ int nexport;
+ Dynsym* export; /* export table */
+ int nimport;
+ Dynsym** import; /* import table */
+};
+
+/*
+ * this structure is known to the linkers
+ */
+struct Dynsym
+{
+ unsigned long sig;
+ unsigned long addr;
+ char *name;
+};
+
+extern Dynsym* dynfindsym(char*, Dynsym*, int);
+extern void dynfreeimport(Dynobj*);
+extern void* dynimport(Dynobj*, char*, unsigned long);
+extern int dynloadable(void*, long (*r)(void*,void*,long), long long(*sk)(void*,long long,int));
+extern Dynobj* dynloadfd(int, Dynsym*, int, unsigned long);
+extern Dynobj* dynloadgen(void*, long (*r)(void*,void*,long), long long (*s)(void*,long long,int), void (*e)(char*), Dynsym*, int, unsigned long);
+extern long dynmagic(void);
+extern void dynobjfree(Dynobj*);
+extern char* dynreloc(unsigned char*, unsigned long, int, Dynsym**, int);
+extern int dyntabsize(Dynsym*);
+
+extern Dynsym _exporttab[]; /* created by linker -x (when desired) */
+
+#endif
diff --git a/sys/include/dynld.h b/sys/include/dynld.h
new file mode 100644
index 0000000..eeac1c2
--- /dev/null
+++ b/sys/include/dynld.h
@@ -0,0 +1,41 @@
+#pragma src "/sys/src/libdynld"
+#pragma lib "libdynld.a"
+
+typedef struct Dynobj Dynobj;
+typedef struct Dynsym Dynsym;
+
+struct Dynobj
+{
+ ulong size; /* total size in bytes */
+ ulong text; /* bytes of text */
+ ulong data; /* bytes of data */
+ ulong bss; /* bytes of bss */
+ uchar* base; /* start of text, data, bss */
+ int nexport;
+ Dynsym* export; /* export table */
+ int nimport;
+ Dynsym** import; /* import table */
+};
+
+/*
+ * this structure is known to the linkers
+ */
+struct Dynsym
+{
+ ulong sig;
+ ulong addr;
+ char *name;
+};
+
+extern Dynsym* dynfindsym(char*, Dynsym*, int);
+extern void dynfreeimport(Dynobj*);
+extern void* dynimport(Dynobj*, char*, ulong);
+extern int dynloadable(void*, long (*r)(void*,void*,long), vlong(*sk)(void*,vlong,int));
+extern Dynobj* dynloadfd(int, Dynsym*, int, ulong);
+extern Dynobj* dynloadgen(void*, long (*r)(void*,void*,long), vlong (*s)(void*,vlong,int), void (*e)(char*), Dynsym*, int, ulong);
+extern long dynmagic(void);
+extern void dynobjfree(Dynobj*);
+extern char* dynreloc(uchar*, ulong, int, Dynsym**, int);
+extern int dyntabsize(Dynsym*);
+
+extern Dynsym _exporttab[]; /* created by linker -x (when desired) */
diff --git a/sys/man/2/dynld b/sys/man/2/dynld
new file mode 100644
index 0000000..8977333
--- /dev/null
+++ b/sys/man/2/dynld
@@ -0,0 +1,287 @@
+.TH DYNLD 2
+.SH NAME
+dynfindsym, dynfreeimport, dynloadfd, dynloadgen, dynobjfree, dyntabsize \- load object file dynamically
+.SH SYNOPSIS
+.B #include <u.h>
+.br
+.B #include <a.out.h>
+.br
+.B #include <dynld.h>
+.PP
+.ta \w'\fLDynsym*** 'u
+.B
+Dynsym* dynfindsym(char *name, Dynsym *syms, int nsym)
+.PP
+.B
+Dynobj* dynloadfd(int fd, Dynsym *exports, int nexport,
+.br
+.B
+ ulong maxsize)
+.PP
+.B
+Dynobj* dynloadgen(void *file, long (*read)(void*,void*,long),
+.br
+.B
+ vlong (*seek)(void*,vlong,int), void (*err)(char*),
+.br
+.B
+ Dynsym *exports, int nexport, ulong maxsize)
+.PP
+.B
+void* dynimport(Dynobj *o, char *name, ulong sig)
+.PP
+.B
+void dynfreeimport(Dynobj *o)
+.PP
+.B
+void dynobjfree(Dynobj *o)
+.PP
+.B
+int dyntabsize(Dynsym *t)
+.PP
+.B
+extern Dynsym _exporttab[];
+.DT
+.SH DESCRIPTION
+These functions allow a process to load further code and data
+into the currently executing image.
+A dynamically-loadable file, called a
+.I module
+here, is a variant of the
+.IR a.out (10.6)
+executable format with some extra components.
+The loader for the architecture
+(see
+.IR 8l (1))
+creates a module file from component object file(s) when given the
+.B -u
+option.
+A module contains text and data sections, an import table, an export table,
+and relocation data.
+The import table lists the symbols the module needs from the loading program;
+the export table lists symbols the module provides when loaded.
+A program that loads a module provides a table of its own symbols to match
+the symbols in the module's import table.
+.PP
+A symbol entry in a symbol table names a global function or data item, and has an associated
+.I signature
+value representing the type of the corresponding function or data in the source code.
+The
+.B Dynsym
+structure defines a symbol:
+.IP
+.EX
+typedef struct {
+ ulong sig;
+ ulong addr;
+ char* name;
+} Dynsym;
+.EE
+.PP
+The structure is known to the loaders
+.IR 8l (1).
+.I Name
+is the linkage name of the function or data.
+.I Addr
+is its address, which is relative to the start of the module before loading,
+and an address in the current address space after loading.
+The signature
+.I sig
+is the value produced by the C compiler's
+.B signof
+operator applied to the type.
+Symbol tables must be sorted by
+.IR name .
+.PP
+An executable that wishes to load modules will normally be linked using the
+.B -x
+option to the appropriate loader
+.IR 8l (1).
+The resulting executable contains an export table
+.B _exporttab
+that lists all the exported symbols of the program (by default, all external symbols).
+A nil name marks the end of the table.
+See
+.IR 8l (1)
+for details.
+The table can be given to the functions below to allow a loaded module
+to access those symbols.
+.PP
+A loaded module is described by a
+.B Dynobj
+structure:
+.IP
+.EX
+typedef struct {
+ ulong size; /* total size in bytes */
+ ulong text; /* bytes of text */
+ ulong data; /* bytes of data */
+ ulong bss; /* bytes of bss */
+ uchar* base; /* start of text, data, bss */
+ int nexport;
+ Dynsym* export; /* export table */
+ int nimport;
+ Dynsym** import; /* import table */
+} Dynobj;
+.EE
+.PP
+Several fields give sizes of the module's components, as noted in comments above.
+.I Base
+gives the address at which the module has been loaded.
+All its internal
+references have been adjusted where needed to reflect its current address.
+.I Export
+points to a symbol table listing the symbols exported by the module;
+.I nexport
+gives the table's length.
+.I Import
+points to a list of symbols imported by the module;
+note that each entry actually points to an entry in a symbol table
+provided by the program that loaded the module (see below).
+.I Nimport
+gives the import table's length.
+If the import table is not required, call
+.I dynfreeimport
+on the module pointer to free it.
+.PP
+.I Dynfindysm
+looks up the entry for the given
+.I name
+in symbol table
+.I syms
+(of length
+.IR nsym ).
+It returns a pointer to the entry if found; nil otherwise.
+The symbol table must be sorted by name in ascending order.
+.PP
+.I Dyntabsize
+returns the length of symbol table
+.IR t ,
+defined to be the number of
+.B Dynsym
+values starting at
+.I t
+that have non-nil
+.I name
+fields.
+It is used to find the length of
+.BR _exporttab .
+.PP
+.I Dynloadfd
+loads a module from the file open for reading on
+.IR fd ,
+and returns the resulting module pointer on success,
+or nil on error.
+If
+.I maxsize
+is non-zero
+the size of the dynamically-loaded module's code and data
+is limited to
+.I maxsize
+bytes.
+.I Exports
+is an array of
+.I nexport
+symbols in the current program that can be imported by the current module.
+It uses
+.IR read (2)
+and
+.IR seek (2)
+to access
+.IR fd ,
+and calls
+.I werrstr
+(see
+.IR errstr (2))
+to set the error string if necessary.
+.PP
+.I Dynloadgen
+is a more general function that can load a module from an
+arbitrary source, not just an open file descriptor.
+(In particular, it can be
+called by the kernel using functions internal to the kernel
+instead of making system calls.)
+.IR Exports ,
+.I nexport
+and
+.I maxsize
+are just as for
+.IR dynloadfd .
+.I File
+is a pointer to a structure defined by the caller that represents the file
+containing the module.
+It is passed to
+.I read
+and
+.IR seek .
+.I Read
+is invoked as
+.BI (*read)( file , buf ,\ \fInbytes\fP)\fR.\fP
+.I Read
+should read
+.I nbytes
+of data from
+.I file
+into
+.I buf
+and return the number of bytes transferred.
+It should return -1 on error.
+.I Seek
+is invoked as
+.BI (*seek)( file , n ,\ \fItype\fP)
+where
+.I n
+and
+.I type
+are just as for
+.IR seek (2);
+it should seek to the requested offset in
+.IR file ,
+or return -1 on error.
+.I Dynloadgen
+returns a pointer to the loaded module on success.
+On error,
+it returns nil after calling its
+.I err
+parameter to set the error string.
+.PP
+.I Dynimport
+returns a pointer to the value of the symbol
+.I name
+in loaded module
+.IR o ,
+or
+.I nil
+if
+.I o
+does not export a symbol with the given
+.IR name .
+If
+.I sig
+is non-zero, the exported symbol's signature must equal
+.IR sig ,
+or
+.I dynimport
+again returns nil.
+For example:
+.IP
+.EX
+Dev *d;
+d = dynimport(obj, "XXXdevtab", signof(*d));
+if(d == nil)
+ error("not a dynamically-loadable driver");
+.EE
+.PP
+.I Dynobjfree
+frees the module
+.IR o .
+There is no reference counting: it is the caller's responsibility to decide whether
+a module is no longer needed.
+.SH SEE ALSO
+.IR 8l (1),
+.\".IR mach (2),
+.IR a.out (6)
+.SH DIAGNOSTICS
+Functions that return pointers return nil on error.
+.I Dynloadfd
+sets the error string and returns nil.
diff --git a/sys/src/libdynld/NOTICE b/sys/src/libdynld/NOTICE
new file mode 100644
index 0000000..fca1655
--- /dev/null
+++ b/sys/src/libdynld/NOTICE
@@ -0,0 +1,28 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+ Copyright © 2004-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/sys/src/libdynld/dynld-386.c b/sys/src/libdynld/dynld-386.c
new file mode 100644
index 0000000..7381675
--- /dev/null
+++ b/sys/src/libdynld/dynld-386.c
@@ -0,0 +1,43 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | I_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ int i;
+ ulong v, *pp;
+
+ p += (ulong)b;
+ pp = (ulong*)p;
+ v = *pp;
+ switch(m){
+ case 0:
+ v += (ulong)b;
+ break;
+ case 1:
+ i = v>>22;
+ v &= 0x3fffff;
+ CHK(i, ntab);
+ v += tab[i]->addr;
+ break;
+ case 2:
+ i = v>>22;
+ CHK(i, ntab);
+ v = tab[i]->addr -p-4;
+ break;
+ default:
+ return "bad relocation mode";
+ }
+ *pp = v;
+ return nil;
+}
diff --git a/sys/src/libdynld/dynld-68000.c b/sys/src/libdynld/dynld-68000.c
new file mode 100644
index 0000000..a258106
--- /dev/null
+++ b/sys/src/libdynld/dynld-68000.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | A_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "68000 unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-amd64.c b/sys/src/libdynld/dynld-amd64.c
new file mode 100644
index 0000000..693af64
--- /dev/null
+++ b/sys/src/libdynld/dynld-amd64.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | S_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "amd64 unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-arm.c b/sys/src/libdynld/dynld-arm.c
new file mode 100644
index 0000000..1e455c9
--- /dev/null
+++ b/sys/src/libdynld/dynld-arm.c
@@ -0,0 +1,45 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | E_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ int i;
+ ulong v, *pp;
+
+ p <<= 2;
+ p += (ulong)b;
+ pp = (ulong*)p;
+ v = *pp;
+ switch(m){
+ case 0:
+ v += (ulong)b;
+ break;
+ case 1:
+ i = v>>22;
+ v &= 0x3fffff;
+ CHK(i, ntab);
+ v += tab[i]->addr;
+ break;
+ case 2:
+ i = v&0x3ff;
+ v &= ~0x3ff;
+ CHK(i, ntab);
+ v |= ((tab[i]->addr-p-8)>>2)&0xffffff;
+ break;
+ default:
+ return "invalid relocation mode";
+ }
+ *pp = v;
+ return nil;
+}
diff --git a/sys/src/libdynld/dynld-arm64.c b/sys/src/libdynld/dynld-arm64.c
new file mode 100644
index 0000000..45cf1db
--- /dev/null
+++ b/sys/src/libdynld/dynld-arm64.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | R_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "arm64 unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-mips.c b/sys/src/libdynld/dynld-mips.c
new file mode 100644
index 0000000..b78b019
--- /dev/null
+++ b/sys/src/libdynld/dynld-mips.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | V_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "mips unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-mips64.c b/sys/src/libdynld/dynld-mips64.c
new file mode 100644
index 0000000..394bd73
--- /dev/null
+++ b/sys/src/libdynld/dynld-mips64.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | M_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "mips64 unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-power.c b/sys/src/libdynld/dynld-power.c
new file mode 100644
index 0000000..5afe1fa
--- /dev/null
+++ b/sys/src/libdynld/dynld-power.c
@@ -0,0 +1,66 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | Q_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ int i;
+ ulong v, *pp0, *pp1;
+
+ p <<= 2;
+ p += (ulong)b;
+ pp0 = (ulong*)p;
+ v = *pp0;
+ switch(m){
+ case 0:
+ v += (ulong)b;
+ break;
+ case 1:
+ i = v>>22;
+ v &= 0x3fffff;
+ CHK(i, ntab);
+ v += tab[i]->addr;
+ break;
+ case 2:
+ i = (v&0xffc)>>2;
+ v &= ~0xffc;
+ CHK(i, ntab);
+ v |= (tab[i]->addr-p)&0x3fffffc;
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ pp1 = (ulong*)(p+4);
+ v = (v<<16)|(*pp1&0xffff);
+ if(m&1)
+ v += (ulong)b;
+ else{
+ i = v>>22;
+ v &= 0x3fffff;
+ CHK(i, ntab);
+ v += tab[i]->addr;
+ }
+ if(m >= 5 && (v&0x8000))
+ v += 0x10000;
+ *pp0 &= ~0xffff;
+ *pp0 |= v>>16;
+ *pp1 &= ~0xffff;
+ *pp1 |= v&0xffff;
+ return nil;
+ default:
+ return "invalid relocation mode";
+ }
+ *pp0 = v;
+ return nil;
+}
diff --git a/sys/src/libdynld/dynld-power64.c b/sys/src/libdynld/dynld-power64.c
new file mode 100644
index 0000000..e9c149a
--- /dev/null
+++ b/sys/src/libdynld/dynld-power64.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | T_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "power64 unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-riscv.c b/sys/src/libdynld/dynld-riscv.c
new file mode 100644
index 000000000..c2d0c57b9
--- /dev/null
+++ b/sys/src/libdynld/dynld-riscv.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | Z_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "riscv unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-riscv64.c b/sys/src/libdynld/dynld-riscv64.c
new file mode 100644
index 000000000..895225b91
--- /dev/null
+++ b/sys/src/libdynld/dynld-riscv64.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | Y_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "riscv64 unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-sparc.c b/sys/src/libdynld/dynld-sparc.c
new file mode 100644
index 0000000..4ab620f
--- /dev/null
+++ b/sys/src/libdynld/dynld-sparc.c
@@ -0,0 +1,21 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | K_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "sparc unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-spim.c b/sys/src/libdynld/dynld-spim.c
new file mode 100644
index 0000000..5e9f208
--- /dev/null
+++ b/sys/src/libdynld/dynld-spim.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | P_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "mips-le (spim) unimplemented";
+}
diff --git a/sys/src/libdynld/dynld-spim64.c b/sys/src/libdynld/dynld-spim64.c
new file mode 100644
index 0000000..10f4654
--- /dev/null
+++ b/sys/src/libdynld/dynld-spim64.c
@@ -0,0 +1,23 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+#define CHK(i,ntab) if((unsigned)(i)>=(ntab))return "bad relocation index"
+
+long
+dynmagic(void)
+{
+ return DYN_MAGIC | N_MAGIC;
+}
+
+char*
+dynreloc(uchar *b, ulong p, int m, Dynsym **tab, int ntab)
+{
+ USED(b);
+ USED(p);
+ USED(m);
+ USED(tab);
+ USED(ntab);
+ return "mips64-le (spim64) unimplemented";
+}
diff --git a/sys/src/libdynld/dynld.c b/sys/src/libdynld/dynld.c
new file mode 100644
index 0000000..fdce17e
--- /dev/null
+++ b/sys/src/libdynld/dynld.c
@@ -0,0 +1,259 @@
+#include <u.h>
+#include <libc.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();
+}
diff --git a/sys/src/libdynld/dynloadfd.c b/sys/src/libdynld/dynloadfd.c
new file mode 100644
index 0000000..61d50ae
--- /dev/null
+++ b/sys/src/libdynld/dynloadfd.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include <a.out.h>
+#include <dynld.h>
+
+typedef struct Fd Fd;
+struct Fd {
+ int fd;
+};
+
+static long
+readfd(void *a, void *buf, long nbytes)
+{
+ return read(((Fd*)a)->fd, buf, nbytes);
+}
+
+static vlong
+seekfd(void *a, vlong off, int t)
+{
+ return seek(((Fd*)a)->fd, off, t);
+}
+
+static void
+errfd(char *s)
+{
+ werrstr("%s", s);
+}
+
+Dynobj*
+dynloadfd(int fd, Dynsym *sym, int nsym, ulong maxsize)
+{
+ Fd f;
+
+ f.fd = fd;
+ return dynloadgen(&f, readfd, seekfd, errfd, sym, nsym, maxsize);
+}
diff --git a/sys/src/libdynld/mkfile b/sys/src/libdynld/mkfile
new file mode 100644
index 0000000..4a7d701
--- /dev/null
+++ b/sys/src/libdynld/mkfile
@@ -0,0 +1,10 @@
+</$objtype/mkfile
+
+LIB=/$objtype/lib/libdynld.a
+
+OFILES=\
+ dynld-$objtype.$O\
+ dynloadfd.$O\
+ dynld.$O\
+
+</sys/src/cmd/mksyslib
diff --git a/sys/src/mkfile b/sys/src/mkfile
index c050ffc..6035022 100644
--- a/sys/src/mkfile
+++ b/sys/src/mkfile
@@ -14,6 +14,7 @@ LIBS=\
libcontrol\
libdisk\
libdraw\
+ libdynld\
libflate\
libframe\
libgeometry\
|