x86_64-asm: =m operand fixes

The problem was with tcctest.c:

    unsigned set;
    __asm__("btsl %1,%0" : "=m"(set) : "Ir"(20) : "cc");

when with tcc compiled with the HAVE_SELINUX option, run with
tcc -run, it would use large addresses far beyond the 32bits
range when tcc did not use the pc-relative mode for accessing
'set' in global data memory.  In fact the assembler did not
know about %rip at all.

Changes:
- memory operands use (%rax) not (%eax)
- conversion from VT_LLOCAL: use type VT_PTR
- support 'k' modifier
- support %rip register
- support X(%rip) pc-relative addresses

The test in tcctest.c is from Michael Matz.
master
grischka 2016-11-20 14:50:56 +01:00
parent 47fd807f9b
commit 4a3741bf02
6 changed files with 81 additions and 33 deletions

View File

@ -176,7 +176,7 @@ $(ARM64_CROSS): $(ARM64_FILES)
# libtcc generation and test # libtcc generation and test
ifndef ONE_SOURCE ifndef ONE_SOURCE
LIBTCC_OBJ = $(filter-out tcc.o,$(patsubst %.c,%.o,$(filter %.c,$(NATIVE_FILES)))) LIBTCC_OBJ = $(filter-out tcc.o,$(patsubst %.c,%.o,$(filter %.c,$(NATIVE_FILES))))
LIBTCC_INC = $(filter %.h,$(CORE_FILES)) $(filter-out $(CORE_FILES),$(NATIVE_FILES)) LIBTCC_INC = $(filter %.h,$(CORE_FILES)) $(filter-out $(CORE_FILES) i386-asm.c,$(NATIVE_FILES))
else else
LIBTCC_OBJ = libtcc.o LIBTCC_OBJ = libtcc.o
LIBTCC_INC = $(NATIVE_FILES) LIBTCC_INC = $(NATIVE_FILES)

View File

@ -276,6 +276,8 @@ static int asm_parse_reg(int *type)
*type = OP_EA32; *type = OP_EA32;
} else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
reg = tok - TOK_ASM_rax; reg = tok - TOK_ASM_rax;
} else if (tok == TOK_ASM_rip) {
reg = 8;
#endif #endif
} else { } else {
error_32: error_32:
@ -444,7 +446,7 @@ static void gen_disp32(ExprValue *pe)
} }
/* generate the modrm operand */ /* generate the modrm operand */
static inline void asm_modrm(int reg, Operand *op) static inline int asm_modrm(int reg, Operand *op)
{ {
int mod, reg1, reg2, sib_reg1; int mod, reg1, reg2, sib_reg1;
@ -459,6 +461,13 @@ static inline void asm_modrm(int reg, Operand *op)
g(0x05 + (reg << 3)); g(0x05 + (reg << 3));
#endif #endif
gen_expr32(&op->e); gen_expr32(&op->e);
#ifdef TCC_TARGET_X86_64
} else if (op->reg == 8) {
ExprValue *pe = &op->e;
g(0x05 + (reg << 3));
gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
return ind;
#endif
} else { } else {
sib_reg1 = op->reg; sib_reg1 = op->reg;
/* fist compute displacement encoding */ /* fist compute displacement encoding */
@ -491,12 +500,13 @@ static inline void asm_modrm(int reg, Operand *op)
gen_expr32(&op->e); gen_expr32(&op->e);
} }
} }
return 0;
} }
ST_FUNC void asm_opcode(TCCState *s1, int opcode) ST_FUNC void asm_opcode(TCCState *s1, int opcode)
{ {
const ASMInstr *pa; const ASMInstr *pa;
int i, modrm_index, reg, v, op1, seg_prefix; int i, modrm_index, reg, v, op1, seg_prefix, pc;
int nb_ops, s; int nb_ops, s;
Operand ops[MAX_OPERANDS], *pop; Operand ops[MAX_OPERANDS], *pop;
int op_type[3]; /* decoded op type */ int op_type[3]; /* decoded op type */
@ -802,6 +812,8 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
} else { } else {
reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
} }
pc = 0;
if (pa->instr_type & OPC_MODRM) { if (pa->instr_type & OPC_MODRM) {
/* first look for an ea operand */ /* first look for an ea operand */
for(i = 0;i < nb_ops; i++) { for(i = 0;i < nb_ops; i++) {
@ -828,8 +840,7 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
break; break;
} }
} }
pc = asm_modrm(reg, &ops[modrm_index]);
asm_modrm(reg, &ops[modrm_index]);
} }
/* emit constants */ /* emit constants */
@ -858,30 +869,29 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode)
else else
v = OP_IM64; v = OP_IM64;
} }
if ((v & (OP_IM8 | OP_IM8S | OP_IM16)) && ops[i].e.sym)
tcc_error("cannot relocate");
if (v & (OP_IM8 | OP_IM8S)) { if (v & (OP_IM8 | OP_IM8S)) {
if (ops[i].e.sym)
goto error_relocate;
g(ops[i].e.v); g(ops[i].e.v);
} else if (v & OP_IM16) { } else if (v & OP_IM16) {
if (ops[i].e.sym) gen_le16(ops[i].e.v);
error_relocate:
tcc_error("cannot relocate");
else
gen_le16(ops[i].e.v);
} else {
if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
gen_disp32(&ops[i].e);
} else {
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
if (v & OP_IM64) } else if (v & OP_IM64) {
gen_expr64(&ops[i].e); gen_expr64(&ops[i].e);
else
#endif #endif
gen_expr32(&ops[i].e); } else if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
} gen_disp32(&ops[i].e);
} else {
gen_expr32(&ops[i].e);
} }
} }
} }
/* after immediate operands, adjust pc-relative address */
if (pc)
*(int*)(text_section->data + pc - 4) -= ind - pc;
} }
/* return the constraint priority (we allocate first the lowest /* return the constraint priority (we allocate first the lowest
@ -1189,17 +1199,20 @@ ST_FUNC void subst_asm_operand(CString *add_str,
cstr_ccat(add_str, '$'); cstr_ccat(add_str, '$');
if (r & VT_SYM) { if (r & VT_SYM) {
cstr_cat(add_str, get_tok_str(sv->sym->v, NULL), -1); cstr_cat(add_str, get_tok_str(sv->sym->v, NULL), -1);
if ((uint32_t)sv->c.i != 0) { if ((uint32_t)sv->c.i == 0)
cstr_ccat(add_str, '+'); goto no_offset;
} else { cstr_ccat(add_str, '+');
return;
}
} }
val = sv->c.i; val = sv->c.i;
if (modifier == 'n') if (modifier == 'n')
val = -val; val = -val;
snprintf(buf, sizeof(buf), "%d", (int)sv->c.i); snprintf(buf, sizeof(buf), "%d", (int)sv->c.i);
cstr_cat(add_str, buf, -1); cstr_cat(add_str, buf, -1);
no_offset:;
#ifdef TCC_TARGET_X86_64
if (r & VT_LVAL)
cstr_cat(add_str, "(%rip)", -1);
#endif
} else if ((r & VT_VALMASK) == VT_LOCAL) { } else if ((r & VT_VALMASK) == VT_LOCAL) {
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
snprintf(buf, sizeof(buf), "%d(%%rbp)", (int)sv->c.i); snprintf(buf, sizeof(buf), "%d(%%rbp)", (int)sv->c.i);
@ -1212,7 +1225,12 @@ ST_FUNC void subst_asm_operand(CString *add_str,
if (reg >= VT_CONST) if (reg >= VT_CONST)
tcc_error("internal compiler error"); tcc_error("internal compiler error");
snprintf(buf, sizeof(buf), "(%%%s)", snprintf(buf, sizeof(buf), "(%%%s)",
get_tok_str(TOK_ASM_eax + reg, NULL)); #ifdef TCC_TARGET_X86_64
get_tok_str(TOK_ASM_rax + reg, NULL)
#else
get_tok_str(TOK_ASM_eax + reg, NULL)
#endif
);
cstr_cat(add_str, buf, -1); cstr_cat(add_str, buf, -1);
} else { } else {
/* register case */ /* register case */
@ -1244,6 +1262,8 @@ ST_FUNC void subst_asm_operand(CString *add_str,
size = -1; size = -1;
} else if (modifier == 'w') { } else if (modifier == 'w') {
size = 2; size = 2;
} else if (modifier == 'k') {
size = 4;
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
} else if (modifier == 'q') { } else if (modifier == 'q') {
size = 8; size = 8;
@ -1311,7 +1331,8 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
output cases) */ output cases) */
SValue sv; SValue sv;
sv = *op->vt; sv = *op->vt;
sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
sv.type.t = VT_PTR;
load(op->reg, &sv); load(op->reg, &sv);
} else if (i >= nb_outputs || op->is_rw) { } else if (i >= nb_outputs || op->is_rw) {
/* load value in register */ /* load value in register */

View File

@ -35,6 +35,7 @@
DEF_ASM(rbp) DEF_ASM(rbp)
DEF_ASM(rsi) DEF_ASM(rsi)
DEF_ASM(rdi) DEF_ASM(rdi)
DEF_ASM(rip)
#endif #endif
DEF_ASM(mm0) DEF_ASM(mm0)
DEF_ASM(mm1) DEF_ASM(mm1)

View File

@ -977,7 +977,8 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands,
} }
modifier = 0; modifier = 0;
if (*str == 'c' || *str == 'n' || if (*str == 'c' || *str == 'n' ||
*str == 'b' || *str == 'w' || *str == 'h') *str == 'b' || *str == 'w' ||
*str == 'h' || *str == 'k')
modifier = *str++; modifier = *str++;
index = find_constraint(operands, nb_operands, str, &str); index = find_constraint(operands, nb_operands, str, &str);
if (index < 0) if (index < 0)
@ -1029,7 +1030,8 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
skip('('); skip('(');
gexpr(); gexpr();
if (is_output) { if (is_output) {
test_lvalue(); if (!(vtop->type.t & VT_ARRAY))
test_lvalue();
} else { } else {
/* we want to avoid LLOCAL case, except when the 'm' /* we want to avoid LLOCAL case, except when the 'm'
constraint is used. Note that it may come from constraint is used. Note that it may come from

View File

@ -52,6 +52,8 @@ static void *win64_add_function_table(TCCState *s1);
static void win64_del_function_table(void *); static void win64_del_function_table(void *);
#endif #endif
// #define HAVE_SELINUX
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
/* Do all relocations (needed before using tcc_get_symbol()) /* Do all relocations (needed before using tcc_get_symbol())
Returns -1 on error. */ Returns -1 on error. */

View File

@ -2565,12 +2565,33 @@ static __inline__ unsigned long long inc64(unsigned long long a)
return res; return res;
} }
struct struct123 {
int a;
int b;
};
struct struct1231 {
unsigned long addr;
};
unsigned long mconstraint_test(struct struct1231 *r)
{
unsigned long ret;
unsigned int a[2];
a[0] = 0;
__asm__ volatile ("lea %2,%0; movl 4(%0),%k0; addl %2,%k0; movl $51,%2; movl $52,4%2; movl $63,%1"
: "=&r" (ret), "=m" (a)
: "m" (*(struct struct123 *)r->addr));
return ret + a[0];
}
unsigned int set; unsigned int set;
void asm_test(void) void asm_test(void)
{ {
char buf[128]; char buf[128];
unsigned int val; unsigned int val;
struct struct123 s1;
struct struct1231 s2 = { (unsigned long)&s1 };
printf("inline asm:\n"); printf("inline asm:\n");
@ -2592,6 +2613,10 @@ void asm_test(void)
printf("mul64=0x%Lx\n", mul64(0x12345678, 0xabcd1234)); printf("mul64=0x%Lx\n", mul64(0x12345678, 0xabcd1234));
printf("inc64=0x%Lx\n", inc64(0x12345678ffffffff)); printf("inc64=0x%Lx\n", inc64(0x12345678ffffffff));
s1.a = 42;
s1.b = 43;
printf("mconstraint: %d", mconstraint_test(&s2));
printf(" %d %d\n", s1.a, s1.b);
set = 0xff; set = 0xff;
sigdelset1(&set, 2); sigdelset1(&set, 2);
sigaddset1(&set, 16); sigaddset1(&set, 16);
@ -2600,9 +2625,6 @@ void asm_test(void)
goto label1; goto label1;
label2: label2:
__asm__("btsl %1,%0" : "=m"(set) : "Ir"(20) : "cc"); __asm__("btsl %1,%0" : "=m"(set) : "Ir"(20) : "cc");
#ifdef __GNUC__ // works strange with GCC 4.3
set=0x1080fd;
#endif
printf("set=0x%x\n", set); printf("set=0x%x\n", set);
val = 0x01020304; val = 0x01020304;
printf("swab32(0x%08x) = 0x%0x\n", val, swab32(val)); printf("swab32(0x%08x) = 0x%0x\n", val, swab32(val));