aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--i386-asm.c14
-rw-r--r--tcc.h1
-rw-r--r--tccasm.c55
-rw-r--r--tests/asmtest.S13
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