diff options
| author | 2025-09-14 17:59:39 +0200 | |
|---|---|---|
| committer | 2025-09-14 18:24:05 +0200 | |
| commit | 7a318363ec4fdcd80d9d0154cef393c9bf205d5e (patch) | |
| tree | c254e3a3b985bc22e84480346c856052ef9963ca | |
| parent | 5753e393954aca532abd6a5c10d6e8ab9a96c96c (diff) | |
preliminary pie and pic
| -rw-r--r-- | amd64/emit.c | 48 | ||||
| -rw-r--r-- | amd64/isel.c | 33 | ||||
| -rw-r--r-- | c.c | 9 | ||||
| -rw-r--r-- | common.h | 1 | ||||
| -rw-r--r-- | elf.c | 5 | ||||
| -rw-r--r-- | main.c | 14 | ||||
| -rw-r--r-- | obj.h | 3 |
7 files changed, 88 insertions, 25 deletions
diff --git a/amd64/emit.c b/amd64/emit.c index df4c4cd..7c88955 100644 --- a/amd64/emit.c +++ b/amd64/emit.c @@ -240,14 +240,21 @@ opermatch(enum operpat pat, struct oper oper) #define DS(S) D(S, sizeof S - 1) static bool usebp; /* use RBP? */ +static const char *curfnsym; +static uchar *fnstart; /* Given an instruction description table, find the first entry that matches * the operands (where dst, src are the operands in intel syntax order) and encode it */ static void encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct oper dst, struct oper src) { - const uchar *opc; int nopc, mod, rex; - bool sib = 0; uchar reg; struct oper mem; const struct desc *en = NULL; + const uchar *opc; + int nopc, mod, rex; + const char *sym; + uchar reg; + struct oper mem; + bool sib = 0; + const struct desc *en = NULL; for (int i = 0; i < ntab; ++i) { if ((tab[i].psiz & cls2siz[k]) && opermatch(tab[i].ptd, dst) && opermatch(tab[i].pts, src)) { @@ -313,16 +320,17 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o else if (en->r8 && in_range(reg, RSP, RDI)) B(0x40); if (mem.t == OSYM) { - /* XXX PIC */ D(opc, nopc); if (mem.cindex == NOINDEX) { /* %rip(var) */ static uchar offs[NOPERENC] = { [EN_MI8] = 1, [EN_MI16] = 2, [EN_MI32] = 4 }; + enum relockind r = (!conht[mem.con].deref && ccopt.pic) ? (rex ? REL_GOTPCRELX : REL_GOTPCRELX_REX) : REL_PCREL32; int off = -4 - offs[en->operenc]; B(/*mod 0*/ (reg & 7) << 3 | RBP); - objreloc(xcon2sym(mem.con), REL_PCREL32, Stext, *pcode - objout.textbegin, mem.disp + off); + objreloc(xcon2sym(mem.con), r, Stext, *pcode - objout.textbegin, mem.disp + off); } else { /* var(,%reg,shift) */ + assert(!ccopt.pic && !ccopt.pie && "cannot encode [RIP-rel + REG] for position independent"); B(/*mod 0*/ (reg & 7) << 3 | RSP); B(mem.cshift << 6 | mem.cindex << 3 | RBP); /* SIB [index*s + disp32] */ objreloc(xcon2sym(mem.con), REL_ABS32S, Stext, *pcode - objout.textbegin, mem.disp); @@ -393,8 +401,15 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o if (rex) B(0x40 | rex); D(opc, nopc); assert(dst.t == OSYM); - objreloc(xcon2sym(dst.con), REL_PCREL32, Stext, *pcode - objout.textbegin, -4); - I32(0); + sym = xcon2sym(dst.con); + if (sym != curfnsym) { + enum relockind r = (ccopt.pie|ccopt.pic) ? REL_PLT32 : REL_PCREL32; + objreloc(sym, r, Stext, *pcode - objout.textbegin, -4); + I32(0); + } else { + /* self-recursive call */ + I32(fnstart - *pcode - 4); + } break; } } @@ -478,6 +493,7 @@ DEFINSTR2(Xxchg, ) DEFINSTR2(Xlea, {4|8, PGPR, PMEM, "\x8D", EN_RM}, /* LEA r32/64,m32/64 */ + { 8, PGPR, PSYM, "\x8D", EN_RM}, /* LEA rel32 */ ) DEFINSTR2(Xadd, {4|8, PGPR, PGPR, "\x03", EN_RR}, /* ADD r32/64, r32/64 */ @@ -764,12 +780,22 @@ gencopy(uchar **pcode, enum irclass cls, struct block *blk, int curi, struct ope } /* normal (not 2-address) case */ Lea: + if (isaddrcon(addr->base) && ccopt.pic) { + assert(!addr->disp && !addr->index.bits); + val = addr->base; + goto GOTLoad; + } Xlea(pcode, cls, dst, ref2oper(val)); } else if (val.bits == ZEROREF.bits && dst.t == OREG && !flagslivep(blk, curi)) { /* dst = 0 -> xor dst, dst; but only if it is ok to clobber flags */ Xxor(pcode, kisint(cls) ? KI4 : cls, dst, dst); } else if (isaddrcon(val)) { - Xlea(pcode, cls, dst, mkoper(OSYM, .con = val.i, .cindex = NOINDEX)); + if (ccopt.pic) GOTLoad: + /* for mov reg, [rip(sym@GOTPCREL)] */ + Xmov(pcode, cls, dst, mkoper(OSYM, .con = val.i, .cindex = NOINDEX)); + else + /* for lea reg, [rip(sym)] */ + Xlea(pcode, cls, dst, mkoper(OSYM, .con = val.i, .cindex = NOINDEX)); } else { struct oper src = mkimmdatregoper(val); if (memcmp(&dst, &src, sizeof dst) != 0) @@ -1067,7 +1093,6 @@ emitbin(struct function *fn) { struct block *blk; uchar **pcode = &objout.code; - uchar *start; int npush = 0; uint epilogueaddr = 0; bool saverestore; @@ -1079,7 +1104,8 @@ emitbin(struct function *fn) memset(blkaddr, 0, nblkaddr * sizeof *blkaddr); nops(pcode, 16); - start = *pcode; + fnstart = *pcode; + curfnsym = fn->name; /** prologue **/ @@ -1129,7 +1155,7 @@ emitbin(struct function *fn) } if (blk->jmp.t == Jret) { /* epilogue */ - uint here = *pcode - start; + uint here = *pcode - fnstart; if (epilogueaddr) { int disp = epilogueaddr - (here + 2); if ((uint)(disp + 128) < 256) {/* can use 1-byte displacement? */ @@ -1149,7 +1175,7 @@ emitbin(struct function *fn) } } else emitbranch(pcode, blk); } while ((blk = blk->lnext) != fn->entry); - objdeffunc(fn->name, fn->globl, start - objout.textbegin, *pcode - start); + objdeffunc(fn->name, fn->globl, fnstart - objout.textbegin, *pcode - fnstart); } void diff --git a/amd64/isel.c b/amd64/isel.c index fcd4ab3..1054502 100644 --- a/amd64/isel.c +++ b/amd64/isel.c @@ -44,6 +44,13 @@ static const uchar opflags[] = { static int iflagsrc = -1; static void +picfixsym(union ref *r, struct block *blk, int *curi) +{ + if (!ccopt.pic || !isaddrcon(*r)) return; + *r = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, .l = *r)); +} + +static void fixarg(union ref *r, struct instr *ins, struct block *blk, int *curi) { int sh; @@ -72,6 +79,7 @@ fixarg(union ref *r, struct instr *ins, struct block *blk, int *curi) wr32le(data, pun.i); } *r = mkdatref(NULL, siz, /*align*/siz, data, siz, /*deref*/1); + picfixsym(r, blk, curi); } else if (in_range(op, Odiv, Ourem) && kisint(ins->cls)) goto DivImm; } else if (r->t == RICON && in_range(op, Odiv, Ourem) && kisint(ins->cls)) { @@ -195,9 +203,7 @@ aadd(struct addr *addr, union ref r) } else if (isnumcon(r)) { return acon(addr, r); } else if (isaddrcon(r)) { - /* XXX PIC */ if (!addr->base.bits && !isaddrcon(addr->index)) addr->base = r; - else if (!addr->index.bits && !isaddrcon(addr->base)) addr->index = r; else return 0; } else if (r.t == RREG) { /* temporaries are single assignment, but register aren't, so they can't be * @@ -212,7 +218,7 @@ aadd(struct addr *addr, union ref r) } static bool -fuseaddr(union ref *r) +fuseaddr(union ref *r, struct block *blk, int *curi) { struct addr addr = { 0 }; @@ -221,6 +227,15 @@ fuseaddr(union ref *r) if (r->t != RTMP) return 0; if (!aadd(&addr, *r)) return 0; + if (ccopt.pic || (ccopt.pie && isaddrcon(addr.base) && addr.index.bits)) { + /* pic needs to load from GOT */ + /* pie cannot encode RIP-relative address with index register */ + /* first load symbol address into a temp register */ + union ref temp = mkaddr((struct addr){.base = addr.base, .disp = ccopt.pic ? 0 : addr.disp}); + addr.base = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, .l = temp)); + if (!ccopt.pic) addr.disp = 0; + } + *r = mkaddr(addr); return 1; } @@ -318,7 +333,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) /* sub x,1 -> dec x */ ins->op = op = Oxdec; ins->r = NOREF; - } else if (iscon(ins->l)) { + } else if (isintcon(ins->l)) { /* sub imm, x -> sub x, imm; neg x */ struct instr sub = *ins; rswap(sub.l, sub.r); @@ -333,7 +348,7 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) temp.op = Ocopy; temp.cls = ins->cls; temp.l = mkref(RTMP, ins - instrtab); - if (fuseaddr(&temp.l)) { + if (fuseaddr(&temp.l, blk, curi)) { *ins = temp; break; } @@ -362,19 +377,21 @@ sel(struct function *fn, struct instr *ins, struct block *blk, int *curi) if (!(in_range(op, Omul, Oumul) && kisint(ins->cls) && isimm32(ins->r))) /* for (I)MUL r,r/m,imm */ ins->inplace = 1; if (iscon(ins->l)) - ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, concls(ins->l), ins->l)); + ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, ins->cls, ins->l)); if (ins->r.bits) case Omove: fixarg(&ins->r, ins, blk, curi); break; case Oloads1: case Oloadu1: case Oloads2: case Oloadu2: case Oloads4: case Oloadu4: case Oloadi8: case Oloadf4: case Oloadf8: - if (!fuseaddr(&ins->l) && ins->l.t != RTMP && ins->l.t != RREG) + if (!fuseaddr(&ins->l, blk, curi) && ins->l.t != RTMP && ins->l.t != RREG) ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, ins->l)); + picfixsym(&ins->l, blk, curi); break; case Ostore1: case Ostore2: case Ostore4: case Ostore8: - if (!fuseaddr(&ins->l) && ins->l.t != RTMP && ins->l.t != RREG) + if (!fuseaddr(&ins->l, blk, curi) && ins->l.t != RTMP && ins->l.t != RREG) ins->l = insertinstr(blk, (*curi)++, mkinstr(Ocopy, KPTR, ins->l)); + picfixsym(&ins->l, blk, curi); fixarg(&ins->r, ins, blk, curi); break; case Ocvtf4f8: case Ocvtf8f4: case Ocvtf4s: case Ocvtf8s: @@ -2447,10 +2447,11 @@ genptroff(struct function *fn, enum op op, uint siz, union ref ptr, assert(siz); idx = cvt(fn, targ_sizetype, tt, idx); - if (siz == 1) off = idx; - else if (idx.t == RICON) - off = mkintcon(cls, idx.i * siz); - else if (ispo2(siz)) + if (siz == 1) off = idx; + else if (idx.t == RICON) { + if (op == Osub) op = Oadd, idx.i = -idx.i; + off = mkintcon(cls, idx.i * (int)siz); + } else if (ispo2(siz)) off = addinstr(fn, mkinstr(Oshl, cls, .l = idx, .r = mkintcon(cls, ilog2(siz)))); else @@ -122,6 +122,7 @@ struct option { bool pedant; bool trigraph; bool nocolor; + bool pie, pic; union { struct { bool p : 1, /* after parsing */ @@ -118,6 +118,9 @@ static const ushort relktab[][NRELOCKIND] = { [REL_ABS32] = 10, /* R_X86_64_32 */ [REL_ABS32S] = 11, /* R_X86_64_32S */ [REL_PCREL32] = 2, /* R_X86_64_PC32 */ + [REL_PLT32] = 4, /* R_X86_64_PLT32 */ + [REL_GOTPCRELX] = 41, /* R_X86_64_GOTPCRELX */ + [REL_GOTPCRELX_REX] = 42, /* R_X86_64_REX_GOTPCRELX */ } }; @@ -132,7 +135,7 @@ elfreloc(const char *sym, enum relockind kind, enum section section, uint off, v case Sdata: ++ndatarel; break; } assert(kind < NRELOCKIND); - vpush(&relocs, ((struct reloc) { section, relktab[mctarg->isa][kind], snam, off, addend})); + vpush(&relocs, ((struct reloc) { section, relktab[mctarg->isa][kind], snam, off, addend })); } static void @@ -131,6 +131,16 @@ optparse(char **args) if (arg[1]) task.out = arg+1; else if (args[1]) task.out = *++args; else goto Bad; + } else if (*arg == 'f') { + const char *flag = arg+1; + bool set = 1; + if (!strncmp(flag, "no-", 3)) { + set = 0; + flag += 3; + } + if (!strcmp(flag, "pie") || !strcmp(flag, "PIE")) ccopt.pie = set; + else if (!strcmp(flag, "pic") || !strcmp(flag, "PIC")) ccopt.pic = set; + else goto Bad; } else if (!strcmp(arg, "v") || !strcmp(arg, "-verbose")) { task.verbose = 1; } else if (!strcmp(arg, "c")) { @@ -242,8 +252,9 @@ dolink(void) vpush(&cmd, "/bin/gcc"); if (task.outft == OFTdll) { vpush(&cmd, "-shared"); + } else if (task.outft == OFTexe) { + vpush(&cmd, ccopt.pie ? "-pie" : "-no-pie"); } - vpush(&cmd, "-no-pie"); vpush(&cmd, "-o"); vpush(&cmd, task.out); assert(task.ninf > 0); @@ -335,6 +346,7 @@ main(int argc, char **argv) /* setup defaults */ detectcolor(); ccopt.cstd = STDC99; + ccopt.pie = 1; /* parse cli ags */ optparse(argv); @@ -14,6 +14,9 @@ enum relockind { REL_ABS32, REL_ABS32S, REL_PCREL32, + REL_PLT32, + REL_GOTPCRELX, + REL_GOTPCRELX_REX, NRELOCKIND, }; enum section { Snone, Stext, Srodata, Sdata, Sbss }; |