From 4a3741bf02eb51c377312bdabc979e5ccbf5bf89 Mon Sep 17 00:00:00 2001 From: grischka Date: Sun, 20 Nov 2016 14:50:56 +0100 Subject: [PATCH] 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. --- Makefile | 2 +- i386-asm.c | 75 +++++++++++++++++++++++++++++++------------------ i386-tok.h | 1 + tccasm.c | 6 ++-- tccrun.c | 2 ++ tests/tcctest.c | 28 ++++++++++++++++-- 6 files changed, 81 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 4370a31..10e0cd6 100644 --- a/Makefile +++ b/Makefile @@ -176,7 +176,7 @@ $(ARM64_CROSS): $(ARM64_FILES) # libtcc generation and test ifndef ONE_SOURCE 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 LIBTCC_OBJ = libtcc.o LIBTCC_INC = $(NATIVE_FILES) diff --git a/i386-asm.c b/i386-asm.c index b25bd42..b875aad 100644 --- a/i386-asm.c +++ b/i386-asm.c @@ -276,6 +276,8 @@ static int asm_parse_reg(int *type) *type = OP_EA32; } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { reg = tok - TOK_ASM_rax; + } else if (tok == TOK_ASM_rip) { + reg = 8; #endif } else { error_32: @@ -444,7 +446,7 @@ static void gen_disp32(ExprValue *pe) } /* 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; @@ -459,6 +461,13 @@ static inline void asm_modrm(int reg, Operand *op) g(0x05 + (reg << 3)); #endif 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 { sib_reg1 = op->reg; /* fist compute displacement encoding */ @@ -491,12 +500,13 @@ static inline void asm_modrm(int reg, Operand *op) gen_expr32(&op->e); } } + return 0; } ST_FUNC void asm_opcode(TCCState *s1, int opcode) { 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; Operand ops[MAX_OPERANDS], *pop; int op_type[3]; /* decoded op type */ @@ -802,6 +812,8 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) } else { reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; } + + pc = 0; if (pa->instr_type & OPC_MODRM) { /* first look for an ea operand */ for(i = 0;i < nb_ops; i++) { @@ -828,8 +840,7 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) break; } } - - asm_modrm(reg, &ops[modrm_index]); + pc = asm_modrm(reg, &ops[modrm_index]); } /* emit constants */ @@ -858,30 +869,29 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) else 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 (ops[i].e.sym) - goto error_relocate; g(ops[i].e.v); } else if (v & OP_IM16) { - if (ops[i].e.sym) - 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 { + gen_le16(ops[i].e.v); #ifdef TCC_TARGET_X86_64 - if (v & OP_IM64) - gen_expr64(&ops[i].e); - else + } else if (v & OP_IM64) { + gen_expr64(&ops[i].e); #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 @@ -1189,17 +1199,20 @@ ST_FUNC void subst_asm_operand(CString *add_str, cstr_ccat(add_str, '$'); if (r & VT_SYM) { cstr_cat(add_str, get_tok_str(sv->sym->v, NULL), -1); - if ((uint32_t)sv->c.i != 0) { - cstr_ccat(add_str, '+'); - } else { - return; - } + if ((uint32_t)sv->c.i == 0) + goto no_offset; + cstr_ccat(add_str, '+'); } val = sv->c.i; if (modifier == 'n') val = -val; snprintf(buf, sizeof(buf), "%d", (int)sv->c.i); 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) { #ifdef TCC_TARGET_X86_64 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) tcc_error("internal compiler error"); 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); } else { /* register case */ @@ -1244,6 +1262,8 @@ ST_FUNC void subst_asm_operand(CString *add_str, size = -1; } else if (modifier == 'w') { size = 2; + } else if (modifier == 'k') { + size = 4; #ifdef TCC_TARGET_X86_64 } else if (modifier == 'q') { size = 8; @@ -1311,7 +1331,8 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, output cases) */ SValue sv; 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); } else if (i >= nb_outputs || op->is_rw) { /* load value in register */ diff --git a/i386-tok.h b/i386-tok.h index fde386f..17d17ce 100644 --- a/i386-tok.h +++ b/i386-tok.h @@ -35,6 +35,7 @@ DEF_ASM(rbp) DEF_ASM(rsi) DEF_ASM(rdi) + DEF_ASM(rip) #endif DEF_ASM(mm0) DEF_ASM(mm1) diff --git a/tccasm.c b/tccasm.c index 911fd48..cff4b98 100644 --- a/tccasm.c +++ b/tccasm.c @@ -977,7 +977,8 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands, } modifier = 0; if (*str == 'c' || *str == 'n' || - *str == 'b' || *str == 'w' || *str == 'h') + *str == 'b' || *str == 'w' || + *str == 'h' || *str == 'k') modifier = *str++; index = find_constraint(operands, nb_operands, str, &str); if (index < 0) @@ -1029,7 +1030,8 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, skip('('); gexpr(); if (is_output) { - test_lvalue(); + if (!(vtop->type.t & VT_ARRAY)) + test_lvalue(); } else { /* we want to avoid LLOCAL case, except when the 'm' constraint is used. Note that it may come from diff --git a/tccrun.c b/tccrun.c index 5575eb5..37e9303 100644 --- a/tccrun.c +++ b/tccrun.c @@ -52,6 +52,8 @@ static void *win64_add_function_table(TCCState *s1); static void win64_del_function_table(void *); #endif +// #define HAVE_SELINUX + /* ------------------------------------------------------------- */ /* Do all relocations (needed before using tcc_get_symbol()) Returns -1 on error. */ diff --git a/tests/tcctest.c b/tests/tcctest.c index abe15f8..5851fb4 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -2565,12 +2565,33 @@ static __inline__ unsigned long long inc64(unsigned long long a) 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; void asm_test(void) { char buf[128]; unsigned int val; + struct struct123 s1; + struct struct1231 s2 = { (unsigned long)&s1 }; printf("inline asm:\n"); @@ -2592,6 +2613,10 @@ void asm_test(void) printf("mul64=0x%Lx\n", mul64(0x12345678, 0xabcd1234)); 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; sigdelset1(&set, 2); sigaddset1(&set, 16); @@ -2600,9 +2625,6 @@ void asm_test(void) goto label1; label2: __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); val = 0x01020304; printf("swab32(0x%08x) = 0x%0x\n", val, swab32(val));