18,7 → 18,10 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
*/ |
|
static int asm_get_local_label_name(TCCState *s1, unsigned int n) |
#include "tcc.h" |
#ifdef CONFIG_TCC_ASM |
|
ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n) |
{ |
char buf[64]; |
TokenSym *ts; |
28,7 → 31,9 |
return ts->tok; |
} |
|
static void asm_expr(TCCState *s1, ExprValue *pe); |
ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe); |
static int tcc_assemble_internal(TCCState *s1, int do_preprocess); |
static Sym sym_dot; |
|
/* We do not use the C expression parser to handle symbols. Maybe the |
C expression parser could be tweaked to do so. */ |
41,7 → 46,7 |
|
switch(tok) { |
case TOK_PPNUM: |
p = tokc.cstr->data; |
p = tokc.str.data; |
n = strtoul(p, (char **)&p, 0); |
if (*p == 'b' || *p == 'f') { |
/* backward or forward label */ |
52,7 → 57,7 |
if (sym && sym->r == 0) |
sym = sym->prev_tok; |
if (!sym) |
error("local label '%d' not found backward", n); |
tcc_error("local label '%d' not found backward", n); |
} else { |
/* forward */ |
if (!sym || sym->r) { |
67,7 → 72,7 |
pe->v = n; |
pe->sym = NULL; |
} else { |
error("invalid number syntax"); |
tcc_error("invalid number syntax"); |
} |
next(); |
break; |
81,7 → 86,7 |
next(); |
asm_expr_unary(s1, pe); |
if (pe->sym) |
error("invalid operation with label"); |
tcc_error("invalid operation with label"); |
if (op == '-') |
pe->v = -pe->v; |
else |
98,6 → 103,14 |
asm_expr(s1, pe); |
skip(')'); |
break; |
case '.': |
pe->v = 0; |
pe->sym = &sym_dot; |
sym_dot.type.t = VT_VOID | VT_STATIC; |
sym_dot.r = cur_text_section->sh_num; |
sym_dot.jnext = ind; |
next(); |
break; |
default: |
if (tok >= TOK_IDENT) { |
/* label case : if the label was not found, add one */ |
109,7 → 122,7 |
} |
if (sym->r == SHN_ABS) { |
/* if absolute symbol, no need to put a symbol value */ |
pe->v = (long)sym->next; |
pe->v = sym->jnext; |
pe->sym = NULL; |
} else { |
pe->v = 0; |
117,7 → 130,7 |
} |
next(); |
} else { |
error("bad expression syntax [%s]", get_tok_str(tok, &tokc)); |
tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc)); |
} |
break; |
} |
137,7 → 150,7 |
next(); |
asm_expr_unary(s1, &e2); |
if (pe->sym || e2.sym) |
error("invalid operation with label"); |
tcc_error("invalid operation with label"); |
switch(op) { |
case '*': |
pe->v *= e2.v; |
145,7 → 158,7 |
case '/': |
if (e2.v == 0) { |
div_error: |
error("division by zero"); |
tcc_error("division by zero"); |
} |
pe->v /= e2.v; |
break; |
178,7 → 191,7 |
next(); |
asm_expr_prod(s1, &e2); |
if (pe->sym || e2.sym) |
error("invalid operation with label"); |
tcc_error("invalid operation with label"); |
switch(op) { |
case '&': |
pe->v &= e2.v; |
225,25 → 238,25 |
/* OK */ |
} else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) { |
/* we also accept defined symbols in the same section */ |
pe->v += (long)pe->sym->next - (long)e2.sym->next; |
pe->v += pe->sym->jnext - e2.sym->jnext; |
} else { |
goto cannot_relocate; |
} |
pe->sym = NULL; /* same symbols can be substracted to NULL */ |
pe->sym = NULL; /* same symbols can be subtracted to NULL */ |
} else { |
cannot_relocate: |
error("invalid operation with label"); |
tcc_error("invalid operation with label"); |
} |
} |
} |
} |
|
static void asm_expr(TCCState *s1, ExprValue *pe) |
ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe) |
{ |
asm_expr_sum(s1, pe); |
} |
|
static int asm_int_expr(TCCState *s1) |
ST_FUNC int asm_int_expr(TCCState *s1) |
{ |
ExprValue e; |
asm_expr(s1, &e); |
264,7 → 277,7 |
if (sym->r) { |
/* the label is already defined */ |
if (!is_local) { |
error("assembler label '%s' already defined", |
tcc_error("assembler label '%s' already defined", |
get_tok_str(label, NULL)); |
} else { |
/* redefinition of local labels is possible */ |
277,7 → 290,7 |
sym->type.t = VT_STATIC | VT_VOID; |
} |
sym->r = sh_num; |
sym->next = (void *)value; |
sym->jnext = value; |
} |
|
static void asm_new_label(TCCState *s1, int label, int is_local) |
298,7 → 311,7 |
sec = SECTION_ABS; |
else |
sec = st->sections[s->r]; |
put_extern_sym2(s, sec, (long)s->next, 0, 0); |
put_extern_sym2(s, sec, s->jnext, 0, 0); |
} |
/* remove label */ |
table_ident[s->v - TOK_IDENT]->sym_label = NULL; |
328,18 → 341,25 |
uint8_t *ptr; |
|
/* assembler directive */ |
next(); |
sec = cur_text_section; |
switch(tok) { |
case TOK_ASM_align: |
case TOK_ASM_skip: |
case TOK_ASM_space: |
case TOK_ASMDIR_align: |
case TOK_ASMDIR_p2align: |
case TOK_ASMDIR_skip: |
case TOK_ASMDIR_space: |
tok1 = tok; |
next(); |
n = asm_int_expr(s1); |
if (tok1 == TOK_ASM_align) { |
if (tok1 == TOK_ASMDIR_p2align) |
{ |
if (n < 0 || n > 30) |
tcc_error("invalid p2align, must be between 0 and 30"); |
n = 1 << n; |
tok1 = TOK_ASMDIR_align; |
} |
if (tok1 == TOK_ASMDIR_align) { |
if (n < 0 || (n & (n-1)) != 0) |
error("alignment must be a positive power of two"); |
tcc_error("alignment must be a positive power of two"); |
offset = (ind + n - 1) & -n; |
size = offset - ind; |
/* the section must have a compatible alignment */ |
361,16 → 381,16 |
} |
ind += size; |
break; |
case TOK_ASM_quad: |
case TOK_ASMDIR_quad: |
next(); |
for(;;) { |
uint64_t vl; |
const char *p; |
|
p = tokc.cstr->data; |
p = tokc.str.data; |
if (tok != TOK_PPNUM) { |
error_constant: |
error("64 bit constant"); |
tcc_error("64 bit constant"); |
} |
vl = strtoll(p, (char **)&p, 0); |
if (*p != '\0') |
388,15 → 408,15 |
next(); |
} |
break; |
case TOK_ASM_byte: |
case TOK_ASMDIR_byte: |
size = 1; |
goto asm_data; |
case TOK_ASM_word: |
case TOK_SHORT: |
case TOK_ASMDIR_word: |
case TOK_ASMDIR_short: |
size = 2; |
goto asm_data; |
case TOK_LONG: |
case TOK_INT: |
case TOK_ASMDIR_long: |
case TOK_ASMDIR_int: |
size = 4; |
asm_data: |
next(); |
422,7 → 442,7 |
next(); |
} |
break; |
case TOK_ASM_fill: |
case TOK_ASMDIR_fill: |
{ |
int repeat, size, val, i, j; |
uint8_t repeat_buf[8]; |
429,7 → 449,7 |
next(); |
repeat = asm_int_expr(s1); |
if (repeat < 0) { |
error("repeat < 0; .fill ignored"); |
tcc_error("repeat < 0; .fill ignored"); |
break; |
} |
size = 1; |
438,7 → 458,7 |
next(); |
size = asm_int_expr(s1); |
if (size < 0) { |
error("size < 0; .fill ignored"); |
tcc_error("size < 0; .fill ignored"); |
break; |
} |
if (size > 8) |
464,22 → 484,52 |
} |
} |
break; |
case TOK_ASM_org: |
case TOK_ASMDIR_rept: |
{ |
int repeat; |
TokenString init_str; |
ParseState saved_parse_state = {0}; |
next(); |
repeat = asm_int_expr(s1); |
tok_str_new(&init_str); |
next(); |
while ((tok != TOK_ASMDIR_endr) && (tok != CH_EOF)) { |
tok_str_add_tok(&init_str); |
next(); |
} |
if (tok == CH_EOF) tcc_error("we at end of file, .endr not found"); |
next(); |
tok_str_add(&init_str, -1); |
tok_str_add(&init_str, 0); |
save_parse_state(&saved_parse_state); |
begin_macro(&init_str, 0); |
while (repeat-- > 0) { |
tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS)); |
macro_ptr = init_str.str; |
} |
end_macro(); |
restore_parse_state(&saved_parse_state); |
break; |
} |
case TOK_ASMDIR_org: |
{ |
unsigned long n; |
next(); |
/* XXX: handle section symbols too */ |
n = asm_int_expr(s1); |
if (n < ind) |
error("attempt to .org backwards"); |
tcc_error("attempt to .org backwards"); |
v = 0; |
size = n - ind; |
goto zero_pad; |
} |
break; |
case TOK_ASM_globl: |
case TOK_ASM_global: |
{ |
case TOK_ASMDIR_globl: |
case TOK_ASMDIR_global: |
case TOK_ASMDIR_weak: |
case TOK_ASMDIR_hidden: |
tok1 = tok; |
do { |
Sym *sym; |
|
next(); |
488,13 → 538,18 |
sym = label_push(&s1->asm_labels, tok, 0); |
sym->type.t = VT_VOID; |
} |
if (tok1 != TOK_ASMDIR_hidden) |
sym->type.t &= ~VT_STATIC; |
if (tok1 == TOK_ASMDIR_weak) |
sym->type.t |= VT_WEAK; |
else if (tok1 == TOK_ASMDIR_hidden) |
sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT; |
next(); |
} |
} while (tok == ','); |
break; |
case TOK_ASM_string: |
case TOK_ASM_ascii: |
case TOK_ASM_asciz: |
case TOK_ASMDIR_string: |
case TOK_ASMDIR_ascii: |
case TOK_ASMDIR_asciz: |
{ |
const uint8_t *p; |
int i, size, t; |
504,9 → 559,9 |
for(;;) { |
if (tok != TOK_STR) |
expect("string constant"); |
p = tokc.cstr->data; |
size = tokc.cstr->size; |
if (t == TOK_ASM_ascii && size > 0) |
p = tokc.str.data; |
size = tokc.str.size; |
if (t == TOK_ASMDIR_ascii && size > 0) |
size--; |
for(i = 0; i < size; i++) |
g(p[i]); |
519,9 → 574,9 |
} |
} |
break; |
case TOK_ASM_text: |
case TOK_ASM_data: |
case TOK_ASM_bss: |
case TOK_ASMDIR_text: |
case TOK_ASMDIR_data: |
case TOK_ASMDIR_bss: |
{ |
char sname[64]; |
tok1 = tok; |
531,12 → 586,104 |
n = asm_int_expr(s1); |
next(); |
} |
sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n); |
if (n) |
sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n); |
else |
sprintf(sname, "%s", get_tok_str(tok1, NULL)); |
use_section(s1, sname); |
} |
break; |
case TOK_SECTION1: |
case TOK_ASMDIR_file: |
{ |
char filename[512]; |
|
filename[0] = '\0'; |
next(); |
|
if (tok == TOK_STR) |
pstrcat(filename, sizeof(filename), tokc.str.data); |
else |
pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL)); |
|
if (s1->warn_unsupported) |
tcc_warning("ignoring .file %s", filename); |
|
next(); |
} |
break; |
case TOK_ASMDIR_ident: |
{ |
char ident[256]; |
|
ident[0] = '\0'; |
next(); |
|
if (tok == TOK_STR) |
pstrcat(ident, sizeof(ident), tokc.str.data); |
else |
pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL)); |
|
if (s1->warn_unsupported) |
tcc_warning("ignoring .ident %s", ident); |
|
next(); |
} |
break; |
case TOK_ASMDIR_size: |
{ |
Sym *sym; |
|
next(); |
sym = label_find(tok); |
if (!sym) { |
tcc_error("label not found: %s", get_tok_str(tok, NULL)); |
} |
|
/* XXX .size name,label2-label1 */ |
if (s1->warn_unsupported) |
tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL)); |
|
next(); |
skip(','); |
while (tok != '\n' && tok != CH_EOF) { |
next(); |
} |
} |
break; |
case TOK_ASMDIR_type: |
{ |
Sym *sym; |
const char *newtype; |
|
next(); |
sym = label_find(tok); |
if (!sym) { |
sym = label_push(&s1->asm_labels, tok, 0); |
sym->type.t = VT_VOID; |
} |
|
next(); |
skip(','); |
if (tok == TOK_STR) { |
newtype = tokc.str.data; |
} else { |
if (tok == '@' || tok == '%') |
next(); |
newtype = get_tok_str(tok, NULL); |
} |
|
if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) { |
sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC; |
} |
else if (s1->warn_unsupported) |
tcc_warning("change type of '%s' from 0x%x to '%s' ignored", |
get_tok_str(sym->v, NULL), sym->type.t, newtype); |
|
next(); |
} |
break; |
case TOK_ASMDIR_section: |
{ |
char sname[256]; |
|
/* XXX: support more options */ |
544,7 → 691,7 |
sname[0] = '\0'; |
while (tok != ';' && tok != TOK_LINEFEED && tok != ',') { |
if (tok == TOK_STR) |
pstrcat(sname, sizeof(sname), tokc.cstr->data); |
pstrcat(sname, sizeof(sname), tokc.str.data); |
else |
pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL)); |
next(); |
555,24 → 702,50 |
if (tok != TOK_STR) |
expect("string constant"); |
next(); |
if (tok == ',') { |
next(); |
if (tok == '@' || tok == '%') |
next(); |
next(); |
} |
} |
last_text_section = cur_text_section; |
use_section(s1, sname); |
} |
break; |
case TOK_ASM_previous: |
case TOK_ASMDIR_previous: |
{ |
Section *sec; |
next(); |
if (!last_text_section) |
error("no previous section referenced"); |
tcc_error("no previous section referenced"); |
sec = cur_text_section; |
use_section1(s1, last_text_section); |
last_text_section = sec; |
} |
break; |
#ifdef TCC_TARGET_I386 |
case TOK_ASMDIR_code16: |
{ |
next(); |
s1->seg_size = 16; |
} |
break; |
case TOK_ASMDIR_code32: |
{ |
next(); |
s1->seg_size = 32; |
} |
break; |
#endif |
#ifdef TCC_TARGET_X86_64 |
/* added for compatibility with GAS */ |
case TOK_ASMDIR_code64: |
next(); |
break; |
#endif |
default: |
error("unknown assembler directive '.%s'", get_tok_str(tok, NULL)); |
tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL)); |
break; |
} |
} |
619,7 → 792,7 |
|
ch = file->buf_ptr[0]; |
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; |
parse_flags = PARSE_FLAG_ASM_COMMENTS; |
parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR; |
if (do_preprocess) |
parse_flags |= PARSE_FLAG_PREPROCESS; |
next(); |
632,12 → 805,12 |
/* horrible gas comment */ |
while (tok != TOK_LINEFEED) |
next(); |
} else if (tok == '.') { |
} else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) { |
asm_parse_directive(s1); |
} else if (tok == TOK_PPNUM) { |
const char *p; |
int n; |
p = tokc.cstr->data; |
p = tokc.str.data; |
n = strtoul(p, (char **)&p, 10); |
if (*p != '\0') |
expect("':'"); |
679,7 → 852,7 |
} |
|
/* Assemble the current file */ |
static int tcc_assemble(TCCState *s1, int do_preprocess) |
ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess) |
{ |
Sym *define_start; |
int ret; |
692,6 → 865,12 |
|
define_start = define_stack; |
|
/* an elf symbol of type STT_FILE must be put so that STB_LOCAL |
symbols can be safely used */ |
put_elf_sym(symtab_section, 0, 0, |
ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0, |
SHN_ABS, file->filename); |
|
ret = tcc_assemble_internal(s1, do_preprocess); |
|
cur_text_section->data_offset = ind; |
709,37 → 888,27 |
end */ |
static void tcc_assemble_inline(TCCState *s1, char *str, int len) |
{ |
BufferedFile *bf, *saved_file; |
int saved_parse_flags, *saved_macro_ptr; |
int saved_parse_flags; |
const int *saved_macro_ptr; |
|
bf = tcc_malloc(sizeof(BufferedFile)); |
memset(bf, 0, sizeof(BufferedFile)); |
bf->fd = -1; |
bf->buf_ptr = str; |
bf->buf_end = str + len; |
str[len] = CH_EOB; |
/* same name as current file so that errors are correctly |
reported */ |
pstrcpy(bf->filename, sizeof(bf->filename), file->filename); |
bf->line_num = file->line_num; |
saved_file = file; |
file = bf; |
saved_parse_flags = parse_flags; |
saved_macro_ptr = macro_ptr; |
|
tcc_open_bf(s1, ":asm:", len); |
memcpy(file->buffer, str, len); |
|
macro_ptr = NULL; |
|
tcc_assemble_internal(s1, 0); |
tcc_close(); |
|
parse_flags = saved_parse_flags; |
macro_ptr = saved_macro_ptr; |
file = saved_file; |
tcc_free(bf); |
} |
|
/* find a constraint by its number or id (gcc 3 extended |
syntax). return -1 if not found. Return in *pp in char after the |
constraint */ |
static int find_constraint(ASMOperand *operands, int nb_operands, |
ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, |
const char *name, const char **pp) |
{ |
int index; |
801,12 → 970,12 |
modifier = *str++; |
index = find_constraint(operands, nb_operands, str, &str); |
if (index < 0) |
error("invalid operand reference after %%"); |
tcc_error("invalid operand reference after %%"); |
op = &operands[index]; |
sv = *op->vt; |
if (op->reg >= 0) { |
sv.r = op->reg; |
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) |
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory) |
sv.r |= VT_LVAL; |
} |
subst_asm_operand(out_str, &sv, modifier); |
830,7 → 999,7 |
nb_operands = *nb_operands_ptr; |
for(;;) { |
if (nb_operands >= MAX_ASM_OPERANDS) |
error("too many asm operands"); |
tcc_error("too many asm operands"); |
op = &operands[nb_operands++]; |
op->id = 0; |
if (tok == '[') { |
843,8 → 1012,8 |
} |
if (tok != TOK_STR) |
expect("string constant"); |
op->constraint = tcc_malloc(tokc.cstr->size); |
strcpy(op->constraint, tokc.cstr->data); |
op->constraint = tcc_malloc(tokc.str.size); |
strcpy(op->constraint, tokc.str.data); |
next(); |
skip('('); |
gexpr(); |
874,27 → 1043,12 |
} |
} |
|
static void parse_asm_str(CString *astr) |
{ |
skip('('); |
/* read the string */ |
if (tok != TOK_STR) |
expect("string constant"); |
cstr_new(astr); |
while (tok == TOK_STR) { |
/* XXX: add \0 handling too ? */ |
cstr_cat(astr, tokc.cstr->data); |
next(); |
} |
cstr_ccat(astr, '\0'); |
} |
|
/* parse the GCC asm() instruction */ |
static void asm_instr(void) |
ST_FUNC void asm_instr(void) |
{ |
CString astr, astr1; |
ASMOperand operands[MAX_ASM_OPERANDS]; |
int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg; |
int nb_outputs, nb_operands, i, must_subst, out_reg; |
uint8_t clobber_regs[NB_ASM_REGS]; |
|
next(); |
916,6 → 1070,7 |
nb_outputs = nb_operands; |
if (tok == ':') { |
next(); |
if (tok != ')') { |
/* input args */ |
parse_asm_operands(operands, &nb_operands, 0); |
if (tok == ':') { |
925,7 → 1080,7 |
for(;;) { |
if (tok != TOK_STR) |
expect("string constant"); |
asm_clobber(clobber_regs, tokc.cstr->data); |
asm_clobber(clobber_regs, tokc.str.data); |
next(); |
if (tok == ',') { |
next(); |
936,12 → 1091,12 |
} |
} |
} |
} |
skip(')'); |
/* NOTE: we do not eat the ';' so that we can restore the current |
token after the assembler parsing */ |
if (tok != ';') |
expect("';'"); |
nb_inputs = nb_operands - nb_outputs; |
|
/* save all values in the memory */ |
save_regs(0); |
989,7 → 1144,7 |
cstr_free(&astr1); |
} |
|
static void asm_global_instr(void) |
ST_FUNC void asm_global_instr(void) |
{ |
CString astr; |
|
1017,3 → 1172,4 |
|
cstr_free(&astr); |
} |
#endif /* CONFIG_TCC_ASM */ |