mirror of
git://c9x.me/qbe.git
synced 2026-05-26 23:44:40 +00:00
extern DYNCONST
New DYNCONST flag to access symbols from dynamically-linked libraries. It can also be used by frontends to implement PIC. Review by mcf
This commit is contained in:
parent
577b4667db
commit
ac48f83f17
6
all.h
6
all.h
@ -293,8 +293,10 @@ struct Use {
|
|||||||
|
|
||||||
struct Sym {
|
struct Sym {
|
||||||
enum {
|
enum {
|
||||||
SGlo,
|
SGlo = 0, /* direct access */
|
||||||
SThr,
|
SThr = 1, /* local-exec TLS */
|
||||||
|
SExt = 2, /* GOT/PLT access */
|
||||||
|
SExtThr = SExt|SThr, /* initial-exec TLS */
|
||||||
} type;
|
} type;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
};
|
};
|
||||||
|
|||||||
59
amd64/emit.c
59
amd64/emit.c
@ -196,12 +196,12 @@ emitcon(Con *con, E *e)
|
|||||||
l = str(con->sym.id);
|
l = str(con->sym.id);
|
||||||
p = l[0] == '"' ? "" : T.assym;
|
p = l[0] == '"' ? "" : T.assym;
|
||||||
if (con->sym.type == SThr) {
|
if (con->sym.type == SThr) {
|
||||||
if (T.apple)
|
assert(!T.apple);
|
||||||
fprintf(e->f, "%s%s@TLVP", p, l);
|
|
||||||
else
|
|
||||||
fprintf(e->f, "%%fs:%s%s@tpoff", p, l);
|
fprintf(e->f, "%%fs:%s%s@tpoff", p, l);
|
||||||
} else
|
} else {
|
||||||
|
assert((con->sym.type & ~SExt) == SGlo);
|
||||||
fprintf(e->f, "%s%s", p, l);
|
fprintf(e->f, "%s%s", p, l);
|
||||||
|
}
|
||||||
if (con->bits.i)
|
if (con->bits.i)
|
||||||
fprintf(e->f, "%+"PRId64, con->bits.i);
|
fprintf(e->f, "%+"PRId64, con->bits.i);
|
||||||
break;
|
break;
|
||||||
@ -380,7 +380,7 @@ Next:
|
|||||||
off = e->fn->con[ref.val];
|
off = e->fn->con[ref.val];
|
||||||
emitcon(&off, e);
|
emitcon(&off, e);
|
||||||
if (off.type == CAddr)
|
if (off.type == CAddr)
|
||||||
if (off.sym.type != SThr || T.apple)
|
if (off.sym.type != SThr)
|
||||||
fprintf(e->f, "(%%rip)");
|
fprintf(e->f, "(%%rip)");
|
||||||
break;
|
break;
|
||||||
case RTmp:
|
case RTmp:
|
||||||
@ -532,14 +532,22 @@ emitins(Ins i, E *e)
|
|||||||
emitf("mov%k %0, %=", &i, e);
|
emitf("mov%k %0, %=", &i, e);
|
||||||
break;
|
break;
|
||||||
case Oaddr:
|
case Oaddr:
|
||||||
if (!T.apple
|
if (rtype(i.arg[0]) != RCon)
|
||||||
&& rtype(i.arg[0]) == RCon
|
goto Table;
|
||||||
&& e->fn->con[i.arg[0].val].sym.type == SThr) {
|
con = &e->fn->con[i.arg[0].val];
|
||||||
|
assert(isreg(i.to) && con->type == CAddr);
|
||||||
|
sym = str(con->sym.id);
|
||||||
|
if (T.apple && (con->sym.type & SThr)) {
|
||||||
|
fprintf(e->f,
|
||||||
|
"\tmovq %s%s@tlvp(%%rip), %%%s\n",
|
||||||
|
sym[0] == '"' ? "" : T.assym, sym,
|
||||||
|
regtoa(i.to.val, SLong));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (con->sym.type) {
|
||||||
|
case SThr:
|
||||||
/* derive the symbol address from the TCB
|
/* derive the symbol address from the TCB
|
||||||
* address at offset 0 of %fs */
|
* address at offset 0 of %fs */
|
||||||
assert(isreg(i.to));
|
|
||||||
con = &e->fn->con[i.arg[0].val];
|
|
||||||
sym = str(con->sym.id);
|
|
||||||
emitf("movq %%fs:0, %L=", &i, e);
|
emitf("movq %%fs:0, %L=", &i, e);
|
||||||
fprintf(e->f, "\tleaq %s%s@tpoff",
|
fprintf(e->f, "\tleaq %s%s@tpoff",
|
||||||
sym[0] == '"' ? "" : T.assym, sym);
|
sym[0] == '"' ? "" : T.assym, sym);
|
||||||
@ -550,15 +558,40 @@ emitins(Ins i, E *e)
|
|||||||
regtoa(i.to.val, SLong),
|
regtoa(i.to.val, SLong),
|
||||||
regtoa(i.to.val, SLong));
|
regtoa(i.to.val, SLong));
|
||||||
break;
|
break;
|
||||||
}
|
case SExtThr:
|
||||||
|
/* initial-exec TLS: load offset from
|
||||||
|
* GOT, add to thread-base register */
|
||||||
|
assert(!con->bits.i);
|
||||||
|
emitf("movq %%fs:0, %L=", &i, e);
|
||||||
|
fprintf(e->f,
|
||||||
|
"\taddq %s%s@gottpoff(%%rip), %%%s\n",
|
||||||
|
sym[0] == '"' ? "" : T.assym, sym,
|
||||||
|
regtoa(i.to.val, SLong));
|
||||||
|
break;
|
||||||
|
case SExt:
|
||||||
|
/* load address from the GOT */
|
||||||
|
assert(!con->bits.i);
|
||||||
|
fprintf(e->f,
|
||||||
|
"\tmovq %s%s@gotpcrel(%%rip), %%%s\n",
|
||||||
|
sym[0] == '"' ? "" : T.assym, sym,
|
||||||
|
regtoa(i.to.val, SLong));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
goto Table;
|
goto Table;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case Ocall:
|
case Ocall:
|
||||||
/* calls simply have a weird syntax in AT&T
|
/* calls simply have a weird syntax in AT&T
|
||||||
* assembly... */
|
* assembly... */
|
||||||
switch (rtype(i.arg[0])) {
|
switch (rtype(i.arg[0])) {
|
||||||
case RCon:
|
case RCon:
|
||||||
|
con = &e->fn->con[i.arg[0].val];
|
||||||
fprintf(e->f, "\tcallq ");
|
fprintf(e->f, "\tcallq ");
|
||||||
emitcon(&e->fn->con[i.arg[0].val], e);
|
emitcon(con, e);
|
||||||
|
if (con->type == CAddr
|
||||||
|
&& (con->sym.type & SExt)
|
||||||
|
&& !T.apple)
|
||||||
|
fprintf(e->f, "@plt");
|
||||||
fprintf(e->f, "\n");
|
fprintf(e->f, "\n");
|
||||||
break;
|
break;
|
||||||
case RTmp:
|
case RTmp:
|
||||||
|
|||||||
14
amd64/isel.c
14
amd64/isel.c
@ -120,8 +120,9 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
|
|||||||
r1 = newtmp("isel", Kl, fn);
|
r1 = newtmp("isel", Kl, fn);
|
||||||
emit(Oaddr, Kl, r1, SLOT(s), R);
|
emit(Oaddr, Kl, r1, SLOT(s), R);
|
||||||
}
|
}
|
||||||
else if (T.apple && hascon(r0, &c, fn)
|
else if (op != Ocall && hascon(r0, &c, fn)
|
||||||
&& c->type == CAddr && c->sym.type == SThr) {
|
&& c->type == CAddr && ((c->sym.type & SExt)
|
||||||
|
|| (T.apple && c->sym.type == SThr))) {
|
||||||
r1 = newtmp("isel", Kl, fn);
|
r1 = newtmp("isel", Kl, fn);
|
||||||
if (c->bits.i) {
|
if (c->bits.i) {
|
||||||
r2 = newtmp("isel", Kl, fn);
|
r2 = newtmp("isel", Kl, fn);
|
||||||
@ -131,16 +132,18 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
|
|||||||
emit(Oadd, Kl, r1, r2, r3);
|
emit(Oadd, Kl, r1, r2, r3);
|
||||||
} else
|
} else
|
||||||
r2 = r1;
|
r2 = r1;
|
||||||
|
if (T.apple && (c->sym.type & SThr)) {
|
||||||
emit(Ocopy, Kl, r2, TMP(RAX), R);
|
emit(Ocopy, Kl, r2, TMP(RAX), R);
|
||||||
r2 = newtmp("isel", Kl, fn);
|
r2 = newtmp("isel", Kl, fn);
|
||||||
r3 = newtmp("isel", Kl, fn);
|
r3 = newtmp("isel", Kl, fn);
|
||||||
emit(Ocall, 0, R, r3, CALL(17));
|
emit(Ocall, 0, R, r3, CALL(17));
|
||||||
emit(Ocopy, Kl, TMP(RDI), r2, R);
|
emit(Ocopy, Kl, TMP(RDI), r2, R);
|
||||||
emit(Oload, Kl, r3, r2, R);
|
emit(Oload, Kl, r3, r2, R);
|
||||||
|
}
|
||||||
cc = *c;
|
cc = *c;
|
||||||
cc.bits.i = 0;
|
cc.bits.i = 0;
|
||||||
r3 = newcon(&cc, fn);
|
r3 = newcon(&cc, fn);
|
||||||
emit(Oload, Kl, r2, r3, R);
|
emit(Oaddr, Kl, r2, r3, R);
|
||||||
if (rtype(r0) == RMem) {
|
if (rtype(r0) == RMem) {
|
||||||
m = &fn->mem[r0.val];
|
m = &fn->mem[r0.val];
|
||||||
m->offset.type = CUndef;
|
m->offset.type = CUndef;
|
||||||
@ -151,9 +154,8 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
|
|||||||
else if (!(isstore(op) && r == &i->arg[1])
|
else if (!(isstore(op) && r == &i->arg[1])
|
||||||
&& !isload(op) && op != Ocall && rtype(r0) == RCon
|
&& !isload(op) && op != Ocall && rtype(r0) == RCon
|
||||||
&& fn->con[r0.val].type == CAddr) {
|
&& fn->con[r0.val].type == CAddr) {
|
||||||
/* apple as does not support 32-bit
|
/* turn address operands into
|
||||||
* absolute addressing, use a rip-
|
* lea/mov instructions
|
||||||
* relative leaq instead
|
|
||||||
*/
|
*/
|
||||||
r1 = newtmp("isel", Kl, fn);
|
r1 = newtmp("isel", Kl, fn);
|
||||||
emit(Oaddr, Kl, r1, r0, R);
|
emit(Oaddr, Kl, r1, r0, R);
|
||||||
|
|||||||
14
arm64/emit.c
14
arm64/emit.c
@ -278,6 +278,10 @@ loadaddr(Con *c, char *rn, E *e)
|
|||||||
s = "\tadrp\tR, SO\n"
|
s = "\tadrp\tR, SO\n"
|
||||||
"\tadd\tR, R, #:lo12:SO\n";
|
"\tadd\tR, R, #:lo12:SO\n";
|
||||||
break;
|
break;
|
||||||
|
case SExtThr:
|
||||||
|
if (!T.apple)
|
||||||
|
die("extern thread unavailable on arm64");
|
||||||
|
/* fall through */
|
||||||
case SThr:
|
case SThr:
|
||||||
if (T.apple)
|
if (T.apple)
|
||||||
s = "\tadrp\tR, S@tlvppage\n"
|
s = "\tadrp\tR, S@tlvppage\n"
|
||||||
@ -287,6 +291,14 @@ loadaddr(Con *c, char *rn, E *e)
|
|||||||
"\tadd\tR, R, #:tprel_hi12:SO, lsl #12\n"
|
"\tadd\tR, R, #:tprel_hi12:SO, lsl #12\n"
|
||||||
"\tadd\tR, R, #:tprel_lo12_nc:SO\n";
|
"\tadd\tR, R, #:tprel_lo12_nc:SO\n";
|
||||||
break;
|
break;
|
||||||
|
case SExt:
|
||||||
|
if (T.apple)
|
||||||
|
s = "\tadrp\tR, S@gotpageO\n"
|
||||||
|
"\tldr\tR, [R, S@gotpageoffO]\n";
|
||||||
|
else
|
||||||
|
s = "\tadrp\tR, :got:SO\n"
|
||||||
|
"\tldr\tR, [R, #:got_lo12:SO]\n";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
l = str(c->sym.id);
|
l = str(c->sym.id);
|
||||||
@ -468,7 +480,7 @@ emitins(Ins *i, E *e)
|
|||||||
goto Table;
|
goto Table;
|
||||||
c = &e->fn->con[i->arg[0].val];
|
c = &e->fn->con[i->arg[0].val];
|
||||||
if (c->type != CAddr
|
if (c->type != CAddr
|
||||||
|| c->sym.type != SGlo
|
|| (c->sym.type & SThr)
|
||||||
|| c->bits.i)
|
|| c->bits.i)
|
||||||
die("invalid call argument");
|
die("invalid call argument");
|
||||||
l = str(c->sym.id);
|
l = str(c->sym.id);
|
||||||
|
|||||||
@ -80,7 +80,7 @@ fixarg(Ref *pr, int k, int phi, Fn *fn)
|
|||||||
c = &fn->con[r0.val];
|
c = &fn->con[r0.val];
|
||||||
if (T.apple
|
if (T.apple
|
||||||
&& c->type == CAddr
|
&& c->type == CAddr
|
||||||
&& c->sym.type == SThr) {
|
&& (c->sym.type & SThr)) {
|
||||||
r1 = newtmp("isel", Kl, fn);
|
r1 = newtmp("isel", Kl, fn);
|
||||||
*pr = r1;
|
*pr = r1;
|
||||||
if (c->bits.i) {
|
if (c->bits.i) {
|
||||||
|
|||||||
11
doc/il.txt
11
doc/il.txt
@ -181,6 +181,8 @@ by zero-extension, or by sign-extension.
|
|||||||
DYNCONST :=
|
DYNCONST :=
|
||||||
CONST
|
CONST
|
||||||
| 'thread' $IDENT # Thread-local symbol
|
| 'thread' $IDENT # Thread-local symbol
|
||||||
|
| 'extern' $IDENT # Extern symbol (GOT)
|
||||||
|
| 'extern' 'thread' $IDENT # Extern thread-local (initial-exec)
|
||||||
|
|
||||||
VAL :=
|
VAL :=
|
||||||
DYNCONST
|
DYNCONST
|
||||||
@ -225,6 +227,15 @@ When the `thread` keyword prefixes a symbol name, the
|
|||||||
symbol's numeric value is resolved at runtime in the
|
symbol's numeric value is resolved at runtime in the
|
||||||
thread-local storage.
|
thread-local storage.
|
||||||
|
|
||||||
|
When the `extern` keyword prefixes a symbol name, the
|
||||||
|
symbol is accessed indirectly through a table edited
|
||||||
|
by the dynamic linker (e.g., GOT/PLT). This enables
|
||||||
|
PIE/PIC code generation. When `extern` is combined
|
||||||
|
with `thread`, the symbol is accessed using the
|
||||||
|
initial-exec TLS model, suitable for thread-local
|
||||||
|
variables defined in shared objects available at
|
||||||
|
startup time (i.e., not loaded through dlopen).
|
||||||
|
|
||||||
Vals are used as arguments in regular, phi, and jump
|
Vals are used as arguments in regular, phi, and jump
|
||||||
instructions within function definitions. They are
|
instructions within function definitions. They are
|
||||||
either constants or function-scope temporaries.
|
either constants or function-scope temporaries.
|
||||||
|
|||||||
26
parse.c
26
parse.c
@ -57,6 +57,7 @@ enum Token {
|
|||||||
Thlt,
|
Thlt,
|
||||||
Texport,
|
Texport,
|
||||||
Tthread,
|
Tthread,
|
||||||
|
Textern,
|
||||||
Tcommon,
|
Tcommon,
|
||||||
Tfunc,
|
Tfunc,
|
||||||
Ttype,
|
Ttype,
|
||||||
@ -116,6 +117,7 @@ static char *kwmap[Ntok] = {
|
|||||||
[Thlt] = "hlt",
|
[Thlt] = "hlt",
|
||||||
[Texport] = "export",
|
[Texport] = "export",
|
||||||
[Tthread] = "thread",
|
[Tthread] = "thread",
|
||||||
|
[Textern] = "extern",
|
||||||
[Tcommon] = "common",
|
[Tcommon] = "common",
|
||||||
[Tfunc] = "function",
|
[Tfunc] = "function",
|
||||||
[Ttype] = "type",
|
[Ttype] = "type",
|
||||||
@ -427,11 +429,10 @@ static Ref
|
|||||||
parseref()
|
parseref()
|
||||||
{
|
{
|
||||||
Con c;
|
Con c;
|
||||||
|
int tok;
|
||||||
|
|
||||||
memset(&c, 0, sizeof c);
|
memset(&c, 0, sizeof c);
|
||||||
switch (next()) {
|
switch ((tok = next())) {
|
||||||
default:
|
|
||||||
return R;
|
|
||||||
case Ttmp:
|
case Ttmp:
|
||||||
return tmpref(tokval.str);
|
return tmpref(tokval.str);
|
||||||
case Tint:
|
case Tint:
|
||||||
@ -448,9 +449,20 @@ parseref()
|
|||||||
c.bits.d = tokval.fltd;
|
c.bits.d = tokval.fltd;
|
||||||
c.flt = 2;
|
c.flt = 2;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
for (;; tok=next()) {
|
||||||
|
switch (tok) {
|
||||||
|
case Textern:
|
||||||
|
c.sym.type |= SExt;
|
||||||
|
continue;
|
||||||
case Tthread:
|
case Tthread:
|
||||||
c.sym.type = SThr;
|
c.sym.type |= SThr;
|
||||||
expect(Tglo);
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (tok != Tglo)
|
||||||
|
return R;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
case Tglo:
|
case Tglo:
|
||||||
c.type = CAddr;
|
c.type = CAddr;
|
||||||
@ -1255,7 +1267,9 @@ printcon(Con *c, FILE *f)
|
|||||||
case CUndef:
|
case CUndef:
|
||||||
break;
|
break;
|
||||||
case CAddr:
|
case CAddr:
|
||||||
if (c->sym.type == SThr)
|
if (c->sym.type & SExt)
|
||||||
|
fprintf(f, "extern ");
|
||||||
|
if (c->sym.type & SThr)
|
||||||
fprintf(f, "thread ");
|
fprintf(f, "thread ");
|
||||||
fprintf(f, "$%s", str(c->sym.id));
|
fprintf(f, "$%s", str(c->sym.id));
|
||||||
if (c->bits.i)
|
if (c->bits.i)
|
||||||
|
|||||||
28
rv64/emit.c
28
rv64/emit.c
@ -131,7 +131,7 @@ slot(Ref r, Fn *fn)
|
|||||||
static void
|
static void
|
||||||
emitaddr(Con *c, FILE *f)
|
emitaddr(Con *c, FILE *f)
|
||||||
{
|
{
|
||||||
assert(c->sym.type == SGlo);
|
assert((c->sym.type & ~SExt) == SGlo);
|
||||||
fputs(str(c->sym.id), f);
|
fputs(str(c->sym.id), f);
|
||||||
if (c->bits.i)
|
if (c->bits.i)
|
||||||
fprintf(f, "+%"PRIi64, c->bits.i);
|
fprintf(f, "+%"PRIi64, c->bits.i);
|
||||||
@ -231,7 +231,20 @@ loadaddr(Con *c, char *rn, FILE *f)
|
|||||||
{
|
{
|
||||||
char off[32];
|
char off[32];
|
||||||
|
|
||||||
if (c->sym.type == SThr) {
|
switch (c->sym.type) {
|
||||||
|
case SGlo:
|
||||||
|
fprintf(f, "\tlui %s, %%hi(", rn);
|
||||||
|
emitaddr(c, f);
|
||||||
|
fprintf(f, ")\n\taddi %s, %s, %%lo(", rn, rn);
|
||||||
|
emitaddr(c, f);
|
||||||
|
fputs(")\n", f);
|
||||||
|
break;
|
||||||
|
case SExt:
|
||||||
|
fprintf(f, "\tla %s, ", rn);
|
||||||
|
emitaddr(c, f);
|
||||||
|
fputc('\n', f);
|
||||||
|
break;
|
||||||
|
case SThr:
|
||||||
if (c->bits.i)
|
if (c->bits.i)
|
||||||
sprintf(off, "+%"PRIi64, c->bits.i);
|
sprintf(off, "+%"PRIi64, c->bits.i);
|
||||||
else
|
else
|
||||||
@ -242,10 +255,9 @@ loadaddr(Con *c, char *rn, FILE *f)
|
|||||||
rn, rn, str(c->sym.id), off);
|
rn, rn, str(c->sym.id), off);
|
||||||
fprintf(f, "\taddi %s, %s, %%tprel_lo(%s)%s\n",
|
fprintf(f, "\taddi %s, %s, %%tprel_lo(%s)%s\n",
|
||||||
rn, rn, str(c->sym.id), off);
|
rn, rn, str(c->sym.id), off);
|
||||||
} else {
|
break;
|
||||||
fprintf(f, "\tla %s, ", rn);
|
case SExtThr:
|
||||||
emitaddr(c, f);
|
die("extern thread unavailable on rv64");
|
||||||
fputc('\n', f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +294,7 @@ fixmem(Ref *pr, Fn *fn, FILE *f)
|
|||||||
if (rtype(r) == RCon) {
|
if (rtype(r) == RCon) {
|
||||||
c = &fn->con[r.val];
|
c = &fn->con[r.val];
|
||||||
if (c->type == CAddr)
|
if (c->type == CAddr)
|
||||||
if (c->sym.type == SThr) {
|
if (c->sym.type != SGlo) {
|
||||||
loadcon(c, T6, Kl, f);
|
loadcon(c, T6, Kl, f);
|
||||||
*pr = TMP(T6);
|
*pr = TMP(T6);
|
||||||
}
|
}
|
||||||
@ -387,7 +399,7 @@ emitins(Ins *i, Fn *fn, FILE *f)
|
|||||||
case RCon:
|
case RCon:
|
||||||
con = &fn->con[i->arg[0].val];
|
con = &fn->con[i->arg[0].val];
|
||||||
if (con->type != CAddr
|
if (con->type != CAddr
|
||||||
|| con->sym.type != SGlo
|
|| (con->sym.type & SThr)
|
||||||
|| con->bits.i)
|
|| con->bits.i)
|
||||||
goto Invalid;
|
goto Invalid;
|
||||||
fprintf(f, "\tcall %s\n", str(con->sym.id));
|
fprintf(f, "\tcall %s\n", str(con->sym.id));
|
||||||
|
|||||||
@ -29,7 +29,7 @@ char *tok[] = {
|
|||||||
"function", "type", "data", "section", "align", "dbgfile",
|
"function", "type", "data", "section", "align", "dbgfile",
|
||||||
"blit", "l", "w", "sh", "uh", "h", "sb", "ub", "b",
|
"blit", "l", "w", "sh", "uh", "h", "sb", "ub", "b",
|
||||||
"d", "s", "z", "loadw", "loadl", "loads", "loadd",
|
"d", "s", "z", "loadw", "loadl", "loads", "loadd",
|
||||||
"alloc1", "alloc2", "thread", "common",
|
"alloc1", "alloc2", "thread", "extern", "common",
|
||||||
|
|
||||||
};
|
};
|
||||||
enum {
|
enum {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user