mirror of
https://github.com/GLEGram/GLEGram-iOS.git
synced 2026-08-29 21:00:34 +02:00
Based on Swiftgram 12.5 (Telegram iOS 12.5). All GLEGram features ported and organized in GLEGram/ folder. Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass, Font Replacement, Fake Profile, Chat Export, Plugin System, and more. See CHANGELOG_12.5.md for full details.
1681 lines
59 KiB
C
1681 lines
59 KiB
C
/*
|
|
* NASM-compatible parser
|
|
*
|
|
* Copyright (C) 2001-2007 Peter Johnson, Michael Urman
|
|
*
|
|
* 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.
|
|
*
|
|
* 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>
|
|
|
|
#include <libyasm.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include "modules/parsers/nasm/nasm-parser.h"
|
|
#include "modules/preprocs/nasm/nasm.h"
|
|
|
|
typedef enum {
|
|
NORM_EXPR,
|
|
DIR_EXPR, /* Can't have seg:off or WRT anywhere */
|
|
DV_EXPR /* Can't have registers anywhere */
|
|
} expr_type;
|
|
|
|
static yasm_bytecode *parse_line(yasm_parser_nasm *parser_nasm);
|
|
static int parse_directive_valparams(yasm_parser_nasm *parser_nasm,
|
|
/*@out@*/ yasm_valparamhead *vps);
|
|
static yasm_bytecode *parse_times(yasm_parser_nasm *parser_nasm);
|
|
static yasm_bytecode *parse_exp(yasm_parser_nasm *parser_nasm);
|
|
static yasm_bytecode *parse_instr(yasm_parser_nasm *parser_nasm);
|
|
static yasm_insn_operand *parse_operand(yasm_parser_nasm *parser_nasm);
|
|
static yasm_insn_operand *parse_memaddr(yasm_parser_nasm *parser_nasm);
|
|
static yasm_expr *parse_expr(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
static yasm_expr *parse_bexpr(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
static yasm_expr *parse_expr0(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
static yasm_expr *parse_expr1(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
static yasm_expr *parse_expr2(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
static yasm_expr *parse_expr3(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
static yasm_expr *parse_expr4(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
static yasm_expr *parse_expr5(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
static yasm_expr *parse_expr6(yasm_parser_nasm *parser_nasm, expr_type type);
|
|
|
|
static void nasm_parser_directive
|
|
(yasm_parser_nasm *parser_nasm, const char *name,
|
|
/*@null@*/ yasm_valparamhead *valparams,
|
|
/*@null@*/ yasm_valparamhead *objext_valparams);
|
|
static void set_nonlocal_label(yasm_parser_nasm *parser_nasm, const char *name);
|
|
static void define_label(yasm_parser_nasm *parser_nasm, /*@only@*/ char *name,
|
|
unsigned int size);
|
|
|
|
static void yasm_ea_set_implicit_size_segment(yasm_parser_nasm *parser_nasm,
|
|
yasm_effaddr *ea, yasm_expr *e)
|
|
{
|
|
if (parser_nasm->tasm) {
|
|
const char *segment = yasm_expr_segment(e);
|
|
ea->data_len = yasm_expr_size(e);
|
|
if (segment) {
|
|
const char *segreg = tasm_get_segment_register(segment);
|
|
if (segreg)
|
|
yasm_arch_parse_check_regtmod(p_object->arch, segreg,
|
|
strlen(segreg), &ea->segreg);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#define is_eol_tok(tok) ((tok) == 0)
|
|
#define is_eol() is_eol_tok(curtok)
|
|
|
|
#define get_next_token() (curtok = nasm_parser_lex(&curval, parser_nasm))
|
|
|
|
static void
|
|
get_peek_token(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
char savech = parser_nasm->tokch;
|
|
if (parser_nasm->peek_token != NONE)
|
|
yasm_internal_error(N_("only can have one token of lookahead"));
|
|
parser_nasm->peek_token =
|
|
nasm_parser_lex(&parser_nasm->peek_tokval, parser_nasm);
|
|
parser_nasm->peek_tokch = parser_nasm->tokch;
|
|
parser_nasm->tokch = savech;
|
|
}
|
|
|
|
static void
|
|
destroy_curtok_(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
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 DIRECTIVE_NAME:
|
|
case FILENAME:
|
|
case ID:
|
|
case LOCAL_ID:
|
|
case SPECIAL_ID:
|
|
case NONLOCAL_ID:
|
|
yasm_xfree(curval.str_val);
|
|
break;
|
|
case STRING:
|
|
yasm_xfree(curval.str.contents);
|
|
break;
|
|
case INSN:
|
|
yasm_bc_destroy(curval.bc);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
curtok = NONE; /* sanity */
|
|
}
|
|
#define destroy_curtok() destroy_curtok_(parser_nasm)
|
|
|
|
/* 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_nasm *parser_nasm)
|
|
{
|
|
if (is_eol())
|
|
return;
|
|
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("junk at end of line, first unrecognized character is `%c'"),
|
|
parser_nasm->tokch);
|
|
|
|
do {
|
|
destroy_curtok();
|
|
get_next_token();
|
|
} while (!is_eol());
|
|
}
|
|
#define demand_eol() demand_eol_(parser_nasm)
|
|
|
|
static const char *
|
|
describe_token(int token)
|
|
{
|
|
static char strch[] = "` '";
|
|
const char *str;
|
|
|
|
switch (token) {
|
|
case 0: str = "end of line"; break;
|
|
case INTNUM: str = "integer"; break;
|
|
case FLTNUM: str = "floating point value"; break;
|
|
case DIRECTIVE_NAME: str = "directive name"; break;
|
|
case FILENAME: str = "filename"; break;
|
|
case STRING: str = "string"; break;
|
|
case SIZE_OVERRIDE: str = "size override"; break;
|
|
case DECLARE_DATA: str = "DB/DW/etc."; break;
|
|
case RESERVE_SPACE: str = "RESB/RESW/etc."; break;
|
|
case INCBIN: str = "INCBIN"; break;
|
|
case EQU: str = "EQU"; break;
|
|
case TIMES: str = "TIMES"; break;
|
|
case SEG: str = "SEG"; break;
|
|
case WRT: str = "WRT"; break;
|
|
case NOSPLIT: str = "NOSPLIT"; break;
|
|
case STRICT: str = "STRICT"; 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 SIGNDIV: str = "//"; break;
|
|
case SIGNMOD: str = "%%"; break;
|
|
case START_SECTION_ID: str = "$$"; break;
|
|
case ID: str = "identifier"; break;
|
|
case LOCAL_ID: str = ".identifier"; break;
|
|
case SPECIAL_ID: str = "..identifier"; break;
|
|
case NONLOCAL_ID: str = "..@identifier"; break;
|
|
case LINE: str = "%line"; break;
|
|
default:
|
|
strch[1] = token;
|
|
str = strch;
|
|
break;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
static int
|
|
expect_(yasm_parser_nasm *parser_nasm, int token)
|
|
{
|
|
if (curtok == token)
|
|
return 1;
|
|
|
|
yasm_error_set(YASM_ERROR_PARSE, "expected %s", describe_token(token));
|
|
destroy_curtok();
|
|
return 0;
|
|
}
|
|
#define expect(token) expect_(parser_nasm, token)
|
|
|
|
void
|
|
nasm_parser_parse(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
unsigned char *line;
|
|
while ((line = (unsigned char *)
|
|
yasm_preproc_get_line(parser_nasm->preproc)) != NULL) {
|
|
yasm_bytecode *bc = NULL, *temp_bc;
|
|
|
|
parser_nasm->s.bot = line;
|
|
parser_nasm->s.tok = line;
|
|
parser_nasm->s.ptr = line;
|
|
parser_nasm->s.cur = line;
|
|
parser_nasm->s.lim = line + strlen((char *)line)+1;
|
|
parser_nasm->s.top = parser_nasm->s.lim;
|
|
|
|
get_next_token();
|
|
if (!is_eol()) {
|
|
bc = parse_line(parser_nasm);
|
|
demand_eol();
|
|
}
|
|
|
|
if (parser_nasm->abspos) {
|
|
/* If we're inside an absolute section, just add to the absolute
|
|
* position rather than appending bytecodes to a section.
|
|
* Only RES* are allowed in an absolute section, so this is easy.
|
|
*/
|
|
if (bc) {
|
|
/*@null@*/ const yasm_expr *numitems, *multiple;
|
|
unsigned int itemsize;
|
|
numitems = yasm_bc_reserve_numitems(bc, &itemsize);
|
|
if (numitems) {
|
|
yasm_expr *e;
|
|
e = yasm_expr_create(YASM_EXPR_MUL,
|
|
yasm_expr_expr(yasm_expr_copy(numitems)),
|
|
yasm_expr_int(yasm_intnum_create_uint(itemsize)),
|
|
cur_line);
|
|
multiple = yasm_bc_get_multiple_expr(bc);
|
|
if (multiple)
|
|
e = yasm_expr_create_tree(e, YASM_EXPR_MUL,
|
|
yasm_expr_copy(multiple),
|
|
cur_line);
|
|
parser_nasm->abspos = yasm_expr_create_tree(
|
|
parser_nasm->abspos, YASM_EXPR_ADD, e, cur_line);
|
|
} else
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("only RES* allowed within absolute section"));
|
|
yasm_bc_destroy(bc);
|
|
}
|
|
temp_bc = NULL;
|
|
} else if (bc) {
|
|
temp_bc = yasm_section_bcs_append(cursect, bc);
|
|
if (temp_bc)
|
|
parser_nasm->prev_bc = temp_bc;
|
|
} else
|
|
temp_bc = NULL;
|
|
yasm_errwarn_propagate(parser_nasm->errwarns, cur_line);
|
|
|
|
if (parser_nasm->save_input)
|
|
yasm_linemap_add_source(parser_nasm->linemap, temp_bc,
|
|
(char *)line);
|
|
yasm_linemap_goto_next(parser_nasm->linemap);
|
|
yasm_xfree(line);
|
|
}
|
|
}
|
|
|
|
/* All parse_* functions expect to be called with curtok being their first
|
|
* token. They should return with curtok being the token *after* their
|
|
* information.
|
|
*/
|
|
|
|
static yasm_bytecode *
|
|
parse_line(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
yasm_bytecode *bc;
|
|
|
|
bc = parse_exp(parser_nasm);
|
|
if (bc)
|
|
return bc;
|
|
|
|
switch (curtok) {
|
|
case LINE: /* LINE INTNUM '+' INTNUM FILENAME */
|
|
{
|
|
yasm_intnum *line, *incr;
|
|
char *filename;
|
|
|
|
get_next_token();
|
|
|
|
if (!expect(INTNUM)) return NULL;
|
|
line = INTNUM_val;
|
|
get_next_token();
|
|
|
|
if (!expect('+')) return NULL;
|
|
get_next_token();
|
|
|
|
if (!expect(INTNUM)) return NULL;
|
|
incr = INTNUM_val;
|
|
get_next_token();
|
|
|
|
if (!expect(FILENAME)) return NULL;
|
|
filename = FILENAME_val;
|
|
get_next_token();
|
|
|
|
/* %line indicates the line number of the *next* line, so subtract
|
|
* out the increment when setting the line number.
|
|
*/
|
|
yasm_linemap_set(parser_nasm->linemap, filename, 0,
|
|
yasm_intnum_get_uint(line) - yasm_intnum_get_uint(incr),
|
|
yasm_intnum_get_uint(incr));
|
|
yasm_intnum_destroy(line);
|
|
yasm_intnum_destroy(incr);
|
|
yasm_xfree(filename);
|
|
return NULL;
|
|
}
|
|
case '[': /* [ directive ] */
|
|
{
|
|
char *dirname;
|
|
yasm_valparamhead dir_vps;
|
|
int have_vps = 1;
|
|
|
|
parser_nasm->state = DIRECTIVE;
|
|
get_next_token();
|
|
|
|
if (!expect(DIRECTIVE_NAME))
|
|
return NULL;
|
|
dirname = DIRECTIVE_NAME_val;
|
|
get_next_token();
|
|
|
|
/* ignore [warning]. TODO: actually implement */
|
|
if (yasm__strcasecmp(dirname, "warning") == 0) {
|
|
yasm_warn_set(YASM_WARN_GENERAL,
|
|
N_("[warning] directive not supported; ignored"));
|
|
|
|
/* throw away the rest of the directive tokens */
|
|
while (!is_eol() && curtok != ']')
|
|
{
|
|
destroy_curtok();
|
|
get_next_token();
|
|
}
|
|
expect(']');
|
|
get_next_token();
|
|
return NULL;
|
|
}
|
|
|
|
if (curtok == ']' || curtok == ':')
|
|
have_vps = 0;
|
|
else if (!parse_directive_valparams(parser_nasm, &dir_vps)) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("invalid arguments to [%s]"), dirname);
|
|
yasm_xfree(dirname);
|
|
return NULL;
|
|
}
|
|
if (curtok == ':') {
|
|
yasm_valparamhead ext_vps;
|
|
get_next_token();
|
|
if (!parse_directive_valparams(parser_nasm, &ext_vps)) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("invalid arguments to [%s]"), dirname);
|
|
yasm_xfree(dirname);
|
|
return NULL;
|
|
}
|
|
nasm_parser_directive(parser_nasm, dirname,
|
|
have_vps ? &dir_vps : NULL, &ext_vps);
|
|
} else
|
|
nasm_parser_directive(parser_nasm, dirname,
|
|
have_vps ? &dir_vps : NULL, NULL);
|
|
yasm_xfree(dirname);
|
|
expect(']');
|
|
get_next_token();
|
|
return NULL;
|
|
}
|
|
case TIMES: /* TIMES expr exp */
|
|
get_next_token();
|
|
return parse_times(parser_nasm);
|
|
case ID:
|
|
case SPECIAL_ID:
|
|
case NONLOCAL_ID:
|
|
case LOCAL_ID:
|
|
{
|
|
char *name = ID_val;
|
|
int local = parser_nasm->tasm
|
|
? (curtok == ID || curtok == LOCAL_ID ||
|
|
(curtok == SPECIAL_ID && name[0] == '@'))
|
|
: (curtok != ID);
|
|
unsigned int size = 0;
|
|
|
|
get_next_token();
|
|
if (is_eol()) {
|
|
/* label alone on the line */
|
|
yasm_warn_set(YASM_WARN_ORPHAN_LABEL,
|
|
N_("label alone on a line without a colon might be in error"));
|
|
if (!local)
|
|
set_nonlocal_label(parser_nasm, name);
|
|
define_label(parser_nasm, name, 0);
|
|
return NULL;
|
|
}
|
|
if (curtok == ':')
|
|
get_next_token();
|
|
|
|
if (curtok == EQU || (parser_nasm->tasm && curtok == '=')) {
|
|
/* label EQU expr */
|
|
yasm_expr *e;
|
|
get_next_token();
|
|
|
|
if (parser_nasm->tasm && curtok == SIZE_OVERRIDE) {
|
|
size = SIZE_OVERRIDE_val;
|
|
get_next_token();
|
|
}
|
|
|
|
e = parse_expr(parser_nasm, NORM_EXPR);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expression expected after %s"), "EQU");
|
|
yasm_xfree(name);
|
|
return NULL;
|
|
}
|
|
yasm_symtab_define_equ(p_symtab, name, e, cur_line);
|
|
yasm_xfree(name);
|
|
return NULL;
|
|
}
|
|
|
|
if (parser_nasm->tasm && curtok == LABEL)
|
|
get_next_token();
|
|
|
|
if (parser_nasm->tasm && curtok == SIZE_OVERRIDE) {
|
|
size = SIZE_OVERRIDE_val;
|
|
get_next_token();
|
|
}
|
|
|
|
if (!local)
|
|
set_nonlocal_label(parser_nasm, name);
|
|
|
|
if (is_eol()) {
|
|
define_label(parser_nasm, name, size);
|
|
return NULL;
|
|
}
|
|
if (curtok == TIMES) {
|
|
define_label(parser_nasm, name, size);
|
|
get_next_token();
|
|
return parse_times(parser_nasm);
|
|
}
|
|
bc = parse_exp(parser_nasm);
|
|
if (!parser_nasm->tasm && !bc)
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("instruction expected after label"));
|
|
if (parser_nasm->tasm && bc && !size)
|
|
size = yasm_bc_elem_size(bc);
|
|
define_label(parser_nasm, name, size);
|
|
return bc;
|
|
}
|
|
default:
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("label or instruction expected at start of line"));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static int
|
|
parse_directive_valparams(yasm_parser_nasm *parser_nasm,
|
|
/*@out@*/ yasm_valparamhead *vps)
|
|
{
|
|
yasm_vps_initialize(vps);
|
|
for (;;) {
|
|
yasm_valparam *vp;
|
|
yasm_expr *e;
|
|
char *id = NULL;
|
|
|
|
/* Look for value first */
|
|
if (curtok == ID) {
|
|
get_peek_token(parser_nasm);
|
|
if (parser_nasm->peek_token == '=') {
|
|
id = ID_val;
|
|
get_next_token(); /* id */
|
|
get_next_token(); /* '=' */
|
|
}
|
|
}
|
|
|
|
/* Look for parameter */
|
|
switch (curtok) {
|
|
case STRING:
|
|
vp = yasm_vp_create_string(id, STRING_val.contents);
|
|
get_next_token();
|
|
break;
|
|
case ID:
|
|
/* We need a peek token, but avoid error if we have one
|
|
* already; we need to work whether or not we hit the
|
|
* "value=" if test above.
|
|
*
|
|
* We cheat and peek ahead to see if this is just an ID or
|
|
* the ID is part of an expression. We assume a + or - means
|
|
* that it's part of an expression (e.g. "x+y" is parsed as
|
|
* the expression "x+y" and not as "x", "+y").
|
|
*/
|
|
if (parser_nasm->peek_token == NONE)
|
|
get_peek_token(parser_nasm);
|
|
switch (parser_nasm->peek_token) {
|
|
case '|': case '^': case '&': case LEFT_OP: case RIGHT_OP:
|
|
case '+': case '-':
|
|
case '*': case '/': case '%': case SIGNDIV: case SIGNMOD:
|
|
break;
|
|
default:
|
|
/* Just an id */
|
|
vp = yasm_vp_create_id(id, ID_val, '$');
|
|
get_next_token();
|
|
goto next;
|
|
}
|
|
/*@fallthrough@*/
|
|
default:
|
|
e = parse_expr(parser_nasm, DIR_EXPR);
|
|
if (!e) {
|
|
yasm_vps_delete(vps);
|
|
return 0;
|
|
}
|
|
vp = yasm_vp_create_expr(id, e);
|
|
break;
|
|
}
|
|
next:
|
|
yasm_vps_append(vps, vp);
|
|
if (curtok == ',')
|
|
get_next_token();
|
|
if (curtok == ']' || curtok == ':' || is_eol())
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static yasm_bytecode *
|
|
parse_times(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
yasm_expr *multiple;
|
|
yasm_bytecode *bc;
|
|
|
|
multiple = parse_bexpr(parser_nasm, DV_EXPR);
|
|
if (!multiple) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX, N_("expression expected after %s"),
|
|
"TIMES");
|
|
return NULL;
|
|
}
|
|
bc = parse_exp(parser_nasm);
|
|
if (!bc) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("instruction expected after TIMES expression"));
|
|
yasm_expr_destroy(multiple);
|
|
return NULL;
|
|
}
|
|
yasm_bc_set_multiple(bc, multiple);
|
|
return bc;
|
|
}
|
|
|
|
static yasm_bytecode *
|
|
parse_exp(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
yasm_bytecode *bc;
|
|
|
|
bc = parse_instr(parser_nasm);
|
|
if (bc)
|
|
return bc;
|
|
|
|
switch (curtok) {
|
|
case DECLARE_DATA:
|
|
{
|
|
unsigned int size = DECLARE_DATA_val/8;
|
|
yasm_datavalhead dvs;
|
|
yasm_dataval *dv;
|
|
yasm_expr *e, *e2;
|
|
|
|
get_next_token();
|
|
|
|
yasm_dvs_initialize(&dvs);
|
|
for (;;) {
|
|
if (curtok == STRING) {
|
|
/* Peek ahead to see if we're in an expr. If we're not,
|
|
* then generate a real string dataval.
|
|
*/
|
|
get_peek_token(parser_nasm);
|
|
if (parser_nasm->peek_token == ','
|
|
|| is_eol_tok(parser_nasm->peek_token)) {
|
|
dv = yasm_dv_create_string(STRING_val.contents,
|
|
STRING_val.len);
|
|
get_next_token();
|
|
goto dv_done;
|
|
}
|
|
}
|
|
if (curtok == '?') {
|
|
yasm_dvs_delete(&dvs);
|
|
get_next_token();
|
|
if (! is_eol_tok(curtok)) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("can not handle more than one '?'"));
|
|
return NULL;
|
|
}
|
|
return yasm_bc_create_reserve(
|
|
p_expr_new_ident(yasm_expr_int(
|
|
yasm_intnum_create_uint(1))),
|
|
size, cur_line);
|
|
}
|
|
if (!(e = parse_bexpr(parser_nasm, DV_EXPR))) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expression or string expected"));
|
|
yasm_dvs_delete(&dvs);
|
|
return NULL;
|
|
}
|
|
if (curtok == DUP) {
|
|
get_next_token();
|
|
if (curtok != '(') {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected ( after DUP"));
|
|
goto error;
|
|
}
|
|
get_next_token();
|
|
if (curtok == '?') {
|
|
get_next_token();
|
|
if (curtok != ')') {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected ) after DUPlicated expression"));
|
|
goto error;
|
|
}
|
|
get_next_token();
|
|
if (! is_eol_tok(curtok)) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("can not handle more than one '?'"));
|
|
goto error;
|
|
}
|
|
yasm_dvs_delete(&dvs);
|
|
return yasm_bc_create_reserve(e, size, cur_line);
|
|
} else if ((e2 = parse_bexpr(parser_nasm, DV_EXPR))) {
|
|
if (curtok != ')') {
|
|
yasm_expr_destroy(e2);
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected ) after DUPlicated expression"));
|
|
goto error;
|
|
}
|
|
get_next_token();
|
|
dv = yasm_dv_create_expr(e2);
|
|
yasm_dv_set_multiple(dv, e);
|
|
} else {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expression or string expected"));
|
|
error:
|
|
yasm_expr_destroy(e);
|
|
yasm_dvs_delete(&dvs);
|
|
return NULL;
|
|
}
|
|
} else
|
|
dv = yasm_dv_create_expr(e);
|
|
dv_done:
|
|
yasm_dvs_append(&dvs, dv);
|
|
if (is_eol())
|
|
break;
|
|
if (!expect(',')) {
|
|
yasm_dvs_delete(&dvs);
|
|
return NULL;
|
|
}
|
|
get_next_token();
|
|
if (is_eol()) /* allow trailing , on list */
|
|
break;
|
|
}
|
|
return yasm_bc_create_data(&dvs, size, 0, p_object->arch,
|
|
cur_line);
|
|
}
|
|
case RESERVE_SPACE:
|
|
{
|
|
unsigned int size = RESERVE_SPACE_val/8;
|
|
yasm_expr *e;
|
|
get_next_token();
|
|
e = parse_bexpr(parser_nasm, DV_EXPR);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expression expected after %s"), "RESx");
|
|
return NULL;
|
|
}
|
|
return yasm_bc_create_reserve(e, size, cur_line);
|
|
}
|
|
case INCBIN:
|
|
{
|
|
char *filename;
|
|
yasm_expr *start = NULL, *maxlen = NULL;
|
|
|
|
get_next_token();
|
|
|
|
if (!expect(STRING)) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("filename string expected after INCBIN"));
|
|
return NULL;
|
|
}
|
|
filename = STRING_val.contents;
|
|
get_next_token();
|
|
|
|
/* optional start expression */
|
|
if (curtok == ',')
|
|
get_next_token();
|
|
if (is_eol())
|
|
goto incbin_done;
|
|
start = parse_bexpr(parser_nasm, DV_EXPR);
|
|
if (!start) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expression expected for INCBIN start"));
|
|
return NULL;
|
|
}
|
|
|
|
/* optional maxlen expression */
|
|
if (curtok == ',')
|
|
get_next_token();
|
|
if (is_eol())
|
|
goto incbin_done;
|
|
maxlen = parse_bexpr(parser_nasm, DV_EXPR);
|
|
if (!maxlen) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expression expected for INCBIN maximum length"));
|
|
return NULL;
|
|
}
|
|
|
|
incbin_done:
|
|
return yasm_bc_create_incbin(filename, start, maxlen,
|
|
parser_nasm->linemap, cur_line);
|
|
}
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static yasm_bytecode *
|
|
parse_instr(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
yasm_bytecode *bc;
|
|
|
|
switch (curtok) {
|
|
case INSN:
|
|
{
|
|
yasm_insn *insn;
|
|
bc = INSN_val;
|
|
insn = yasm_bc_get_insn(bc);
|
|
|
|
get_next_token();
|
|
if (is_eol())
|
|
return bc; /* no operands */
|
|
|
|
/* parse operands */
|
|
for (;;) {
|
|
yasm_insn_operand *op = parse_operand(parser_nasm);
|
|
if (!op) {
|
|
if (insn->num_operands == 0)
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("unexpected %s after instruction"),
|
|
describe_token(curtok));
|
|
else
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected operand, got %s"),
|
|
describe_token(curtok));
|
|
yasm_bc_destroy(bc);
|
|
return NULL;
|
|
}
|
|
yasm_insn_ops_append(insn, op);
|
|
|
|
if (is_eol())
|
|
break;
|
|
if (!expect(',')) {
|
|
yasm_bc_destroy(bc);
|
|
return NULL;
|
|
}
|
|
get_next_token();
|
|
}
|
|
return bc;
|
|
}
|
|
case PREFIX:
|
|
{
|
|
uintptr_t prefix = PREFIX_val;
|
|
get_next_token();
|
|
bc = parse_instr(parser_nasm);
|
|
if (!bc)
|
|
bc = yasm_arch_create_empty_insn(p_object->arch, cur_line);
|
|
yasm_insn_add_prefix(yasm_bc_get_insn(bc), prefix);
|
|
return bc;
|
|
}
|
|
case SEGREG:
|
|
{
|
|
uintptr_t segreg = SEGREG_val;
|
|
get_next_token();
|
|
bc = parse_instr(parser_nasm);
|
|
if (!bc)
|
|
bc = yasm_arch_create_empty_insn(p_object->arch, cur_line);
|
|
yasm_insn_add_seg_prefix(yasm_bc_get_insn(bc), segreg);
|
|
return bc;
|
|
}
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static yasm_insn_operand *
|
|
parse_operand(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
yasm_insn_operand *op;
|
|
switch (curtok) {
|
|
case '[':
|
|
{
|
|
get_next_token();
|
|
op = parse_memaddr(parser_nasm);
|
|
|
|
expect(']');
|
|
get_next_token();
|
|
|
|
if (!op) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("memory address expected"));
|
|
return NULL;
|
|
}
|
|
|
|
if (parser_nasm->tasm && !is_eol() && curtok != ',') {
|
|
yasm_expr *e = NULL, *f;
|
|
yasm_effaddr *ea;
|
|
|
|
switch (op->type) {
|
|
case YASM_INSN__OPERAND_IMM:
|
|
e = op->data.val;
|
|
break;
|
|
case YASM_INSN__OPERAND_MEMORY:
|
|
if (op->data.ea->disp.rel) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("relative adressing not supported\n"));
|
|
return NULL;
|
|
}
|
|
e = yasm_expr_copy(op->data.ea->disp.abs);
|
|
yasm_arch_ea_destroy(p_object->arch, op->data.ea);
|
|
break;
|
|
case YASM_INSN__OPERAND_REG:
|
|
case YASM_INSN__OPERAND_SEGREG:
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("register adressing not supported\n"));
|
|
return NULL;
|
|
}
|
|
yasm_xfree(op);
|
|
f = parse_bexpr(parser_nasm, NORM_EXPR);
|
|
if (!f) {
|
|
yasm_expr_destroy(e);
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after ]"));
|
|
return NULL;
|
|
}
|
|
e = p_expr_new_tree(e, YASM_EXPR_ADD, f);
|
|
ea = yasm_arch_ea_create(p_object->arch, e);
|
|
yasm_ea_set_implicit_size_segment(parser_nasm, ea, e);
|
|
op = yasm_operand_create_mem(ea);
|
|
}
|
|
return op;
|
|
}
|
|
case OFFSET:
|
|
{
|
|
yasm_insn_operand *op2;
|
|
get_next_token();
|
|
if (parser_nasm->masm && curtok == ID && !yasm__strcasecmp(ID_val, "flat")) {
|
|
get_next_token();
|
|
if (curtok == ':') {
|
|
get_next_token();
|
|
}
|
|
}
|
|
op = parse_operand(parser_nasm);
|
|
if (!op) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("memory address expected"));
|
|
return NULL;
|
|
}
|
|
if (op->type == YASM_INSN__OPERAND_IMM)
|
|
return op;
|
|
if (op->type != YASM_INSN__OPERAND_MEMORY) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("OFFSET applied to non-memory operand"));
|
|
return NULL;
|
|
}
|
|
if (op->data.ea->disp.rel) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("OFFSET applied to non-absolute memory operand"));
|
|
return NULL;
|
|
}
|
|
if (op->data.ea->disp.abs)
|
|
op2 = yasm_operand_create_imm(op->data.ea->disp.abs);
|
|
else
|
|
op2 = yasm_operand_create_imm(p_expr_new_ident(
|
|
yasm_expr_int(yasm_intnum_create_uint(0))));
|
|
yasm_xfree(op);
|
|
return op2;
|
|
}
|
|
case SEGREG:
|
|
{
|
|
uintptr_t segreg = SEGREG_val;
|
|
get_next_token();
|
|
if (parser_nasm->tasm && curtok == ':') {
|
|
get_next_token();
|
|
op = parse_operand(parser_nasm);
|
|
if (!op)
|
|
return NULL;
|
|
if (op->type == YASM_INSN__OPERAND_IMM) {
|
|
yasm_effaddr *ea = yasm_arch_ea_create(p_object->arch,
|
|
op->data.val);
|
|
yasm_insn_operand *op2;
|
|
yasm_ea_set_implicit_size_segment(parser_nasm, ea,
|
|
op->data.val);
|
|
op2 = yasm_operand_create_mem(ea);
|
|
op2->size = op->size;
|
|
yasm_xfree(op);
|
|
op = op2;
|
|
}
|
|
if (op->type != YASM_INSN__OPERAND_MEMORY) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("segment applied to non-memory operand"));
|
|
return NULL;
|
|
}
|
|
yasm_ea_set_segreg(op->data.ea, segreg);
|
|
return op;
|
|
}
|
|
op = yasm_operand_create_segreg(segreg);
|
|
return op;
|
|
}
|
|
case REG:
|
|
op = yasm_operand_create_reg(REG_val);
|
|
get_next_token();
|
|
return op;
|
|
case REGGROUP:
|
|
{
|
|
unsigned long regindex;
|
|
uintptr_t reg = REGGROUP_val;
|
|
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 STRICT:
|
|
get_next_token();
|
|
op = parse_operand(parser_nasm);
|
|
if (op)
|
|
op->strict = 1;
|
|
return op;
|
|
case SIZE_OVERRIDE:
|
|
{
|
|
unsigned int size = SIZE_OVERRIDE_val;
|
|
get_next_token();
|
|
if (parser_nasm->masm && curtok == ID && !yasm__strcasecmp(ID_val, "ptr")) {
|
|
get_next_token();
|
|
}
|
|
op = parse_operand(parser_nasm);
|
|
if (!op)
|
|
return NULL;
|
|
if (op->type == YASM_INSN__OPERAND_REG &&
|
|
yasm_arch_get_reg_size(p_object->arch, op->data.reg) != size)
|
|
yasm_error_set(YASM_ERROR_TYPE,
|
|
N_("cannot override register size"));
|
|
else {
|
|
/* Silently override others unless a warning is turned on.
|
|
* This is to allow overrides such as:
|
|
* %define arg1 dword [bp+4]
|
|
* cmp word arg1, 2
|
|
* Which expands to:
|
|
* cmp word dword [bp+4], 2
|
|
*/
|
|
if (op->size != 0) {
|
|
if (op->size != size)
|
|
yasm_warn_set(YASM_WARN_SIZE_OVERRIDE,
|
|
N_("overriding operand size from %u-bit to %u-bit"),
|
|
op->size, size);
|
|
else
|
|
yasm_warn_set(YASM_WARN_SIZE_OVERRIDE,
|
|
N_("double operand size override"));
|
|
}
|
|
op->size = size;
|
|
}
|
|
return op;
|
|
}
|
|
case TARGETMOD:
|
|
{
|
|
uintptr_t tmod = TARGETMOD_val;
|
|
get_next_token();
|
|
op = parse_operand(parser_nasm);
|
|
if (op)
|
|
op->targetmod = tmod;
|
|
return op;
|
|
}
|
|
case ID:
|
|
case LOCAL_ID:
|
|
case NONLOCAL_ID:
|
|
if (parser_nasm->tasm) {
|
|
get_peek_token(parser_nasm);
|
|
if (parser_nasm->peek_token == '[') {
|
|
yasm_symrec *sym = yasm_symtab_use(p_symtab, ID_val,
|
|
cur_line);
|
|
yasm_expr *e = p_expr_new_ident(yasm_expr_sym(sym)), *f;
|
|
yasm_effaddr *ea;
|
|
yasm_xfree(ID_val);
|
|
get_next_token();
|
|
get_next_token();
|
|
f = parse_bexpr(parser_nasm, NORM_EXPR);
|
|
if (!f) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after ["));
|
|
return NULL;
|
|
}
|
|
e = p_expr_new_tree(e, YASM_EXPR_ADD, f);
|
|
if (!expect(']')) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX, N_("missing closing bracket"));
|
|
return NULL;
|
|
}
|
|
get_next_token();
|
|
ea = yasm_arch_ea_create(p_object->arch, e);
|
|
yasm_ea_set_implicit_size_segment(parser_nasm, ea, e);
|
|
op = yasm_operand_create_mem(ea);
|
|
return op;
|
|
}
|
|
}
|
|
/* Fallthrough */
|
|
default:
|
|
{
|
|
yasm_expr *e = parse_bexpr(parser_nasm, NORM_EXPR);
|
|
if (!e)
|
|
return NULL;
|
|
if (curtok != ':') {
|
|
if (parser_nasm->tasm && yasm_expr_size(e)) {
|
|
yasm_effaddr *ea = yasm_arch_ea_create(p_object->arch, e);
|
|
yasm_ea_set_implicit_size_segment(parser_nasm, ea, e);
|
|
op = yasm_operand_create_mem(ea);
|
|
return op;
|
|
} else if (curtok == '[') {
|
|
yasm_expr *f;
|
|
yasm_effaddr *ea;
|
|
yasm_insn_operand *op2;
|
|
|
|
op = parse_operand(parser_nasm);
|
|
if (!op)
|
|
return NULL;
|
|
|
|
f = op->data.ea->disp.abs;
|
|
e = p_expr_new_tree(e, YASM_EXPR_ADD, f);
|
|
ea = yasm_arch_ea_create(p_object->arch, e);
|
|
yasm_ea_set_implicit_size_segment(parser_nasm, ea, e);
|
|
op2 = yasm_operand_create_mem(ea);
|
|
|
|
yasm_xfree(op);
|
|
|
|
return op2;
|
|
} else {
|
|
return yasm_operand_create_imm(e);
|
|
}
|
|
} else {
|
|
yasm_expr *off;
|
|
get_next_token();
|
|
off = parse_bexpr(parser_nasm, NORM_EXPR);
|
|
if (!off) {
|
|
yasm_expr_destroy(e);
|
|
return NULL;
|
|
}
|
|
op = yasm_operand_create_imm(off);
|
|
op->seg = e;
|
|
return op;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* memory addresses */
|
|
static yasm_insn_operand *
|
|
parse_memaddr(yasm_parser_nasm *parser_nasm)
|
|
{
|
|
yasm_insn_operand *op;
|
|
switch (curtok) {
|
|
case SEGREG:
|
|
{
|
|
uintptr_t segreg = SEGREG_val;
|
|
get_next_token();
|
|
if (!expect(':')) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("`:' required after segment register"));
|
|
return NULL;
|
|
}
|
|
get_next_token();
|
|
op = parse_memaddr(parser_nasm);
|
|
if (op)
|
|
yasm_ea_set_segreg(op->data.ea, segreg);
|
|
return op;
|
|
}
|
|
case SIZE_OVERRIDE:
|
|
{
|
|
unsigned int size = SIZE_OVERRIDE_val;
|
|
get_next_token();
|
|
op = parse_memaddr(parser_nasm);
|
|
if (op)
|
|
op->data.ea->disp.size = size;
|
|
return op;
|
|
}
|
|
case NOSPLIT:
|
|
get_next_token();
|
|
op = parse_memaddr(parser_nasm);
|
|
if (op)
|
|
op->data.ea->nosplit = 1;
|
|
return op;
|
|
case REL:
|
|
get_next_token();
|
|
op = parse_memaddr(parser_nasm);
|
|
if (op) {
|
|
op->data.ea->pc_rel = 1;
|
|
op->data.ea->not_pc_rel = 0;
|
|
}
|
|
return op;
|
|
case ABS:
|
|
get_next_token();
|
|
op = parse_memaddr(parser_nasm);
|
|
if (op) {
|
|
op->data.ea->pc_rel = 0;
|
|
op->data.ea->not_pc_rel = 1;
|
|
}
|
|
return op;
|
|
default:
|
|
{
|
|
yasm_expr *e = parse_bexpr(parser_nasm, NORM_EXPR);
|
|
if (!e)
|
|
return NULL;
|
|
if (curtok != ':') {
|
|
yasm_effaddr *ea = yasm_arch_ea_create(p_object->arch, e);
|
|
yasm_ea_set_implicit_size_segment(parser_nasm, ea, e);
|
|
return yasm_operand_create_mem(ea);
|
|
} else {
|
|
yasm_effaddr *ea;
|
|
yasm_expr *off;
|
|
get_next_token();
|
|
off = parse_bexpr(parser_nasm, NORM_EXPR);
|
|
if (!off) {
|
|
yasm_expr_destroy(e);
|
|
return NULL;
|
|
}
|
|
ea = yasm_arch_ea_create(p_object->arch, off);
|
|
yasm_ea_set_implicit_size_segment(parser_nasm, ea, off);
|
|
op = yasm_operand_create_mem(ea);
|
|
op->seg = e;
|
|
return op;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Expression grammar parsed is:
|
|
*
|
|
* expr : bexpr [ : bexpr ]
|
|
* bexpr : expr0 [ WRT expr6 ]
|
|
* expr0 : expr1 [ {|} expr1...]
|
|
* expr1 : expr2 [ {^} expr2...]
|
|
* expr2 : expr3 [ {&} expr3...]
|
|
* expr3 : expr4 [ {<<,>>} expr4...]
|
|
* expr4 : expr5 [ {+,-} expr5...]
|
|
* expr5 : expr6 [ {*,/,%,//,%%} expr6...]
|
|
* expr6 : { ~,+,-,SEG } expr6
|
|
* | (expr)
|
|
* | symbol
|
|
* | $
|
|
* | number
|
|
*/
|
|
|
|
#define parse_expr_common(leftfunc, tok, rightfunc, op) \
|
|
do { \
|
|
yasm_expr *e, *f; \
|
|
e = leftfunc(parser_nasm, type); \
|
|
if (!e) \
|
|
return NULL; \
|
|
\
|
|
while (curtok == tok) { \
|
|
get_next_token(); \
|
|
f = rightfunc(parser_nasm, type); \
|
|
if (!f) { \
|
|
yasm_error_set(YASM_ERROR_SYNTAX, \
|
|
N_("expected expression after %s"), \
|
|
describe_token(op)); \
|
|
yasm_expr_destroy(e); \
|
|
return NULL; \
|
|
} \
|
|
e = p_expr_new_tree(e, op, f); \
|
|
} \
|
|
return e; \
|
|
} while(0)
|
|
|
|
static yasm_expr *
|
|
parse_expr(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
switch (type) {
|
|
case DIR_EXPR:
|
|
/* directive expressions can't handle seg:off or WRT */
|
|
return parse_expr0(parser_nasm, type);
|
|
default:
|
|
parse_expr_common(parse_bexpr, ':', parse_bexpr, YASM_EXPR_SEGOFF);
|
|
}
|
|
/*@notreached@*/
|
|
return NULL;
|
|
}
|
|
|
|
static yasm_expr *
|
|
parse_bexpr(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
parse_expr_common(parse_expr0, WRT, parse_expr6, YASM_EXPR_WRT);
|
|
}
|
|
|
|
static yasm_expr *
|
|
parse_expr0(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
parse_expr_common(parse_expr1, '|', parse_expr1, YASM_EXPR_OR);
|
|
}
|
|
|
|
static yasm_expr *
|
|
parse_expr1(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
parse_expr_common(parse_expr2, '^', parse_expr2, YASM_EXPR_XOR);
|
|
}
|
|
|
|
static yasm_expr *
|
|
parse_expr2(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
parse_expr_common(parse_expr3, '&', parse_expr3, YASM_EXPR_AND);
|
|
}
|
|
|
|
static yasm_expr *
|
|
parse_expr3(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
yasm_expr *e, *f;
|
|
e = parse_expr4(parser_nasm, type);
|
|
if (!e)
|
|
return NULL;
|
|
|
|
while (curtok == LEFT_OP || curtok == RIGHT_OP) {
|
|
int op = curtok;
|
|
get_next_token();
|
|
f = parse_expr4(parser_nasm, type);
|
|
if (!f) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"),
|
|
describe_token(op));
|
|
yasm_expr_destroy(e);
|
|
return NULL;
|
|
}
|
|
|
|
switch (op) {
|
|
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_expr4(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
yasm_expr *e, *f;
|
|
e = parse_expr5(parser_nasm, type);
|
|
if (!e)
|
|
return NULL;
|
|
|
|
while (curtok == '+' || curtok == '-') {
|
|
int op = curtok;
|
|
get_next_token();
|
|
f = parse_expr5(parser_nasm, type);
|
|
if (!f) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"),
|
|
describe_token(op));
|
|
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_expr5(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
yasm_expr *e, *f;
|
|
e = parse_expr6(parser_nasm, type);
|
|
if (!e)
|
|
return NULL;
|
|
|
|
while (curtok == '*' || curtok == '/' || curtok == '%'
|
|
|| curtok == SIGNDIV || curtok == SIGNMOD) {
|
|
int op = curtok;
|
|
get_next_token();
|
|
f = parse_expr6(parser_nasm, type);
|
|
if (!f) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"),
|
|
describe_token(op));
|
|
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 SIGNDIV: e = p_expr_new_tree(e, YASM_EXPR_SIGNDIV, f); break;
|
|
case SIGNMOD: e = p_expr_new_tree(e, YASM_EXPR_SIGNMOD, f); break;
|
|
}
|
|
}
|
|
return e;
|
|
}
|
|
|
|
static yasm_expr *
|
|
parse_expr6(yasm_parser_nasm *parser_nasm, expr_type type)
|
|
{
|
|
yasm_expr *e;
|
|
yasm_symrec *sym;
|
|
|
|
switch (curtok) {
|
|
case '+':
|
|
get_next_token();
|
|
e = parse_expr6(parser_nasm, type);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"), "`+'");
|
|
}
|
|
return e;
|
|
case '-':
|
|
get_next_token();
|
|
e = parse_expr6(parser_nasm, type);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"), "`-'");
|
|
return NULL;
|
|
}
|
|
return p_expr_new_branch(YASM_EXPR_NEG, e);
|
|
case '~':
|
|
get_next_token();
|
|
e = parse_expr6(parser_nasm, type);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"), "`~'");
|
|
return NULL;
|
|
}
|
|
return p_expr_new_branch(YASM_EXPR_NOT, e);
|
|
case LOW:
|
|
get_next_token();
|
|
e = parse_expr6(parser_nasm, type);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"), "LOW");
|
|
return NULL;
|
|
}
|
|
return p_expr_new_tree(e, YASM_EXPR_AND,
|
|
p_expr_new_ident(yasm_expr_int(yasm_intnum_create_uint(0xff))));
|
|
case HIGH:
|
|
get_next_token();
|
|
e = parse_expr6(parser_nasm, type);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"), "HIGH");
|
|
return NULL;
|
|
}
|
|
return p_expr_new_tree(
|
|
p_expr_new_tree(e, YASM_EXPR_SHR,
|
|
p_expr_new_ident(yasm_expr_int(
|
|
yasm_intnum_create_uint(8)))),
|
|
YASM_EXPR_AND,
|
|
p_expr_new_ident(yasm_expr_int(yasm_intnum_create_uint(0xff))));
|
|
case SEG:
|
|
get_next_token();
|
|
e = parse_expr6(parser_nasm, type);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"), "SEG");
|
|
return NULL;
|
|
}
|
|
return p_expr_new_branch(YASM_EXPR_SEG, e);
|
|
case '(':
|
|
get_next_token();
|
|
e = parse_expr(parser_nasm, type);
|
|
if (!e) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("expected expression after %s"), "`('");
|
|
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 REG:
|
|
if (type == DV_EXPR) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("data values can't have registers"));
|
|
return NULL;
|
|
}
|
|
e = p_expr_new_ident(yasm_expr_reg(REG_val));
|
|
get_next_token();
|
|
return e;
|
|
}
|
|
|
|
/* directives allow very little and handle IDs specially */
|
|
if (type == DIR_EXPR) {
|
|
switch (curtok) {
|
|
case ID:
|
|
sym = yasm_symtab_use(p_symtab, ID_val, cur_line);
|
|
e = p_expr_new_ident(yasm_expr_sym(sym));
|
|
yasm_xfree(ID_val);
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
} else switch (curtok) {
|
|
case FLTNUM:
|
|
e = p_expr_new_ident(yasm_expr_float(FLTNUM_val));
|
|
break;
|
|
case STRING:
|
|
{
|
|
yasm_intnum *intn;
|
|
if (parser_nasm->tasm)
|
|
intn = yasm_intnum_create_charconst_tasm(STRING_val.contents);
|
|
else
|
|
intn = yasm_intnum_create_charconst_nasm(STRING_val.contents);
|
|
e = p_expr_new_ident(yasm_expr_int(intn));
|
|
yasm_xfree(STRING_val.contents);
|
|
break;
|
|
}
|
|
case SPECIAL_ID:
|
|
sym = yasm_objfmt_get_special_sym(p_object, ID_val+2, "nasm");
|
|
if (sym) {
|
|
e = p_expr_new_ident(yasm_expr_sym(sym));
|
|
yasm_xfree(ID_val);
|
|
break;
|
|
}
|
|
/*@fallthrough@*/
|
|
case ID:
|
|
case LOCAL_ID:
|
|
case NONLOCAL_ID:
|
|
sym = yasm_symtab_use(p_symtab, ID_val, cur_line);
|
|
e = p_expr_new_ident(yasm_expr_sym(sym));
|
|
yasm_xfree(ID_val);
|
|
break;
|
|
case '$':
|
|
/* "$" references the current assembly position */
|
|
if (parser_nasm->abspos)
|
|
e = yasm_expr_copy(parser_nasm->abspos);
|
|
else {
|
|
sym = yasm_symtab_define_curpos(p_symtab, "$",
|
|
parser_nasm->prev_bc, cur_line);
|
|
e = p_expr_new_ident(yasm_expr_sym(sym));
|
|
}
|
|
break;
|
|
case START_SECTION_ID:
|
|
/* "$$" references the start of the current section */
|
|
if (parser_nasm->absstart)
|
|
e = yasm_expr_copy(parser_nasm->absstart);
|
|
else {
|
|
sym = yasm_symtab_define_label(p_symtab, "$$",
|
|
yasm_section_bcs_first(cursect), 0, cur_line);
|
|
e = p_expr_new_ident(yasm_expr_sym(sym));
|
|
}
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
get_next_token();
|
|
return e;
|
|
}
|
|
|
|
static void
|
|
set_nonlocal_label(yasm_parser_nasm *parser_nasm, const char *name)
|
|
{
|
|
if (!parser_nasm->tasm || tasm_locals) {
|
|
if (parser_nasm->locallabel_base)
|
|
yasm_xfree(parser_nasm->locallabel_base);
|
|
parser_nasm->locallabel_base_len = strlen(name);
|
|
parser_nasm->locallabel_base =
|
|
yasm_xmalloc(parser_nasm->locallabel_base_len+1);
|
|
strcpy(parser_nasm->locallabel_base, name);
|
|
}
|
|
}
|
|
|
|
static void
|
|
define_label(yasm_parser_nasm *parser_nasm, char *name, unsigned int size)
|
|
{
|
|
yasm_symrec *symrec;
|
|
|
|
if (parser_nasm->abspos)
|
|
symrec = yasm_symtab_define_equ(p_symtab, name,
|
|
yasm_expr_copy(parser_nasm->abspos),
|
|
cur_line);
|
|
else
|
|
symrec = yasm_symtab_define_label(p_symtab, name, parser_nasm->prev_bc,
|
|
1, cur_line);
|
|
|
|
yasm_symrec_set_size(symrec, size);
|
|
yasm_symrec_set_segment(symrec, tasm_segment);
|
|
|
|
yasm_xfree(name);
|
|
}
|
|
|
|
static void
|
|
dir_align(yasm_object *object, yasm_valparamhead *valparams,
|
|
yasm_valparamhead *objext_valparams, unsigned long line)
|
|
{
|
|
yasm_valparam *vp = yasm_vps_first(valparams);
|
|
yasm_expr *boundval = yasm_vp_expr(vp, object->symtab, line);
|
|
/*@depedent@*/ yasm_intnum *boundintn;
|
|
|
|
/* Largest .align in the section specifies section alignment.
|
|
* Note: this doesn't match NASM behavior, but is a lot more
|
|
* intelligent!
|
|
*/
|
|
if (boundval && (boundintn = yasm_expr_get_intnum(&boundval, 0))) {
|
|
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(object->cur_section))
|
|
yasm_section_set_align(object->cur_section, boundint, line);
|
|
}
|
|
}
|
|
|
|
/* As this directive is called only when nop is used as fill, always
|
|
* use arch (nop) fill.
|
|
*/
|
|
yasm_section_bcs_append(object->cur_section,
|
|
yasm_bc_create_align(boundval, NULL, NULL,
|
|
/*yasm_section_is_code(object->cur_section) ?*/
|
|
yasm_arch_get_fill(object->arch)/* : NULL*/,
|
|
line));
|
|
}
|
|
|
|
static void
|
|
nasm_parser_directive(yasm_parser_nasm *parser_nasm, const char *name,
|
|
yasm_valparamhead *valparams,
|
|
yasm_valparamhead *objext_valparams)
|
|
{
|
|
unsigned long line = cur_line;
|
|
yasm_valparam *vp;
|
|
|
|
if (!yasm_object_directive(p_object, name, "nasm", valparams,
|
|
objext_valparams, line))
|
|
;
|
|
else if (yasm__strcasecmp(name, "absolute") == 0) {
|
|
if (!valparams) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("directive `%s' requires an argument"),
|
|
"absolute");
|
|
} else {
|
|
vp = yasm_vps_first(valparams);
|
|
if (parser_nasm->absstart)
|
|
yasm_expr_destroy(parser_nasm->absstart);
|
|
if (parser_nasm->abspos)
|
|
yasm_expr_destroy(parser_nasm->abspos);
|
|
parser_nasm->absstart = yasm_vp_expr(vp, p_object->symtab, line);
|
|
parser_nasm->abspos = yasm_expr_copy(parser_nasm->absstart);
|
|
cursect = NULL;
|
|
parser_nasm->prev_bc = NULL;
|
|
}
|
|
} else if (yasm__strcasecmp(name, "align") == 0) {
|
|
/* Really, we shouldn't end up with an align directive in an absolute
|
|
* section (as it's supposed to be only used for nop fill), but handle
|
|
* it gracefully anyway.
|
|
*/
|
|
if (parser_nasm->abspos) {
|
|
yasm_expr *boundval, *e;
|
|
vp = yasm_vps_first(valparams);
|
|
boundval = yasm_vp_expr(vp, p_object->symtab, line);
|
|
e = yasm_expr_create_tree(
|
|
yasm_expr_create_tree(yasm_expr_copy(parser_nasm->absstart),
|
|
YASM_EXPR_SUB,
|
|
yasm_expr_copy(parser_nasm->abspos),
|
|
cur_line),
|
|
YASM_EXPR_AND,
|
|
yasm_expr_create(YASM_EXPR_SUB, yasm_expr_expr(boundval),
|
|
yasm_expr_int(yasm_intnum_create_uint(1)),
|
|
cur_line),
|
|
cur_line);
|
|
parser_nasm->abspos = yasm_expr_create_tree(
|
|
parser_nasm->abspos, YASM_EXPR_ADD, e, cur_line);
|
|
} else if (!valparams) {
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("directive `%s' requires an argument"), "align");
|
|
} else
|
|
dir_align(p_object, valparams, objext_valparams, line);
|
|
} else if (yasm__strcasecmp(name, "default") == 0) {
|
|
if (!valparams)
|
|
;
|
|
else {
|
|
vp = yasm_vps_first(valparams);
|
|
while (vp) {
|
|
const char *id = yasm_vp_id(vp);
|
|
if (id) {
|
|
if (yasm__strcasecmp(id, "rel") == 0)
|
|
yasm_arch_set_var(p_object->arch, "default_rel", 1);
|
|
else if (yasm__strcasecmp(id, "abs") == 0)
|
|
yasm_arch_set_var(p_object->arch, "default_rel", 0);
|
|
else
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("unrecognized default `%s'"), id);
|
|
} else
|
|
yasm_error_set(YASM_ERROR_SYNTAX,
|
|
N_("unrecognized default value"));
|
|
vp = yasm_vps_next(vp);
|
|
}
|
|
}
|
|
} else
|
|
yasm_error_set(YASM_ERROR_SYNTAX, N_("unrecognized directive `%s'"),
|
|
name);
|
|
|
|
if (parser_nasm->absstart && cursect) {
|
|
/* We switched to a new section. Get out of absolute section mode. */
|
|
yasm_expr_destroy(parser_nasm->absstart);
|
|
parser_nasm->absstart = NULL;
|
|
if (parser_nasm->abspos) {
|
|
yasm_expr_destroy(parser_nasm->abspos);
|
|
parser_nasm->abspos = NULL;
|
|
}
|
|
}
|
|
|
|
if (cursect) {
|
|
/* In case cursect changed or a bytecode was added, update prev_bc. */
|
|
parser_nasm->prev_bc = yasm_section_bcs_last(cursect);
|
|
}
|
|
|
|
if (valparams)
|
|
yasm_vps_delete(valparams);
|
|
if (objext_valparams)
|
|
yasm_vps_delete(objext_valparams);
|
|
}
|
|
|
|
yasm_bytecode *
|
|
gas_intel_syntax_parse_instr(yasm_parser_nasm *parser_nasm, unsigned char *instr)
|
|
{
|
|
yasm_bytecode *bc = NULL;
|
|
char *sinstr = (char *) instr;
|
|
|
|
parser_nasm->s.bot = instr;
|
|
parser_nasm->s.tok = instr;
|
|
parser_nasm->s.ptr = instr;
|
|
parser_nasm->s.cur = instr;
|
|
parser_nasm->s.lim = instr + strlen(sinstr) + 1;
|
|
parser_nasm->s.top = parser_nasm->s.lim;
|
|
parser_nasm->peek_token = NONE;
|
|
|
|
get_next_token();
|
|
if (!is_eol()) {
|
|
bc = parse_instr(parser_nasm);
|
|
}
|
|
|
|
return bc;
|
|
}
|