From 854df54e1839c8b96d1aaa9aeaa32c2ebbf535f8 Mon Sep 17 00:00:00 2001 From: lemon Date: Sat, 13 Dec 2025 14:21:22 +0100 Subject: fix position independent loads of function symbols. For `extern int x[1];`, can use PCREL32 for &x. But for `extern int x(int)`, must use GOTREL, when not being called directly (that's PLT). Therefore the type of an external symbol (actually just whether it denotes a function) matters when deciding what kind of relocation to emit, so keep that information. --- c/c.c | 6 +++--- ir/intrin.c | 2 +- ir/ir.c | 6 +++--- ir/ir.h | 4 ++-- test/15-reloc.c | 29 +++++++++++++++++++++++++++++ test/reloc.c | 9 --------- test/run.sh | 2 +- x86_64/emit.c | 14 ++++++++------ x86_64/isel.c | 2 +- 9 files changed, 48 insertions(+), 26 deletions(-) create mode 100644 test/15-reloc.c delete mode 100644 test/reloc.c diff --git a/c/c.c b/c/c.c index aff0a9d..7e521b6 100644 --- a/c/c.c +++ b/c/c.c @@ -2755,7 +2755,7 @@ expraddr(struct function *fn, const struct expr *ex) assert(decl->id >= 0); return mkref(RTMP, decl->id); case SCEXTERN: case SCNONE: case SCSTATIC: - return mksymref(decl->sym); + return mksymref(decl->sym, decl->ty.t == TYFUNC); default: assert(0); } @@ -2798,7 +2798,7 @@ expraddr(struct function *fn, const struct expr *ex) ip->off = objnewdat(sym, ip->sec, 0, typesize(ty), typealign(ty)); if (!iniwriterec(NULL, ip, 0, (struct expr *)ex)) error(&ex->span, "cannot not evaluate expression statically"); - return mksymref(sym); + return mksymref(sym, 0); } case ESEQ: expreffects(fn, &ex->sub[0]); @@ -2888,7 +2888,7 @@ geninit(struct function *fn, union type t, union ref dst, const struct expr *src addinstr(fn, mkarginstr(cls2type(KPTR), dst)); addinstr(fn, mkarginstr(cls2type(KI32), ZEROREF)); addinstr(fn, mkarginstr(cls2type(type2cls[targ_sizetype]), mkintcon(type2cls[targ_sizetype], siz))); - call.l = mksymref("memset"); + call.l = mksymref("memset", 1); call.r = mkcallarg(cls2type(KPTR), 3, -1); addinstr(fn, call); } diff --git a/ir/intrin.c b/ir/intrin.c index cd2865a..2ef826c 100644 --- a/ir/intrin.c +++ b/ir/intrin.c @@ -24,7 +24,7 @@ intrin(struct block *blk, int *curi, enum intrin in, struct arg *args, int narg, /* memcpy */ *args[1].ty = *args[0].ty = mktyperef(cls2type(KPTR)); insertinstr(blk, (*curi)++, mkarginstr(cls2type(cls), mkintcon(cls, td->siz))); - *this = mkinstr(Ocall, 0, mksymref("memcpy"), this->r); + *this = mkinstr(Ocall, 0, mksymref("memcpy", 1), this->r); calltab.p[this->r.i].narg = 3; calltab.p[this->r.i].ret = cls2type(0); return 0; diff --git a/ir/ir.c b/ir/ir.c index dbd6206..cd93992 100644 --- a/ir/ir.c +++ b/ir/ir.c @@ -94,7 +94,7 @@ addcon(const struct xcon *con) { uint h = hashb(0, con, sizeof *con); uint i = h, n = countof(conht); - assert(con->issym || con->isdat || con->cls); + assert((con->issym ^ con->isdat && !(con->isdat && con->isfunc)) || con->cls); for (;; ++i) { i &= countof(conht) - 1; if (!conht[i].issym && !conht[i].isdat && !conht[i].cls) { @@ -138,9 +138,9 @@ mkfltcon(enum irclass k, double f) } union ref -mksymref(const char *s) +mksymref(const char *s, bool isfunc) { - struct xcon con = { .issym = 1, .sym = s }; + struct xcon con = { .issym = 1, .sym = s, .isfunc = isfunc }; return mkref(RXCON, addcon(&con)); } diff --git a/ir/ir.h b/ir/ir.h index e16cdda..1a1d907 100644 --- a/ir/ir.h +++ b/ir/ir.h @@ -26,7 +26,7 @@ struct irdat { }; struct xcon { - bool issym, isdat, deref; + bool issym, isdat, isfunc, deref; uchar cls; union { const char *sym; @@ -250,7 +250,7 @@ union ref mkfltcon(enum irclass, double); #define isaddrcon(r,derefok) ((r).t == RXCON && !conht[(r).i].cls && (derefok || !conht[(r).i].deref)) #define intconval(r) ((r).t == RICON ? (r).i : conht[(r).i].i) #define fltconval(r) ((r).t == RICON ? (r).i : conht[(r).i].f) -union ref mksymref(const char *); +union ref mksymref(const char *, bool isfunc); union ref mkdatref(const char *name, union type ctype, uint siz, uint align, const void *, uint n, bool deref); const char *xcon2sym(int ref); struct instr mkalloca(uint siz, uint align); diff --git a/test/15-reloc.c b/test/15-reloc.c new file mode 100644 index 0000000..8d1b323 --- /dev/null +++ b/test/15-reloc.c @@ -0,0 +1,29 @@ +/* EXPECT: +1.3 +5 +ok +*/ + +const void *const relro = &relro; +float get_value(unsigned x) { + static const float values [] = {1.1f, 1.2f, 1.3f, 1.4f}; + return x < 4 ? values[x] : 0.0f; +} + +static const short tb[] = { 1,3,34,5,23,2,5,5,-7 }; +static const short *ptb = &tb[4]; + +static int ou(int k) { return ptb[k]; } + +int (*(getputs(void)))(const char *) { + extern int puts(const char *); + int (*t)(const char *) = puts; + return t; +} + +#include +int main() { + printf("%g\n", get_value(2)); + printf("%d\n", ou(2)); + getputs()("ok"); +} diff --git a/test/reloc.c b/test/reloc.c deleted file mode 100644 index ccc03ec..0000000 --- a/test/reloc.c +++ /dev/null @@ -1,9 +0,0 @@ - - -const void *const relro = &relro; - -float get_value(unsigned x) -{ - static const float values [] = {1.1f, 1.2f, 1.3f, 1.4f}; - return x < 4 ? values[x] : 0.0f; -} diff --git a/test/run.sh b/test/run.sh index 34d8101..8954ea7 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,7 +1,7 @@ #!/bin/env sh cd $(dirname "$0") -ANTCC=../antcc +ANTCC="../antcc $CFLAGS" ntest=0 npass=0 diff --git a/x86_64/emit.c b/x86_64/emit.c index 14a62db..2f7db11 100644 --- a/x86_64/emit.c +++ b/x86_64/emit.c @@ -333,9 +333,11 @@ encode(uchar **pcode, const struct desc *tab, int ntab, enum irclass k, struct o 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_REX : REL_GOTPCRELX) - : REL_PCREL32; + enum relockind r; + if ((!conht[mem.con].deref && ccopt.pic) || conht[mem.con].isfunc) + r = (rex ? REL_GOTPCRELX_REX : REL_GOTPCRELX); + else + r = REL_PCREL32; B(/*mod 0*/ (reg & 7) << 3 | RBP); objreloc(xcon2sym(mem.con), r, Stext, *pcode - objout.textbegin, mem.disp - 4 - offs[en->operenc]); } else { @@ -521,7 +523,7 @@ DEFINSTR2(Xxchg, ) DEFINSTR2(Xlea, {4|8, PGPR, PMEM, O("\x8D"), EN_RM}, /* LEA r32/64,m32/64 */ - { 8, PGPR, PSYM, O("\x8D"), EN_RM}, /* LEA rel32 */ + { 8, PGPR, PSYM, O("\x8D"), EN_RM}, /* LEA r32/64,rel32 */ ) DEFINSTR2(Xadd, {4|8, PGPR, PGPR, O("\x03"), EN_RR}, /* ADD r32/64, r32/64 */ @@ -862,7 +864,7 @@ gencopy(uchar **pcode, enum irclass cls, struct block *blk, int curi, struct ope } /* normal (not 2-address) case */ Lea: - if (isaddrcon(addr->base,0) && ccopt.pic) { + if (isaddrcon(addr->base,0) && (ccopt.pic || conht[addr->base.i].isfunc)) { assert(!addr->disp && !addr->index.bits); val = addr->base; goto GOTLoad; @@ -872,7 +874,7 @@ gencopy(uchar **pcode, enum irclass cls, struct block *blk, int curi, struct ope /* dst = 0 -> xor dst, dst; but only if it is ok to clobber flags */ Xxor(pcode, kisint(cls) ? KI32 : cls, dst, dst); } else if (isaddrcon(val,0)) { - if (ccopt.pic) GOTLoad: + if (ccopt.pic || conht[val.i].isfunc) GOTLoad: /* for mov reg, [rip(sym@GOTPCREL)] */ Xmov(pcode, cls, dst, mkoper(OSYM, .con = val.i, .cindex = NOINDEX)); else diff --git a/x86_64/isel.c b/x86_64/isel.c index 5d373f3..67a4358 100644 --- a/x86_64/isel.c +++ b/x86_64/isel.c @@ -310,7 +310,7 @@ fuseaddr(union ref *r, struct block *blk, int *curi) if (r->t != RTMP) return 0; if (!aadd(&addr, blk, curi, *r)) return 0; - if (isaddrcon(addr.base,0) && (ccopt.pic || (ccopt.pie && addr.index.bits))) { + if (isaddrcon(addr.base,0) && (ccopt.pic || (ccopt.pie && addr.index.bits) || conht[addr.base.i].isfunc)) { /* pic needs to load from GOT */ /* pie cannot encode RIP-relative address with index register */ /* first load symbol address into a temp register */ -- cgit v1.2.3