#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include "xed-examples-util.h"
#include "xed-asmparse.h"
static int asp_dbg_verbosity = 1;
static char* asp_strdup(const char* s);
static void upcase(char* s);
static void delete_slist_t(slist_t* s);
static slist_t* get_slist_node(void);
static void clean_out_memparse_rec_t(memparse_rec_t* p);
static void delete_opnd_list_t(opnd_list_t* s);
static opnd_list_t* get_opnd_list_node(void);
static void add_decorator(opnd_list_t* onode, char* d);
static void grab_prefixes(char**p, xed_enc_line_parsed_t* v);
static void study_prefixes(xed_enc_line_parsed_t* v);
static void grab_inst(char**p, xed_enc_line_parsed_t* v);
static void grab_operand(char**p, xed_enc_line_parsed_t* v);
static int isreg(char* s);
static int isdecorator(char* s);
static int64_t letter_cvt(char a, char base);
static int asm_isnumber(char* s, int64_t* onum, int arg_negative);
static int ismemref(char* s);
static int valid_decorator(char const* s);
static int grab_decorator(char* s, unsigned int pos, char** optr);
static void parse_reg(xed_enc_line_parsed_t* v, char* s, opnd_list_t* onode);
static void parse_decorator(char* s, opnd_list_t* onode);
static void parse_memref(char* s, opnd_list_t* onode);
static void refine_operand(xed_enc_line_parsed_t* v, char* s);
static void refine_operands(xed_enc_line_parsed_t* v);
static unsigned int skip_spaces(char *s, unsigned int offset);
static char* asp_strdup(char const* s) {
return xed_strdup(s);
}
void asp_set_verbosity(int v) {
asp_dbg_verbosity = v;
}
void asp_printf(const char* format, ...) {
if (asp_dbg_verbosity < 1)
return;
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
void asp_dbg_printf(const char* format, ...) {
if (asp_dbg_verbosity < 2)
return;
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
}
void asp_error_printf(const char* format, ...) {
va_list args;
va_start(args, format);
fprintf(stderr, "ERROR: ");
vfprintf(stderr, format, args);
va_end(args);
}
static void upcase(char* s) {
(void)xed_upcase_buf(s);
}
static void delete_slist_t(slist_t* s) {
slist_t* p = s;
while(p) {
slist_t* t = p;
p=p->next;
free(t->s); free(t);
}
}
static slist_t* get_slist_node(void) {
slist_t* node = (slist_t*)malloc(sizeof(slist_t));
assert(node != 0);
node->s = 0;
node->next = 0;
return node;
}
static void clean_out_memparse_rec_t(memparse_rec_t* p) {
if (p->seg)
free(p->seg);
if (p->base)
free(p->base);
if (p->index)
free(p->index);
if (p->disp)
free(p->disp);
if (p->scale)
free(p->scale);
p->mem_size = 0; p->mem_bits = 0;
p->ndisp = 0;
}
static void delete_opnd_list_t(opnd_list_t* s) {
opnd_list_t* p = s;
while(p) {
opnd_list_t* t = p;
free(p->s);
clean_out_memparse_rec_t(&p->mem);
delete_slist_t(p->decorators);
p = p->next;
free(t);
}
}
xed_enc_line_parsed_t* asp_get_xed_enc_node(void) {
xed_enc_line_parsed_t* v = (xed_enc_line_parsed_t*)
malloc(sizeof(xed_enc_line_parsed_t));
assert(v != 0);
memset(v, 0, sizeof(xed_enc_line_parsed_t));
return v;
}
void asp_delete_xed_enc_line_parsed_t(xed_enc_line_parsed_t* v) {
if (v->iclass_str)
free(v->iclass_str);
if (v->input)
free(v->input);
delete_slist_t(v->operands);
delete_slist_t(v->prefixes);
delete_opnd_list_t(v->opnds);
free(v);
}
static opnd_list_t* get_opnd_list_node(void) {
opnd_list_t* p = (opnd_list_t*)malloc(sizeof(opnd_list_t));
assert(p != 0);
memset(p, 0, sizeof(opnd_list_t));
p->type = OPND_INVALID;
return p;
}
static void add_decorator(opnd_list_t* onode, char* d) {
slist_t* dnode = get_slist_node();
dnode->s = d;
dnode->next = onode->decorators;
onode->decorators = dnode;
}
static char const* decorators[] = {
"{K0}",
"{K1}",
"{K2}",
"{K3}",
"{K4}",
"{K5}",
"{K6}",
"{K7}",
"{Z}",
"{RNE-SAE}",
"{RD-SAE}",
"{RU-SAE}",
"{RZ-SAE}",
"{SAE}",
"{1TO2}",
"{1TO4}",
"{1TO8}",
"{1TO16}",
"{1TO32}",
"{1TO64}",
0 };
static char const* mem_size_qualifiers[] = {
"BYTE",
"WORD",
"DWORD",
"QWORD",
"XMMWORD",
"YMMWORD",
"ZMMWORD",
0
};
static char const* scales[] = {
"1",
"2",
"4",
"8",
0
};
static void study_prefixes(xed_enc_line_parsed_t* v) {
slist_t* p = v->prefixes;
while(p) {
if (strcmp("REPNE",p->s) == 0)
v->seen_repne = 1;
else if (strcmp("REPE",p->s) == 0)
v->seen_repe = 1;
else if (strcmp("REP",p->s) == 0)
v->seen_repe = 1;
else if (strcmp("LOCK",p->s) == 0)
v->seen_lock = 1;
p = p->next;
}
}
static void grab_prefixes(char**p, xed_enc_line_parsed_t* v)
{
char const* prefixes[] = { "DATA16", "DATA32",
"ADDR16", "ADDR32",
"REX", "REWXW",
"XACQUIRE", "XRELEASE",
"LOCK",
"REP", "REPE", "REPNE",
0 };
char* h = asp_strdup(*p);
char* q = h;
char* r = h;
unsigned int found=1;
do {
unsigned int i=0;
r = q;
while(*q) {
if (isspace(*q)) {
*q = 0; q++;
break;
}
q++;
}
found = 0;
for (i=0; prefixes[i]; i++) {
if (strcmp(r, prefixes[i]) == 0) {
slist_t* node = 0;
found = 1;
asp_dbg_printf("PREFIX [%s]\n",r);
node = get_slist_node();
node->s = asp_strdup(r);
if (v->prefixes)
node->next = v->prefixes;
v->prefixes = node;
while(*q && isspace(*q))
q++;
break;
}
}
}
while(found);
*p = *p + (r-h);
free(h);
}
static void grab_inst(char**p, xed_enc_line_parsed_t* v)
{
char* q = *p;
while(*q) {
if (isspace(*q)) {
*q = 0; q++;
break;
}
q++;
}
v->iclass_str = asp_strdup(*p);
asp_dbg_printf("MNEMONIC [%s]\n",v->iclass_str);
*p = q;
}
static void grab_operand(char**p, xed_enc_line_parsed_t* v)
{
slist_t* node = 0;
char* q = *p;
char* r = 0;
while(*q && isspace(*q))
q++;
r = q;
while(*q && *q != ',')
q++;
if (*q) {
*q = 0; q++;
}
if (q>r) {
char *z = q-1; while (z > r) {
if (*z == 0) {
z--;
continue;
}
if (isspace(*z)) {
*z = 0;
z--;
continue;
}
break;
}
}
asp_dbg_printf("OPERAND: [%s]\n", r);
node = get_slist_node();
node->s = asp_strdup(r);
if (v->operands)
node->next = v->operands;
v->operands = node;
*p = q;
}
#if 0#endif
static int isreg(char* s) { if (s) {
if (isalpha(s[0])) {
int i;
for(i=1;s[i];i++)
if ( !isalnum(s[i]) &&
s[i] != '{' &&
s[i] != '(' &&
s[i] != ')' &&
s[i] != '-' &&
s[i] != '}' )
return 0;
return 1;
}
}
return 0;
}
static int isdecorator(char* s) {
if (s) {
if (s[0] == '{') {
int i;
for(i=1;s[i];i++)
if ( !isalnum(s[i]) &&
s[i] != '-' &&
s[i] != '}' )
return 0;
return 1;
}
}
return 0;
}
static int islongptr(char *s) {
if (!s)
return 0;
if (s[0] == 'F' && s[1] == 'A' && s[2] == 'R') {
s += 3;
s += skip_spaces(s, 0);
}
int column_pos = -1;
for (int i = 0; s[i]; i++) {
if (s[i] == ':') {
column_pos = i;
break;
}
}
if (column_pos < 0)
return 0;
char *first = s;
char *second = s + column_pos + 1;
s[column_pos] = '\0'; int64_t unused = 0;
int res = asm_isnumber(first, &unused, 0)
&& asm_isnumber(second, &unused, 0); s[column_pos] = ':'; return res;
}
static int64_t letter_cvt(char a, char base) {
return (int64_t)(a-base);
}
static int asm_isnumber(char* s, int64_t* onum, int arg_negative) {
int binary = 0;
int hex = 0;
int octal = 0;
int negative = 0;
unsigned int i = 0;
unsigned int j = 0;
unsigned int len = xed_strlen(s);
int64_t val = 0;
if (arg_negative) {
negative = 1;
}
if (s[0]=='-') {
negative = 1;
i++;
}
if (s[0] == '+') {
i++;
}
if (i < len && isdigit(s[i])) { if (s[i]=='0' && i+1 < len) {
if (s[i+1] == 'B') {
binary = 1;
i+=2;
}
else if (s[i+1] == 'X') {
hex = 1;
i+=2;
}
else {
octal = 1;
i++;
}
}
if (binary) {
for(j=i;j<len;j++) {
if (s[j] != '0' && s[j] != '1')
return 0; else
val = val << 1 | (s[j] == '1');
}
}
else if (hex) {
for(j=i;j<len;j++) {
if (!isdigit(s[j]) && (s[j] < 'A' || s[j] > 'F') )
return 0; else if (isdigit(s[j]))
val = val << 4 | letter_cvt(s[j],'0');
else
val = val << 4 | (letter_cvt(s[j],'A')+10 );
}
}
else if (octal) {
for(j=i;j<len;j++) {
if (s[j] < '0' || s[j] > '7')
return 0; else
val = val << 3 | letter_cvt(s[j],'0');
}
}
else { for(j=i;j<len;j++) {
if (!isdigit(s[j]))
return 0; else
val = val * 10 + letter_cvt(s[j],'0');
}
}
asp_dbg_printf("IMM value 0x%016llx\n",val);
if (negative) {
if ((val >> 63ULL) == 1) {
asp_error_printf("Bad immediate operand - too big to be negative: %s\n",s);
exit(1);
}
else {
val = - val; asp_dbg_printf("IMM value 0x%016llx\n",val);
}
}
*onum = val;
return 1;
}
return 0;
}
static unsigned int skip_spaces(char *s, unsigned int offset) {
while (s[offset] && isspace(s[offset])) {
offset++;
}
return offset;
}
static int ismemref(char* s) { if (s) {
unsigned int i=0,offset=0;
for(i=0;mem_size_qualifiers[i];i++) {
unsigned int len;
len = xed_strlen(mem_size_qualifiers[i]);
if (strncmp(mem_size_qualifiers[i],s,len) == 0) {
offset = len;
break;
}
}
offset = skip_spaces(s, offset);
if (!strncmp(s + offset, "PTR", 3)) {
offset += 3;
offset = skip_spaces(s, offset);
}
if (s[offset] == '[') {
unsigned int len = xed_strlen(s);
for(i=len-1;i>offset && i>0;i--) {
if (s[i] == ']')
return 1;
}
}
}
return 0;
}
#define BLEN 100
static int valid_decorator(char const* s) {
int i=0;
while(decorators[i]) {
if (strcmp(decorators[i],s) == 0)
return 1;
i++;
}
return 0;
}
static int grab_decorator(char* s, unsigned int pos, char** optr)
{
char tbuf[BLEN];
int tpos=0;
char* p = s+pos;
int start = 0;
while(*p) {
if (start == 0 && *p == '{') {
start = 1;
tbuf[tpos++] = *p;
}
else if (start) {
tbuf[tpos++] = *p;
if (*p == '}') {
tbuf[tpos]=0;
if (valid_decorator(tbuf)) {
*optr = asp_strdup(tbuf);
return (int)(pos+1);
}
else {
asp_error_printf("Bad decorator: %s\n", tbuf);
exit(1);
}
}
}
else {
break;
}
p++;
pos++;
}
*optr = 0;
if (start) { asp_error_printf("Bad decorator: %s\n", tbuf);
exit(1);
return -1; }
return 0;
}
static void parse_reg(xed_enc_line_parsed_t* v, char* s, opnd_list_t* onode)
{
char tbuf[BLEN];
unsigned int i=0;
unsigned int len=0;
len = xed_strlen(s);
while(i<len && s[i] && s[i] != '{') {
tbuf[i] = s[i];
i++;
}
tbuf[i]=0;
asp_dbg_printf("REGISTER: %s\n",tbuf);
onode->s = asp_strdup(tbuf);
onode->type = OPND_REG;
onode->reg = str2xed_reg_enum_t(onode->s);
if (onode->reg >= XED_REG_CR0 && onode->reg <= XED_REG_CR15) {
v->seen_cr = 1;
}
if (onode->reg >= XED_REG_DR0 && onode->reg <= XED_REG_DR7) {
v->seen_dr = 1;
}
while (i<len && s[i] == '{') {
char* d = 0;
int r;
r = grab_decorator(s,i, &d);
if (r<0) {
asp_error_printf("Decorator parsing error\n");
break;
}
i = (unsigned int)r;
if (d) {
asp_dbg_printf("DECORATOR: %s\n",d);
add_decorator(onode,d);
}
if (d==0)
break;
}
}
static void parse_decorator(char* s, opnd_list_t* onode)
{
unsigned int i=0;
unsigned int len=0;
len = xed_strlen(s);
onode->s = 0;
onode->type = OPND_DECORATOR;
while (i<len && s[i] == '{') {
char* d = 0;
int r;
r = grab_decorator(s,i, &d);
if (r<0) {
asp_error_printf("DECORATOR PARSING ERROR\n");
break;
}
i = (unsigned int)r;
if (d) {
if (onode->s==0) {
asp_dbg_printf("DECORATOR: %s\n",d);
onode->s = d;
}
else {
asp_error_printf("Too many lone decorators %s\n",s);
exit(1);
}
}
if (d==0)
break;
}
if (onode->s == 0) {
asp_dbg_printf("No decorators: %s\n",s);
exit(1);
}
}
static void parse_memref(char* s, opnd_list_t* onode)
{
memparse_rec_t r = { 0 };
r.len = xed_strlen(s);
assert(r.len < BLEN);
char tbuf[BLEN];
char stmp[BLEN];
char *q;
unsigned int i=0;
int p=0;
int plusses=0;
int last_star=0;
unsigned int offset=0;
for(i=0;mem_size_qualifiers[i];i++) {
unsigned int len;
len = xed_strlen(mem_size_qualifiers[i]);
if (strncmp(mem_size_qualifiers[i],s,len) == 0) {
asp_dbg_printf("MEM SIZE QUALIFIER: %s\n",mem_size_qualifiers[i]);
r.mem_size = mem_size_qualifiers[i]; r.mem_bits = 1U << (i+3);
offset = len;
break;
}
}
offset = skip_spaces(s, offset);
if (!strncmp(s+offset, "PTR", 3)) {
offset += 3;
}
for(i=0;s[offset+i];i++) {
unsigned int src_pos = offset + i;
if (!isspace(s[src_pos]))
stmp[p++] = s[src_pos];
}
stmp[p]=0;
p=0;
r.len=xed_strlen(stmp);
for(i=0;i<r.len;i++) {
if (stmp[i] == '[') {
assert(i==0);
continue;
}
else if (stmp[i] == '+') { plusses++;
tbuf[p++]=0;
p=0;
q = asp_strdup(tbuf);
if (r.base && r.index)
r.scale=q;
else if (r.base)
r.index=q;
else
r.base=q;
}
else if (stmp[i] == '-') { plusses++;
r.minus++;
tbuf[p++]=0;
p=0;
q = asp_strdup(tbuf);
if (r.index)
r.scale=q;
else
r.index=q;
}
else if (stmp[i] == ':') { tbuf[p++]=0;
p=0;
if (r.seg)
free(r.seg);
r.seg = asp_strdup(tbuf);
}
else if (stmp[i] == '*') { tbuf[p++]=0;
p=0;
last_star=1;
if (r.index)
free(r.index);
r.index=asp_strdup(tbuf);
continue; }
else if (stmp[i] == ']') { int start_digit;
tbuf[p++]=0;
p=0;
q = asp_strdup(tbuf);
start_digit = isdigit(q[0]);
if (start_digit && (r.scale || r.minus || plusses==2 || last_star==0)) {
r.disp = q;
if (!asm_isnumber(q, &r.ndisp, r.minus)) {
asp_error_printf("Bad displacement: %s\n", q);
exit(1);
}
}
else if (last_star && start_digit)
r.scale=q;
else if (r.base==0 && start_digit==0)
r.base=q;
else if (r.index==0 && start_digit==0)
r.index=q;
else {
asp_error_printf("Internal error parsing memory operand\n");
exit(1); }
i++; break; }
else if (isalnum(stmp[i])) {
tbuf[p++]= stmp[i];
}
else {
asp_error_printf("Internal error parsing SIB\n");
exit(1); }
last_star=0;
}
while (i<r.len && stmp[i] == '{') {
char* d = 0;
int rr;
rr = grab_decorator(stmp,i, &d);
if (rr<0) {
asp_error_printf("Decorator parsing error\n");
break;
}
i = (unsigned int) rr;
if (d) {
asp_dbg_printf("DECORATOR: %s\n",d);
add_decorator(onode, d);
}
if (d==0)
break;
}
if (r.scale) {
int found = 0;
for(i=0;scales[i];i++) {
if (strcmp(r.scale,scales[i]) == 0) {
found = 1;
r.nscale = 1U<<i;
}
}
if (!found) {
asp_error_printf("bad scale: %s\n",r.scale);
exit(1);
}
}
if (r.seg)
asp_dbg_printf("SEG: %s\n",r.seg);
if (r.base)
asp_dbg_printf("BASE: %s\n",r.base);
if (r.index)
asp_dbg_printf("INDEX: %s\n",r.index);
if (r.scale)
asp_dbg_printf("SCALE: %s\n",r.scale);
if (r.disp)
asp_dbg_printf("DISP: %s = 0x%016llx\n",r.disp, r.ndisp);
onode->type = OPND_MEM;
onode->mem = r;
}
static void parse_long_pointer(char* s, opnd_list_t* onode)
{
if (s[0] == 'F' && s[1] == 'A' && s[2] == 'R') {
s += 3;
s += skip_spaces(s, 0);
}
int column_pos = -1;
for (int i = 0; s[i]; i++) {
if (s[i] == ':') {
column_pos = i;
break;
}
}
assert(column_pos >= 0);
char *first = s;
char *second = s + column_pos + 1;
s[column_pos] = '\0'; int64_t first_num, second_num;
asm_isnumber(first, &first_num, 0);
asm_isnumber(second, &second_num, 0);
onode->farptr.seg = s;
onode->farptr.offset = s + column_pos + 1;
onode->farptr.seg_value = first_num;
onode->farptr.offset_value = second_num;
onode->type = OPND_FARPTR;
}
static void refine_operand(xed_enc_line_parsed_t* v, char* s)
{
opnd_list_t* onode = get_opnd_list_node();
int64_t num = 0;
asp_dbg_printf("REFINE OPERAND [%s]\n", s);
if (isreg(s)) {
asp_dbg_printf("REGISTER-ish: %s\n",s);
parse_reg(v,s,onode);
}
else if (asm_isnumber(s,&num,0)) {
asp_dbg_printf("Immediate or displacement: %s\n",s);
onode->type = OPND_IMM;
onode->s = asp_strdup(s);
onode->imm = num;
}
else if (ismemref(s)) {
asp_dbg_printf("MEMREF-ish\n");
parse_memref(s,onode);
}
else if (isdecorator(s)) {
asp_dbg_printf("LONE DECORATOR\n");
parse_decorator(s,onode);
}
else if (islongptr(s)) {
asp_dbg_printf("LONG POINTER\n");
v->seen_far_ptr = 1;
parse_long_pointer(s,onode);
}
else {
asp_error_printf("Bad operand: %s\n",s);
exit(1);
}
onode->next = v->opnds;
v->opnds = onode;
}
static void refine_operands(xed_enc_line_parsed_t* v)
{
slist_t* p = 0;
if (v->operands) {
p = v->operands;
while(p) {
refine_operand(v,p->s);
p = p->next;
}
}
}
void asp_parse_line(xed_enc_line_parsed_t* v)
{
char* p = asp_strdup(v->input);
char* q = p; int inst = 0;
int prefixes = 0;
upcase(p);
while(*p) {
if (isspace(*p)) {
p++; continue;
}
if (prefixes==0) {
grab_prefixes(&p,v); study_prefixes(v);
prefixes = 1;
continue;
}
if (inst==0) {
grab_inst(&p,v); inst = 1;
continue;
}
if (inst==1) { grab_operand(&p, v); continue;
}
}
refine_operands(v);
free(q);
}
void asp_print_parsed_line(xed_enc_line_parsed_t* v) {
slist_t* p=0;
opnd_list_t* q=0;
asp_printf("MODE: %d\n",v->mode);
asp_printf("MNEMONIC: %s\n",v->iclass_str);
asp_printf("PREFIXES: ");
p = v->prefixes;
while(p) {
asp_printf("%s ", p->s);
p = p->next;
}
asp_printf("\n");
asp_printf("OPERANDS: ");
p = v->operands;
while(p) {
asp_printf("<%s> ", p->s);
p = p->next;
}
asp_printf("\n");
asp_printf("OPERANDS DECODED:\n");
q = v->opnds;
while(q) {
slist_t* d = 0;
asp_printf("\t");
if (q->s) asp_printf("%s ", q->s);
switch (q->type) {
case OPND_REG: asp_printf("REG "); break;
case OPND_IMM: asp_printf("IMM 0x%016llx ", q->imm); break;
case OPND_DECORATOR: asp_printf("DECORATOR "); break;
case OPND_INVALID: asp_printf("INVALID "); break;
case OPND_MEM:
asp_printf("MEM "); break;
case OPND_FARPTR:
asp_printf("FAR PTR %s:%s", q->farptr.seg, q->farptr.offset);
break;
default:
assert(0 && "Unhandled operand type");
break;
}
d = q->decorators;
while(d) {
asp_printf("%s ",d->s);
d = d->next;
}
q = q->next;
asp_printf("\n");
}
}