diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | amd64/all.h | 6 | ||||
| -rw-r--r-- | amd64/emit.c | 36 | ||||
| -rw-r--r-- | amd64/isel.c | 18 | ||||
| -rw-r--r-- | common.h | 13 | ||||
| -rw-r--r-- | elf.c | 400 | ||||
| -rw-r--r-- | elf.h | 149 | ||||
| -rw-r--r-- | ir.c | 17 | ||||
| -rw-r--r-- | ir.h | 1 | ||||
| -rw-r--r-- | main.c | 14 | ||||
| -rw-r--r-- | mem.c | 10 | ||||
| -rw-r--r-- | obj.c | 53 | ||||
| -rw-r--r-- | obj.h | 9 | ||||
| -rw-r--r-- | test/test2.c | 3 |
14 files changed, 692 insertions, 39 deletions
@@ -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: @@ -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) @@ -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: */ @@ -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: */ @@ -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) { @@ -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); @@ -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(); @@ -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; } @@ -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: */ @@ -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; } |