12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304 |
- /*
- * GAS like assembler for TCC
- *
- * Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #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;
- snprintf(buf, sizeof(buf), "L..%u", n);
- ts = tok_alloc(buf, strlen(buf));
- return ts->tok;
- }
- ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
- static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
- 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.
-
- If CSYM is non-null we take symbol info from it, otherwise
- we look up NAME in the C symbol table and use that. */
- ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
- {
- Sym *sym = label_find(name);
- if (!sym) {
- sym = label_push(&tcc_state->asm_labels, name, 0);
- sym->type.t = VT_VOID | VT_EXTERN;
- if (!csym) {
- csym = sym_find(name);
- /* 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->sym_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->c = 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->a.dllimport = 1;
- } else {
- sym->type.t |= VT_STATIC;
- }
- }
- return sym;
- }
- /* We do not use the C expression parser to handle symbols. Maybe the
- C expression parser could be tweaked to do so. */
- static void asm_expr_unary(TCCState *s1, ExprValue *pe)
- {
- Sym *sym;
- int op, label;
- uint64_t n;
- const char *p;
- switch(tok) {
- case TOK_PPNUM:
- p = tokc.str.data;
- n = strtoull(p, (char **)&p, 0);
- if (*p == 'b' || *p == 'f') {
- /* backward or forward label */
- label = asm_get_local_label_name(s1, n);
- sym = label_find(label);
- if (*p == 'b') {
- /* backward : find the last corresponding defined label */
- if (sym && sym->r == 0)
- sym = sym->prev_tok;
- if (!sym)
- tcc_error("local label '%d' not found backward", n);
- } else {
- /* forward */
- if (!sym || sym->r) {
- /* if the last label is defined, then define a new one */
- sym = label_push(&s1->asm_labels, label, 0);
- sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
- }
- }
- 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");
- }
- next();
- break;
- case '+':
- next();
- asm_expr_unary(s1, pe);
- break;
- case '-':
- case '~':
- op = tok;
- next();
- asm_expr_unary(s1, pe);
- if (pe->sym)
- tcc_error("invalid operation with label");
- if (op == '-')
- pe->v = -pe->v;
- else
- pe->v = ~pe->v;
- break;
- case TOK_CCHAR:
- case TOK_LCHAR:
- pe->v = tokc.i;
- pe->sym = NULL;
- pe->pcrel = 0;
- next();
- break;
- case '(':
- next();
- asm_expr(s1, pe);
- skip(')');
- break;
- 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;
- next();
- break;
- default:
- if (tok >= TOK_IDENT) {
- /* label case : if the label was not found, add one */
- sym = get_asm_sym(tok, NULL);
- if (sym->r == SHN_ABS) {
- /* 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 {
- tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
- }
- break;
- }
- }
-
- static void asm_expr_prod(TCCState *s1, ExprValue *pe)
- {
- int op;
- ExprValue e2;
- asm_expr_unary(s1, pe);
- for(;;) {
- op = tok;
- if (op != '*' && op != '/' && op != '%' &&
- op != TOK_SHL && op != TOK_SAR)
- break;
- next();
- asm_expr_unary(s1, &e2);
- if (pe->sym || e2.sym)
- tcc_error("invalid operation with label");
- switch(op) {
- case '*':
- pe->v *= e2.v;
- break;
- case '/':
- if (e2.v == 0) {
- div_error:
- tcc_error("division by zero");
- }
- pe->v /= e2.v;
- break;
- case '%':
- if (e2.v == 0)
- goto div_error;
- pe->v %= e2.v;
- break;
- case TOK_SHL:
- pe->v <<= e2.v;
- break;
- default:
- case TOK_SAR:
- pe->v >>= e2.v;
- break;
- }
- }
- }
- static void asm_expr_logic(TCCState *s1, ExprValue *pe)
- {
- int op;
- ExprValue e2;
- asm_expr_prod(s1, pe);
- for(;;) {
- op = tok;
- if (op != '&' && op != '|' && op != '^')
- break;
- next();
- asm_expr_prod(s1, &e2);
- if (pe->sym || e2.sym)
- tcc_error("invalid operation with label");
- switch(op) {
- case '&':
- pe->v &= e2.v;
- break;
- case '|':
- pe->v |= e2.v;
- break;
- default:
- case '^':
- pe->v ^= e2.v;
- break;
- }
- }
- }
- static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
- {
- int op;
- ExprValue e2;
- asm_expr_logic(s1, pe);
- for(;;) {
- op = tok;
- if (op != '+' && op != '-')
- break;
- next();
- asm_expr_logic(s1, &e2);
- if (op == '+') {
- if (pe->sym != NULL && e2.sym != NULL)
- goto cannot_relocate;
- pe->v += e2.v;
- if (pe->sym == NULL && e2.sym != NULL)
- pe->sym = e2.sym;
- } else {
- pe->v -= e2.v;
- /* NOTE: we are less powerful than gas in that case
- because we store only one symbol in the expression */
- 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");
- }
- }
- }
- }
- static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe)
- {
- int op;
- ExprValue e2;
- asm_expr_sum(s1, pe);
- for(;;) {
- op = tok;
- if (op != TOK_EQ && op != TOK_NE
- && (op > TOK_GT || op < TOK_ULE))
- break;
- next();
- asm_expr_sum(s1, &e2);
- if (pe->sym || e2.sym)
- tcc_error("invalid operation with label");
- switch(op) {
- case TOK_EQ:
- pe->v = pe->v == e2.v;
- break;
- case TOK_NE:
- pe->v = pe->v != e2.v;
- break;
- case TOK_LT:
- pe->v = (int64_t)pe->v < (int64_t)e2.v;
- break;
- case TOK_GE:
- pe->v = (int64_t)pe->v >= (int64_t)e2.v;
- break;
- case TOK_LE:
- pe->v = (int64_t)pe->v <= (int64_t)e2.v;
- break;
- case TOK_GT:
- pe->v = (int64_t)pe->v > (int64_t)e2.v;
- break;
- default:
- break;
- }
- /* GAS compare results are -1/0 not 1/0. */
- pe->v = -(int64_t)pe->v;
- }
- }
- ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
- {
- asm_expr_cmp(s1, pe);
- }
- ST_FUNC int asm_int_expr(TCCState *s1)
- {
- ExprValue e;
- asm_expr(s1, &e);
- if (e.sym)
- expect("constant");
- return e.v;
- }
- /* NOTE: the same name space as C labels is used to avoid using too
- much memory when storing labels in TokenStrings */
- static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
- int sh_num, int value)
- {
- Sym *sym;
- sym = label_find(label);
- if (sym) {
- /* A VT_EXTERN symbol, even if it has a section is considered
- overridable. This is how we "define" .set targets. Real
- definitions won't have VT_EXTERN set. */
- if (sym->r && !(sym->type.t & VT_EXTERN)) {
- /* the label is already defined */
- if (!is_local) {
- tcc_error("assembler label '%s' already defined",
- get_tok_str(label, NULL));
- } else {
- /* redefinition of local labels is possible */
- goto new_label;
- }
- }
- } else {
- new_label:
- sym = label_push(&s1->asm_labels, label, 0);
- /* If we need a symbol to hold a value, mark it as
- tentative only (for .set). If this is for a real label
- we'll remove VT_EXTERN. */
- sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
- }
- sym->r = sh_num;
- sym->jnext = value;
- return sym;
- }
- static Sym* asm_new_label(TCCState *s1, int label, int is_local)
- {
- return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
- }
- /* Set the value of LABEL to that of some expression (possibly
- involving other symbols). LABEL can be overwritten later still. */
- static Sym* set_symbol(TCCState *s1, int label)
- {
- long n;
- ExprValue e;
- next();
- asm_expr(s1, &e);
- n = e.v;
- if (e.sym)
- n += e.sym->jnext;
- return asm_new_label1(s1, label, 0, e.sym ? e.sym->r : SHN_ABS, n);
- }
- static void asm_free_labels(TCCState *st)
- {
- Sym *s, *s1;
- Section *sec;
- for(s = st->asm_labels; s != NULL; s = s1) {
- s1 = s->prev;
- /* define symbol value in object file */
- s->type.t &= ~VT_EXTERN;
- if (s->r && !s->a.dllimport) {
- if (s->r == SHN_ABS)
- sec = SECTION_ABS;
- else
- sec = st->sections[s->r];
- put_extern_sym2(s, sec, s->jnext, 0, 0);
- }
- /* remove label */
- table_ident[s->v - TOK_IDENT]->sym_label = NULL;
- sym_free(s);
- }
- st->asm_labels = NULL;
- }
- static void use_section1(TCCState *s1, Section *sec)
- {
- cur_text_section->data_offset = ind;
- cur_text_section = sec;
- ind = cur_text_section->data_offset;
- }
- static void use_section(TCCState *s1, const char *name)
- {
- Section *sec;
- sec = find_section(s1, name);
- use_section1(s1, sec);
- }
- static void push_section(TCCState *s1, const char *name)
- {
- Section *sec = find_section(s1, name);
- sec->prev = cur_text_section;
- use_section1(s1, sec);
- }
- static void pop_section(TCCState *s1)
- {
- Section *prev = cur_text_section->prev;
- if (!prev)
- tcc_error(".popsection without .pushsection");
- cur_text_section->prev = NULL;
- use_section1(s1, prev);
- }
- static void asm_parse_directive(TCCState *s1, int global)
- {
- int n, offset, v, size, tok1;
- Section *sec;
- uint8_t *ptr;
- /* assembler directive */
- sec = cur_text_section;
- switch(tok) {
- case TOK_ASMDIR_align:
- case TOK_ASMDIR_balign:
- case TOK_ASMDIR_p2align:
- case TOK_ASMDIR_skip:
- case TOK_ASMDIR_space:
- tok1 = tok;
- next();
- n = asm_int_expr(s1);
- 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 || tok1 == TOK_ASMDIR_balign) {
- if (n < 0 || (n & (n-1)) != 0)
- 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 */
- if (sec->sh_addralign < n)
- sec->sh_addralign = n;
- } else {
- if (n < 0)
- n = 0;
- size = n;
- }
- v = 0;
- if (tok == ',') {
- next();
- v = asm_int_expr(s1);
- }
- zero_pad:
- if (sec->sh_type != SHT_NOBITS) {
- sec->data_offset = ind;
- ptr = section_ptr_add(sec, size);
- memset(ptr, v, size);
- }
- ind += size;
- break;
- case TOK_ASMDIR_quad:
- #ifdef TCC_TARGET_X86_64
- size = 8;
- goto asm_data;
- #else
- next();
- for(;;) {
- uint64_t vl;
- const char *p;
- p = tokc.str.data;
- if (tok != TOK_PPNUM) {
- error_constant:
- tcc_error("64 bit constant");
- }
- vl = strtoll(p, (char **)&p, 0);
- if (*p != '\0')
- goto error_constant;
- next();
- if (sec->sh_type != SHT_NOBITS) {
- /* XXX: endianness */
- gen_le32(vl);
- gen_le32(vl >> 32);
- } else {
- ind += 8;
- }
- if (tok != ',')
- break;
- next();
- }
- break;
- #endif
- case TOK_ASMDIR_byte:
- size = 1;
- goto asm_data;
- case TOK_ASMDIR_word:
- case TOK_ASMDIR_short:
- size = 2;
- goto asm_data;
- case TOK_ASMDIR_long:
- case TOK_ASMDIR_int:
- size = 4;
- asm_data:
- next();
- for(;;) {
- ExprValue e;
- asm_expr(s1, &e);
- if (sec->sh_type != SHT_NOBITS) {
- if (size == 4) {
- gen_expr32(&e);
- #ifdef TCC_TARGET_X86_64
- } else if (size == 8) {
- gen_expr64(&e);
- #endif
- } else {
- if (e.sym)
- expect("constant");
- if (size == 1)
- g(e.v);
- else
- gen_le16(e.v);
- }
- } else {
- ind += size;
- }
- if (tok != ',')
- break;
- next();
- }
- break;
- case TOK_ASMDIR_fill:
- {
- int repeat, size, val, i, j;
- uint8_t repeat_buf[8];
- next();
- repeat = asm_int_expr(s1);
- if (repeat < 0) {
- tcc_error("repeat < 0; .fill ignored");
- break;
- }
- size = 1;
- val = 0;
- if (tok == ',') {
- next();
- size = asm_int_expr(s1);
- if (size < 0) {
- tcc_error("size < 0; .fill ignored");
- break;
- }
- if (size > 8)
- size = 8;
- if (tok == ',') {
- next();
- val = asm_int_expr(s1);
- }
- }
- /* XXX: endianness */
- repeat_buf[0] = val;
- repeat_buf[1] = val >> 8;
- repeat_buf[2] = val >> 16;
- repeat_buf[3] = val >> 24;
- repeat_buf[4] = 0;
- repeat_buf[5] = 0;
- repeat_buf[6] = 0;
- repeat_buf[7] = 0;
- for(i = 0; i < repeat; i++) {
- for(j = 0; j < size; j++) {
- g(repeat_buf[j]);
- }
- }
- }
- break;
- case TOK_ASMDIR_rept:
- {
- int repeat;
- TokenString *init_str;
- next();
- repeat = asm_int_expr(s1);
- init_str = tok_str_alloc();
- while (next(), tok != TOK_ASMDIR_endr) {
- if (tok == CH_EOF)
- tcc_error("we at end of file, .endr not found");
- tok_str_add_tok(init_str);
- }
- tok_str_add(init_str, -1);
- tok_str_add(init_str, 0);
- begin_macro(init_str, 1);
- while (repeat-- > 0) {
- tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS),
- global);
- macro_ptr = init_str->str;
- }
- end_macro();
- next();
- break;
- }
- case TOK_ASMDIR_org:
- {
- unsigned long n;
- ExprValue e;
- next();
- 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;
- size = n - ind;
- goto zero_pad;
- }
- break;
- case TOK_ASMDIR_set:
- next();
- tok1 = tok;
- next();
- /* Also accept '.set stuff', but don't do anything with this.
- It's used in GAS to set various features like '.set mips16'. */
- if (tok == ',')
- set_symbol(s1, tok1);
- break;
- case TOK_ASMDIR_globl:
- case TOK_ASMDIR_global:
- case TOK_ASMDIR_weak:
- case TOK_ASMDIR_hidden:
- tok1 = tok;
- do {
- Sym *sym;
- next();
- sym = get_asm_sym(tok, NULL);
- if (tok1 != TOK_ASMDIR_hidden)
- sym->type.t &= ~VT_STATIC;
- if (tok1 == TOK_ASMDIR_weak)
- sym->a.weak = 1;
- else if (tok1 == TOK_ASMDIR_hidden)
- sym->a.visibility = STV_HIDDEN;
- next();
- } while (tok == ',');
- break;
- case TOK_ASMDIR_string:
- case TOK_ASMDIR_ascii:
- case TOK_ASMDIR_asciz:
- {
- const uint8_t *p;
- int i, size, t;
- t = tok;
- next();
- for(;;) {
- if (tok != TOK_STR)
- expect("string constant");
- 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]);
- next();
- if (tok == ',') {
- next();
- } else if (tok != TOK_STR) {
- break;
- }
- }
- }
- break;
- case TOK_ASMDIR_text:
- case TOK_ASMDIR_data:
- case TOK_ASMDIR_bss:
- {
- char sname[64];
- tok1 = tok;
- n = 0;
- next();
- if (tok != ';' && tok != TOK_LINEFEED) {
- n = asm_int_expr(s1);
- next();
- }
- 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_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 != TOK_LINEFEED && tok != ';' && tok != CH_EOF) {
- next();
- }
- }
- break;
- case TOK_ASMDIR_type:
- {
- Sym *sym;
- const char *newtype;
- next();
- sym = get_asm_sym(tok, NULL);
- 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_pushsection:
- case TOK_ASMDIR_section:
- {
- char sname[256];
- int old_nb_section = s1->nb_sections;
- tok1 = tok;
- /* XXX: support more options */
- next();
- sname[0] = '\0';
- while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
- if (tok == TOK_STR)
- pstrcat(sname, sizeof(sname), tokc.str.data);
- else
- pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
- next();
- }
- if (tok == ',') {
- /* skip section options */
- next();
- if (tok != TOK_STR)
- expect("string constant");
- next();
- if (tok == ',') {
- next();
- if (tok == '@' || tok == '%')
- next();
- next();
- }
- }
- last_text_section = cur_text_section;
- if (tok1 == TOK_ASMDIR_section)
- 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:
- {
- Section *sec;
- next();
- if (!last_text_section)
- tcc_error("no previous section referenced");
- sec = cur_text_section;
- use_section1(s1, last_text_section);
- last_text_section = sec;
- }
- break;
- case TOK_ASMDIR_popsection:
- next();
- pop_section(s1);
- 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:
- tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
- break;
- }
- }
- /* assemble a file */
- static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
- {
- int opcode;
- int saved_parse_flags = parse_flags;
- /* XXX: undefine C labels */
- parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
- if (do_preprocess)
- parse_flags |= PARSE_FLAG_PREPROCESS;
- for(;;) {
- next();
- if (tok == TOK_EOF)
- break;
- /* generate line number info */
- if (global && s1->do_debug)
- tcc_debug_line(s1);
- parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
- redo:
- if (tok == '#') {
- /* horrible gas comment */
- while (tok != TOK_LINEFEED)
- next();
- } else if (tok >= TOK_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) {
- asm_parse_directive(s1, global);
- } else if (tok == TOK_PPNUM) {
- Sym *sym;
- const char *p;
- int n;
- p = tokc.str.data;
- n = strtoul(p, (char **)&p, 10);
- if (*p != '\0')
- expect("':'");
- /* new local label */
- sym = asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
- /* Remove the marker for tentative definitions. */
- sym->type.t &= ~VT_EXTERN;
- next();
- skip(':');
- goto redo;
- } else if (tok >= TOK_IDENT) {
- /* instruction or label */
- opcode = tok;
- next();
- if (tok == ':') {
- /* handle "extern void vide(void); __asm__("vide: ret");" as
- "__asm__("globl vide\nvide: ret");" */
- Sym *sym = sym_find(opcode);
- if (sym && (sym->type.t & VT_EXTERN) && global) {
- sym = label_find(opcode);
- if (!sym) {
- sym = label_push(&s1->asm_labels, opcode, 0);
- sym->type.t = VT_VOID | VT_EXTERN;
- }
- }
- /* new label */
- sym = asm_new_label(s1, opcode, 0);
- sym->type.t &= ~VT_EXTERN;
- next();
- goto redo;
- } else if (tok == '=') {
- set_symbol(s1, opcode);
- goto redo;
- } else {
- asm_opcode(s1, opcode);
- }
- }
- /* end of line */
- if (tok != ';' && tok != TOK_LINEFEED)
- expect("end of line");
- parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
- }
- asm_free_labels(s1);
- parse_flags = saved_parse_flags;
- return 0;
- }
- /* Assemble the current file */
- ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
- {
- int ret;
- tcc_debug_start(s1);
- /* default section is text */
- cur_text_section = text_section;
- ind = cur_text_section->data_offset;
- nocode_wanted = 0;
- ret = tcc_assemble_internal(s1, do_preprocess, 1);
- cur_text_section->data_offset = ind;
- tcc_debug_end(s1);
- return ret;
- }
- /********************************************************************/
- /* GCC inline asm support */
- /* assemble the string 'str' in the current C compilation unit without
- C preprocessing. NOTE: str is modified by modifying the '\0' at the
- end */
- static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global)
- {
- const int *saved_macro_ptr = macro_ptr;
- int dotid = set_idnum('.', IS_ID);
- tcc_open_bf(s1, ":asm:", len);
- memcpy(file->buffer, str, len);
- macro_ptr = NULL;
- tcc_assemble_internal(s1, 0, global);
- tcc_close();
- set_idnum('.', dotid);
- macro_ptr = saved_macro_ptr;
- }
- /* 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 */
- ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
- const char *name, const char **pp)
- {
- int index;
- TokenSym *ts;
- const char *p;
- if (isnum(*name)) {
- index = 0;
- while (isnum(*name)) {
- index = (index * 10) + (*name) - '0';
- name++;
- }
- if ((unsigned)index >= nb_operands)
- index = -1;
- } else if (*name == '[') {
- name++;
- p = strchr(name, ']');
- if (p) {
- ts = tok_alloc(name, p - name);
- for(index = 0; index < nb_operands; index++) {
- if (operands[index].id == ts->tok)
- goto found;
- }
- index = -1;
- found:
- name = p + 1;
- } else {
- index = -1;
- }
- } else {
- index = -1;
- }
- if (pp)
- *pp = name;
- return index;
- }
- static void subst_asm_operands(ASMOperand *operands, int nb_operands,
- CString *out_str, CString *in_str)
- {
- int c, index, modifier;
- const char *str;
- ASMOperand *op;
- SValue sv;
- cstr_new(out_str);
- str = in_str->data;
- for(;;) {
- c = *str++;
- if (c == '%') {
- if (*str == '%') {
- str++;
- goto add_char;
- }
- modifier = 0;
- if (*str == 'c' || *str == 'n' ||
- *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
- *str == 'q' ||
- /* P in GCC would add "@PLT" to symbol refs in PIC mode,
- and make literal operands not be decorated with '$'. */
- *str == 'P')
- modifier = *str++;
- index = find_constraint(operands, nb_operands, str, &str);
- if (index < 0)
- 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 && op->is_memory)
- sv.r |= VT_LVAL;
- }
- subst_asm_operand(out_str, &sv, modifier);
- } else {
- add_char:
- cstr_ccat(out_str, c);
- if (c == '\0')
- break;
- }
- }
- }
- static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
- int is_output)
- {
- ASMOperand *op;
- int nb_operands;
- if (tok != ':') {
- nb_operands = *nb_operands_ptr;
- for(;;) {
- CString astr;
- if (nb_operands >= MAX_ASM_OPERANDS)
- tcc_error("too many asm operands");
- op = &operands[nb_operands++];
- op->id = 0;
- if (tok == '[') {
- next();
- if (tok < TOK_IDENT)
- expect("identifier");
- op->id = tok;
- next();
- skip(']');
- }
- parse_mult_str(&astr, "string constant");
- op->constraint = tcc_malloc(astr.size);
- strcpy(op->constraint, astr.data);
- cstr_free(&astr);
- skip('(');
- gexpr();
- if (is_output) {
- if (!(vtop->type.t & VT_ARRAY))
- test_lvalue();
- } else {
- /* we want to avoid LLOCAL case, except when the 'm'
- constraint is used. Note that it may come from
- register storage, so we need to convert (reg)
- case */
- if ((vtop->r & VT_LVAL) &&
- ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
- (vtop->r & VT_VALMASK) < VT_CONST) &&
- !strchr(op->constraint, 'm')) {
- gv(RC_INT);
- }
- }
- op->vt = vtop;
- skip(')');
- if (tok == ',') {
- next();
- } else {
- break;
- }
- }
- *nb_operands_ptr = nb_operands;
- }
- }
- /* parse the GCC asm() instruction */
- ST_FUNC void asm_instr(void)
- {
- CString astr, astr1;
- ASMOperand operands[MAX_ASM_OPERANDS];
- int nb_outputs, nb_operands, i, must_subst, out_reg;
- uint8_t clobber_regs[NB_ASM_REGS];
- next();
- /* since we always generate the asm() instruction, we can ignore
- volatile */
- if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
- next();
- }
- parse_asm_str(&astr);
- nb_operands = 0;
- nb_outputs = 0;
- must_subst = 0;
- memset(clobber_regs, 0, sizeof(clobber_regs));
- if (tok == ':') {
- next();
- must_subst = 1;
- /* output args */
- parse_asm_operands(operands, &nb_operands, 1);
- nb_outputs = nb_operands;
- if (tok == ':') {
- next();
- if (tok != ')') {
- /* input args */
- parse_asm_operands(operands, &nb_operands, 0);
- if (tok == ':') {
- /* clobber list */
- /* XXX: handle registers */
- next();
- for(;;) {
- if (tok != TOK_STR)
- expect("string constant");
- asm_clobber(clobber_regs, tokc.str.data);
- next();
- if (tok == ',') {
- next();
- } else {
- break;
- }
- }
- }
- }
- }
- }
- skip(')');
- /* NOTE: we do not eat the ';' so that we can restore the current
- token after the assembler parsing */
- if (tok != ';')
- expect("';'");
-
- /* save all values in the memory */
- save_regs(0);
- /* compute constraints */
- asm_compute_constraints(operands, nb_operands, nb_outputs,
- clobber_regs, &out_reg);
- /* substitute the operands in the asm string. No substitution is
- done if no operands (GCC behaviour) */
- #ifdef ASM_DEBUG
- printf("asm: \"%s\"\n", (char *)astr.data);
- #endif
- if (must_subst) {
- subst_asm_operands(operands, nb_operands, &astr1, &astr);
- cstr_free(&astr);
- } else {
- astr1 = astr;
- }
- #ifdef ASM_DEBUG
- printf("subst_asm: \"%s\"\n", (char *)astr1.data);
- #endif
- /* generate loads */
- asm_gen_code(operands, nb_operands, nb_outputs, 0,
- clobber_regs, out_reg);
- /* assemble the string with tcc internal assembler */
- tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
- /* restore the current C token */
- next();
- /* store the output values if needed */
- asm_gen_code(operands, nb_operands, nb_outputs, 1,
- clobber_regs, out_reg);
-
- /* free everything */
- for(i=0;i<nb_operands;i++) {
- ASMOperand *op;
- op = &operands[i];
- tcc_free(op->constraint);
- vpop();
- }
- cstr_free(&astr1);
- }
- ST_FUNC void asm_global_instr(void)
- {
- CString astr;
- int saved_nocode_wanted = nocode_wanted;
- /* Global asm blocks are always emitted. */
- nocode_wanted = 0;
- next();
- parse_asm_str(&astr);
- skip(')');
- /* NOTE: we do not eat the ';' so that we can restore the current
- token after the assembler parsing */
- if (tok != ';')
- expect("';'");
-
- #ifdef ASM_DEBUG
- printf("asm_global: \"%s\"\n", (char *)astr.data);
- #endif
- cur_text_section = text_section;
- ind = cur_text_section->data_offset;
- /* assemble the string with tcc internal assembler */
- tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
-
- cur_text_section->data_offset = ind;
- /* restore the current C token */
- next();
- cstr_free(&astr);
- nocode_wanted = saved_nocode_wanted;
- }
- #endif /* CONFIG_TCC_ASM */
|