aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--amd64/all.h6
-rw-r--r--amd64/emit.c36
-rw-r--r--amd64/isel.c18
-rw-r--r--common.h13
-rw-r--r--elf.c400
-rw-r--r--elf.h149
-rw-r--r--ir.c17
-rw-r--r--ir.h1
-rw-r--r--main.c14
-rw-r--r--mem.c10
-rw-r--r--obj.c53
-rw-r--r--obj.h9
-rw-r--r--test/test2.c3
14 files changed, 692 insertions, 39 deletions
diff --git a/Makefile b/Makefile
index 73de595..96b49f4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-SRC=main.c io.c mem.c c.c lex.c type.c targ.c eval.c ir.c irdump.c intrin.c abi0.c regalloc.c amd64/sysv.c amd64/isel.c amd64/emit.c obj.c
+SRC=main.c io.c mem.c c.c lex.c type.c targ.c eval.c ir.c irdump.c intrin.c abi0.c regalloc.c amd64/sysv.c amd64/isel.c amd64/emit.c obj.c elf.c
CFLAGS=-Wall -std=c11 -pedantic
OBJ=$(patsubst %.c,obj/%.o,$(SRC))
DEP=$(OBJ:.o=.d)
diff --git a/amd64/all.h b/amd64/all.h
index 3b08761..a038ffd 100644
--- a/amd64/all.h
+++ b/amd64/all.h
@@ -13,6 +13,12 @@ enum reg {
};
static inline void
+wr16le(uchar *p, ushort x)
+{
+ p[0] = x >> 0; p[1] = x >> 8;
+}
+
+static inline void
wr32le(uchar *p, uint x)
{
p[0] = x >> 0; p[1] = x >> 8;
diff --git a/amd64/emit.c b/amd64/emit.c
index b7cea24..ec04de7 100644
--- a/amd64/emit.c
+++ b/amd64/emit.c
@@ -149,6 +149,7 @@ enum operpat {
PFPR,
P1, /* imm = 1 */
PI8,
+ PI16,
PI32,
PU32,
PMEM,
@@ -162,6 +163,9 @@ enum operenc {
EN_M, /* mem */
EN_RI8, /* reg, imm8 with /0 */
EN_RI32, /* reg, imm32 with /0 */
+ EN_MI8, /* mem, imm8 with /x */
+ EN_MI16, /* mem, imm16 with /x */
+ EN_MI32, /* mem, imm32 with /x */
EN_OI, /* reg, imm32 with op + reg */
EN_I32, /* imm32 */
EN_R32, /* rel32 */
@@ -188,6 +192,7 @@ opermatch(enum operpat pat, struct oper oper)
case PFPR: return oper.t == OREG && oper.reg >= XMM0;
case P1: return oper.t == OIMM && oper.imm == 1;
case PI8: return oper.t == OIMM && (uint)(oper.imm+128) < 256;
+ case PI16: return oper.t == OIMM && (short)oper.imm == oper.imm;
case PI32: return oper.t == OIMM;
case PU32: return oper.t == OIMM && oper.imm >= 0;
case PMEM: return in_range(oper.t, OMEM, OCONR);
@@ -199,6 +204,7 @@ opermatch(enum operpat pat, struct oper oper)
/* code output helpers */
#define B(b) (*(*pcode)++ = (b))
#define D(xs, N) (memcpy(*pcode, (xs), (N)), (*pcode) += (N))
+#define I16(w) (wr16le(*pcode, (w)), *pcode += 4)
#define I32(w) (wr32le(*pcode, (w)), *pcode += 4)
#define DS(S) D(S, sizeof S - 1)
@@ -247,7 +253,7 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
mem = src;
reg = dst.reg;
goto Mem;
- case EN_M:
+ case EN_M: case EN_MI8: case EN_MI16: case EN_MI32:
mem = dst;
reg = en->ext;
Mem:
@@ -277,7 +283,16 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
if (sib)
B(mem.shift << 6 | (mem.index & 7) << 3 | (mem.base & 7));
if (mod == 1) B(mem.disp);
- else if (mod == 2 || (mod == 0 && mem.base == RBP)) I32(mem.disp);
+ else if (mod == 2 || (mod == 0 && mem.base == RBP/*RIP-rel*/)) {
+ if (mem.t == OCONR) {
+ objreloc(xcon2sym(mem.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4);
+ mem.disp = 0;
+ }
+ I32(mem.disp);
+ }
+ if (en->operenc == EN_MI8) B(src.imm);
+ if (en->operenc == EN_MI16) I16(src.imm);
+ if (en->operenc == EN_MI32) I32(src.imm);
break;
case EN_R: case EN_RI32: case EN_RI8:
rex |= (dst.reg >> 3) << 0; /* REX.B */
@@ -304,7 +319,9 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
case EN_R32:
if (rex) B(0x40 | rex);
D(opc, nopc);
- I32(-1);
+ assert(dst.t == OCONR);
+ objreloc(xcon2sym(dst.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4);
+ I32(0);
break;
}
}
@@ -326,10 +343,12 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o
}
DEFINSTR2(Xmovb,
- {-1, PMEM, PGPR, "\x88", EN_MR, .r8=1}, /* MOV m8, r8 */
+ {-1, PMEM, PGPR, "\x88", EN_MR, .r8=1}, /* MOV m8, r8 */
+ {-1, PMEM, PI8, "\xC6", EN_MI8, .r8=1}, /* MOV m8, imm8 */
)
DEFINSTR2(Xmovw,
{-1, PMEM, PGPR, "\x66\x89", EN_MR}, /* MOV m16, r16 */
+ {-1, PMEM, PI16, "\xC6", EN_MI16}, /* MOV m16, imm16 */
)
static void Xmov(uchar **pcode, enum irclass k, struct oper dst, struct oper src)
{
@@ -430,9 +449,9 @@ DEFINSTR1(Xidiv,
{4|8, PMEM, 0, "\xF7", EN_M, .ext=7}, /* IDIV m32/64 */
)
DEFINSTR1(Xcall,
- {-1, PSYM, 0, "\xE8", EN_R32}, /* CALL rel32 */
- {-1, PGPR, 0, "\xFF", EN_R, .ext=2}, /* CALL r64 */
- {-1, PMEM, 0, "\xFF", EN_M, .ext=2}, /* CALL m64 */
+ {-1, PSYM, 0, "\xE8", EN_R32, .norexw=1}, /* CALL rel32 */
+ {-1, PGPR, 0, "\xFF", EN_R, .ext=2, .norexw=1}, /* CALL r64 */
+ {-1, PMEM, 0, "\xFF", EN_M, .ext=2, .norexw=1}, /* CALL m64 */
)
static void
@@ -637,8 +656,10 @@ emitbin(struct function *fn)
{
struct block *blk;
uchar **pcode = &objout.code;
+ uchar *start;
aligncode(pcode, 16);
+ start = *pcode;
/** prologue **/
if (fn->stksiz != 0)
@@ -667,6 +688,7 @@ emitbin(struct function *fn)
B(0xC3); /* ret */
}
} while ((blk = blk->lnext) != fn->entry);
+ objdeffunc(fn->name, fn->globl, start - objout.textbegin, *pcode - start);
}
void
diff --git a/amd64/isel.c b/amd64/isel.c
index 7725013..0205313 100644
--- a/amd64/isel.c
+++ b/amd64/isel.c
@@ -202,7 +202,15 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
}
goto ALU;
case Oadd:
- if (ins->l.bits == mkref(RICON, 1).bits) {
+ if (kisint(ins->cls) && (addarg4addrp(ins->l) || addarg4addrp(ins->r))) {
+ temp.op = Ocopy;
+ temp.cls = ins->cls;
+ temp.l = mkref(RTMP, ins - instrtab);
+ if (fuseaddr(fn, &temp.l)) {
+ *ins = temp;
+ break;
+ }
+ } else if (ins->l.bits == mkref(RICON, 1).bits) {
/* add 1,x -> inc x */
ins->op = op = Oxinc;
ins->l = ins->r;
@@ -213,14 +221,6 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi)
ins->op = op = Oxinc;
ins->r = NOREF;
goto ALU;
- } else if (kisint(ins->cls) && (addarg4addrp(ins->l) || addarg4addrp(ins->r))) {
- temp.op = Ocopy;
- temp.cls = ins->cls;
- temp.l = mkref(RTMP, ins - instrtab);
- if (fuseaddr(fn, &temp.l)) {
- *ins = temp;
- break;
- }
}
/* fallthru */
case Omul: case Oumul:
diff --git a/common.h b/common.h
index a486426..3d051af 100644
--- a/common.h
+++ b/common.h
@@ -296,7 +296,7 @@ typearrlen(union type t)
extern uchar targ_primsizes[];
extern uchar targ_primalign[];
extern enum typetag targ_sizetype, targ_ptrdifftype;
-extern bool targ_charsigned, targ_bigendian;
+extern bool targ_charsigned, targ_bigendian, targ_64bit;
extern const struct mctarg *mctarg;
void targ_init(const char *);
@@ -351,20 +351,23 @@ int imap_get_(struct imapbase *, short k);
int imap_set_(struct imapbase *, void **v, uint vsiz, short k);
#define imap_free(m) (free((m)->mb.k), memset((m), 0, sizeof *(m)))
#define imap_init(m, N) (imap_free(m), imap_init_(&(m)->mb, (void **)&(m)->v, sizeof*(m)->v, (N))
+#define imap_clear(m) ((m)->mb.bs ? bszero((m)->mb.bs, BSSIZE((m)->mb.N)) : (void)0, \
+ (m)->mb.n = 0)
#define imap_get(m, k) (((m)->tmp = imap_get_(&(m)->mb, k)) < 0 ? NULL : &(m)->v[(m)->tmp])
#define imap_set(m, k, x) ((m)->tmp = imap_set_(&(m)->mb, (void **)&(m)->v, sizeof*(m)->v, k), \
(m)->v[(m)->tmp] = (x))
struct pmapbase { void **k; uint n, N; };
/* map of non-null ptr -> T */
-#define pmap_of(T) struct { T *v; int tmp; struct imapbase mb; }
+#define pmap_of(T) struct { T *v; int tmp; struct pmapbase mb; }
void pmap_init_(struct pmapbase *, void **v, uint vsiz, uint N);
-int pmap_get_(struct pmapbase *, void *k);
-int pmap_set_(struct pmapbase *, void **v, uint vsiz, void *k);
+int pmap_get_(struct pmapbase *, const void *k);
+int pmap_set_(struct pmapbase *, void **v, uint vsiz, const void *k);
#define pmap_free(m) (free((m)->mb.k), memset((m), 0, sizeof *(m)))
#define pmap_init(m, N) (pmap_free(m), pmap_init_(&(m)->mb, (void **)&(m)->v, sizeof*(m)->v, (N))
#define pmap_get(m, k) (((m)->tmp = pmap_get_(&(m)->mb, k)) < 0 ? NULL : &(m)->v[(m)->tmp])
-#define pmap_set(m, k, x) ((m)->v[pmap_set_(&(m)->mb, (void **)&(m)->v, sizeof*(m)->v, k)] = (x))
+#define pmap_set(m, k, x) ((m)->tmp = pmap_set_(&(m)->mb, (void **)&(m)->v, sizeof*(m)->v, k), \
+ (m)->v[(m)->tmp] = (x))
static inline bool
bstest(const struct bitset *bs, uint i)
diff --git a/elf.c b/elf.c
new file mode 100644
index 0000000..653dac5
--- /dev/null
+++ b/elf.c
@@ -0,0 +1,400 @@
+#include "elf.h"
+#include "common.h"
+#include "obj.h"
+#include "ir.h"
+#include <unistd.h>
+#include <stdlib.h> /* qsort */
+
+static struct elfhdr hdr;
+static vec_of(uchar) strs;
+static vec_of(struct elfsym) symtab;
+struct reloc {
+ uchar section;
+ ushort kind;
+ uint sym;
+ uint off;
+ vlong addend;
+};
+static uint ntextrel, nrodatarel, ndatarel;
+static vec_of(struct reloc) relocs;
+
+void
+elfinit(void)
+{
+ memcpy(hdr.i_mag, ELFMAG, 4);
+ hdr.i_class = ELFCLASS32 + targ_64bit;
+ hdr.i_data = ELFDATA2LSB + targ_bigendian;
+ hdr.i_version = ELFVERSION;
+ hdr.i_osabi = ELFOSABI_SYSV;
+ hdr.i_abiversion = 0;
+ hdr.type = ET_REL;
+ switch (mctarg->isa) {
+ case ISamd64: hdr.machine = EM_X86_64; break;
+ }
+ hdr.version = ELFVERSION;
+ hdr.ehsize = targ_64bit ? 64 : 52;
+ hdr.shentsize = targ_64bit ? 64 : 40;
+ vpush(&symtab, ((struct elfsym) { 0 }));
+ vpush(&symtab, ((struct elfsym) { 0, ELF_S_INFO(0, STT_FILE) }));
+}
+
+uint
+str2idx(const char *s)
+{
+ static pmap_of(uint) ht;
+ uint *p, i;
+
+ if ((p = pmap_get(&ht, s))) return *p;
+ if (!strs.n) vpush(&strs, 0);
+ i = strs.n;
+ vpushn(&strs, s, strlen(s)+1);
+ pmap_set(&ht, s, i);
+ return i;
+}
+
+static struct elfsym *
+findsym(uint name)
+{
+ for (int i = 0; i < symtab.n; ++i)
+ if (symtab.p[i].name == name)
+ return &symtab.p[i];
+ return NULL;
+}
+
+enum {
+ TEXT_SHNDX = 1,
+ RODATA_SHNDX = 2,
+ DATA_SHNDX = 3,
+ BSS_SHNDX = 4,
+};
+
+void
+elfaddsym(const char *nam, int info, enum section sect, uvlong value, uvlong size)
+{
+ uint str = str2idx(nam);
+ struct elfsym *sym = findsym(str), sym0 = {0};
+
+ if (!sym) {
+ sym = &sym0;
+ sym->name = str;
+ }
+ sym->info = info;
+ sym->other = 0;
+ switch (sect) {
+ case Snone: sym->shndx = SHN_UND; break;
+ case Stext: sym->shndx = TEXT_SHNDX; break;
+ case Srodata: sym->shndx = RODATA_SHNDX; break;
+ case Sdata: sym->shndx = DATA_SHNDX; break;
+ case Sbss: sym->shndx = BSS_SHNDX; break;
+ }
+ sym->value = value;
+ sym->size = size;
+ if (sym == &sym0) vpush(&symtab, sym0);
+}
+
+static const ushort relktab[][NRELOCKIND] = {
+ [ISamd64] = {
+ [REL_ABS] = 5, /* R_X86_64_COPY */
+ [REL_PCREL32] = 2, /* R_X86_64_PC32 */
+ }
+};
+
+void
+elfreloc(const char *sym, enum relockind kind, enum section section, uint off, vlong addend)
+{
+ uint snam = str2idx(sym);
+ switch (section) {
+ default: assert(0);
+ case Stext: ++ntextrel; break;
+ case Srodata: ++nrodatarel; break;
+ case Sdata: ++ndatarel; break;
+ }
+ assert(kind < NRELOCKIND);
+ vpush(&relocs, ((struct reloc) { section, relktab[mctarg->isa][kind], snam, off, addend}));
+}
+
+static void
+put16le(struct wbuf *out, ushort x)
+{
+ uchar b[2] = { x, x >> 8 };
+ iowrite(out, b, 2);
+}
+
+static void
+put16be(struct wbuf *out, ushort x)
+{
+ uchar b[2] = { x >> 8, x };
+ iowrite(out, b, 2);
+}
+
+static void
+put32le(struct wbuf *out, uint x)
+{
+ uchar b[4] = { x, x >> 8, x >> 16, x >> 24 };
+ iowrite(out, b, 4);
+}
+
+static void
+put32be(struct wbuf *out, uint x)
+{
+ uchar b[4] = { x >> 24, x >> 16, x >> 8, x };
+ iowrite(out, b, 4);
+}
+
+static void
+putword64le(struct wbuf *out, uvlong x)
+{
+ uchar b[8] = { x, x >> 8, x >> 16, x >> 24, x >> 32, x >> 40, x >> 48, x >> 56 };
+ iowrite(out, b, 8);
+}
+
+static void
+putword64be(struct wbuf *out, uvlong x)
+{
+ uchar b[8] = { x >> 56, x >> 48, x >> 40, x >> 32, x >> 24, x >> 16, x >> 8, x };
+ iowrite(out, b, 8);
+}
+
+static void
+putword32le(struct wbuf *out, uvlong x) { put32le(out, x); }
+static void
+putword32be(struct wbuf *out, uvlong x) { put32be(out, x); }
+
+static void (*putword)(struct wbuf *, uvlong);
+static void (*put16)(struct wbuf *, ushort);
+static void (*put32)(struct wbuf *, uint);
+
+static void
+elfputshdr(struct wbuf *out, const struct elfshdr *shdr)
+{
+ put32(out, shdr->name);
+ put32(out, shdr->type);
+ putword(out, shdr->flags);
+ putword(out, shdr->addr);
+ putword(out, shdr->offset);
+ putword(out, shdr->size);
+ put32(out, shdr->link);
+ put32(out, shdr->info);
+ putword(out, shdr->addralign);
+ putword(out, shdr->entsize);
+}
+
+static void
+elfputsym(struct wbuf *out, const struct elfsym *sym)
+{
+ put32(out, sym->name);
+ ioputc(out, sym->info);
+ ioputc(out, sym->other);
+ put16(out, sym->shndx);
+ putword(out, sym->value);
+ putword(out, sym->size);
+}
+
+static void
+elfputrel(struct wbuf *out, const struct elfrel *rel)
+{
+ putword(out, rel->offset);
+ putword(out, rel->info);
+}
+
+static void
+elfputrela(struct wbuf *out, const struct elfrela *rel)
+{
+ putword(out, rel->offset);
+ putword(out, rel->info);
+ putword(out, rel->addend);
+}
+
+/* ensure .symtab entries are ordered like this:
+ * (0. zero entry: NOTYPE LOCAL UND)
+ * (1. file: FILE LOCAL UND "...")
+ * 2. locals
+ * 3. defined globals
+ * 4. undefined globals
+ */
+static int
+symcmp(const void *L, const void *R)
+{
+ const struct elfsym *l = L, *r = R;
+ int tmp, lbind = l->info >> 4, rbind = r->info >> 4;
+ if ((tmp = lbind - rbind)) return tmp; /* locals prio */
+ if ((tmp = r->shndx - l->shndx)) return tmp; /* section prio (real sections > SHN_UND) */
+ return l->name - r->name;
+}
+
+static void
+wordalign(struct wbuf *out, int align)
+{
+ size_t off = out->len + lseek(out->fd, 0, SEEK_CUR);
+ while (off++ & (align - 1)) ioputc(out, 0);
+}
+
+void
+elffini(struct wbuf *out)
+{
+ enum { shnam_text = 1, shnam_rodata = 7, shnam_data = 15, shnam_bss = 21, shnam_shstrtab = 26,
+ shnam_strtab = 36, shnam_symtab = 44, shnam_reltext = 52, shnam_relrodata = 62, shnam_reldata = 74 };
+ static const char shstrs[] = "\0.text\0.rodata\0.data\0.bss\0.shstrtab\0.strtab\0.symtab\0"
+ ".rel.text\0.rel.rodata\0.rel.data";
+ int align = targ_64bit ? 8 : 4;
+
+ symtab.p[1].name = str2idx(getfilename(0));
+ qsort(symtab.p+2, symtab.n-2, sizeof *symtab.p, symcmp);
+ /* fixup relocs */
+ for (int i = 0; i < relocs.n; ++i) {
+ struct reloc *rel = &relocs.p[i];
+ struct elfsym *sym;
+ sym = findsym(rel->sym);
+ if (sym) rel->sym = sym - symtab.p;
+ else {
+ sym = &(struct elfsym) { rel->sym, ELF_S_INFO(STB_GLOBAL, STT_FUNC), 0, SHN_UND };
+ rel->sym = symtab.n;
+ vpush(&symtab, *sym);
+ }
+ }
+ size_t codesize = alignup(objout.code - objout.textbegin, align),
+ rodataoff = hdr.ehsize + codesize,
+ rodatasize = 0,
+ dataoff = rodataoff + rodatasize,
+ datasize = 0,
+ bsssize = 0,
+ shstrsoff = dataoff + datasize,
+ shstrssize = sizeof(shstrs),
+ strsoff = shstrsoff + shstrssize,
+ strssize = strs.n,
+ symtaboff = alignup(strsoff + strssize, align),
+ symtabsize = symtab.n * (targ_64bit ? 24 : 16),
+ reltextoff = symtaboff + symtabsize,
+ reltextsize = ntextrel * (targ_64bit ? 24 : 12),
+ relrodataoff = reltextoff + reltextsize,
+ relrodatasize = nrodatarel * (targ_64bit ? 24 : 12),
+ reldataoff = relrodataoff + relrodatasize,
+ reldatasize = ndatarel * (targ_64bit ? 24 : 12);
+ int nlocal = 0;
+
+ put16 = !targ_bigendian ? put16le : put16be;
+ put32 = !targ_bigendian ? put32le : put32be;
+ if (!targ_bigendian) putword = !targ_64bit ? putword32le : putword64le;
+ else putword = !targ_64bit ? putword32be : putword64be;
+
+ hdr.shoff = alignup(reldataoff + reldatasize, align);
+ hdr.shnum = 11;
+ hdr.shstrndx = 5;
+
+ /* elf header */
+ iowrite(out, &hdr, offsetof(struct elfhdr, type));
+ put16(out, hdr.type);
+ put16(out, hdr.machine);
+ put32(out, hdr.version);
+ putword(out, hdr.entry);
+ putword(out, hdr.phoff);
+ putword(out, hdr.shoff);
+ put32(out, hdr.flags);
+ put16(out, hdr.ehsize);
+ put16(out, hdr.phentsize);
+ put16(out, hdr.phnum);
+ put16(out, hdr.shentsize);
+ put16(out, hdr.shnum);
+ put16(out, hdr.shstrndx);
+
+ /* .text progbits */
+ iowrite(out, objout.textbegin, codesize);
+
+ /* .rodata progbits */
+
+ /* .data progbits */
+
+ /* section names */
+ iowrite(out, shstrs, sizeof shstrs);
+
+ /* strings */
+ iowrite(out, strs.p, strs.n);
+
+ /* symtab */
+ wordalign(out, align);
+ for (int i = 0; i < symtab.n; ++i) {
+ struct elfsym *sym = &symtab.p[i];
+ if (sym->info >> 4 == STB_LOCAL) ++nlocal;
+ elfputsym(out, sym);
+ }
+
+ /* rel.* */
+ assert(relocs.n == ntextrel + nrodatarel + ndatarel);
+ for (enum section s = Stext; s <= Sbss; ++s) {
+ for (int i = 0; i < relocs.n; ++i) {
+ struct reloc *rel = &relocs.p[i];
+ if (rel->section != s) continue;
+ elfputrela(out, &(struct elfrela) {rel->off, ELF_R_INFO(rel->sym, rel->kind), rel->addend});
+ }
+ }
+
+ /** Section Headers **/
+ wordalign(out, align);
+ /* §0 null section */
+ elfputshdr(out, &(struct elfshdr){0});
+ /* §1 .text */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_text, .type = SHT_PROGBITS,
+ .flags = SHF_ALLOC | SHF_EXECINSTR,
+ .offset = hdr.ehsize, .size = codesize,
+ .addralign = align,});
+ /* §2 .rodata */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_rodata, .type = SHT_PROGBITS,
+ .flags = SHF_ALLOC,
+ .offset = rodataoff, .size = rodatasize,
+ .addralign = 1,});
+ /* §3 .data */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_data, .type = SHT_PROGBITS,
+ .flags = SHF_ALLOC | SHF_WRITE,
+ .offset = dataoff, .size = datasize,
+ .addralign = 1,});
+ /* §4 .bss */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_bss, .type = SHT_NOBITS,
+ .size = bsssize,
+ .flags = SHF_ALLOC | SHF_WRITE,
+ .addralign = 1,});
+ /* §5 .shstrtab */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_shstrtab, .type = SHT_STRTAB,
+ .offset = shstrsoff, .size = shstrssize,
+ .flags = SHF_STRINGS });
+ /* §6 .strtab */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_strtab, .type = SHT_STRTAB,
+ .offset = strsoff, .size = strssize,
+ .flags = SHF_STRINGS });
+ /* §7 .symtab */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_symtab, .type = SHT_SYMTAB,
+ .offset = symtaboff, .size = symtabsize,
+ .flags = SHF_STRINGS,
+ .link = 6, /* .strtab */
+ .info = nlocal,
+ .entsize = targ_64bit ? 24 : 16 });
+ /* §8 .rel.text */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_reltext, .type = SHT_RELA,
+ .offset = reltextoff, .size = reltextsize,
+ .link = 7, /* .symtab */
+ .entsize = targ_64bit ? 24 : 12,
+ .info = TEXT_SHNDX });
+ /* §9 .rel.rodata */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_relrodata, .type = SHT_RELA,
+ .offset = relrodataoff, .size = relrodatasize,
+ .link = 7, /* .symtab */
+ .entsize = targ_64bit ? 24 : 12,
+ .info = RODATA_SHNDX });
+ /* §10 .rel.data */
+ elfputshdr(out, &(struct elfshdr){
+ .name = shnam_reldata, .type = SHT_RELA,
+ .offset = reldataoff, .size = reldatasize,
+ .link = 7, /* .symtab */
+ .entsize = targ_64bit ? 24 : 12,
+ .info = DATA_SHNDX });
+}
+
+/* vim:set ts=3 sw=3 expandtab: */
diff --git a/elf.h b/elf.h
new file mode 100644
index 0000000..273b2fa
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,149 @@
+#include "common.h"
+
+#define ELFMAG "\177ELF"
+enum {
+ ELFCLASS32 = 1,
+ ELFCLASS64 = 2,
+
+ ELFDATA2LSB = 1,
+ ELFDATA2MSB = 2,
+
+ ELFVERSION = 1,
+
+ ELFOSABI_SYSV = 0,
+ ELFOSABI_ARM = 97,
+ ELFOSABI_STANDALONE = 255,
+
+ ET_NONE = 0,
+ ET_REL, ET_EXEC, ET_DYN, ET_CORE,
+
+ EM_NONE = 0,
+ EM_386 = 3,
+ EM_486 = 6,
+ EM_MIPS = 8,
+ EM_MIPS_RS4_BE = 0xA,
+ EM_ARM = 0x28,
+ EM_X86_64 = 0x3E,
+ EM_ARM64 = 0xB7,
+};
+
+struct elfhdr {
+ uchar i_mag[4],
+ i_class,
+ i_data,
+ i_version,
+ i_osabi,
+ i_abiversion,
+ i_pad[7];
+ ushort type,
+ machine;
+ uint version;
+ uvlong entry,
+ phoff,
+ shoff;
+ uint flags;
+ ushort ehsize,
+ phentsize,
+ phnum,
+ shentsize,
+ shnum,
+ shstrndx;
+};
+
+enum {
+ SHT_NULL = 0x0,
+ SHT_PROGBITS = 0x1,
+ SHT_SYMTAB = 0x2,
+ SHT_STRTAB = 0x3,
+ SHT_RELA = 0x4,
+ SHT_HASH = 0x5,
+ SHT_DYNAMIC = 0x6,
+ SHT_NOTE = 0x7,
+ SHT_NOBITS = 0x8,
+ SHT_REL = 0x9,
+ SHT_SHLIB = 0xA,
+ SHT_DYNSYM = 0xB,
+ SHT_INIT_ARRAY = 0xE,
+ SHT_FINI_ARRAY = 0xF,
+ SHT_PREINIT_ARRAY = 0x10,
+ SHT_GROUP = 0x12,
+ SHT_SYMTAB_SHNDX = 0x13,
+};
+
+enum {
+ SHF_WRITE = 0x1,
+ SHF_ALLOC = 0x2,
+ SHF_EXECINSTR = 0x4,
+ SHF_MERGE = 0x10,
+ SHF_STRINGS = 0x20,
+ SHF_INFO_LINK = 0x40,
+ SHF_LINK_ORDER = 0x80,
+ SHF_OS_NONCONFORMING = 0x100,
+ SHF_GROUP = 0x200,
+ SHF_TLS = 0x400,
+};
+
+struct elfshdr {
+ uint name,
+ type;
+ uvlong flags,
+ addr,
+ offset,
+ size;
+ uint link,
+ info;
+ uvlong addralign,
+ entsize;
+};
+
+enum {
+ STB_LOCAL,
+ STB_GLOBAL,
+ STB_WEAK
+};
+
+enum {
+ STT_NOTYPE,
+ STT_OBJECT,
+ STT_FUNC,
+ STT_SECTION,
+ STT_FILE,
+};
+
+enum {
+ SHN_UND = 0,
+ SHN_ABS = 0xFFF1,
+};
+
+#define ELF_S_INFO(b,t) ((b) << 4 | (t))
+struct elfsym {
+ uint name;
+ uchar info,
+ other;
+ ushort shndx;
+ uvlong value,
+ size;
+};
+
+#define ELF_R_INFO(s,t) ((uvlong) (s) << 32 | (t))
+struct elfrel {
+ uvlong offset, info;
+};
+
+struct elfrela {
+ uvlong offset, info;
+ vlong addend;
+};
+
+struct elfphdr {
+ uint type,
+ flags;
+ uvlong offset,
+ vaddr,
+ paddr,
+ filesz,
+ memsz,
+ align;
+};
+
+/* vim:set ts=3 sw=3 expandtab: */
diff --git a/ir.c b/ir.c
index d8772ad..1351f02 100644
--- a/ir.c
+++ b/ir.c
@@ -1,4 +1,5 @@
#include "ir.h"
+#include "common.h"
uchar type2cls[NTYPETAG];
uchar cls2siz[KF8+1];
@@ -191,6 +192,22 @@ mkdatref(uint siz, uint align, const void *bytes, uint n, bool deref)
return mkref(RXCON, addcon(&(struct xcon){.isdat = 1, .deref = deref, .dat = dattab.n - 1}));
}
+const char *
+xcon2sym(int ref)
+{
+ extern const char *intern(const char *);
+ char buf[32];
+ struct wbuf wbuf = MEMBUF(buf, sizeof buf);
+ struct xcon *con = &conht[ref];
+
+ assert(con->isdat || con->issym);
+ if (con->issym) return con->sym;
+ bfmt(&wbuf, ".L.%d", con->dat);
+ ioputc(&wbuf, 0);
+ assert(!wbuf.err);
+ return intern(buf);
+}
+
struct instr
mkalloca(uint siz, uint align)
{
diff --git a/ir.h b/ir.h
index f00f6f0..5187e4b 100644
--- a/ir.h
+++ b/ir.h
@@ -208,6 +208,7 @@ union ref mkfltcon(enum irclass, double);
#define fltconval(r) (conht[(r).i].f)
union ref mksymref(const char *);
union ref mkdatref(uint siz, uint align, const void *, uint n, bool deref);
+const char *xcon2sym(int ref);
struct instr mkalloca(uint siz, uint align);
void conputdat(struct irdat *, uint off, enum typetag t, const void *dat);
union ref mkcallarg(union irtype ret, uint narg, int vararg);
diff --git a/main.c b/main.c
index 5e3e635..c3dbfeb 100644
--- a/main.c
+++ b/main.c
@@ -25,10 +25,10 @@ optval(const char *arg, const char *opt)
}
static void
-optparse(const char **file, const char **targ, char **args)
+optparse(const char **file, const char **outfile, const char **targ, char **args)
{
const char *arg, *x;
- *file = *targ = NULL;
+ *outfile = *file = *targ = NULL;
while ((arg = *++args)) {
if (*arg++ != '-') {
*file = arg-1;
@@ -52,6 +52,10 @@ optparse(const char **file, const char **targ, char **args)
case 'r': ccopt.dbg.r = 1; break;
default: warn(NULL, "-d: invalid debug flag %'c", *arg);
}
+ } else if (*arg == 'o') {
+ if (arg[1]) *outfile = arg+1;
+ else if (args[1]) *outfile = *++args;
+ else goto Bad;
} else Bad: warn(NULL, "invalid option: %'s", arg-1);
}
}
@@ -70,19 +74,19 @@ int
main(int argc, char **argv)
{
struct parser pr;
- const char *file, *targ;
+ const char *file, *outfile, *targ;
atexit(flushstd);
detectcolor();
ccopt.cstd = STDC99; /* C99 by default */
- optparse(&file, &targ, argv);
+ optparse(&file, &outfile, &targ, argv);
if (!file) {
efmt("usage: %s [options] <file>\n", *argv);
return 1;
}
targ_init(targ ? targ : "amd64-sysv");
- objini("a.out");
+ objini(outfile ? outfile : "a.out");
initparser(&pr, file);
parse(&pr);
if (!nerror) objfini();
diff --git a/mem.c b/mem.c
index 4817b7d..ad2082d 100644
--- a/mem.c
+++ b/mem.c
@@ -50,7 +50,7 @@ vpush_(void **p, int *pcap, uint *pn, uint siz)
*pcap = -cap;
} else if (*pn == -*pcap) { /* dyn buf */
*p = xrealloc(*p, -(*pcap *= 2) * siz);
- }
+ } else return;
assert(-(volatile int)*pcap > *pn && "overflow");
}
@@ -221,7 +221,7 @@ pmap_init_(struct pmapbase *m, void **v, uint vsiz, uint N)
m->N = N;
}
-int pmap_get_(struct pmapbase *m, void *k)
+int pmap_get_(struct pmapbase *m, const void *k)
{
assert(k && "null key");
if (!m->N) return -1;
@@ -239,7 +239,7 @@ pmap_rehash(struct pmapbase *m, void **v, uint vsiz)
int i, j;
void *k;
void *newv;
- uint N2 = m->N << 1,
+ uint N2 = m->N ? m->N << 1 : 16,
sizk = N2*sizeof*m->k,
sizv = N2*vsiz;
@@ -265,7 +265,7 @@ pmap_rehash(struct pmapbase *m, void **v, uint vsiz)
m->N = N2;
}
-int pmap_set_(struct pmapbase *m, void **v, uint vsiz, void *k)
+int pmap_set_(struct pmapbase *m, void **v, uint vsiz, const void *k)
{
assert(k && "null key");
if (m->n >= m->N >> 1) {
@@ -277,7 +277,7 @@ int pmap_set_(struct pmapbase *m, void **v, uint vsiz, void *k)
if (m->k[i] == k)
return i;
if (!m->k[i]) {
- m->k[i] = k;
+ m->k[i] = (void *)k;
++m->n;
return i;
}
diff --git a/obj.c b/obj.c
index 78aab14..68d183f 100644
--- a/obj.c
+++ b/obj.c
@@ -1,27 +1,66 @@
#include "obj.h"
#include "common.h"
+#include "ir.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+void elfinit(void);
+void elfaddsym(const char *, int info, enum section, uvlong value, uvlong size);
+void elfreloc(const char *sym, enum relockind, enum section, uint off, vlong addend);
+void elffini(struct wbuf *);
struct objfile objout;
+enum { NTEXT = 4<<20 /* 4MiB */ };
+
void
objini(const char *file)
{
- enum { NTEXT = 4<<20 /* 4MiB */ };
assert(!objout.file);
objout.file = file;
objout.code = objout.textbegin = mapzeros(NTEXT);
objout.textend = objout.textbegin + NTEXT;
+
+ switch (mctarg->objkind) {
+ case OBJELF: elfinit(); break;
+ }
+}
+
+void
+objdeffunc(const char *nam, bool globl, uint off, uint siz)
+{
+ switch (mctarg->objkind) {
+ case OBJELF:
+ elfaddsym(nam, /*STT_LOCAL/GLOBAL*/globl << 4 | /*STT_FUNC*/2, Stext, off, siz);
+ break;
+ }
+}
+
+void
+objreloc(const char *sym, enum relockind reloc, enum section section, uint off, vlong addend)
+{
+ switch (mctarg->objkind) {
+ case OBJELF:
+ elfreloc(sym, reloc, section, off, addend);
+ break;
+ }
}
void
objfini(void)
{
- void *popen(char *, char *), pclose(void *);
- long fwrite(void *, size_t, size_t, void *);
- void *cmd = popen("ndisasm -b64 -", "w");
- ioflush(&bstderr); ioflush(&bstdout);
- fwrite(objout.textbegin, 1, objout.code - objout.textbegin, cmd);
- pclose(cmd);
+ static char buf[1<<10];
+ struct wbuf out = FDBUF(buf, sizeof buf, open(objout.file, O_WRONLY | O_CREAT | O_TRUNC, 0666));
+ if (out.fd < 0) fatal(NULL, "could not open %'s for writing: %s", objout.file, strerror(errno));
+
+ switch (mctarg->objkind) {
+ case OBJELF: elffini(&out); break;
+ }
+
+ munmap(objout.textbegin, NTEXT);
+ ioflush(&out);
+ close(out.fd);
}
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/obj.h b/obj.h
index 0258119..a0d54da 100644
--- a/obj.h
+++ b/obj.h
@@ -6,7 +6,16 @@ extern struct objfile {
uchar *code;
} objout;
+enum relockind {
+ REL_ABS,
+ REL_PCREL32,
+ NRELOCKIND,
+};
+enum section { Snone, Stext, Srodata, Sdata, Sbss };
+
void objini(const char *);
+void objdeffunc(const char *nam, bool globl, uint off, uint siz);
+void objreloc(const char *sym, enum relockind, enum section, uint off, vlong addend);
void objfini(void);
/* vim:set ts=3 sw=3 expandtab: */
diff --git a/test/test2.c b/test/test2.c
index 7d39164..fec9b14 100644
--- a/test/test2.c
+++ b/test/test2.c
@@ -15,6 +15,9 @@ v2d add(v2d a, v2d b)
}
short s(int a, int b) {
+ extern h();
+ h(a +a * a);
+ ++a;
return a + b;
}