summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOri Bernstein <ori@eigenstate.org>2017-09-25 19:19:54 -0700
committerOri Bernstein <ori@eigenstate.org>2017-09-25 23:58:02 -0700
commit2eb61ae0040a66043b41863c2348fdacd37cc091 (patch)
treeaeb4cbf7fe1ffa2ea59ed2479cb541c0f384aebd
downloadlnk-2eb61ae0040a66043b41863c2348fdacd37cc091.tar.gz
Linker: Initial commit.
-rw-r--r--aout.myr11
-rw-r--r--bld.proj7
-rw-r--r--elf.myr137
-rw-r--r--macho.myr11
-rw-r--r--main.myr214
-rw-r--r--types.myr73
6 files changed, 453 insertions, 0 deletions
diff --git a/aout.myr b/aout.myr
new file mode 100644
index 0000000..8288a45
--- /dev/null
+++ b/aout.myr
@@ -0,0 +1,11 @@
+use std
+
+use "types"
+
+pkg =
+ const emitaout : (lnk : lnk# -> void)
+;;
+
+const emitaout = {lnk
+}
+
diff --git a/bld.proj b/bld.proj
new file mode 100644
index 0000000..3546b80
--- /dev/null
+++ b/bld.proj
@@ -0,0 +1,7 @@
+bin lnk =
+ main.myr
+ types.myr
+ elf.myr
+ macho.myr
+ aout.myr
+;;
diff --git a/elf.myr b/elf.myr
new file mode 100644
index 0000000..1e25536
--- /dev/null
+++ b/elf.myr
@@ -0,0 +1,137 @@
+use std
+use bio
+
+use "types"
+
+pkg =
+ const emitelf : (lnk : lnk# -> void)
+;;
+
+
+/* identification entries */
+const Elfclass64 = 2
+const Elfdata2lsb = 1
+const Elfabifreebsd = 9
+const Elfversion = 1
+const Elfabiversion = 0
+
+/* header values */
+const Typeexec : uint16 = 2
+const Machx64 : uint16 = 62
+const Phoff : uint64 = 64
+const Shoff : uint64 = 0
+const Flags : uint32 = 0
+
+const Hdrsz : uint32 = 64
+const Nphdr : uint32 = 4
+const Nshdr : uint32 = 0
+
+/* program header values */
+const Phdrsz : uint32 = 56
+const Ptload : uint32 = 1
+const Pfx : uint32 = 1
+const Pfw : uint32 = 2
+const Pfr : uint32 = 4
+const Pfrw : uint32 = 6
+const Pfrx : uint32 = 5
+const Pflags : uint32 = 0
+
+const Nsegment = 4
+const Nsection = 0
+const Align = 4096
+
+const emitelf = {lnk
+ match bio.create(lnk.outf, bio.Wr, 0o755)
+ | `std.Err e:
+ std.fatal("could not open output: {}\n", e)
+ | `std.Ok f:
+ header(f, lnk)
+ //ident(f, lnk)
+ //strtab(f, lnk)
+ sdata(f, lnk, lnk.text, Align)
+ sdata(f, lnk, lnk.data, Align)
+ sdata(f, lnk, lnk.rodata, Align)
+ bio.close(f)
+ ;;
+}
+
+const header = {f, lnk
+ wstr(f, "\x7fELF") // 3
+ w8(f, Elfclass64) // 4
+ w8(f, Elfdata2lsb) // 5
+ w8(f, Elfversion) // 6
+ w8(f, Elfabifreebsd) // 7
+ w8(f, Elfabiversion)
+ pad(f, 7)
+
+ w16(f, Typeexec)
+ w16(f, Machx64)
+ w32(f, (Elfversion : uint32))
+ w64(f, lnk.entry) /* entry point */
+ w64(f, (Hdrsz : uint64)) /* program header offset */
+ w64(f, 0/*FIXME*/) /* section header offset */
+ w32(f, Flags) /* no flags */
+ w16(f, (Hdrsz : uint16))
+ w16(f, (Phdrsz : uint16))
+ w16(f, Nsegment)
+ w16(f, 0)
+ w16(f, Nsection)
+ w16(f, 0)
+
+ phdrs(f, lnk)
+ //shdr(f, lnk)
+}
+
+const phdrs = {f, lnk
+ phdr(f, lnk, lnk.text, lnk.text.len, Pfrx)
+ phdr(f, lnk, lnk.data, lnk.data.len, Pfrw)
+ phdr(f, lnk, lnk.rodata, lnk.rodata.len, Pfr)
+ phdr(f, lnk, "", lnk.bss, Pfrw)
+}
+
+const phdr = {f, lnk, buf, sz, prot
+ w32(f, Ptload)
+ w32(f, prot)
+
+ w64(f, lnk.foff)
+ w64(f, lnk.voff)
+ w64(f, lnk.voff)
+
+ w64(f, buf.len) /* size on disk */
+ w64(f, sz) /* size in memory */
+ w64(f, 4096) /* align to page size */
+ lnk.foff += buf.len
+ lnk.voff += sz
+}
+
+const sdata = {f, lnk, buf, align
+ //pad(f, 4096 - f.voff % 4096)
+ wstr(f, buf)
+}
+
+//const sectionheader = {f, lnk
+// /* text section */
+// /* data section */
+// /* rodata section */
+// /* bss section */
+// /* strtab */
+//}
+
+const wstr = {f, s; check(bio.write(f, s))}
+const w8 = {f, v; check(bio.putb(f, v))}
+const w16 = {f, v; check(bio.putle16(f, v))}
+const w32 = {f, v : uint32; check(bio.putle32(f, v))}
+const w64 = {f, v; check(bio.putle64(f, v))}
+const pad = {f, n
+ for var i = 0; i < n; i++
+ check(bio.putb(f, 0))
+ ;;
+}
+
+generic check = {st : bio.status(@a)
+ match st
+ | `bio.Ok _: /* nothing */
+ | `bio.Err e: std.fatal("i/o error: {}\n", e)
+ | `bio.Eof: std.fatal("i/o error: eof\n")
+ ;;
+}
diff --git a/macho.myr b/macho.myr
new file mode 100644
index 0000000..75a9e93
--- /dev/null
+++ b/macho.myr
@@ -0,0 +1,11 @@
+use std
+
+use "types"
+
+pkg =
+ const emitmacho : (lnk : lnk# -> void)
+;;
+
+const emitmacho = {lnk
+}
+
diff --git a/main.myr b/main.myr
new file mode 100644
index 0000000..7cb835a
--- /dev/null
+++ b/main.myr
@@ -0,0 +1,214 @@
+use std
+use bio
+
+use "types"
+use "elf"
+use "macho"
+use "aout"
+
+const main = {args
+ var outf, sys
+ var lnk : lnk
+ var cmd
+
+ outf = "lnk.6"
+ sys = ""
+ cmd = std.optparse(args, &[
+ .argdesc="objs...",
+ .opts=[
+ [.opt='o', .arg="out",
+ .desc="output to file `out`",
+ .dest=`std.Some &outf],
+ [.opt='s', .arg="sys",
+ .desc="emit for system `sys`",
+ .dest=`std.Some &sys],
+ ][:]
+ ])
+
+ lnk = [
+ .obj=[][:],
+
+ .text="",
+ .data="",
+ .rodata="",
+ .bss = 0,
+
+ .outf=outf,
+ .sys=sys,
+ .stab=std.mkht(std.strhash, std.streq)
+ ]
+ for a : cmd.args
+ loadobj(&lnk, a)
+ ;;
+ link(&lnk)
+ std.put("text: {}\n", lnk.text)
+ match sys
+ /* generic binary targets */
+ | "elf": emitelf(&lnk)
+ | "macho": emitmacho(&lnk)
+ | "aout": emitaout(&lnk)
+
+ /* and figure it out by system */
+ | "openbsd": emitelf(&lnk)
+ | "freebsd": emitelf(&lnk)
+ | "netbsd": emitelf(&lnk)
+ | "linux": emitelf(&lnk)
+ | "osx": emitmacho(&lnk)
+ | "plan9": emitaout(&lnk)
+ | _: std.fatal("unknown target {}\n", sys)
+ ;;
+}
+
+const link = {lnk
+ for o : lnk.obj
+ for s : o.sym
+ match s.kind & Symkind
+ | Symtxt: addsym(lnk, &lnk.text, &s)
+ | Symdat: addsym(lnk, &lnk.data, &s)
+ | Symro: addsym(lnk, &lnk.rodata, &s)
+ | Symbss:
+ s.off = lnk.bss
+ lnk.bss += s.len
+ | _: /* skip in output */
+ ;;
+ ;;
+ ;;
+ for o : lnk.obj
+ for r : o.rel
+ relocate(lnk, r)
+ ;;
+ ;;
+}
+
+const addsym = {lnk, buf, sym
+ match std.htget(lnk.stab, sym.name)
+ | `std.None:
+ sym.off = buf#.len
+ std.sljoin(buf, sym.buf)
+ std.htput(lnk.stab, sym.name, sym)
+ | `std.Some old:
+ if sym.kind & Symonce == 0 || old.kind & Symonce == 0
+ std.fatal("duplicate symbol {}\n", sym.name)
+ ;;
+ ;;
+}
+
+const relocate = {lnk, r
+ var buf : byte[8]
+ var addr, sz
+
+ match std.htget(lnk.stab, r.sym)
+ | `std.None:
+ std.fatal("unknown symbol {}\n", r.sym)
+ | `std.Some s:
+ std.put("relocating: {}\n", r)
+ addr = lnk.base + s.off + r.arg
+ match r.kind & Relkind
+ | Rel8: sz = 1
+ | Rel16: sz = 2
+ | Rel32: sz = 4
+ | Rel64: sz = 8
+ | Relbase:
+ match s.kind & Symkind
+ | Symtxt: lnk.cur = lnk.text[s.off:s.off+s.len]
+ | Symdat: lnk.cur = lnk.data[s.off:s.off+s.len]
+ | Symro: lnk.cur = lnk.rodata[s.off:s.off+s.len]
+ | Symbss: std.fatal("relocation in bss\n")
+ | _: std.fatal("corrupt relocation\n")
+ ;;
+ -> void
+ | _:
+ std.fatal("corrupt relocation\n")
+ ;;
+ if addr > (1ul << 8*sz)
+ std.fatal("relocation overflow in {}\n", s.name)
+ ;;
+ if r.off + sz > lnk.cur.len
+ std.fatal("out of bounds relocation in {}\n", s.name)
+ ;;
+ std.put("addr: {}\n", addr)
+ /* FIXME: endianness */
+ std.putle64(buf[:], addr)
+ std.slcp(lnk.cur[r.off:r.off+sz], buf[:sz])
+ ;;
+}
+
+const loadobj = {lnk, path
+ var rel, str, sym, o
+ var obj
+
+ match std.slurp(path)
+ | `std.Ok buf: obj.buf = buf
+ | `std.Err e: std.fatal("could not open {}: {}\n", path, e)
+ ;;
+
+ if !std.hasprefix(obj.buf, "uobj")
+ std.fatal("unable to read {}: bad magic number\n", path)
+ ;;
+
+ (str, o) = r64(obj.buf, 4)
+ (rel, o) = r64(obj.buf, o)
+ (sym, o) = r64(obj.buf, o)
+ obj.str = obj.buf[str:rel]
+ obj.rel = readrel(obj.buf[rel:sym], obj.str)
+ obj.sym = readsym(obj.buf[o:str], o, obj.str, obj.buf)
+
+ std.slpush(&lnk.obj, obj)
+}
+
+const readrel = {buf, strs
+ var rel, r, o
+
+ o = 0
+ rel = [][:]
+ while o != buf.len
+ (r.sym, o) = rsym(buf, o, strs)
+ (r.kind, o) = r32(buf, o)
+ (r.off, o) = r64(buf, o)
+ std.put("r.kind: {x}\n", r.kind)
+ if (r.kind & Relarg) != 0
+ (r.arg, o) = r64(buf, o)
+ else
+ r.arg = 0
+ ;;
+ std.slpush(&rel, r)
+ ;;
+ -> rel
+}
+
+const readsym = {hbuf, o, strs, dbuf
+ var off, len
+ var sym, s
+
+ o = 0
+ sym = [][:]
+ while o != hbuf.len
+ (s.name, o) = rsym(hbuf, o, strs)
+ (s.kind, o) = r32(hbuf, o)
+ (off, o) = r64(hbuf, o)
+ (len, o) = r64(hbuf, o)
+ s.len = len
+ if s.kind != Symbss
+ s.buf = dbuf[off:off + len]
+ ;;
+ std.slpush(&sym, s)
+ ;;
+ -> sym
+}
+
+const rsym = {buf, off, strs
+ var sym, idx
+
+ (idx, off) = r32(buf, off)
+ sym = std.cstrconv(strs[idx:])
+ -> (sym, off)
+}
+
+const r32 = {buf, off
+ -> (std.getle32(buf[off:off+4]), off + 4)
+}
+
+const r64 = {buf, off
+ -> (std.getle64(buf[off:off+8]), off + 8)
+}
+
diff --git a/types.myr b/types.myr
new file mode 100644
index 0000000..818442e
--- /dev/null
+++ b/types.myr
@@ -0,0 +1,73 @@
+use std
+
+pkg =
+ /* relocation kinds */
+ const Rel8 : uint32 = 1
+ const Rel16 : uint32 = 2
+ const Rel32 : uint32 = 3
+ const Rel64 : uint32 = 4
+ const Relbase : uint32 = 5
+ const Relkind : uint32 = 0xffff
+ const Relabs : uint32 = 1 << 16
+ const Relarg : uint32 = 1 << 17
+
+ /* sym kinds */
+ const Symdat : uint32 = 1
+ const Symtxt : uint32 = 2
+ const Symbss : uint32 = 3
+ const Symro : uint32 = 4
+ const Symusr : uint32 = 5
+ const Symkind : uint32 = 0xffff
+
+ /* sym flags */
+ const Symonce : uint32 = 1 << 16
+
+ type lnk = struct
+ /* input */
+ obj : obj[:]
+
+ /* output */
+ text : byte[:]
+ data : byte[:]
+ rodata : byte[:]
+ bss : uint64
+
+ /* emission state */
+ base : uint64
+ entry : uint64
+ foff : uint64
+ voff : uint64
+ outf : byte[:]
+ sys : byte[:]
+ cur : byte[:] /* current output symbol buffer */
+ stab : std.htab(byte[:], sym#)#
+ ;;
+
+ type obj = struct
+ sym : sym[:]
+ rel : rel[:]
+ buf : byte[:]
+ str : byte[:]
+
+ /* state */
+ path : byte[:]
+ ;;
+
+ type sym = struct
+ /* input */
+ name : byte[:]
+ kind : uint32
+ buf : byte[:]
+ len : uint64
+
+ /* state */
+ off : uint64
+ ;;
+
+ type rel = struct
+ sym : byte[:]
+ kind : uint32
+ off : uint64
+ arg : uint64
+ ;;
+;;