This makes the asm symbols use the same members as the C symbols for global decls, e.g. using the ELF symbol to hold offset and section. That allows us to use only one symbol table for C and asm symbols and to get rid of hacks to synch between them. We still need some special handling for symbols that come purely from asm sources.
Unified · Split
| @@ -500,12 +500,13 @@ ST_FUNC void gen_expr64(ExprValue *pe) · 499 unmodified lines | |||
| 500 | static void gen_disp32(ExprValue *pe) | 500 | static void gen_disp32(ExprValue *pe) |
| 501 | { | 501 | { |
| 502 | Sym *sym = pe->sym; | 502 | Sym *sym = pe->sym; |
| 503 | if (sym && sym->r == cur_text_section->sh_num) { | 503 | ElfSym *esym = elfsym(sym); |
| 504 | if (esym && esym->st_shndx == cur_text_section->sh_num) { | ||
| 504 | /* same section: we can output an absolute value. Note | 505 | /* same section: we can output an absolute value. Note |
| 505 | that the TCC compiler behaves differently here because | 506 | that the TCC compiler behaves differently here because |
| 506 | it always outputs a relocation to ease (future) code | 507 | it always outputs a relocation to ease (future) code |
| 507 | elimination in the linker */ | 508 | elimination in the linker */ |
| 508 | gen_le32(pe->v + sym->jnext - ind - 4); | 509 | gen_le32(pe->v + esym->st_value - ind - 4); |
| 509 | } else { | 510 | } else { |
| 510 | if (sym && sym->type.t == VT_VOID) { | 511 | if (sym && sym->type.t == VT_VOID) { |
| 511 | sym->type.t = VT_FUNC; | 512 | sym->type.t = VT_FUNC; |
| @@ -1017,16 +1018,14 @@ ST_FUNC void asm_opcode(TCCState *s1, int opcode) · 505 unmodified lines | |||
| 1017 | if (pa->instr_type & OPC_B) | 1018 | if (pa->instr_type & OPC_B) |
| 1018 | v += s >= 1; | 1019 | v += s >= 1; |
| 1019 | if (nb_ops == 1 && pa->op_type[0] == OPT_DISP8) { | 1020 | if (nb_ops == 1 && pa->op_type[0] == OPT_DISP8) { |
| 1020 | Sym *sym; | 1021 | ElfSym *esym; |
| 1021 | int jmp_disp; | 1022 | int jmp_disp; |
| 1022 | 1023 | ||
| 1023 | /* see if we can really generate the jump with a byte offset */ | 1024 | /* see if we can really generate the jump with a byte offset */ |
| 1024 | sym = ops[0].e.sym; | 1025 | esym = elfsym(ops[0].e.sym); |
| 1025 | if (!sym) | 1026 | if (!esym || esym->st_shndx != cur_text_section->sh_num) |
| 1026 | goto no_short_jump; | 1027 | goto no_short_jump; |
| 1027 | if (sym->r != cur_text_section->sh_num) | 1028 | jmp_disp = ops[0].e.v + esym->st_value - ind - 2 - (v >= 0xff); |
| 1028 | goto no_short_jump; | ||
| 1029 | jmp_disp = ops[0].e.v + sym->jnext - ind - 2 - (v >= 0xff); | ||
| 1030 | if (jmp_disp == (int8_t)jmp_disp) { | 1029 | if (jmp_disp == (int8_t)jmp_disp) { |
| 1031 | /* OK to generate jump */ | 1030 | /* OK to generate jump */ |
| 1032 | ops[0].e.sym = 0; | 1031 | ops[0].e.sym = 0; |
| @@ -356,6 +356,7 @@ extern long double strtold (const char *__nptr, char **__endptr); · 355 unmodified lines | |||
| 356 | #endif | 356 | #endif |
| 357 | /* target address type */ | 357 | /* target address type */ |
| 358 | #define addr_t ElfW(Addr) | 358 | #define addr_t ElfW(Addr) |
| 359 | #define ElfSym ElfW(Sym) | ||
| 359 | 360 | ||
| 360 | #if PTR_SIZE == 8 && !defined TCC_TARGET_PE | 361 | #if PTR_SIZE == 8 && !defined TCC_TARGET_PE |
| 361 | # define LONG_SIZE 8 | 362 | # define LONG_SIZE 8 |
| @@ -383,7 +384,6 @@ typedef struct TokenSym { · 21 unmodified lines | |||
| 383 | struct Sym *sym_label; /* direct pointer to label */ | 384 | struct Sym *sym_label; /* direct pointer to label */ |
| 384 | struct Sym *sym_struct; /* direct pointer to structure */ | 385 | struct Sym *sym_struct; /* direct pointer to structure */ |
| 385 | struct Sym *sym_identifier; /* direct pointer to identifier */ | 386 | struct Sym *sym_identifier; /* direct pointer to identifier */ |
| 386 | struct Sym *sym_asm_label; /* direct pointer to asm label */ | ||
| 387 | int tok; /* token number */ | 387 | int tok; /* token number */ |
| 388 | int len; | 388 | int len; |
| 389 | char str[1]; | 389 | char str[1]; |
| @@ -440,9 +440,8 @@ struct SymAttr { · 50 unmodified lines | |||
| 440 | visibility : 2, | 440 | visibility : 2, |
| 441 | dllexport : 1, | 441 | dllexport : 1, |
| 442 | dllimport : 1, | 442 | dllimport : 1, |
| 443 | asmcsym : 1, | ||
| 444 | asmexport : 1, | 443 | asmexport : 1, |
| 445 | unused : 3; | 444 | unused : 4; |
| 446 | }; | 445 | }; |
| 447 | 446 | ||
| 448 | /* function attributes or temporary attributes for parsing */ | 447 | /* function attributes or temporary attributes for parsing */ |
| @@ -799,6 +798,7 @@ struct TCCState { · 350 unmodified lines | |||
| 799 | int nb_sym_attrs; | 798 | int nb_sym_attrs; |
| 800 | /* tiny assembler state */ | 799 | /* tiny assembler state */ |
| 801 | Sym *asm_labels; | 800 | Sym *asm_labels; |
| 801 | ElfSym esym_dot; | ||
| 802 | 802 | ||
| 803 | #ifdef TCC_TARGET_PE | 803 | #ifdef TCC_TARGET_PE |
| 804 | /* PE info */ | 804 | /* PE info */ |
| @@ -1147,7 +1147,7 @@ ST_FUNC Sym *sym_push(int v, CType *type, int r, int c); · 342 unmodified lines | |||
| 1147 | ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep); | 1147 | ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep); |
| 1148 | ST_INLN Sym *struct_find(int v); | 1148 | ST_INLN Sym *struct_find(int v); |
| 1149 | ST_INLN Sym *sym_find(int v); | 1149 | ST_INLN Sym *sym_find(int v); |
| 1150 | ST_FUNC Sym *global_identifier_push(int v, int t, int c); | 1150 | ST_FUNC Sym *global_identifier_push_1(Sym **, int v, int t, int c); |
| 1151 | 1151 | ||
| 1152 | ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen); | 1152 | ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen); |
| 1153 | ST_FUNC int tcc_open(TCCState *s1, const char *filename); | 1153 | ST_FUNC int tcc_open(TCCState *s1, const char *filename); |
| @@ -1316,6 +1316,7 @@ ST_INLN int is_float(int t); · 162 unmodified lines | |||
| 1316 | ST_FUNC int ieee_finite(double d); | 1316 | ST_FUNC int ieee_finite(double d); |
| 1317 | ST_FUNC void test_lvalue(void); | 1317 | ST_FUNC void test_lvalue(void); |
| 1318 | ST_FUNC void vpushi(int v); | 1318 | ST_FUNC void vpushi(int v); |
| 1319 | ST_FUNC ElfSym *elfsym(Sym *); | ||
| 1319 | ST_FUNC Sym *external_global_sym(int v, CType *type, int r); | 1320 | ST_FUNC Sym *external_global_sym(int v, CType *type, int r); |
| 1320 | ST_FUNC void vset(CType *type, int r, int v); | 1321 | ST_FUNC void vset(CType *type, int r, int v); |
| 1321 | ST_FUNC void vswap(void); | 1322 | ST_FUNC void vswap(void); |
| @@ -1618,6 +1619,8 @@ PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp); · 296 unmodified lines | |||
| 1618 | # define ST_PE_IMPORT 0x20 | 1619 | # define ST_PE_IMPORT 0x20 |
| 1619 | # define ST_PE_STDCALL 0x40 | 1620 | # define ST_PE_STDCALL 0x40 |
| 1620 | #endif | 1621 | #endif |
| 1622 | #define ST_ASM_SET 0x04 | ||
| 1623 | |||
| 1621 | /* ------------ tccrun.c ----------------- */ | 1624 | /* ------------ tccrun.c ----------------- */ |
| 1622 | #ifdef TCC_IS_NATIVE | 1625 | #ifdef TCC_IS_NATIVE |
| 1623 | #ifdef CONFIG_TCC_STATIC | 1626 | #ifdef CONFIG_TCC_STATIC |
| @@ -36,64 +36,40 @@ static Sym sym_dot; · 35 unmodified lines | |||
| 36 | 36 | ||
| 37 | static Sym *asm_label_find(int v) | 37 | static Sym *asm_label_find(int v) |
| 38 | { | 38 | { |
| 39 | v -= TOK_IDENT; | 39 | Sym *sym = sym_find(v); |
| 40 | if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) | 40 | while (sym && sym->sym_scope) |
| 41 | return NULL; | 41 | sym = sym->prev_tok; |
| 42 | return table_ident[v]->sym_asm_label; | 42 | return sym; |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | static Sym *asm_label_push(Sym **ptop, int v) | 45 | static Sym *asm_label_push(int v, int t) |
| 46 | { | 46 | { |
| 47 | Sym *s, **ps; | 47 | Sym *sym = global_identifier_push_1(&tcc_state->asm_labels, v, t, 0); |
| 48 | s = sym_push2(ptop, v, 0, 0); | 48 | /* We always add VT_EXTERN, for sym definition that's tentative |
| 49 | ps = &table_ident[v - TOK_IDENT]->sym_asm_label; | 49 | (for .set, removed for real defs), for mere references it's correct |
| 50 | s->prev_tok = *ps; | 50 | as is. */ |
| 51 | *ps = s; | 51 | sym->type.t |= VT_VOID | VT_EXTERN; |
| 52 | return s; | 52 | sym->r = VT_CONST | VT_SYM; |
| 53 | return sym; | ||
| 53 | } | 54 | } |
| 54 | 55 | ||
| 55 | /* Return a symbol we can use inside the assembler, having name NAME. | 56 | /* Return a symbol we can use inside the assembler, having name NAME. |
| 56 | The assembler symbol table is different from the C symbol table | 57 | Symbols from asm and C source share a namespace. If we generate |
| 57 | (and the Sym members are used differently). But we must be able | 58 | an asm symbol it's also a (file-global) C symbol, but it's |
| 58 | to look up file-global C symbols from inside the assembler, e.g. | 59 | either not accessible by name (like "L.123"), or its type information |
| 59 | for global asm blocks to be able to refer to defined C symbols. | 60 | is such that it's not usable without a proper C declaration. |
| 60 | 61 | ||
| 61 | This routine gives back either an existing asm-internal | 62 | Sometimes we need symbols accessible by name from asm, which |
| 62 | symbol, or a new one. In the latter case the new asm-internal | 63 | are anonymous in C, in this case CSYM can be used to transfer |
| 63 | symbol is initialized with info from the C symbol table. | 64 | all information from that symbol to the (possibly newly created) |
| 64 | 65 | asm symbol. */ | |
| 65 | If CSYM is non-null we take symbol info from it, otherwise | ||
| 66 | we look up NAME in the C symbol table and use that. */ | ||
| 67 | ST_FUNC Sym* get_asm_sym(int name, Sym *csym) | 66 | ST_FUNC Sym* get_asm_sym(int name, Sym *csym) |
| 68 | { | 67 | { |
| 69 | Sym *sym = asm_label_find(name); | 68 | Sym *sym = asm_label_find(name); |
| 70 | if (!sym) { | 69 | if (!sym) { |
| 71 | sym = asm_label_push(&tcc_state->asm_labels, name); | 70 | sym = asm_label_push(name, 0); |
| 72 | sym->type.t = VT_VOID | VT_EXTERN; | 71 | if (csym) |
| 73 | if (!csym) { | 72 | sym->c = csym->c; |
| 74 | csym = sym_find(name); | ||
| 75 | /* We might be called for an asm block from inside a C routine | ||
| 76 | and so might have local decls on the identifier stack. Search | ||
| 77 | for the first global one. */ | ||
| 78 | while (csym && csym->sym_scope) | ||
| 79 | csym = csym->prev_tok; | ||
| 80 | } | ||
| 81 | /* Now, if we have a defined global symbol copy over | ||
| 82 | section and offset. */ | ||
| 83 | if (csym && | ||
| 84 | ((csym->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST)) && | ||
| 85 | csym->c) { | ||
| 86 | ElfW(Sym) *esym; | ||
| 87 | esym = &((ElfW(Sym) *)symtab_section->data)[csym->c]; | ||
| 88 | sym->c = csym->c; | ||
| 89 | sym->r = esym->st_shndx; | ||
| 90 | sym->jnext = esym->st_value; | ||
| 91 | /* XXX can't yet store st_size anywhere. */ | ||
| 92 | sym->type.t = VT_VOID | (csym->type.t & VT_STATIC); | ||
| 93 | /* Mark that this asm symbol doesn't need to be fed back. */ | ||
| 94 | sym->a.asmcsym = 1; | ||
| 95 | sym->a.asmexport = !(csym->type.t & VT_STATIC); | ||
| 96 | } | ||
| 97 | } | 73 | } |
| 98 | return sym; | 74 | return sym; |
| 99 | } | 75 | } |
| @@ -118,16 +94,15 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) · 18 unmodified lines | |||
| 118 | sym = asm_label_find(label); | 94 | sym = asm_label_find(label); |
| 119 | if (*p == 'b') { | 95 | if (*p == 'b') { |
| 120 | /* backward : find the last corresponding defined label */ | 96 | /* backward : find the last corresponding defined label */ |
| 121 | if (sym && sym->r == 0) | 97 | if (sym && (!sym->c || elfsym(sym)->st_shndx == SHN_UNDEF)) |
| 122 | sym = sym->prev_tok; | 98 | sym = sym->prev_tok; |
| 123 | if (!sym) | 99 | if (!sym) |
| 124 | tcc_error("local label '%d' not found backward", n); | 100 | tcc_error("local label '%d' not found backward", n); |
| 125 | } else { | 101 | } else { |
| 126 | /* forward */ | 102 | /* forward */ |
| 127 | if (!sym || sym->r) { | 103 | if (!sym || (sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)) { |
| 128 | /* if the last label is defined, then define a new one */ | 104 | /* if the last label is defined, then define a new one */ |
| 129 | sym = asm_label_push(&s1->asm_labels, label); | 105 | sym = asm_label_push(label, VT_STATIC); |
| 130 | sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN; | ||
| 131 | } | 106 | } |
| 132 | } | 107 | } |
| 133 | pe->v = 0; | 108 | pe->v = 0; |
| @@ -175,17 +150,20 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) · 41 unmodified lines | |||
| 175 | pe->sym = &sym_dot; | 150 | pe->sym = &sym_dot; |
| 176 | pe->pcrel = 0; | 151 | pe->pcrel = 0; |
| 177 | sym_dot.type.t = VT_VOID | VT_STATIC; | 152 | sym_dot.type.t = VT_VOID | VT_STATIC; |
| 178 | sym_dot.r = cur_text_section->sh_num; | 153 | sym_dot.c = -1; |
| 179 | sym_dot.jnext = ind; | 154 | tcc_state->esym_dot.st_shndx = cur_text_section->sh_num; |
| 155 | tcc_state->esym_dot.st_value = ind; | ||
| 180 | next(); | 156 | next(); |
| 181 | break; | 157 | break; |
| 182 | default: | 158 | default: |
| 183 | if (tok >= TOK_IDENT) { | 159 | if (tok >= TOK_IDENT) { |
| 160 | ElfSym *esym; | ||
| 184 | /* label case : if the label was not found, add one */ | 161 | /* label case : if the label was not found, add one */ |
| 185 | sym = get_asm_sym(tok, NULL); | 162 | sym = get_asm_sym(tok, NULL); |
| 186 | if (sym->r == SHN_ABS) { | 163 | esym = elfsym(sym); |
| 164 | if (esym && esym->st_shndx == SHN_ABS) { | ||
| 187 | /* if absolute symbol, no need to put a symbol value */ | 165 | /* if absolute symbol, no need to put a symbol value */ |
| 188 | pe->v = sym->jnext; | 166 | pe->v = esym->st_value; |
| 189 | pe->sym = NULL; | 167 | pe->sym = NULL; |
| 190 | pe->pcrel = 0; | 168 | pe->pcrel = 0; |
| 191 | } else { | 169 | } else { |
| @@ -299,20 +277,26 @@ static inline void asm_expr_sum(TCCState *s1, ExprValue *pe) · 107 unmodified lines | |||
| 299 | } else if (pe->sym == e2.sym) { | 277 | } else if (pe->sym == e2.sym) { |
| 300 | /* OK */ | 278 | /* OK */ |
| 301 | pe->sym = NULL; /* same symbols can be subtracted to NULL */ | 279 | pe->sym = NULL; /* same symbols can be subtracted to NULL */ |
| 302 | } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) { | 280 | } else { |
| 303 | /* we also accept defined symbols in the same section */ | 281 | ElfSym *esym1, *esym2; |
| 304 | pe->v += pe->sym->jnext - e2.sym->jnext; | 282 | esym1 = elfsym(pe->sym); |
| 305 | pe->sym = NULL; | 283 | esym2 = elfsym(e2.sym); |
| 306 | } else if (e2.sym->r == cur_text_section->sh_num) { | 284 | if (esym1 && esym1->st_shndx == esym2->st_shndx |
| 307 | /* When subtracting a defined symbol in current section | 285 | && esym1->st_shndx != SHN_UNDEF) { |
| 308 | this actually makes the value PC-relative. */ | 286 | /* we also accept defined symbols in the same section */ |
| 309 | pe->v -= e2.sym->jnext - ind - 4; | 287 | pe->v += esym1->st_value - esym2->st_value; |
| 310 | pe->pcrel = 1; | 288 | pe->sym = NULL; |
| 311 | e2.sym = NULL; | 289 | } else if (esym2->st_shndx == cur_text_section->sh_num) { |
| 312 | } else { | 290 | /* When subtracting a defined symbol in current section |
| 313 | cannot_relocate: | 291 | this actually makes the value PC-relative. */ |
| 314 | tcc_error("invalid operation with label"); | 292 | pe->v -= esym2->st_value - ind - 4; |
| 315 | } | 293 | pe->pcrel = 1; |
| 294 | e2.sym = NULL; | ||
| 295 | } else { | ||
| 296 | cannot_relocate: | ||
| 297 | tcc_error("invalid operation with label"); | ||
| 298 | } | ||
| 299 | } | ||
| 316 | } | 300 | } |
| 317 | } | 301 | } |
| 318 | } | 302 | } |
| @@ -377,13 +361,15 @@ static Sym* asm_new_label1(TCCState *s1, int label, int is_local, · 58 unmodified lines | |||
| 377 | int sh_num, int value) | 361 | int sh_num, int value) |
| 378 | { | 362 | { |
| 379 | Sym *sym; | 363 | Sym *sym; |
| 364 | ElfSym *esym; | ||
| 380 | 365 | ||
| 381 | sym = asm_label_find(label); | 366 | sym = asm_label_find(label); |
| 382 | if (sym) { | 367 | if (sym) { |
| 368 | esym = elfsym(sym); | ||
| 383 | /* A VT_EXTERN symbol, even if it has a section is considered | 369 | /* A VT_EXTERN symbol, even if it has a section is considered |
| 384 | overridable. This is how we "define" .set targets. Real | 370 | overridable. This is how we "define" .set targets. Real |
| 385 | definitions won't have VT_EXTERN set. */ | 371 | definitions won't have VT_EXTERN set. */ |
| 386 | if (sym->r && !(sym->type.t & VT_EXTERN)) { | 372 | if (esym && esym->st_shndx != SHN_UNDEF && !(sym->type.t & VT_EXTERN)) { |
| 387 | /* the label is already defined */ | 373 | /* the label is already defined */ |
| 388 | if (!is_local) { | 374 | if (!is_local) { |
| 389 | tcc_error("assembler label '%s' already defined", | 375 | tcc_error("assembler label '%s' already defined", |
| @@ -395,14 +381,13 @@ static Sym* asm_new_label1(TCCState *s1, int label, int is_local, · 5 unmodified lines | |||
| 395 | } | 381 | } |
| 396 | } else { | 382 | } else { |
| 397 | new_label: | 383 | new_label: |
| 398 | sym = asm_label_push(&s1->asm_labels, label); | 384 | sym = asm_label_push(label, is_local ? VT_STATIC : 0); |
| 399 | /* If we need a symbol to hold a value, mark it as | ||
| 400 | tentative only (for .set). If this is for a real label | ||
| 401 | we'll remove VT_EXTERN. */ | ||
| 402 | sym->type.t = VT_VOID | (is_local ? VT_STATIC : 0) | VT_EXTERN; | ||
| 403 | } | 385 | } |
| 404 | sym->r = sh_num; | 386 | if (!sym->c) |
| 405 | sym->jnext = value; | 387 | put_extern_sym2(sym, NULL, 0, 0, 0); |
| 388 | esym = elfsym(sym); | ||
| 389 | esym->st_shndx = sh_num; | ||
| 390 | esym->st_value = value; | ||
| 406 | return sym; | 391 | return sym; |
| 407 | } | 392 | } |
| 408 | 393 | ||
| @@ -417,79 +402,41 @@ static Sym* set_symbol(TCCState *s1, int label) · 8 unmodified lines | |||
| 417 | { | 402 | { |
| 418 | long n; | 403 | long n; |
| 419 | ExprValue e; | 404 | ExprValue e; |
| 405 | Sym *sym; | ||
| 406 | ElfSym *esym; | ||
| 420 | next(); | 407 | next(); |
| 421 | asm_expr(s1, &e); | 408 | asm_expr(s1, &e); |
| 422 | n = e.v; | 409 | n = e.v; |
| 423 | if (e.sym) | 410 | esym = elfsym(e.sym); |
| 424 | n += e.sym->jnext; | 411 | if (esym) |
| 425 | return asm_new_label1(s1, label, 0, e.sym ? e.sym->r : SHN_ABS, n); | 412 | n += esym->st_value; |
| 426 | } | 413 | sym = asm_new_label1(s1, label, 0, esym ? esym->st_shndx : SHN_ABS, n); |
| 427 | 414 | elfsym(sym)->st_other |= ST_ASM_SET; | |
| 428 | /* Patch ELF symbol associated with SYM based on the assemblers | 415 | return sym; |
| 429 | understanding. */ | ||
| 430 | static void patch_binding(Sym *sym) | ||
| 431 | { | ||
| 432 | ElfW(Sym) *esym; | ||
| 433 | if (0 == sym->c) | ||
| 434 | return; | ||
| 435 | esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; | ||
| 436 | if (sym->a.visibility) | ||
| 437 | esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1)) | ||
| 438 | | sym->a.visibility; | ||
| 439 | |||
| 440 | esym->st_info = ELFW(ST_INFO)(sym->a.weak ? STB_WEAK | ||
| 441 | : (sym->type.t & VT_STATIC) ? STB_LOCAL | ||
| 442 | : STB_GLOBAL, | ||
| 443 | ELFW(ST_TYPE)(esym->st_info)); | ||
| 444 | } | 416 | } |
| 445 | 417 | ||
| 446 | ST_FUNC void asm_free_labels(TCCState *st) | 418 | ST_FUNC void asm_free_labels(TCCState *st) |
| 447 | { | 419 | { |
| 448 | Sym *s, *s1; | 420 | Sym *s, *s1; |
| 449 | Section *sec; | ||
| 450 | 421 | ||
| 451 | for(s = st->asm_labels; s != NULL; s = s1) { | 422 | for(s = st->asm_labels; s != NULL; s = s1) { |
| 452 | int was_ext = s->type.t & VT_EXTERN; | 423 | ElfSym *esym = elfsym(s); |
| 453 | s1 = s->prev; | 424 | s1 = s->prev; |
| 454 | /* define symbol value in object file and care for updating | 425 | /* Possibly update binding and visibility from asm directives. */ |
| 455 | the C and asm symbols */ | ||
| 456 | s->type.t &= ~VT_EXTERN; | 426 | s->type.t &= ~VT_EXTERN; |
| 457 | if (!s->a.asmcsym) { | 427 | if (esym) { |
| 458 | Sym *csym = sym_find(s->v); | 428 | if (!s->a.asmexport && esym->st_shndx != SHN_UNDEF) |
| 459 | ElfW(Sym) *esym = NULL; | 429 | s->type.t |= VT_STATIC; |
| 460 | if (csym) { | 430 | if (s->a.visibility) |
| 461 | s->a.asmexport |= !(csym->type.t & VT_STATIC); | 431 | esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1)) |
| 462 | if (csym->c) { | 432 | | s->a.visibility; |
| 463 | esym = &((ElfW(Sym) *)symtab_section->data)[csym->c]; | 433 | esym->st_info = ELFW(ST_INFO)(s->a.weak ? STB_WEAK |
| 464 | if (s->c) { | 434 | : (s->type.t & VT_STATIC) ? STB_LOCAL |
| 465 | /* We have generated code and possibly relocs | 435 | : STB_GLOBAL, |
| 466 | referencing the symtab entry s->c (the asm | 436 | ELFW(ST_TYPE)(esym->st_info)); |
| 467 | ELF symbol). If that's undefined but the C | ||
| 468 | symbol is defined, copy over the info. */ | ||
| 469 | ElfW(Sym) *asm_esym = &((ElfW(Sym) *)symtab_section->data)[s->c]; | ||
| 470 | if (asm_esym->st_shndx == SHN_UNDEF) { | ||
| 471 | *asm_esym = *esym; | ||
| 472 | } | ||
| 473 | } | ||
| 474 | } | ||
| 475 | } | ||
| 476 | if (!s->a.asmexport) | ||
| 477 | s->type.t |= VT_STATIC; | ||
| 478 | if (s->r) { | ||
| 479 | if (s->r == SHN_ABS) | ||
| 480 | sec = SECTION_ABS; | ||
| 481 | else | ||
| 482 | sec = st->sections[s->r]; | ||
| 483 | /* !was_ext so that we run into an multi-def error if | ||
| 484 | we defined it in C and in asm (non-tentatively) */ | ||
| 485 | if (!esym || esym->st_shndx == SHN_UNDEF || !was_ext) | ||
| 486 | put_extern_sym2(s, sec, s->jnext, 0, 0); | ||
| 487 | } else /* undefined symbols are global */ | ||
| 488 | s->type.t &= ~VT_STATIC, s->a.asmexport = 1; | ||
| 489 | patch_binding(s); | ||
| 490 | } | 437 | } |
| 491 | /* remove label */ | 438 | /* remove label */ |
| 492 | table_ident[s->v - TOK_IDENT]->sym_asm_label = NULL; | 439 | table_ident[s->v - TOK_IDENT]->sym_identifier = s->prev_tok; |
| 493 | sym_free(s); | 440 | sym_free(s); |
| 494 | } | 441 | } |
| 495 | st->asm_labels = NULL; | 442 | st->asm_labels = NULL; |
| @@ -715,13 +662,15 @@ static void asm_parse_directive(TCCState *s1, int global) · 219 unmodified lines | |||
| 715 | { | 662 | { |
| 716 | unsigned long n; | 663 | unsigned long n; |
| 717 | ExprValue e; | 664 | ExprValue e; |
| 665 | ElfSym *esym; | ||
| 718 | next(); | 666 | next(); |
| 719 | asm_expr(s1, &e); | 667 | asm_expr(s1, &e); |
| 720 | n = e.v; | 668 | n = e.v; |
| 721 | if (e.sym) { | 669 | esym = elfsym(e.sym); |
| 722 | if (e.sym->r != cur_text_section->sh_num) | 670 | if (esym) { |
| 671 | if (esym->st_shndx != cur_text_section->sh_num) | ||
| 723 | expect("constant or same-section symbol"); | 672 | expect("constant or same-section symbol"); |
| 724 | n += e.sym->jnext; | 673 | n += esym->st_value; |
| 725 | } | 674 | } |
| 726 | if (n < ind) | 675 | if (n < ind) |
| 727 | tcc_error("attempt to .org backwards"); | 676 | tcc_error("attempt to .org backwards"); |
| @@ -978,7 +927,6 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global) · 250 unmodified lines | |||
| 978 | int opcode; | 927 | int opcode; |
| 979 | int saved_parse_flags = parse_flags; | 928 | int saved_parse_flags = parse_flags; |
| 980 | 929 | ||
| 981 | /* XXX: undefine C labels */ | ||
| 982 | parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR; | 930 | parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR; |
| 983 | if (do_preprocess) | 931 | if (do_preprocess) |
| 984 | parse_flags |= PARSE_FLAG_PREPROCESS; | 932 | parse_flags |= PARSE_FLAG_PREPROCESS; |
| @@ -1017,18 +965,8 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global) · 32 unmodified lines | |||
| 1017 | opcode = tok; | 965 | opcode = tok; |
| 1018 | next(); | 966 | next(); |
| 1019 | if (tok == ':') { | 967 | if (tok == ':') { |
| 1020 | /* handle "extern void vide(void); __asm__("vide: ret");" as | ||
| 1021 | "__asm__("globl vide\nvide: ret");" */ | ||
| 1022 | Sym *sym = sym_find(opcode); | ||
| 1023 | if (sym && (sym->type.t & VT_EXTERN) && global) { | ||
| 1024 | sym = asm_label_find(opcode); | ||
| 1025 | if (!sym) { | ||
| 1026 | sym = asm_label_push(&s1->asm_labels, opcode); | ||
| 1027 | sym->type.t = VT_VOID | VT_EXTERN; | ||
| 1028 | } | ||
| 1029 | } | ||
| 1030 | /* new label */ | 968 | /* new label */ |
| 1031 | sym = asm_new_label(s1, opcode, 0); | 969 | Sym *sym = asm_new_label(s1, opcode, 0); |
| 1032 | sym->type.t &= ~VT_EXTERN; | 970 | sym->type.t &= ~VT_EXTERN; |
| 1033 | next(); | 971 | next(); |
| 1034 | goto redo; | 972 | goto redo; |
| @@ -310,7 +310,8 @@ static void rebuild_hash(Section *s, unsigned int nb_buckets) · 309 unmodified lines | |||
| 310 | 310 | ||
| 311 | sym = (ElfW(Sym) *)s->data + 1; | 311 | sym = (ElfW(Sym) *)s->data + 1; |
| 312 | for(sym_index = 1; sym_index < nb_syms; sym_index++) { | 312 | for(sym_index = 1; sym_index < nb_syms; sym_index++) { |
| 313 | if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) { | 313 | if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL |
| 314 | || sym->st_shndx == SHN_UNDEF) { | ||
| 314 | h = elf_hash(strtab + sym->st_name) % nb_buckets; | 315 | h = elf_hash(strtab + sym->st_name) % nb_buckets; |
| 315 | *ptr = hash[h]; | 316 | *ptr = hash[h]; |
| 316 | hash[h] = sym_index; | 317 | hash[h] = sym_index; |
| @@ -349,8 +350,9 @@ ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, · 32 unmodified lines | |||
| 349 | int *ptr, *base; | 350 | int *ptr, *base; |
| 350 | ptr = section_ptr_add(hs, sizeof(int)); | 351 | ptr = section_ptr_add(hs, sizeof(int)); |
| 351 | base = (int *)hs->data; | 352 | base = (int *)hs->data; |
| 352 | /* only add global or weak symbols */ | 353 | /* only add global, weak or undef symbols. The latter might |
| 353 | if (ELFW(ST_BIND)(info) != STB_LOCAL) { | 354 | become global late (from asm references). */ |
| 355 | if (ELFW(ST_BIND)(info) != STB_LOCAL || shndx == SHN_UNDEF) { | ||
| 354 | /* add another hashing entry */ | 356 | /* add another hashing entry */ |
| 355 | nbuckets = base[0]; | 357 | nbuckets = base[0]; |
| 356 | h = elf_hash((unsigned char *) name) % nbuckets; | 358 | h = elf_hash((unsigned char *) name) % nbuckets; |
| @@ -486,6 +488,10 @@ ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size, · 129 unmodified lines | |||
| 486 | /* data symbol keeps precedence over common/bss */ | 488 | /* data symbol keeps precedence over common/bss */ |
| 487 | } else if (s == tcc_state->dynsymtab_section) { | 489 | } else if (s == tcc_state->dynsymtab_section) { |
| 488 | /* we accept that two DLL define the same symbol */ | 490 | /* we accept that two DLL define the same symbol */ |
| 491 | } else if (esym->st_other & ST_ASM_SET) { | ||
| 492 | /* If the existing symbol came from an asm .set | ||
| 493 | we can override. */ | ||
| 494 | goto do_patch; | ||
| 489 | } else { | 495 | } else { |
| 490 | #if 0 | 496 | #if 0 |
| 491 | printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n", | 497 | printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n", |
| @@ -286,14 +286,23 @@ ST_FUNC int tccgen_compile(TCCState *s1) · 285 unmodified lines | |||
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | /* ------------------------------------------------------------------------- */ | 288 | /* ------------------------------------------------------------------------- */ |
| 289 | ST_FUNC ElfSym *elfsym(Sym *s) | ||
| 290 | { | ||
| 291 | if (!s || !s->c) | ||
| 292 | return NULL; | ||
| 293 | if (s->c == -1) | ||
| 294 | return &tcc_state->esym_dot; | ||
| 295 | else | ||
| 296 | return &((ElfSym *)symtab_section->data)[s->c]; | ||
| 297 | } | ||
| 298 | |||
| 289 | /* apply storage attributes to Elf symbol */ | 299 | /* apply storage attributes to Elf symbol */ |
| 290 | 300 | ||
| 291 | static void update_storage(Sym *sym) | 301 | static void update_storage(Sym *sym) |
| 292 | { | 302 | { |
| 293 | ElfW(Sym) *esym; | 303 | ElfSym *esym = elfsym(sym); |
| 294 | if (0 == sym->c) | 304 | if (!esym) |
| 295 | return; | 305 | return; |
| 296 | esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; | ||
| 297 | if (sym->a.visibility) | 306 | if (sym->a.visibility) |
| 298 | esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1)) | 307 | esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1)) |
| 299 | | sym->a.visibility; | 308 | | sym->a.visibility; |
| @@ -325,7 +334,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, Section *section, · 25 unmodified lines | |||
| 325 | int can_add_underscore) | 334 | int can_add_underscore) |
| 326 | { | 335 | { |
| 327 | int sym_type, sym_bind, sh_num, info, other, t; | 336 | int sym_type, sym_bind, sh_num, info, other, t; |
| 328 | ElfW(Sym) *esym; | 337 | ElfSym *esym; |
| 329 | const char *name; | 338 | const char *name; |
| 330 | char buf1[256]; | 339 | char buf1[256]; |
| 331 | #ifdef CONFIG_TCC_BCHECK | 340 | #ifdef CONFIG_TCC_BCHECK |
| @@ -402,7 +411,7 @@ ST_FUNC void put_extern_sym2(Sym *sym, Section *section, · 70 unmodified lines | |||
| 402 | info = ELFW(ST_INFO)(sym_bind, sym_type); | 411 | info = ELFW(ST_INFO)(sym_bind, sym_type); |
| 403 | sym->c = set_elf_sym(symtab_section, value, size, info, other, sh_num, name); | 412 | sym->c = set_elf_sym(symtab_section, value, size, info, other, sh_num, name); |
| 404 | } else { | 413 | } else { |
| 405 | esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; | 414 | esym = elfsym(sym); |
| 406 | esym->st_value = value; | 415 | esym->st_value = value; |
| 407 | esym->st_size = size; | 416 | esym->st_size = size; |
| 408 | esym->st_shndx = sh_num; | 417 | esym->st_shndx = sh_num; |
| @@ -569,23 +578,28 @@ ST_FUNC Sym *sym_push(int v, CType *type, int r, int c) · 160 unmodified lines | |||
| 569 | } | 578 | } |
| 570 | 579 | ||
| 571 | /* push a global identifier */ | 580 | /* push a global identifier */ |
| 572 | ST_FUNC Sym *global_identifier_push(int v, int t, int c) | 581 | ST_FUNC Sym *global_identifier_push_1(Sym **ptop, int v, int t, int c) |
| 573 | { | 582 | { |
| 574 | Sym *s, **ps; | 583 | Sym *s, **ps; |
| 575 | s = sym_push2(&global_stack, v, t, c); | 584 | s = sym_push2(ptop, v, t, c); |
| 576 | /* don't record anonymous symbol */ | 585 | /* don't record anonymous symbol */ |
| 577 | if (v < SYM_FIRST_ANOM) { | 586 | if (v < SYM_FIRST_ANOM) { |
| 578 | ps = &table_ident[v - TOK_IDENT]->sym_identifier; | 587 | ps = &table_ident[v - TOK_IDENT]->sym_identifier; |
| 579 | /* modify the top most local identifier, so that | 588 | /* modify the top most local identifier, so that |
| 580 | sym_identifier will point to 's' when popped */ | 589 | sym_identifier will point to 's' when popped */ |
| 581 | while (*ps != NULL) | 590 | while (*ps != NULL && (*ps)->sym_scope) |
| 582 | ps = &(*ps)->prev_tok; | 591 | ps = &(*ps)->prev_tok; |
| 583 | s->prev_tok = NULL; | 592 | s->prev_tok = *ps; |
| 584 | *ps = s; | 593 | *ps = s; |
| 585 | } | 594 | } |
| 586 | return s; | 595 | return s; |
| 587 | } | 596 | } |
| 588 | 597 | ||
| 598 | static Sym *global_identifier_push(int v, int t, int c) | ||
| 599 | { | ||
| 600 | return global_identifier_push_1(&global_stack, v, t, c); | ||
| 601 | } | ||
| 602 | |||
| 589 | /* pop symbols until top reaches 'b'. If KEEP is non-zero don't really | 603 | /* pop symbols until top reaches 'b'. If KEEP is non-zero don't really |
| 590 | pop them yet from the list, but do remove them from the token array. */ | 604 | pop them yet from the list, but do remove them from the token array. */ |
| 591 | ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep) | 605 | ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep) |
| @@ -834,9 +848,13 @@ ST_FUNC Sym *external_global_sym(int v, CType *type, int r) · 242 unmodified lines | |||
| 834 | /* Merge some storage attributes. */ | 848 | /* Merge some storage attributes. */ |
| 835 | static void patch_storage(Sym *sym, AttributeDef *ad, CType *type) | 849 | static void patch_storage(Sym *sym, AttributeDef *ad, CType *type) |
| 836 | { | 850 | { |
| 837 | if (type && !is_compatible_types(&sym->type, type)) | 851 | if (type) { |
| 838 | tcc_error("incompatible types for redefinition of '%s'", | 852 | if ((sym->type.t & VT_BTYPE) == VT_VOID) /* from asm */ |
| 839 | get_tok_str(sym->v, NULL)); | 853 | sym->type = *type; |
| 854 | else if (!is_compatible_types(&sym->type, type)) | ||
| 855 | tcc_error("incompatible types for redefinition of '%s'", | ||
| 856 | get_tok_str(sym->v, NULL)); | ||
| 857 | } | ||
| 840 | #ifdef TCC_TARGET_PE | 858 | #ifdef TCC_TARGET_PE |
| 841 | if (sym->a.dllimport != ad->a.dllimport) | 859 | if (sym->a.dllimport != ad->a.dllimport) |
| 842 | tcc_error("incompatible dll linkage for redefinition of '%s'", | 860 | tcc_error("incompatible dll linkage for redefinition of '%s'", |
| @@ -6426,9 +6444,9 @@ static void init_putv(CType *type, Section *sec, unsigned long c) · 5583 unmodified lines | |||
| 6426 | (vtop->type.t & VT_BTYPE) != VT_PTR) { | 6444 | (vtop->type.t & VT_BTYPE) != VT_PTR) { |
| 6427 | /* These come from compound literals, memcpy stuff over. */ | 6445 | /* These come from compound literals, memcpy stuff over. */ |
| 6428 | Section *ssec; | 6446 | Section *ssec; |
| 6429 | ElfW(Sym) *esym; | 6447 | ElfSym *esym; |
| 6430 | ElfW_Rel *rel; | 6448 | ElfW_Rel *rel; |
| 6431 | esym = &((ElfW(Sym) *)symtab_section->data)[vtop->sym->c]; | 6449 | esym = elfsym(vtop->sym); |
| 6432 | ssec = tcc_state->sections[esym->st_shndx]; | 6450 | ssec = tcc_state->sections[esym->st_shndx]; |
| 6433 | memmove (ptr, ssec->data + esym->st_value, size); | 6451 | memmove (ptr, ssec->data + esym->st_value, size); |
| 6434 | if (ssec->reloc) { | 6452 | if (ssec->reloc) { |
| @@ -6895,8 +6913,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 460 unmodified lines | |||
| 6895 | /* no init data, we won't add more to the symbol */ | 6913 | /* no init data, we won't add more to the symbol */ |
| 6896 | goto no_alloc; | 6914 | goto no_alloc; |
| 6897 | } else if (sym->c) { | 6915 | } else if (sym->c) { |
| 6898 | ElfW(Sym) *esym; | 6916 | ElfSym *esym = elfsym(sym); |
| 6899 | esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; | ||
| 6900 | if (esym->st_shndx == data_section->sh_num) | 6917 | if (esym->st_shndx == data_section->sh_num) |
| 6901 | tcc_error("redefinition of '%s'", get_tok_str(v, NULL)); | 6918 | tcc_error("redefinition of '%s'", get_tok_str(v, NULL)); |
| 6902 | } | 6919 | } |
| @@ -7030,8 +7047,7 @@ static void gen_function(Sym *sym) · 127 unmodified lines | |||
| 7030 | sym_pop(&local_stack, NULL, 0); | 7047 | sym_pop(&local_stack, NULL, 0); |
| 7031 | /* end of function */ | 7048 | /* end of function */ |
| 7032 | /* patch symbol size */ | 7049 | /* patch symbol size */ |
| 7033 | ((ElfW(Sym) *)symtab_section->data)[sym->c].st_size = | 7050 | elfsym(sym)->st_size = ind - func_ind; |
| 7034 | ind - func_ind; | ||
| 7035 | tcc_debug_funcend(tcc_state, ind - func_ind); | 7051 | tcc_debug_funcend(tcc_state, ind - func_ind); |
| 7036 | /* It's better to crash than to generate wrong code */ | 7052 | /* It's better to crash than to generate wrong code */ |
| 7037 | cur_text_section = NULL; | 7053 | cur_text_section = NULL; |
| @@ -7209,6 +7225,11 @@ static int decl0(int l, int is_for_loop_init, Sym *func_sym) · 171 unmodified lines | |||
| 7209 | sym = sym_find(v); | 7225 | sym = sym_find(v); |
| 7210 | if (sym) { | 7226 | if (sym) { |
| 7211 | Sym *ref; | 7227 | Sym *ref; |
| 7228 | /* If type is VT_VOID the symbol was created by tccasm | ||
| 7229 | first, and we see the first reference from C now. */ | ||
| 7230 | if ((sym->type.t & VT_BTYPE) == VT_VOID) | ||
| 7231 | sym->type = type; | ||
| 7232 | |||
| 7212 | if ((sym->type.t & VT_BTYPE) != VT_FUNC) | 7233 | if ((sym->type.t & VT_BTYPE) != VT_FUNC) |
| 7213 | goto func_error1; | 7234 | goto func_error1; |
| 7214 | 7235 | ||
| @@ -7330,12 +7351,12 @@ found: · 115 unmodified lines | |||
| 7330 | sym = external_sym(v, &type, r, &ad); | 7351 | sym = external_sym(v, &type, r, &ad); |
| 7331 | if (ad.alias_target) { | 7352 | if (ad.alias_target) { |
| 7332 | Section tsec; | 7353 | Section tsec; |
| 7333 | ElfW(Sym) *esym; | 7354 | ElfSym *esym; |
| 7334 | Sym *alias_target; | 7355 | Sym *alias_target; |
| 7335 | alias_target = sym_find(ad.alias_target); | 7356 | alias_target = sym_find(ad.alias_target); |
| 7336 | if (!alias_target || !alias_target->c) | 7357 | esym = elfsym(alias_target); |
| 7358 | if (!esym) | ||
| 7337 | tcc_error("unsupported forward __alias__ attribute"); | 7359 | tcc_error("unsupported forward __alias__ attribute"); |
| 7338 | esym = &((ElfW(Sym) *)symtab_section->data)[alias_target->c]; | ||
| 7339 | tsec.sh_num = esym->st_shndx; | 7360 | tsec.sh_num = esym->st_shndx; |
| 7340 | /* Local statics have a scope until now (for | 7361 | /* Local statics have a scope until now (for |
| 7341 | warnings), remove it here. */ | 7362 | warnings), remove it here. */ |
| @@ -432,7 +432,6 @@ static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len) · 431 unmodified lines | |||
| 432 | ts->sym_label = NULL; | 432 | ts->sym_label = NULL; |
| 433 | ts->sym_struct = NULL; | 433 | ts->sym_struct = NULL; |
| 434 | ts->sym_identifier = NULL; | 434 | ts->sym_identifier = NULL; |
| 435 | ts->sym_asm_label = NULL; | ||
| 436 | ts->len = len; | 435 | ts->len = len; |
| 437 | ts->hash_next = NULL; | 436 | ts->hash_next = NULL; |
| 438 | memcpy(ts->str, str, len); | 437 | memcpy(ts->str, str, len); |
| @@ -14,11 +14,13 @@ static int x1_c(void) · 13 unmodified lines | |||
| 14 | 14 | ||
| 15 | asm(".text;"_"x1: call "_"x1_c; ret"); | 15 | asm(".text;"_"x1: call "_"x1_c; ret"); |
| 16 | 16 | ||
| 17 | void callx4(void); | ||
| 17 | int main(int argc, char *argv[]) | 18 | int main(int argc, char *argv[]) |
| 18 | { | 19 | { |
| 19 | asm("call "_"x1"); | 20 | asm("call "_"x1"); |
| 20 | asm("call "_"x2"); | 21 | asm("call "_"x2"); |
| 21 | asm("call "_"x3"); | 22 | asm("call "_"x3"); |
| 23 | callx4(); | ||
| 22 | return 0; | 24 | return 0; |
| 23 | } | 25 | } |
| 24 | 26 | ||
| @@ -30,3 +32,8 @@ int x2(void) · 5 unmodified lines | |||
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | extern int x3(void); | 34 | extern int x3(void); |
| 35 | |||
| 36 | void x4(void) | ||
| 37 | { | ||
| 38 | printf("x4\n"); | ||
| 39 | } | ||
| @@ -5,3 +5,6 @@ int x3(void) · 4 unmodified lines | |||
| 5 | printf("x3\n"); | 5 | printf("x3\n"); |
| 6 | return 3; | 6 | return 3; |
| 7 | } | 7 | } |
| 8 | |||
| 9 | void callx4(void); | ||
| 10 | __asm__("callx4: call x4; ret"); | ||