| /* |
| * GAS-compatible parser |
| * |
| * Copyright (C) 2005-2007 Peter Johnson |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the author nor the names of other contributors |
| * may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| #include <util.h> |
| RCSID("$Id$"); |
| |
| #define YASM_LIB_INTERNAL |
| #define YASM_EXPR_INTERNAL |
| #include <libyasm.h> |
| |
| #include <limits.h> |
| #include <math.h> |
| |
| #include "modules/parsers/gas/gas-parser.h" |
| |
| static yasm_bytecode *parse_line(yasm_parser_gas *parser_gas); |
| static yasm_bytecode *parse_instr(yasm_parser_gas *parser_gas); |
| static int parse_dirvals(yasm_parser_gas *parser_gas, yasm_valparamhead *vps); |
| static int parse_datavals(yasm_parser_gas *parser_gas, yasm_datavalhead *dvs); |
| static int parse_strvals(yasm_parser_gas *parser_gas, yasm_datavalhead *dvs); |
| static yasm_effaddr *parse_memaddr(yasm_parser_gas *parser_gas); |
| static yasm_insn_operand *parse_operand(yasm_parser_gas *parser_gas); |
| static yasm_expr *parse_expr(yasm_parser_gas *parser_gas); |
| static yasm_expr *parse_expr0(yasm_parser_gas *parser_gas); |
| static yasm_expr *parse_expr1(yasm_parser_gas *parser_gas); |
| static yasm_expr *parse_expr2(yasm_parser_gas *parser_gas); |
| |
| static void define_label(yasm_parser_gas *parser_gas, char *name, int local); |
| static void define_lcomm(yasm_parser_gas *parser_gas, /*@only@*/ char *name, |
| yasm_expr *size, /*@null@*/ yasm_expr *align); |
| static yasm_section *gas_get_section |
| (yasm_parser_gas *parser_gas, /*@only@*/ char *name, /*@null@*/ char *flags, |
| /*@null@*/ char *type, /*@null@*/ yasm_valparamhead *objext_valparams, |
| int builtin); |
| static void gas_switch_section |
| (yasm_parser_gas *parser_gas, /*@only@*/ char *name, /*@null@*/ char *flags, |
| /*@null@*/ char *type, /*@null@*/ yasm_valparamhead *objext_valparams, |
| int builtin); |
| static yasm_bytecode *gas_parser_align |
| (yasm_parser_gas *parser_gas, yasm_section *sect, yasm_expr *boundval, |
| /*@null@*/ yasm_expr *fillval, /*@null@*/ yasm_expr *maxskipval, |
| int power2); |
| static yasm_bytecode *gas_parser_dir_fill |
| (yasm_parser_gas *parser_gas, /*@only@*/ yasm_expr *repeat, |
| /*@only@*/ /*@null@*/ yasm_expr *size, |
| /*@only@*/ /*@null@*/ yasm_expr *value); |
| #if 0 |
| static void gas_parser_directive |
| (yasm_parser_gas *parser_gas, const char *name, |
| yasm_valparamhead *valparams, |
| /*@null@*/ yasm_valparamhead *objext_valparams); |
| #endif |
| |
| #define is_eol_tok(tok) ((tok) == '\n' || (tok) == ';' || (tok) == 0) |
| #define is_eol() is_eol_tok(curtok) |
| |
| #define get_next_token() (curtok = gas_parser_lex(&curval, parser_gas)) |
| |
| static void |
| get_peek_token(yasm_parser_gas *parser_gas) |
| { |
| char savech = parser_gas->tokch; |
| if (parser_gas->peek_token != NONE) |
| yasm_internal_error(N_("only can have one token of lookahead")); |
| parser_gas->peek_token = |
| gas_parser_lex(&parser_gas->peek_tokval, parser_gas); |
| parser_gas->peek_tokch = parser_gas->tokch; |
| parser_gas->tokch = savech; |
| } |
| |
| static void |
| destroy_curtok_(yasm_parser_gas *parser_gas) |
| { |
| if (curtok < 256) |
| ; |
| else switch ((enum tokentype)curtok) { |
| case INTNUM: |
| yasm_intnum_destroy(curval.intn); |
| break; |
| case FLTNUM: |
| yasm_floatnum_destroy(curval.flt); |
| break; |
| case ID: |
| case LABEL: |
| yasm_xfree(curval.str_val); |
| break; |
| case STRING: |
| yasm_xfree(curval.str.contents); |
| break; |
| default: |
| break; |
| } |
| curtok = NONE; /* sanity */ |
| } |
| #define destroy_curtok() destroy_curtok_(parser_gas) |
| |
| /* Eat all remaining tokens to EOL, discarding all of them. If there's any |
| * intervening tokens, generates an error (junk at end of line). |
| */ |
| static void |
| demand_eol_(yasm_parser_gas *parser_gas) |
| { |
| if (is_eol()) |
| return; |
| |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("junk at end of line, first unrecognized character is `%c'"), |
| parser_gas->tokch); |
| |
| do { |
| destroy_curtok(); |
| get_next_token(); |
| } while (!is_eol()); |
| } |
| #define demand_eol() demand_eol_(parser_gas) |
| |
| static int |
| expect_(yasm_parser_gas *parser_gas, int token) |
| { |
| static char strch[] = "` '"; |
| const char *str; |
| |
| if (curtok == token) |
| return 1; |
| |
| switch (token) { |
| case INTNUM: str = "integer"; break; |
| case FLTNUM: str = "floating point value"; break; |
| case STRING: str = "string"; break; |
| case INSN: str = "instruction"; break; |
| case PREFIX: str = "instruction prefix"; break; |
| case REG: str = "register"; break; |
| case REGGROUP: str = "register group"; break; |
| case SEGREG: str = "segment register"; break; |
| case TARGETMOD: str = "target modifier"; break; |
| case LEFT_OP: str = "<<"; break; |
| case RIGHT_OP: str = ">>"; break; |
| case ID: str = "identifier"; break; |
| case LABEL: str = "label"; break; |
| case LINE: |
| case DIR_ALIGN: |
| case DIR_ASCII: |
| case DIR_COMM: |
| case DIR_DATA: |
| case DIR_ENDR: |
| case DIR_EQU: |
| case DIR_FILE: |
| case DIR_FILL: |
| case DIR_LEB128: |
| case DIR_LINE: |
| case DIR_LOCAL: |
| case DIR_LCOMM: |
| case DIR_ORG: |
| case DIR_REPT: |
| case DIR_SECTION: |
| case DIR_SECTNAME: |
| case DIR_SKIP: |
| case DIR_ZERO: |
| str = "directive"; |
| break; |
| default: |
| strch[1] = token; |
| str = strch; |
| break; |
| } |
| yasm_error_set(YASM_ERROR_PARSE, "expected %s", str); |
| destroy_curtok(); |
| return 0; |
| } |
| #define expect(token) expect_(parser_gas, token) |
| |
| void |
| gas_parser_parse(yasm_parser_gas *parser_gas) |
| { |
| while (get_next_token() != 0) { |
| yasm_bytecode *bc = NULL, *temp_bc; |
| |
| if (!is_eol()) { |
| bc = parse_line(parser_gas); |
| demand_eol(); |
| } |
| |
| yasm_errwarn_propagate(parser_gas->errwarns, cur_line); |
| |
| temp_bc = yasm_section_bcs_append(cursect, bc); |
| if (temp_bc) |
| parser_gas->prev_bc = temp_bc; |
| if (curtok == ';') |
| continue; /* don't advance line number until \n */ |
| if (parser_gas->save_input) |
| yasm_linemap_add_source(parser_gas->linemap, |
| temp_bc, |
| (char *)parser_gas->save_line[parser_gas->save_last ^ 1]); |
| yasm_linemap_goto_next(parser_gas->linemap); |
| parser_gas->dir_line++; /* keep track for .line followed by .file */ |
| } |
| } |
| |
| static yasm_bytecode * |
| parse_line(yasm_parser_gas *parser_gas) |
| { |
| yasm_bytecode *bc; |
| yasm_expr *e; |
| yasm_intnum *intn; |
| yasm_datavalhead dvs; |
| yasm_valparamhead vps; |
| yasm_valparam *vp; |
| unsigned int ival; |
| char *id; |
| |
| if (is_eol()) |
| return NULL; |
| |
| bc = parse_instr(parser_gas); |
| if (bc) |
| return bc; |
| |
| switch (curtok) { |
| case ID: |
| id = ID_val; |
| parser_gas->state = INSTDIR; |
| get_next_token(); /* ID */ |
| if (curtok == ':') { |
| /* Label */ |
| parser_gas->state = INITIAL; |
| get_next_token(); /* : */ |
| define_label(parser_gas, id, 0); |
| return parse_line(parser_gas); |
| } else if (curtok == '=') { |
| /* EQU */ |
| /* TODO: allow redefinition, assigning to . (same as .org) */ |
| parser_gas->state = INITIAL; |
| get_next_token(); /* = */ |
| e = parse_expr(parser_gas); |
| if (e) |
| yasm_symtab_define_equ(p_symtab, id, e, cur_line); |
| else |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected after `%s'"), "="); |
| yasm_xfree(id); |
| return NULL; |
| } |
| |
| /* possibly a directive; try to parse it */ |
| parse_dirvals(parser_gas, &vps); |
| if (!yasm_object_directive(p_object, id, "gas", &vps, NULL, |
| cur_line)) { |
| yasm_vps_delete(&vps); |
| yasm_xfree(id); |
| return NULL; |
| } |
| yasm_vps_delete(&vps); |
| if (id[0] == '.') |
| yasm_warn_set(YASM_WARN_GENERAL, |
| N_("directive `%s' not recognized"), id); |
| else |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("instruction not recognized: `%s'"), id); |
| yasm_xfree(id); |
| return NULL; |
| case LABEL: |
| define_label(parser_gas, LABEL_val, 0); |
| get_next_token(); /* LABEL */ |
| return parse_line(parser_gas); |
| |
| /* Line directive */ |
| case DIR_LINE: |
| get_next_token(); /* DIR_LINE */ |
| |
| if (!expect(INTNUM)) return NULL; |
| if (yasm_intnum_sign(INTNUM_val) < 0) { |
| get_next_token(); /* INTNUM */ |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("line number is negative")); |
| return NULL; |
| } |
| |
| parser_gas->dir_line = yasm_intnum_get_uint(INTNUM_val); |
| yasm_intnum_destroy(INTNUM_val); |
| get_next_token(); /* INTNUM */ |
| |
| if (parser_gas->dir_fileline == 3) { |
| /* Have both file and line */ |
| yasm_linemap_set(parser_gas->linemap, NULL, |
| parser_gas->dir_line, 1); |
| } else if (parser_gas->dir_fileline == 1) { |
| /* Had previous file directive only */ |
| parser_gas->dir_fileline = 3; |
| yasm_linemap_set(parser_gas->linemap, parser_gas->dir_file, |
| parser_gas->dir_line, 1); |
| } else { |
| /* Didn't see file yet */ |
| parser_gas->dir_fileline = 2; |
| } |
| return NULL; |
| |
| /* Macro directives */ |
| case DIR_REPT: |
| get_next_token(); /* DIR_REPT */ |
| e = parse_expr(parser_gas); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected after `%s'"), |
| ".rept"); |
| return NULL; |
| } |
| intn = yasm_expr_get_intnum(&e, 0); |
| |
| if (!intn) { |
| yasm_error_set(YASM_ERROR_NOT_ABSOLUTE, |
| N_("rept expression not absolute")); |
| } else if (yasm_intnum_sign(intn) < 0) { |
| yasm_error_set(YASM_ERROR_VALUE, |
| N_("rept expression is negative")); |
| } else { |
| gas_rept *rept = yasm_xmalloc(sizeof(gas_rept)); |
| STAILQ_INIT(&rept->lines); |
| rept->startline = cur_line; |
| rept->numrept = yasm_intnum_get_uint(intn); |
| rept->numdone = 0; |
| rept->line = NULL; |
| rept->linepos = 0; |
| rept->ended = 0; |
| rept->oldbuf = NULL; |
| rept->oldbuflen = 0; |
| rept->oldbufpos = 0; |
| parser_gas->rept = rept; |
| } |
| return NULL; |
| case DIR_ENDR: |
| get_next_token(); /* DIR_ENDR */ |
| /* Shouldn't ever get here unless we didn't get a DIR_REPT first */ |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("endr without matching rept")); |
| return NULL; |
| |
| /* Alignment directives */ |
| case DIR_ALIGN: |
| { |
| yasm_expr *bound, *fill=NULL, *maxskip=NULL; |
| |
| ival = DIR_ALIGN_val; |
| get_next_token(); /* DIR_ALIGN */ |
| |
| bound = parse_expr(parser_gas); |
| if (!bound) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_(".align directive must specify alignment")); |
| return NULL; |
| } |
| |
| if (curtok == ',') { |
| get_next_token(); /* ',' */ |
| fill = parse_expr(parser_gas); |
| if (curtok == ',') { |
| get_next_token(); /* ',' */ |
| maxskip = parse_expr(parser_gas); |
| } |
| } |
| |
| return gas_parser_align(parser_gas, cursect, bound, fill, maxskip, |
| (int)ival); |
| } |
| case DIR_ORG: |
| get_next_token(); /* DIR_ORG */ |
| if (!expect(INTNUM)) return NULL; |
| /* TODO: support expr instead of intnum */ |
| bc = yasm_bc_create_org(yasm_intnum_get_uint(INTNUM_val), cur_line); |
| yasm_intnum_destroy(INTNUM_val); |
| get_next_token(); /* INTNUM */ |
| return bc; |
| |
| /* Data visibility directives */ |
| case DIR_LOCAL: |
| get_next_token(); /* DIR_LOCAL */ |
| if (!expect(ID)) return NULL; |
| yasm_symtab_declare(p_symtab, ID_val, YASM_SYM_DLOCAL, cur_line); |
| yasm_xfree(ID_val); |
| get_next_token(); /* ID */ |
| return NULL; |
| case DIR_COMM: |
| case DIR_LCOMM: |
| { |
| yasm_expr *align = NULL; |
| /*@null@*/ /*@dependent@*/ yasm_symrec *sym; |
| int is_lcomm = curtok == DIR_LCOMM; |
| |
| get_next_token(); /* DIR_LOCAL */ |
| |
| if (!expect(ID)) return NULL; |
| id = ID_val; |
| get_next_token(); /* ID */ |
| if (!expect(',')) { |
| yasm_xfree(id); |
| return NULL; |
| } |
| get_next_token(); /* , */ |
| e = parse_expr(parser_gas); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("size expected for `%s'"), |
| ".COMM"); |
| return NULL; |
| } |
| if (curtok == ',') { |
| /* Optional alignment expression */ |
| get_next_token(); /* ',' */ |
| align = parse_expr(parser_gas); |
| } |
| /* If already explicitly declared local, treat like LCOMM */ |
| if (is_lcomm |
| || ((sym = yasm_symtab_get(p_symtab, id)) |
| && yasm_symrec_get_visibility(sym) == YASM_SYM_DLOCAL)) { |
| define_lcomm(parser_gas, id, e, align); |
| } else if (align) { |
| /* Give third parameter as objext valparam */ |
| yasm_valparamhead *extvps = yasm_vps_create(); |
| vp = yasm_vp_create_expr(NULL, align); |
| yasm_vps_append(extvps, vp); |
| |
| sym = yasm_symtab_declare(p_symtab, id, YASM_SYM_COMMON, |
| cur_line); |
| yasm_symrec_set_common_size(sym, e); |
| yasm_symrec_set_objext_valparams(sym, extvps); |
| |
| yasm_xfree(id); |
| } else { |
| sym = yasm_symtab_declare(p_symtab, id, YASM_SYM_COMMON, |
| cur_line); |
| yasm_symrec_set_common_size(sym, e); |
| yasm_xfree(id); |
| } |
| return NULL; |
| } |
| |
| /* Integer data definition directives */ |
| case DIR_ASCII: |
| ival = DIR_ASCII_val; |
| get_next_token(); /* DIR_ASCII */ |
| if (!parse_strvals(parser_gas, &dvs)) |
| return NULL; |
| return yasm_bc_create_data(&dvs, 1, (int)ival, p_object->arch, |
| cur_line); |
| case DIR_DATA: |
| ival = DIR_DATA_val; |
| get_next_token(); /* DIR_DATA */ |
| if (!parse_datavals(parser_gas, &dvs)) |
| return NULL; |
| return yasm_bc_create_data(&dvs, ival, 0, p_object->arch, |
| cur_line); |
| case DIR_LEB128: |
| ival = DIR_LEB128_val; |
| get_next_token(); /* DIR_LEB128 */ |
| if (!parse_datavals(parser_gas, &dvs)) |
| return NULL; |
| return yasm_bc_create_leb128(&dvs, (int)ival, cur_line); |
| |
| /* Empty space / fill data definition directives */ |
| case DIR_ZERO: |
| get_next_token(); /* DIR_ZERO */ |
| e = parse_expr(parser_gas); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected after `%s'"), ".ZERO"); |
| return NULL; |
| } |
| |
| yasm_dvs_initialize(&dvs); |
| yasm_dvs_append(&dvs, yasm_dv_create_expr( |
| p_expr_new_ident(yasm_expr_int(yasm_intnum_create_uint(0))))); |
| bc = yasm_bc_create_data(&dvs, 1, 0, p_object->arch, cur_line); |
| yasm_bc_set_multiple(bc, e); |
| return bc; |
| case DIR_SKIP: |
| { |
| yasm_expr *e_val; |
| |
| get_next_token(); /* DIR_SKIP */ |
| e = parse_expr(parser_gas); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected after `%s'"), ".SKIP"); |
| return NULL; |
| } |
| if (curtok != ',') |
| return yasm_bc_create_reserve(e, 1, cur_line); |
| get_next_token(); /* ',' */ |
| e_val = parse_expr(parser_gas); |
| yasm_dvs_initialize(&dvs); |
| yasm_dvs_append(&dvs, yasm_dv_create_expr(e_val)); |
| bc = yasm_bc_create_data(&dvs, 1, 0, p_object->arch, cur_line); |
| |
| yasm_bc_set_multiple(bc, e); |
| return bc; |
| } |
| |
| /* fill data definition directive */ |
| case DIR_FILL: |
| { |
| yasm_expr *sz=NULL, *val=NULL; |
| get_next_token(); /* DIR_FILL */ |
| e = parse_expr(parser_gas); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected after `%s'"), ".FILL"); |
| return NULL; |
| } |
| if (curtok == ',') { |
| get_next_token(); /* ',' */ |
| sz = parse_expr(parser_gas); |
| if (curtok == ',') { |
| get_next_token(); /* ',' */ |
| val = parse_expr(parser_gas); |
| } |
| } |
| return gas_parser_dir_fill(parser_gas, e, sz, val); |
| } |
| |
| /* Section directives */ |
| case DIR_SECTNAME: |
| gas_switch_section(parser_gas, DIR_SECTNAME_val, NULL, NULL, NULL, |
| 1); |
| get_next_token(); /* DIR_SECTNAME */ |
| return NULL; |
| case DIR_SECTION: |
| { |
| /* DIR_SECTION ID ',' STRING ',' '@' ID ',' dirvals */ |
| char *sectname, *flags = NULL, *type = NULL; |
| int have_vps = 0; |
| |
| get_next_token(); /* DIR_SECTION */ |
| |
| if (!expect(ID)) return NULL; |
| sectname = ID_val; |
| get_next_token(); /* ID */ |
| |
| if (curtok == ',') { |
| get_next_token(); /* ',' */ |
| if (!expect(STRING)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("flag string expected")); |
| yasm_xfree(sectname); |
| return NULL; |
| } |
| flags = STRING_val.contents; |
| get_next_token(); /* STRING */ |
| } |
| |
| if (curtok == ',') { |
| get_next_token(); /* ',' */ |
| if (!expect('@')) { |
| yasm_xfree(sectname); |
| yasm_xfree(flags); |
| return NULL; |
| } |
| get_next_token(); /* '@' */ |
| if (!expect(ID)) { |
| yasm_xfree(sectname); |
| yasm_xfree(flags); |
| return NULL; |
| } |
| type = ID_val; |
| get_next_token(); /* ID */ |
| } |
| |
| if (curtok == ',') { |
| get_next_token(); /* ',' */ |
| if (parse_dirvals(parser_gas, &vps)) |
| have_vps = 1; |
| } |
| |
| gas_switch_section(parser_gas, sectname, flags, type, |
| have_vps ? &vps : NULL, 0); |
| yasm_xfree(flags); |
| return NULL; |
| } |
| |
| /* Other directives */ |
| case DIR_EQU: |
| /* ID ',' expr */ |
| get_next_token(); /* DIR_EQU */ |
| if (!expect(ID)) return NULL; |
| id = ID_val; |
| get_next_token(); /* ID */ |
| if (!expect(',')) { |
| yasm_xfree(id); |
| return NULL; |
| } |
| get_next_token(); /* ',' */ |
| e = parse_expr(parser_gas); |
| if (e) |
| yasm_symtab_define_equ(p_symtab, id, e, cur_line); |
| else |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression expected after `%s'"), ","); |
| yasm_xfree(id); |
| return NULL; |
| |
| case DIR_FILE: |
| get_next_token(); /* DIR_FILE */ |
| if (curtok == STRING) { |
| /* No file number; this form also sets the assembler's |
| * internal line number. |
| */ |
| char *filename = STRING_val.contents; |
| get_next_token(); /* STRING */ |
| if (parser_gas->dir_fileline == 3) { |
| /* Have both file and line */ |
| const char *old_fn; |
| unsigned long old_line; |
| |
| yasm_linemap_lookup(parser_gas->linemap, cur_line, &old_fn, |
| &old_line); |
| yasm_linemap_set(parser_gas->linemap, filename, old_line, |
| 1); |
| } else if (parser_gas->dir_fileline == 2) { |
| /* Had previous line directive only */ |
| parser_gas->dir_fileline = 3; |
| yasm_linemap_set(parser_gas->linemap, filename, |
| parser_gas->dir_line, 1); |
| } else { |
| /* Didn't see line yet, save file */ |
| parser_gas->dir_fileline = 1; |
| if (parser_gas->dir_file) |
| yasm_xfree(parser_gas->dir_file); |
| parser_gas->dir_file = yasm__xstrdup(filename); |
| } |
| |
| /* Pass change along to debug format */ |
| yasm_vps_initialize(&vps); |
| vp = yasm_vp_create_string(NULL, filename); |
| yasm_vps_append(&vps, vp); |
| |
| yasm_object_directive(p_object, ".file", "gas", &vps, NULL, |
| cur_line); |
| |
| yasm_vps_delete(&vps); |
| return NULL; |
| } |
| |
| /* fileno filename form */ |
| yasm_vps_initialize(&vps); |
| |
| if (!expect(INTNUM)) return NULL; |
| vp = yasm_vp_create_expr(NULL, |
| p_expr_new_ident(yasm_expr_int(INTNUM_val))); |
| yasm_vps_append(&vps, vp); |
| get_next_token(); /* INTNUM */ |
| |
| if (!expect(STRING)) { |
| yasm_vps_delete(&vps); |
| return NULL; |
| } |
| vp = yasm_vp_create_string(NULL, STRING_val.contents); |
| yasm_vps_append(&vps, vp); |
| get_next_token(); /* STRING */ |
| |
| yasm_object_directive(p_object, ".file", "gas", &vps, NULL, |
| cur_line); |
| |
| yasm_vps_delete(&vps); |
| return NULL; |
| default: |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("label or instruction expected at start of line")); |
| return NULL; |
| } |
| } |
| |
| static yasm_bytecode * |
| parse_instr(yasm_parser_gas *parser_gas) |
| { |
| yasm_bytecode *bc; |
| |
| switch (curtok) { |
| case INSN: |
| { |
| yystype insn = curval; /* structure copy */ |
| yasm_insn_operands operands; |
| int num_operands = 0; |
| |
| get_next_token(); |
| if (is_eol()) { |
| /* no operands */ |
| return yasm_bc_create_insn(p_object->arch, insn.arch_data, |
| 0, NULL, cur_line); |
| } |
| |
| /* parse operands */ |
| yasm_ops_initialize(&operands); |
| for (;;) { |
| yasm_insn_operand *op = parse_operand(parser_gas); |
| if (!op) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression syntax error")); |
| yasm_ops_delete(&operands, 1); |
| return NULL; |
| } |
| yasm_ops_append(&operands, op); |
| num_operands++; |
| |
| if (is_eol()) |
| break; |
| if (!expect(',')) { |
| yasm_ops_delete(&operands, 1); |
| return NULL; |
| } |
| get_next_token(); |
| } |
| return yasm_bc_create_insn(p_object->arch, insn.arch_data, |
| num_operands, &operands, cur_line); |
| } |
| case PREFIX: |
| { |
| yystype prefix = curval; /* structure copy */ |
| get_next_token(); /* PREFIX */ |
| bc = parse_instr(parser_gas); |
| if (!bc) |
| bc = yasm_bc_create_empty_insn(p_object->arch, cur_line); |
| yasm_bc_insn_add_prefix(bc, prefix.arch_data); |
| return bc; |
| } |
| case SEGREG: |
| { |
| uintptr_t segreg = SEGREG_val[0]; |
| get_next_token(); /* SEGREG */ |
| bc = parse_instr(parser_gas); |
| if (!bc) |
| bc = yasm_bc_create_empty_insn(p_object->arch, cur_line); |
| yasm_bc_insn_add_seg_prefix(bc, segreg); |
| } |
| default: |
| return NULL; |
| } |
| } |
| |
| static int |
| parse_dirvals(yasm_parser_gas *parser_gas, yasm_valparamhead *vps) |
| { |
| yasm_valparam *vp; |
| yasm_expr *e; |
| int num = 0; |
| |
| yasm_vps_initialize(vps); |
| |
| for (;;) { |
| switch (curtok) { |
| case ID: |
| get_peek_token(parser_gas); |
| switch (parser_gas->peek_token) { |
| case '+': case '-': |
| case '|': case '^': case '&': case '!': |
| case '*': case '/': case '%': case LEFT_OP: case RIGHT_OP: |
| e = parse_expr(parser_gas); |
| vp = yasm_vp_create_expr(NULL, e); |
| break; |
| default: |
| /* Just an ID */ |
| vp = yasm_vp_create_id(NULL, ID_val, '\0'); |
| get_next_token(); /* ID */ |
| break; |
| } |
| break; |
| case STRING: |
| vp = yasm_vp_create_string(NULL, STRING_val.contents); |
| get_next_token(); /* STRING */ |
| break; |
| case '@': |
| /* XXX: is throwing it away *really* the right thing? */ |
| get_next_token(); /* @ */ |
| continue; |
| default: |
| e = parse_expr(parser_gas); |
| if (!e) |
| return num; |
| vp = yasm_vp_create_expr(NULL, e); |
| break; |
| } |
| yasm_vps_append(vps, vp); |
| num++; |
| if (curtok == ',') |
| get_next_token(); /* ',' */ |
| } |
| return num; |
| } |
| |
| static int |
| parse_datavals(yasm_parser_gas *parser_gas, yasm_datavalhead *dvs) |
| { |
| yasm_expr *e; |
| yasm_dataval *dv; |
| int num = 0; |
| |
| yasm_dvs_initialize(dvs); |
| |
| for (;;) { |
| e = parse_expr(parser_gas); |
| if (!e) { |
| yasm_dvs_delete(dvs); |
| yasm_dvs_initialize(dvs); |
| return 0; |
| } |
| dv = yasm_dv_create_expr(e); |
| yasm_dvs_append(dvs, dv); |
| num++; |
| if (curtok != ',') |
| break; |
| get_next_token(); /* ',' */ |
| } |
| return num; |
| } |
| |
| static int |
| parse_strvals(yasm_parser_gas *parser_gas, yasm_datavalhead *dvs) |
| { |
| yasm_dataval *dv; |
| int num = 0; |
| |
| yasm_dvs_initialize(dvs); |
| |
| for (;;) { |
| if (!expect(STRING)) { |
| yasm_dvs_delete(dvs); |
| yasm_dvs_initialize(dvs); |
| return 0; |
| } |
| dv = yasm_dv_create_string(STRING_val.contents, STRING_val.len); |
| yasm_dvs_append(dvs, dv); |
| get_next_token(); /* STRING */ |
| num++; |
| if (curtok != ',') |
| break; |
| get_next_token(); /* ',' */ |
| } |
| return num; |
| } |
| |
| /* instruction operands */ |
| /* memory addresses */ |
| static yasm_effaddr * |
| parse_memaddr(yasm_parser_gas *parser_gas) |
| { |
| yasm_effaddr *ea = NULL; |
| yasm_expr *e1, *e2; |
| int strong = 0; |
| |
| if (curtok == SEGREG) { |
| uintptr_t segreg = SEGREG_val[0]; |
| get_next_token(); /* SEGREG */ |
| if (!expect(':')) return NULL; |
| get_next_token(); /* ':' */ |
| ea = parse_memaddr(parser_gas); |
| if (!ea) |
| return NULL; |
| yasm_ea_set_segreg(ea, segreg); |
| return ea; |
| } |
| |
| /* We want to parse a leading expression, except when it's actually |
| * just a memory address (with no preceding expression) such as |
| * (REG...) or (,...). |
| */ |
| get_peek_token(parser_gas); |
| if (curtok != '(' || (parser_gas->peek_token != REG |
| && parser_gas->peek_token != ',')) |
| e1 = parse_expr(parser_gas); |
| else |
| e1 = NULL; |
| |
| if (curtok == '(') { |
| int havereg = 0; |
| uintptr_t reg = 0; |
| yasm_intnum *scale = NULL; |
| |
| get_next_token(); /* '(' */ |
| |
| /* base register */ |
| if (curtok == REG) { |
| e2 = p_expr_new_ident(yasm_expr_reg(REG_val[0])); |
| get_next_token(); /* REG */ |
| } else |
| e2 = p_expr_new_ident(yasm_expr_int(yasm_intnum_create_uint(0))); |
| |
| if (curtok == ')') |
| goto done; |
| |
| if (!expect(',')) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("invalid memory expression")); |
| if (e1) yasm_expr_destroy(e1); |
| yasm_expr_destroy(e2); |
| return NULL; |
| } |
| get_next_token(); /* ',' */ |
| |
| if (curtok == ')') |
| goto done; |
| |
| /* index register */ |
| if (curtok == REG) { |
| reg = REG_val[0]; |
| havereg = 1; |
| get_next_token(); /* REG */ |
| if (curtok != ',') { |
| scale = yasm_intnum_create_uint(1); |
| goto done; |
| } |
| get_next_token(); /* ',' */ |
| } |
| |
| /* scale */ |
| if (!expect(INTNUM)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("non-integer scale")); |
| if (e1) yasm_expr_destroy(e1); |
| yasm_expr_destroy(e2); |
| return NULL; |
| } |
| scale = INTNUM_val; |
| get_next_token(); /* INTNUM */ |
| |
| done: |
| if (!expect(')')) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("invalid memory expression")); |
| if (scale) yasm_intnum_destroy(scale); |
| if (e1) yasm_expr_destroy(e1); |
| yasm_expr_destroy(e2); |
| return NULL; |
| } |
| get_next_token(); /* ')' */ |
| |
| if (scale) { |
| if (!havereg) { |
| if (yasm_intnum_get_uint(scale) != 1) |
| yasm_warn_set(YASM_WARN_GENERAL, |
| N_("scale factor of %u without an index register"), |
| yasm_intnum_get_uint(scale)); |
| yasm_intnum_destroy(scale); |
| } else |
| e2 = p_expr_new(yasm_expr_expr(e2), YASM_EXPR_ADD, |
| yasm_expr_expr(p_expr_new(yasm_expr_reg(reg), YASM_EXPR_MUL, |
| yasm_expr_int(scale)))); |
| } |
| |
| if (e1) { |
| /* Ordering is critical here to correctly detecting presence of |
| * RIP in RIP-relative expressions. |
| */ |
| e1 = p_expr_new_tree(e2, YASM_EXPR_ADD, e1); |
| } else |
| e1 = e2; |
| strong = 1; |
| } |
| |
| if (!e1) |
| return NULL; |
| ea = yasm_arch_ea_create(p_object->arch, e1); |
| if (strong) |
| yasm_ea_set_strong(ea, 1); |
| return ea; |
| } |
| |
| static yasm_insn_operand * |
| parse_operand(yasm_parser_gas *parser_gas) |
| { |
| yasm_effaddr *ea; |
| yasm_insn_operand *op; |
| uintptr_t reg; |
| |
| switch (curtok) { |
| case REG: |
| reg = REG_val[0]; |
| get_next_token(); /* REG */ |
| return yasm_operand_create_reg(reg); |
| case SEGREG: |
| /* need to see if it's really a memory address */ |
| get_peek_token(parser_gas); |
| if (parser_gas->peek_token == ':') { |
| ea = parse_memaddr(parser_gas); |
| if (!ea) |
| return NULL; |
| return yasm_operand_create_mem(ea); |
| } |
| reg = SEGREG_val[0]; |
| get_next_token(); /* SEGREG */ |
| return yasm_operand_create_segreg(reg); |
| case REGGROUP: |
| { |
| unsigned long regindex; |
| reg = REGGROUP_val[0]; |
| get_next_token(); /* REGGROUP */ |
| if (curtok != '(') |
| return yasm_operand_create_reg(reg); |
| get_next_token(); /* '(' */ |
| if (!expect(INTNUM)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("integer register index expected")); |
| return NULL; |
| } |
| regindex = yasm_intnum_get_uint(INTNUM_val); |
| get_next_token(); /* INTNUM */ |
| if (!expect(')')) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("missing closing parenthesis for register index")); |
| return NULL; |
| } |
| get_next_token(); /* ')' */ |
| reg = yasm_arch_reggroup_get_reg(p_object->arch, reg, regindex); |
| if (reg == 0) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("bad register index `%u'"), |
| regindex); |
| return NULL; |
| } |
| return yasm_operand_create_reg(reg); |
| } |
| case '$': |
| { |
| yasm_expr *e; |
| get_next_token(); /* '$' */ |
| e = parse_expr(parser_gas); |
| if (!e) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression missing after `%s'"), "$"); |
| return NULL; |
| } |
| return yasm_operand_create_imm(e); |
| } |
| case '*': |
| get_next_token(); /* '*' */ |
| if (curtok == REG) { |
| op = yasm_operand_create_reg(REG_val[0]); |
| get_next_token(); /* REG */ |
| } else { |
| ea = parse_memaddr(parser_gas); |
| if (!ea) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expression missing after `%s'"), "*"); |
| return NULL; |
| } |
| op = yasm_operand_create_mem(ea); |
| } |
| op->deref = 1; |
| return op; |
| default: |
| ea = parse_memaddr(parser_gas); |
| if (!ea) |
| return NULL; |
| return yasm_operand_create_mem(ea); |
| } |
| } |
| |
| /* Expression grammar parsed is: |
| * |
| * expr : expr0 [ {+,-} expr0...] |
| * expr0 : expr1 [ {|,^,&,!} expr1...] |
| * expr1 : expr2 [ {*,/,%,<<,>>} expr2...] |
| * expr2 : { ~,+,- } expr2 |
| * | (expr) |
| * | symbol |
| * | number |
| */ |
| |
| static yasm_expr * |
| parse_expr(yasm_parser_gas *parser_gas) |
| { |
| yasm_expr *e, *f; |
| e = parse_expr0(parser_gas); |
| if (!e) |
| return NULL; |
| |
| while (curtok == '+' || curtok == '-') { |
| int op = curtok; |
| get_next_token(); |
| f = parse_expr0(parser_gas); |
| if (!f) { |
| yasm_expr_destroy(e); |
| return NULL; |
| } |
| |
| switch (op) { |
| case '+': e = p_expr_new_tree(e, YASM_EXPR_ADD, f); break; |
| case '-': e = p_expr_new_tree(e, YASM_EXPR_SUB, f); break; |
| } |
| } |
| return e; |
| } |
| |
| static yasm_expr * |
| parse_expr0(yasm_parser_gas *parser_gas) |
| { |
| yasm_expr *e, *f; |
| e = parse_expr1(parser_gas); |
| if (!e) |
| return NULL; |
| |
| while (curtok == '|' || curtok == '^' || curtok == '&' || curtok == '!') { |
| int op = curtok; |
| get_next_token(); |
| f = parse_expr1(parser_gas); |
| if (!f) { |
| yasm_expr_destroy(e); |
| return NULL; |
| } |
| |
| switch (op) { |
| case '|': e = p_expr_new_tree(e, YASM_EXPR_OR, f); break; |
| case '^': e = p_expr_new_tree(e, YASM_EXPR_XOR, f); break; |
| case '&': e = p_expr_new_tree(e, YASM_EXPR_AND, f); break; |
| case '!': e = p_expr_new_tree(e, YASM_EXPR_NOR, f); break; |
| } |
| } |
| return e; |
| } |
| |
| static yasm_expr * |
| parse_expr1(yasm_parser_gas *parser_gas) |
| { |
| yasm_expr *e, *f; |
| e = parse_expr2(parser_gas); |
| if (!e) |
| return NULL; |
| |
| while (curtok == '*' || curtok == '/' || curtok == '%' || curtok == LEFT_OP |
| || curtok == RIGHT_OP) { |
| int op = curtok; |
| get_next_token(); |
| f = parse_expr2(parser_gas); |
| if (!f) { |
| yasm_expr_destroy(e); |
| return NULL; |
| } |
| |
| switch (op) { |
| case '*': e = p_expr_new_tree(e, YASM_EXPR_MUL, f); break; |
| case '/': e = p_expr_new_tree(e, YASM_EXPR_DIV, f); break; |
| case '%': e = p_expr_new_tree(e, YASM_EXPR_MOD, f); break; |
| case LEFT_OP: e = p_expr_new_tree(e, YASM_EXPR_SHL, f); break; |
| case RIGHT_OP: e = p_expr_new_tree(e, YASM_EXPR_SHR, f); break; |
| } |
| } |
| return e; |
| } |
| |
| static yasm_expr * |
| parse_expr2(yasm_parser_gas *parser_gas) |
| { |
| yasm_expr *e; |
| yasm_symrec *sym; |
| |
| switch (curtok) { |
| case '+': |
| get_next_token(); |
| return parse_expr2(parser_gas); |
| case '-': |
| get_next_token(); |
| e = parse_expr2(parser_gas); |
| if (!e) |
| return NULL; |
| return p_expr_new_branch(YASM_EXPR_NEG, e); |
| case '~': |
| get_next_token(); |
| e = parse_expr2(parser_gas); |
| if (!e) |
| return NULL; |
| return p_expr_new_branch(YASM_EXPR_NOT, e); |
| case '(': |
| get_next_token(); |
| e = parse_expr(parser_gas); |
| if (!e) |
| return NULL; |
| if (!expect(')')) { |
| yasm_error_set(YASM_ERROR_SYNTAX, N_("missing parenthesis")); |
| return NULL; |
| } |
| get_next_token(); |
| return e; |
| case INTNUM: |
| e = p_expr_new_ident(yasm_expr_int(INTNUM_val)); |
| get_next_token(); |
| return e; |
| case FLTNUM: |
| e = p_expr_new_ident(yasm_expr_float(FLTNUM_val)); |
| get_next_token(); |
| return e; |
| case ID: |
| case DIR_SECTNAME: |
| { |
| char *name = ID_val; |
| get_next_token(); /* ID */ |
| if (curtok == '@') { |
| /* TODO: this is needed for shared objects, e.g. sym@PLT */ |
| get_next_token(); /* '@' */ |
| if (!expect(ID)) { |
| yasm_error_set(YASM_ERROR_SYNTAX, |
| N_("expected identifier after `@'")); |
| yasm_xfree(name); |
| return NULL; |
| } |
| yasm_xfree(ID_val); |
| get_next_token(); /* ID */ |
| sym = yasm_symtab_use(p_symtab, name, cur_line); |
| yasm_xfree(name); |
| return p_expr_new_ident(yasm_expr_sym(sym)); |
| } |
| |
| /* "." references the current assembly position */ |
| if (name[1] == '\0' && name[0] == '.') |
| sym = yasm_symtab_define_curpos(p_symtab, ".", |
| parser_gas->prev_bc, cur_line); |
| else |
| sym = yasm_symtab_use(p_symtab, name, cur_line); |
| yasm_xfree(name); |
| return p_expr_new_ident(yasm_expr_sym(sym)); |
| } |
| default: |
| return NULL; |
| } |
| } |
| |
| static void |
| define_label(yasm_parser_gas *parser_gas, char *name, int local) |
| { |
| if (!local) { |
| if (parser_gas->locallabel_base) |
| yasm_xfree(parser_gas->locallabel_base); |
| parser_gas->locallabel_base_len = strlen(name); |
| parser_gas->locallabel_base = |
| yasm_xmalloc(parser_gas->locallabel_base_len+1); |
| strcpy(parser_gas->locallabel_base, name); |
| } |
| |
| yasm_symtab_define_label(p_symtab, name, parser_gas->prev_bc, 1, |
| cur_line); |
| yasm_xfree(name); |
| } |
| |
| static void |
| define_lcomm(yasm_parser_gas *parser_gas, /*@only@*/ char *name, |
| yasm_expr *size, /*@null@*/ yasm_expr *align) |
| { |
| /* Put into .bss section. */ |
| /*@dependent@*/ yasm_section *bss = |
| gas_get_section(parser_gas, yasm__xstrdup(".bss"), NULL, NULL, NULL, 1); |
| |
| if (align) { |
| /* XXX: assume alignment is in bytes, not power-of-two */ |
| yasm_section_bcs_append(bss, gas_parser_align(parser_gas, bss, align, |
| NULL, NULL, 0)); |
| } |
| |
| yasm_symtab_define_label(p_symtab, name, yasm_section_bcs_last(bss), 1, |
| cur_line); |
| yasm_section_bcs_append(bss, yasm_bc_create_reserve(size, 1, cur_line)); |
| yasm_xfree(name); |
| } |
| |
| static yasm_section * |
| gas_get_section(yasm_parser_gas *parser_gas, char *name, |
| /*@null@*/ char *flags, /*@null@*/ char *type, |
| /*@null@*/ yasm_valparamhead *objext_valparams, |
| int builtin) |
| { |
| yasm_valparamhead vps; |
| yasm_valparam *vp; |
| char *gasflags; |
| yasm_section *new_section; |
| |
| yasm_vps_initialize(&vps); |
| vp = yasm_vp_create_id(NULL, name, '\0'); |
| yasm_vps_append(&vps, vp); |
| |
| if (!builtin) { |
| if (flags) |
| gasflags = yasm__xstrdup(flags); |
| else |
| gasflags = yasm__xstrdup(""); |
| vp = yasm_vp_create_string(yasm__xstrdup("gasflags"), gasflags); |
| yasm_vps_append(&vps, vp); |
| if (type) { |
| vp = yasm_vp_create_id(NULL, type, '\0'); |
| yasm_vps_append(&vps, vp); |
| } |
| } |
| |
| new_section = yasm_objfmt_section_switch(p_object, &vps, objext_valparams, |
| cur_line); |
| |
| yasm_vps_delete(&vps); |
| return new_section; |
| } |
| |
| static void |
| gas_switch_section(yasm_parser_gas *parser_gas, char *name, |
| /*@null@*/ char *flags, /*@null@*/ char *type, |
| /*@null@*/ yasm_valparamhead *objext_valparams, |
| int builtin) |
| { |
| yasm_section *new_section; |
| |
| new_section = gas_get_section(parser_gas, yasm__xstrdup(name), flags, type, |
| objext_valparams, builtin); |
| if (new_section) { |
| cursect = new_section; |
| parser_gas->prev_bc = yasm_section_bcs_last(new_section); |
| } else |
| yasm_error_set(YASM_ERROR_GENERAL, N_("invalid section name `%s'"), |
| name); |
| |
| yasm_xfree(name); |
| |
| if (objext_valparams) |
| yasm_vps_delete(objext_valparams); |
| } |
| |
| static yasm_bytecode * |
| gas_parser_align(yasm_parser_gas *parser_gas, yasm_section *sect, |
| yasm_expr *boundval, /*@null@*/ yasm_expr *fillval, |
| /*@null@*/ yasm_expr *maxskipval, int power2) |
| { |
| yasm_intnum *boundintn; |
| |
| /* Convert power of two to number of bytes if necessary */ |
| if (power2) |
| boundval = yasm_expr_create(YASM_EXPR_SHL, |
| yasm_expr_int(yasm_intnum_create_uint(1)), |
| yasm_expr_expr(boundval), cur_line); |
| |
| /* Largest .align in the section specifies section alignment. */ |
| boundintn = yasm_expr_get_intnum(&boundval, 0); |
| if (boundintn) { |
| unsigned long boundint = yasm_intnum_get_uint(boundintn); |
| |
| /* Alignments must be a power of two. */ |
| if (is_exp2(boundint)) { |
| if (boundint > yasm_section_get_align(sect)) |
| yasm_section_set_align(sect, boundint, cur_line); |
| } |
| } |
| |
| return yasm_bc_create_align(boundval, fillval, maxskipval, |
| yasm_section_is_code(sect) ? |
| yasm_arch_get_fill(p_object->arch) : NULL, |
| cur_line); |
| } |
| |
| static yasm_bytecode * |
| gas_parser_dir_fill(yasm_parser_gas *parser_gas, /*@only@*/ yasm_expr *repeat, |
| /*@only@*/ /*@null@*/ yasm_expr *size, |
| /*@only@*/ /*@null@*/ yasm_expr *value) |
| { |
| yasm_datavalhead dvs; |
| yasm_bytecode *bc; |
| unsigned int ssize; |
| |
| if (size) { |
| /*@dependent@*/ /*@null@*/ yasm_intnum *intn; |
| intn = yasm_expr_get_intnum(&size, 0); |
| if (!intn) { |
| yasm_error_set(YASM_ERROR_NOT_ABSOLUTE, |
| N_("size must be an absolute expression")); |
| yasm_expr_destroy(repeat); |
| yasm_expr_destroy(size); |
| if (value) |
| yasm_expr_destroy(value); |
| return NULL; |
| } |
| ssize = yasm_intnum_get_uint(intn); |
| } else |
| ssize = 1; |
| |
| if (!value) |
| value = yasm_expr_create_ident( |
| yasm_expr_int(yasm_intnum_create_uint(0)), cur_line); |
| |
| yasm_dvs_initialize(&dvs); |
| yasm_dvs_append(&dvs, yasm_dv_create_expr(value)); |
| bc = yasm_bc_create_data(&dvs, ssize, 0, p_object->arch, cur_line); |
| |
| yasm_bc_set_multiple(bc, repeat); |
| |
| return bc; |
| } |
| #if 0 |
| static void |
| gas_parser_directive(yasm_parser_gas *parser_gas, const char *name, |
| yasm_valparamhead *valparams, |
| yasm_valparamhead *objext_valparams) |
| { |
| unsigned long line = cur_line; |
| |
| /* Handle (mostly) output-format independent directives here */ |
| if (!yasm_arch_parse_directive(p_object->arch, name, valparams, |
| objext_valparams, parser_gas->object, line)) { |
| ; |
| } else if (yasm_objfmt_directive(p_object, name, valparams, |
| objext_valparams, line)) { |
| yasm_error_set(YASM_ERROR_GENERAL, N_("unrecognized directive [%s]"), |
| name); |
| } |
| |
| yasm_vps_delete(valparams); |
| if (objext_valparams) |
| yasm_vps_delete(objext_valparams); |
| } |
| #endif |