mirror of
https://github.com/mirror/tinycc.git
synced 2025-03-02 08:20:06 +08:00
Generate PIC code so that we can create shared objects properly.
- Add got_table in TCCState. This approach is naive and the distance between executable code and GOT can be longer than 32bit. - Handle R_X86_64_GOTPCREL properly. We use got_table for TCC_OUTPUT_MEMORY case for now. - Fix load() and store() so that they access global variables via GOT.
This commit is contained in:
parent
6c10429aa5
commit
830b7533c9
7
tcc.c
7
tcc.c
@ -544,9 +544,12 @@ struct TCCState {
|
|||||||
FILE *outfile;
|
FILE *outfile;
|
||||||
|
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
/* buffer to store jump tables */
|
/* buffer to store jump tables used when the output is memory */
|
||||||
char *jmp_table;
|
char *jmp_table;
|
||||||
int jmp_table_num;
|
int jmp_table_num;
|
||||||
|
/* buffer to store got tables used when the output is memory */
|
||||||
|
void **got_table;
|
||||||
|
int got_table_num;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -10505,6 +10508,7 @@ TCCState *tcc_new(void)
|
|||||||
|
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
s->jmp_table = NULL;
|
s->jmp_table = NULL;
|
||||||
|
s->got_table = NULL;
|
||||||
#endif
|
#endif
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@ -10544,6 +10548,7 @@ void tcc_delete(TCCState *s1)
|
|||||||
|
|
||||||
#ifdef TCC_TARGET_X86_64
|
#ifdef TCC_TARGET_X86_64
|
||||||
tcc_free(s1->jmp_table);
|
tcc_free(s1->jmp_table);
|
||||||
|
tcc_free(s1->got_table);
|
||||||
#endif
|
#endif
|
||||||
tcc_free(s1);
|
tcc_free(s1);
|
||||||
}
|
}
|
||||||
|
29
tccelf.c
29
tccelf.c
@ -502,6 +502,25 @@ static unsigned long add_jmp_table(TCCState *s1, unsigned long val)
|
|||||||
*(unsigned long *)(p + 6) = val;
|
*(unsigned long *)(p + 6) = val;
|
||||||
return (unsigned long)p;
|
return (unsigned long)p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define GOT_TABLE_ENTRY_MAX_NUM 4096
|
||||||
|
static unsigned long add_got_table(TCCState *s1, unsigned long val)
|
||||||
|
{
|
||||||
|
unsigned long *p;
|
||||||
|
if (!s1->got_table) {
|
||||||
|
int size = sizeof(void *) * GOT_TABLE_ENTRY_MAX_NUM;
|
||||||
|
s1->got_table_num = 0;
|
||||||
|
s1->got_table = (char *)tcc_malloc(size);
|
||||||
|
}
|
||||||
|
if (s1->got_table_num == GOT_TABLE_ENTRY_MAX_NUM) {
|
||||||
|
error("relocating >%d symbols are not supported",
|
||||||
|
GOT_TABLE_ENTRY_MAX_NUM);
|
||||||
|
}
|
||||||
|
p = s1->got_table + s1->got_table_num;
|
||||||
|
s1->got_table_num++;
|
||||||
|
*p = val;
|
||||||
|
return (unsigned long)p;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* relocate a given section (CPU dependent) */
|
/* relocate a given section (CPU dependent) */
|
||||||
@ -727,11 +746,13 @@ static void relocate_section(TCCState *s1, Section *s)
|
|||||||
*(int *)ptr = val;
|
*(int *)ptr = val;
|
||||||
break;
|
break;
|
||||||
case R_X86_64_GOTPCREL:
|
case R_X86_64_GOTPCREL:
|
||||||
*(int *)ptr += s1->got->sh_addr - addr;
|
if (s1->output_type == TCC_OUTPUT_MEMORY) {
|
||||||
/* XXX: is this OK? */
|
val = add_got_table(s1, val - rel->r_addend) + rel->r_addend;
|
||||||
if (s1->output_type == TCC_OUTPUT_DLL) {
|
*(int *)ptr += val - addr;
|
||||||
*(int *)ptr += s1->got_offsets[sym_index] - 4;
|
break;
|
||||||
}
|
}
|
||||||
|
*(int *)ptr += (s1->got->sh_addr - addr +
|
||||||
|
s1->got_offsets[sym_index] - 4);
|
||||||
break;
|
break;
|
||||||
case R_X86_64_GOTTPOFF:
|
case R_X86_64_GOTTPOFF:
|
||||||
*(int *)ptr += val - s1->got->sh_addr;
|
*(int *)ptr += val - s1->got->sh_addr;
|
||||||
|
126
x86_64-gen.c
126
x86_64-gen.c
@ -53,9 +53,11 @@ enum {
|
|||||||
|
|
||||||
TREG_XMM0 = 3,
|
TREG_XMM0 = 3,
|
||||||
TREG_ST0 = 4,
|
TREG_ST0 = 4,
|
||||||
|
|
||||||
|
TREG_MEM = 0x10,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define REX_BASE(reg) ((reg) >> 3)
|
#define REX_BASE(reg) (((reg) >> 3) & 1)
|
||||||
#define REG_VALUE(reg) ((reg) & 7)
|
#define REG_VALUE(reg) ((reg) & 7)
|
||||||
|
|
||||||
int reg_classes[NB_REGS] = {
|
int reg_classes[NB_REGS] = {
|
||||||
@ -205,15 +207,37 @@ static void gen_addrpc32(int r, Sym *sym, int c)
|
|||||||
gen_le32(c-4);
|
gen_le32(c-4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* generate a modrm reference. 'op_reg' contains the addtionnal 3
|
/* output got address with relocation */
|
||||||
opcode bits */
|
static void gen_gotpcrel(int r, Sym *sym, int c)
|
||||||
static void gen_modrm(int op_reg, int r, Sym *sym, int c)
|
|
||||||
{
|
{
|
||||||
op_reg = op_reg << 3;
|
Section *sr;
|
||||||
|
ElfW(Rela) *rel;
|
||||||
|
greloc(cur_text_section, sym, ind, R_X86_64_GOTPCREL);
|
||||||
|
sr = cur_text_section->reloc;
|
||||||
|
rel = (ElfW(Rela) *)(sr->data + sr->data_offset - sizeof(ElfW(Rela)));
|
||||||
|
rel->r_addend = -4;
|
||||||
|
gen_le32(0);
|
||||||
|
|
||||||
|
if (c) {
|
||||||
|
/* we use add c, %xxx for displacement */
|
||||||
|
o(0x48 + REX_BASE(r));
|
||||||
|
o(0x81);
|
||||||
|
o(0xc0 + REG_VALUE(r));
|
||||||
|
gen_le32(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got)
|
||||||
|
{
|
||||||
|
op_reg = REG_VALUE(op_reg) << 3;
|
||||||
if ((r & VT_VALMASK) == VT_CONST) {
|
if ((r & VT_VALMASK) == VT_CONST) {
|
||||||
/* constant memory reference */
|
/* constant memory reference */
|
||||||
o(0x05 | op_reg);
|
o(0x05 | op_reg);
|
||||||
gen_addrpc32(r, sym, c);
|
if (is_got) {
|
||||||
|
gen_gotpcrel(r, sym, c);
|
||||||
|
} else {
|
||||||
|
gen_addrpc32(r, sym, c);
|
||||||
|
}
|
||||||
} else if ((r & VT_VALMASK) == VT_LOCAL) {
|
} else if ((r & VT_VALMASK) == VT_LOCAL) {
|
||||||
/* currently, we use only ebp as base */
|
/* currently, we use only ebp as base */
|
||||||
if (c == (char)c) {
|
if (c == (char)c) {
|
||||||
@ -223,15 +247,30 @@ static void gen_modrm(int op_reg, int r, Sym *sym, int c)
|
|||||||
} else {
|
} else {
|
||||||
oad(0x85 | op_reg, c);
|
oad(0x85 | op_reg, c);
|
||||||
}
|
}
|
||||||
|
} else if ((r & VT_VALMASK) >= TREG_MEM) {
|
||||||
|
if (c) {
|
||||||
|
g(0x80 | op_reg | REG_VALUE(r));
|
||||||
|
gen_le32(c);
|
||||||
|
} else {
|
||||||
|
g(0x00 | op_reg | REG_VALUE(r));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
g(0x00 | op_reg | (r & VT_VALMASK));
|
g(0x00 | op_reg | (r & VT_VALMASK));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* generate a modrm reference. 'op_reg' contains the addtionnal 3
|
||||||
|
opcode bits */
|
||||||
|
static void gen_modrm(int op_reg, int r, Sym *sym, int c)
|
||||||
|
{
|
||||||
|
gen_modrm_impl(op_reg, r, sym, c, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* generate a modrm reference. 'op_reg' contains the addtionnal 3
|
/* generate a modrm reference. 'op_reg' contains the addtionnal 3
|
||||||
opcode bits */
|
opcode bits */
|
||||||
static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c)
|
static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c)
|
||||||
{
|
{
|
||||||
|
int is_got;
|
||||||
int rex = 0x48 | (REX_BASE(op_reg) << 2);
|
int rex = 0x48 | (REX_BASE(op_reg) << 2);
|
||||||
if ((r & VT_VALMASK) != VT_CONST &&
|
if ((r & VT_VALMASK) != VT_CONST &&
|
||||||
(r & VT_VALMASK) != VT_LOCAL) {
|
(r & VT_VALMASK) != VT_LOCAL) {
|
||||||
@ -239,23 +278,8 @@ static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c)
|
|||||||
}
|
}
|
||||||
o(rex);
|
o(rex);
|
||||||
o(opcode);
|
o(opcode);
|
||||||
op_reg = REG_VALUE(op_reg) << 3;
|
is_got = (op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC);
|
||||||
if ((r & VT_VALMASK) == VT_CONST) {
|
gen_modrm_impl(op_reg, r, sym, c, is_got);
|
||||||
/* constant memory reference */
|
|
||||||
o(0x05 | op_reg);
|
|
||||||
gen_addrpc32(r, sym, c);
|
|
||||||
} else if ((r & VT_VALMASK) == VT_LOCAL) {
|
|
||||||
/* currently, we use only ebp as base */
|
|
||||||
if (c == (char)c) {
|
|
||||||
/* short reference */
|
|
||||||
o(0x45 | op_reg);
|
|
||||||
g(c);
|
|
||||||
} else {
|
|
||||||
oad(0x85 | op_reg, c);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
g(0x00 | op_reg | (r & VT_VALMASK));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -269,6 +293,21 @@ void load(int r, SValue *sv)
|
|||||||
ft = sv->type.t;
|
ft = sv->type.t;
|
||||||
fc = sv->c.ul;
|
fc = sv->c.ul;
|
||||||
|
|
||||||
|
/* we use indirect access via got */
|
||||||
|
if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) &&
|
||||||
|
(fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) {
|
||||||
|
/* use the result register as a temporal register */
|
||||||
|
int tr = r | TREG_MEM;
|
||||||
|
if (is_float(ft)) {
|
||||||
|
/* we cannot use float registers as a temporal register */
|
||||||
|
tr = get_reg(RC_INT) | TREG_MEM;
|
||||||
|
}
|
||||||
|
gen_modrm64(0x8b, tr, fr, sv->sym, 0);
|
||||||
|
|
||||||
|
/* load from the temporal register */
|
||||||
|
fr = tr | VT_LVAL;
|
||||||
|
}
|
||||||
|
|
||||||
v = fr & VT_VALMASK;
|
v = fr & VT_VALMASK;
|
||||||
if (fr & VT_LVAL) {
|
if (fr & VT_LVAL) {
|
||||||
if (v == VT_LLOCAL) {
|
if (v == VT_LLOCAL) {
|
||||||
@ -305,14 +344,21 @@ void load(int r, SValue *sv)
|
|||||||
} else {
|
} else {
|
||||||
if (v == VT_CONST) {
|
if (v == VT_CONST) {
|
||||||
if ((ft & VT_BTYPE) == VT_LLONG) {
|
if ((ft & VT_BTYPE) == VT_LLONG) {
|
||||||
|
assert(!(fr & VT_SYM));
|
||||||
o(0x48);
|
o(0x48);
|
||||||
o(0xb8 + REG_VALUE(r)); /* mov $xx, r */
|
o(0xb8 + REG_VALUE(r)); /* mov $xx, r */
|
||||||
gen_addr64(fr, sv->sym, sv->c.ull);
|
gen_addr64(fr, sv->sym, sv->c.ull);
|
||||||
} else {
|
} else {
|
||||||
if (fr & VT_SYM) {
|
if (fr & VT_SYM) {
|
||||||
o(0x8d48);
|
if (sv->sym->type.t & VT_STATIC) {
|
||||||
o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */
|
o(0x8d48);
|
||||||
gen_addrpc32(fr, sv->sym, fc);
|
o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */
|
||||||
|
gen_addrpc32(fr, sv->sym, fc);
|
||||||
|
} else {
|
||||||
|
o(0x8b48);
|
||||||
|
o(0x05 + REG_VALUE(r) * 8); /* mov xx(%rip), r */
|
||||||
|
gen_gotpcrel(r, sv->sym, fc);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
o(0xb8 + REG_VALUE(r)); /* mov $xx, r */
|
o(0xb8 + REG_VALUE(r)); /* mov $xx, r */
|
||||||
gen_le32(fc);
|
gen_le32(fc);
|
||||||
@ -362,25 +408,42 @@ void store(int r, SValue *v)
|
|||||||
{
|
{
|
||||||
int fr, bt, ft, fc;
|
int fr, bt, ft, fc;
|
||||||
int op64 = 0;
|
int op64 = 0;
|
||||||
|
/* store the REX prefix in this variable when PIC is enabled */
|
||||||
|
int pic = 0;
|
||||||
|
|
||||||
ft = v->type.t;
|
ft = v->type.t;
|
||||||
fc = v->c.ul;
|
fc = v->c.ul;
|
||||||
fr = v->r & VT_VALMASK;
|
fr = v->r & VT_VALMASK;
|
||||||
bt = ft & VT_BTYPE;
|
bt = ft & VT_BTYPE;
|
||||||
|
|
||||||
|
/* we need to access the variable via got */
|
||||||
|
if (fr == VT_CONST && (v->r & VT_SYM)) {
|
||||||
|
/* mov xx(%rip), %r11 */
|
||||||
|
o(0x1d8b4c);
|
||||||
|
gen_gotpcrel(TREG_R11, v->sym, v->c.ul);
|
||||||
|
pic = is64_type(bt) ? 0x49 : 0x41;
|
||||||
|
}
|
||||||
|
|
||||||
/* XXX: incorrect if float reg to reg */
|
/* XXX: incorrect if float reg to reg */
|
||||||
if (bt == VT_FLOAT) {
|
if (bt == VT_FLOAT) {
|
||||||
o(0x7e0f66); /* movd */
|
o(0x66);
|
||||||
|
o(pic);
|
||||||
|
o(0x7e0f); /* movd */
|
||||||
r = 0;
|
r = 0;
|
||||||
} else if (bt == VT_DOUBLE) {
|
} else if (bt == VT_DOUBLE) {
|
||||||
o(0xd60f66); /* movq */
|
o(0x66);
|
||||||
|
o(pic);
|
||||||
|
o(0xd60f); /* movq */
|
||||||
r = 0;
|
r = 0;
|
||||||
} else if (bt == VT_LDOUBLE) {
|
} else if (bt == VT_LDOUBLE) {
|
||||||
o(0xc0d9); /* fld %st(0) */
|
o(0xc0d9); /* fld %st(0) */
|
||||||
|
o(pic);
|
||||||
o(0xdb); /* fstpt */
|
o(0xdb); /* fstpt */
|
||||||
r = 7;
|
r = 7;
|
||||||
} else {
|
} else {
|
||||||
if (bt == VT_SHORT)
|
if (bt == VT_SHORT)
|
||||||
o(0x66);
|
o(0x66);
|
||||||
|
o(pic);
|
||||||
if (bt == VT_BYTE || bt == VT_BOOL)
|
if (bt == VT_BYTE || bt == VT_BOOL)
|
||||||
o(0x88);
|
o(0x88);
|
||||||
else if (is64_type(bt))
|
else if (is64_type(bt))
|
||||||
@ -388,7 +451,12 @@ void store(int r, SValue *v)
|
|||||||
else
|
else
|
||||||
o(0x89);
|
o(0x89);
|
||||||
}
|
}
|
||||||
if (op64) {
|
if (pic) {
|
||||||
|
/* xxx r, (%r11) where xxx is mov, movq, fld, or etc */
|
||||||
|
if (op64)
|
||||||
|
o(op64);
|
||||||
|
o(3 + (r << 3));
|
||||||
|
} else if (op64) {
|
||||||
if (fr == VT_CONST ||
|
if (fr == VT_CONST ||
|
||||||
fr == VT_LOCAL ||
|
fr == VT_LOCAL ||
|
||||||
(v->r & VT_LVAL)) {
|
(v->r & VT_LVAL)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user