From 8e4da42384164d6eb862665d907b3b1138da2f94 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Wed, 29 Jun 2016 15:57:32 +0200 Subject: [PATCH] Accept more asm expressions In particular subtracting a defined symbol from current section makes the value PC relative, and .org accepts symbolic expressions as well, if the symbol is from the current section. --- i386-asm.c | 14 ++++++++----- tcc.h | 1 + tccasm.c | 55 +++++++++++++++++++++++++++++++++---------------- tests/asmtest.S | 13 ++++++++++++ 4 files changed, 60 insertions(+), 23 deletions(-) diff --git a/i386-asm.c b/i386-asm.c index e7449bf..064b8aa 100644 --- a/i386-asm.c +++ b/i386-asm.c @@ -356,8 +356,7 @@ static void parse_operand(TCCState *s1, Operand *op) next(); asm_expr(s1, &e); op->type = OP_IM32; - op->e.v = e.v; - op->e.sym = e.sym; + op->e = e; if (!op->e.sym) { if (op->e.v == (uint8_t)op->e.v) op->type |= OP_IM8; @@ -378,8 +377,7 @@ static void parse_operand(TCCState *s1, Operand *op) op->shift = 0; if (tok != '(') { asm_expr(s1, &e); - op->e.v = e.v; - op->e.sym = e.sym; + op->e = e; } else { next(); if (tok == '%') { @@ -395,6 +393,7 @@ static void parse_operand(TCCState *s1, Operand *op) op->e.v = e.v; op->e.sym = e.sym; } + op->e.pcrel = 0; } if (tok == '(') { int type = 0; @@ -425,7 +424,12 @@ static void parse_operand(TCCState *s1, Operand *op) /* XXX: unify with C code output ? */ ST_FUNC void gen_expr32(ExprValue *pe) { - gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); + if (pe->pcrel) + /* If PC-relative, always set VT_SYM, even without symbol, + so as to force a relocation to be emitted. */ + gen_addrpc32(VT_SYM, pe->sym, pe->v); + else + gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); } #ifdef TCC_TARGET_X86_64 diff --git a/tcc.h b/tcc.h index 4de48e7..7279150 100644 --- a/tcc.h +++ b/tcc.h @@ -555,6 +555,7 @@ typedef struct CachedInclude { typedef struct ExprValue { uint64_t v; Sym *sym; + int pcrel; } ExprValue; #define MAX_ASM_OPERANDS 30 diff --git a/tccasm.c b/tccasm.c index ee14073..27645a8 100644 --- a/tccasm.c +++ b/tccasm.c @@ -67,11 +67,13 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) sym->type.t = VT_STATIC | VT_VOID; } } - pe->v = 0; - pe->sym = sym; + pe->v = 0; + pe->sym = sym; + pe->pcrel = 0; } else if (*p == '\0') { pe->v = n; pe->sym = NULL; + pe->pcrel = 0; } else { tcc_error("invalid number syntax"); } @@ -97,6 +99,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) case TOK_LCHAR: pe->v = tokc.i; pe->sym = NULL; + pe->pcrel = 0; next(); break; case '(': @@ -107,6 +110,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) case '.': pe->v = 0; pe->sym = &sym_dot; + pe->pcrel = 0; sym_dot.type.t = VT_VOID | VT_STATIC; sym_dot.r = cur_text_section->sh_num; sym_dot.jnext = ind; @@ -125,9 +129,11 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) /* if absolute symbol, no need to put a symbol value */ pe->v = sym->jnext; pe->sym = NULL; + pe->pcrel = 0; } else { pe->v = 0; pe->sym = sym; + pe->pcrel = 0; } next(); } else { @@ -230,20 +236,21 @@ static inline void asm_expr_sum(TCCState *s1, ExprValue *pe) pe->v -= e2.v; /* NOTE: we are less powerful than gas in that case because we store only one symbol in the expression */ - if (!pe->sym && !e2.sym) { - /* OK */ - } else if (pe->sym && !e2.sym) { - /* OK */ - } else if (pe->sym && e2.sym) { - if (pe->sym == e2.sym) { - /* OK */ - } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) { - /* we also accept defined symbols in the same section */ - pe->v += pe->sym->jnext - e2.sym->jnext; - } else { - goto cannot_relocate; - } - pe->sym = NULL; /* same symbols can be subtracted to NULL */ + if (!e2.sym) { + /* OK */ + } else if (pe->sym == e2.sym) { + /* OK */ + pe->sym = NULL; /* same symbols can be subtracted to NULL */ + } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) { + /* we also accept defined symbols in the same section */ + pe->v += pe->sym->jnext - e2.sym->jnext; + pe->sym = NULL; + } else if (e2.sym->r == cur_text_section->sh_num) { + /* When subtracting a defined symbol in current section + this actually makes the value PC-relative. */ + pe->v -= e2.sym->jnext - ind - 4; + pe->pcrel = 1; + e2.sym = NULL; } else { cannot_relocate: tcc_error("invalid operation with label"); @@ -531,9 +538,15 @@ static void asm_parse_directive(TCCState *s1) case TOK_ASMDIR_org: { unsigned long n; + ExprValue e; next(); - /* XXX: handle section symbols too */ - n = asm_int_expr(s1); + asm_expr(s1, &e); + n = e.v; + if (e.sym) { + if (e.sym->r != cur_text_section->sh_num) + expect("constant or same-section symbol"); + n += e.sym->jnext; + } if (n < ind) tcc_error("attempt to .org backwards"); v = 0; @@ -703,6 +716,7 @@ static void asm_parse_directive(TCCState *s1) case TOK_ASMDIR_section: { char sname[256]; + int old_nb_section = s1->nb_sections; tok1 = tok; /* XXX: support more options */ @@ -733,6 +747,11 @@ static void asm_parse_directive(TCCState *s1) use_section(s1, sname); else push_section(s1, sname); + /* If we just allocated a new section reset its alignment to + 1. new_section normally acts for GCC compatibility and + sets alignment to PTR_SIZE. The assembler behaves different. */ + if (old_nb_section != s1->nb_sections) + cur_text_section->sh_addralign = 1; } break; case TOK_ASMDIR_previous: diff --git a/tests/asmtest.S b/tests/asmtest.S index 2518510..f4eb248 100644 --- a/tests/asmtest.S +++ b/tests/asmtest.S @@ -727,6 +727,19 @@ nop .popsection .popsection +1: ud2 +.pushsection __bug_table,"a" +.align 8 +2: .long 1b - 2b + .long 0x600000 - 2b + .long 1b + 42 + .long 43 + 1b + .long 2b + 144 + .long 145 + 2b + .word 164, 0 + .org 2b+32 +.popsection + movd %esi, %mm1 movd %edi, %xmm2 movd (%ebx), %mm3