aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tccasm.c62
-rw-r--r--tests/tcctest.c28
2 files changed, 71 insertions, 19 deletions
diff --git a/tccasm.c b/tccasm.c
index 0bf6456..192e485 100644
--- a/tccasm.c
+++ b/tccasm.c
@@ -35,6 +35,45 @@ ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
static int tcc_assemble_internal(TCCState *s1, int do_preprocess);
static Sym sym_dot;
+/* Return a symbol we can use inside the assembler, having name NAME.
+ The assembler symbol table is different from the C symbol table
+ (and the Sym members are used differently). But we must be able
+ to look up file-global C symbols from inside the assembler, e.g.
+ for global asm blocks to be able to refer to defined C symbols.
+
+ This routine gives back either an existing asm-internal
+ symbol, or a new one. In the latter case the new asm-internal
+ symbol is initialized with info from the C symbol table. */
+static Sym* get_asm_sym(int name)
+{
+ Sym *sym = label_find(name);
+ if (!sym) {
+ Sym *csym = sym_find(name);
+ sym = label_push(&tcc_state->asm_labels, name, 0);
+ sym->type.t = VT_VOID | VT_EXTERN;
+ /* We might be called for an asm block from inside a C routine
+ and so might have local decls on the identifier stack. Search
+ for the first global one. */
+ while (csym && csym->scope)
+ csym = csym->prev_tok;
+ /* Now, if we have a defined global symbol copy over
+ section and offset. */
+ if (csym &&
+ ((csym->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST)) &&
+ csym->c) {
+ ElfW(Sym) *esym;
+ esym = &((ElfW(Sym) *)symtab_section->data)[csym->c];
+ sym->r = esym->st_shndx;
+ sym->jnext = esym->st_value;
+ /* XXX can't yet store st_size anywhere. */
+ sym->type.t &= ~VT_EXTERN;
+ /* Mark that this asm symbol doesn't need to be fed back. */
+ sym->type.t |= VT_IMPORT;
+ }
+ }
+ return sym;
+}
+
/* We do not use the C expression parser to handle symbols. Maybe the
C expression parser could be tweaked to do so. */
@@ -119,12 +158,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe)
default:
if (tok >= TOK_IDENT) {
/* label case : if the label was not found, add one */
- sym = label_find(tok);
- if (!sym) {
- sym = label_push(&s1->asm_labels, tok, 0);
- /* NOTE: by default, the symbol is global */
- sym->type.t = VT_VOID | VT_EXTERN;
- }
+ sym = get_asm_sym(tok);
if (sym->r == SHN_ABS) {
/* if absolute symbol, no need to put a symbol value */
pe->v = sym->jnext;
@@ -359,7 +393,6 @@ static Sym* asm_new_label(TCCState *s1, int label, int is_local)
involving other symbols). LABEL can be overwritten later still. */
static Sym* set_symbol(TCCState *s1, int label)
{
- Sym *sym;
long n;
ExprValue e;
next();
@@ -379,7 +412,7 @@ static void asm_free_labels(TCCState *st)
s1 = s->prev;
/* define symbol value in object file */
s->type.t &= ~VT_EXTERN;
- if (s->r) {
+ if (s->r && !(s->type.t & VT_IMPORT)) {
if (s->r == SHN_ABS)
sec = SECTION_ABS;
else
@@ -649,11 +682,7 @@ static void asm_parse_directive(TCCState *s1)
Sym *sym;
next();
- sym = label_find(tok);
- if (!sym) {
- sym = label_push(&s1->asm_labels, tok, 0);
- sym->type.t = VT_VOID | VT_EXTERN;
- }
+ sym = get_asm_sym(tok);
if (tok1 != TOK_ASMDIR_hidden)
sym->type.t &= ~VT_STATIC;
if (tok1 == TOK_ASMDIR_weak)
@@ -772,12 +801,7 @@ static void asm_parse_directive(TCCState *s1)
const char *newtype;
next();
- sym = label_find(tok);
- if (!sym) {
- sym = label_push(&s1->asm_labels, tok, 0);
- sym->type.t = VT_VOID | VT_EXTERN;
- }
-
+ sym = get_asm_sym(tok);
next();
skip(',');
if (tok == TOK_STR) {
diff --git a/tests/tcctest.c b/tests/tcctest.c
index d8c8bcf..30d78b8 100644
--- a/tests/tcctest.c
+++ b/tests/tcctest.c
@@ -2647,6 +2647,24 @@ void other_constraints_test(void)
printf ("oc1: %d\n", ret == (unsigned long)&var);
}
+/* Test global asm blocks playing with aliases. */
+void base_func(void)
+{
+ printf ("asmc: base\n");
+}
+
+extern void override_func1 (void);
+extern void override_func2 (void);
+
+asm(".weak override_func1\n.set override_func1, base_func");
+asm(".set override_func1, base_func");
+asm(".set override_func2, base_func");
+
+void override_func2 (void)
+{
+ printf ("asmc: override2\n");
+}
+
unsigned int set;
void asm_test(void)
@@ -2655,6 +2673,10 @@ void asm_test(void)
unsigned int val;
struct struct123 s1;
struct struct1231 s2 = { (unsigned long)&s1 };
+ /* Hide the outer base_func, but check later that the inline
+ asm block gets the outer one. */
+ int base_func = 42;
+ void override_func3 (void);
printf("inline asm:\n");
@@ -2692,6 +2714,12 @@ void asm_test(void)
printf("set=0x%x\n", set);
val = 0x01020304;
printf("swab32(0x%08x) = 0x%0x\n", val, swab32(val));
+ override_func1();
+ override_func2();
+ /* The base_func ref from the following inline asm should find
+ the global one, not the local decl from this function. */
+ asm volatile(".weak override_func3\n.set override_func3, base_func");
+ override_func3();
return;
label1:
goto label2;