#include "errcode.h"
#include "file.h"
#include "util.h"
#include "yasm.h"
#include <ctype.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_MSC_VER) && (_MSC_VER < 1900)
# define snprintf _snprintf_c
#endif
static int create_section_label_name(char *label, size_t size,
const char *name, const char *attribute)
{
int written;
written = snprintf(label, size, "section_%s_%s", name, attribute);
if ((written < 0) || (size <= (size_t) written))
return -err_no_mem;
return 0;
}
static int add_section_label(struct label *l, const char *name,
const char *attribute, uint64_t value,
struct label **length)
{
char label[255];
int errcode;
errcode = create_section_label_name(label, sizeof(label), name,
attribute);
if (errcode < 0)
return errcode;
errcode = l_append(l, label, value);
if (errcode < 0)
return errcode;
if (length)
*length = l_find(l, label);
return 0;
}
static int parse_section_label(struct label *l, const char *name,
const char *attribute)
{
uint64_t addr;
char *value;
value = strtok(NULL, " ]");
if (!value)
return -err_section_attribute_no_value;
if (sscanf(value, "%" PRIx64, &addr) != 1)
return -err_parse_int;
return add_section_label(l, name, attribute, addr, NULL);
}
static int parse_section(char *line, struct label *l, struct label **length)
{
char *name, *attribute;
int errcode;
name = strtok(line, " ");
if (!name)
return -err_section_no_name;
errcode = add_section_label(l, name, "length", 0ull, length);
if (errcode < 0)
return errcode;
for (;;) {
attribute = strtok(NULL, " =]");
if (!attribute)
return 0;
if (strcmp(attribute, "start") == 0) {
errcode = parse_section_label(l, name, "start");
if (errcode < 0)
return errcode;
} else if (strcmp(attribute, "vstart") == 0) {
errcode = parse_section_label(l, name, "vstart");
if (errcode < 0)
return errcode;
} else
return -err_section_unknown_attribute;
}
}
static int lookup_section_label(struct label *l, const char *name,
const char *attribute, uint64_t *value)
{
char label[255];
int errcode;
errcode = create_section_label_name(label, sizeof(label), name,
attribute);
if (errcode < 0)
return errcode;
return l_lookup(l, value, label);
}
static int lookup_section_vstart(struct label *l, char *line,
uint64_t *vstart)
{
char *name;
name = strtok(line, " ");
if (!name)
return -err_section_no_name;
return lookup_section_label(l, name, "vstart", vstart);
}
static const char key_section[] = "[section";
static const char key_org[] = "[org";
int parse_yasm_labels(struct label *l, const struct text *t)
{
int errcode, no_org_directive;
size_t i;
uint64_t base_addr;
char line[1024], *end;
struct label *length;
if (bug_on(!t))
return -err_internal;
base_addr = 0;
no_org_directive = 1;
length = NULL;
end = line + sizeof(line);
for (i = 0; i < t->n; i++) {
char *tmp;
errcode = text_line(t, line, sizeof(line), i);
if (errcode < 0)
return errcode;
tmp = strstr(line, key_section);
if (tmp) {
tmp += sizeof(key_section) - 1;
errcode = parse_section(tmp, l, &length);
if (errcode < 0)
return errcode;
continue;
}
tmp = strstr(line, key_org);
if (tmp) {
char *org;
org = tmp + sizeof(key_org) - 1;
tmp = strstr(org, "]");
if (!tmp)
return -err_no_org_directive;
*tmp = 0;
errcode = str_to_uint64(org, &base_addr, 0);
if (errcode < 0)
return errcode;
errcode = l_append(l, "org", base_addr);
if (errcode < 0)
return errcode;
no_org_directive = 0;
continue;
}
if (length) {
uint64_t value, size;
tmp = strtok(line, " ");
if (!tmp)
continue;
errcode = str_to_uint64(tmp, &value, 10);
if (errcode < 0)
continue;
tmp = strtok(NULL, " ");
if (!tmp)
continue;
errcode = str_to_uint64(tmp, &value, 16);
if (errcode < 0)
continue;
tmp = strtok(NULL, " ");
if (!tmp)
continue;
errcode = str_to_uint64(tmp, &value, 16);
if (errcode < 0)
continue;
for (size = 0; value != 0; value >>= 8)
size += 1;
length->addr += size;
}
}
if (no_org_directive)
return -err_no_org_directive;
for (i = 0; i < t->n; i++) {
char *tmp;
uint64_t addr;
errcode = text_line(t, line, sizeof(line), i);
if (errcode < 0)
goto error;
tmp = strstr(line, key_section);
if (tmp) {
tmp += sizeof(key_section) - 1;
errcode = lookup_section_vstart(l, tmp, &base_addr);
if (errcode < 0)
return errcode;
continue;
}
tmp = strtok(line, " ");
if (!tmp)
continue;
tmp = strtok(NULL, " ");
if (!tmp)
continue;
if (!make_label(tmp, end)) {
uint64_t laddr;
if (sscanf(tmp, "%" PRIx64, &addr) != 1)
continue;
tmp = strtok(NULL, " ");
if (!tmp)
continue;
tmp = strtok(NULL, " ");
if (!make_label(tmp, end))
continue;
laddr = addr + base_addr;
if (laddr < base_addr) {
errcode = -err_label_addr;
goto error;
}
errcode = l_append(l, tmp, laddr);
if (errcode < 0)
goto error;
continue;
}
errcode = -err_label_addr;
for (i += 1; i < t->n; i++) {
int errcode_text;
errcode_text = text_line(t, line, sizeof(line), i);
if (errcode_text < 0) {
errcode = errcode_text;
break;
}
if (sscanf(line, "%*d %" PRIx64 " %*x %*s", &addr)
== 1) {
uint64_t laddr;
laddr = addr + base_addr;
if (laddr < base_addr) {
errcode = -err_label_addr;
break;
}
errcode = l_append(l, tmp, laddr);
break;
}
}
if (errcode == -err_label_addr)
fprintf(stderr, "label '%s' has no address\n", tmp);
if (errcode < 0)
goto error;
}
return 0;
error:
l_free(l->next);
free(l->name);
l->next = NULL;
l->name = NULL;
return errcode;
}
int make_label(char *s, const char *end)
{
size_t n, size;
if (bug_on(!s))
return 0;
if (bug_on(end <= s))
return 0;
size = (size_t) ((uintptr_t) end - (uintptr_t) s);
n = strnlen(s, size);
if (size <= n)
return 0;
if (n == 0 || s[n-1] != ':')
return 0;
s[n-1] = '\0';
return 1;
}
struct state *st_alloc(void)
{
return calloc(1, sizeof(struct state));
}
void st_free(struct state *st)
{
if (!st)
return;
free(st->filename);
free(st->line);
free(st);
}
int st_print_err(const struct state *st, const char *s, int errcode)
{
if (bug_on(!st))
return -err_internal;
if (bug_on(!(-err_max < errcode && errcode < 0)))
return -err_internal;
if (!s)
s = "";
fprintf(stderr, "%s:%d: error: %s (%s)\n", st->filename, st->n-1, s,
errstr[-errcode]);
return errcode;
}
static int st_set_file(struct state *st, const char *filename, int inc, int n)
{
int errcode;
if (bug_on(!st))
return -err_internal;
if (bug_on(!filename))
return -err_internal;
free(st->filename);
errcode = duplicate_name(&st->filename, filename, FILENAME_MAX);
if (errcode < 0)
return errcode;
st->inc = inc;
st->n = n;
return 0;
}
static int st_update(struct state *st, const char *s)
{
int errcode;
free(st->line);
st->line = NULL;
errcode = duplicate_name(&st->line, s, FILENAME_MAX);
if (errcode < 0)
return errcode;
st->n += st->inc;
return 0;
}
struct pt_directive *pd_alloc(size_t n)
{
struct pt_directive *pd;
pd = calloc(1, sizeof(*pd));
if (!pd)
return NULL;
pd->name = malloc(n);
if (!pd->name)
goto error;
pd->payload = malloc(n);
if (!pd->payload)
goto error;
pd->nlen = n;
pd->plen = n;
return pd;
error:
pd_free(pd);
return NULL;
}
void pd_free(struct pt_directive *pd)
{
if (!pd)
return;
free(pd->name);
free(pd->payload);
free(pd);
}
int pd_set(struct pt_directive *pd, enum pt_directive_kind kind,
const char *name, const char *payload)
{
if (bug_on(!pd))
return -err_internal;
if (bug_on(!name))
return -err_internal;
if (bug_on(!payload))
return -err_internal;
pd->kind = kind;
strncpy(pd->name, name, pd->nlen);
if (pd->nlen > 0)
pd->name[pd->nlen - 1] = '\0';
strncpy(pd->payload, payload, pd->plen);
if (pd->plen > 0)
pd->payload[pd->plen - 1] = '\0';
return 0;
}
static const char pt_marker[] = "@pt ";
#if defined(FEATURE_SIDEBAND)
static const char sb_marker[] = "@sb ";
#endif
int pd_parse(struct pt_directive *pd, struct state *st)
{
enum pt_directive_kind kind;
char *line, *comment, *openpar, *closepar, *directive, *payload;
int errcode;
char *c;
if (bug_on(!pd))
return -err_internal;
if (bug_on(!st))
return -err_internal;
errcode = duplicate_name(&line, st->line, FILENAME_MAX);
if (errcode < 0)
return errcode;
for (c = line; *c; ++c)
*c = (char) tolower(*c);
errcode = -err_no_directive;
comment = strchr(line, ';');
if (!comment)
goto cleanup;
directive = strstr(comment+1, pt_marker);
if (directive) {
directive += sizeof(pt_marker) - 1;
kind = pdk_pt;
} else {
#if defined(FEATURE_SIDEBAND)
directive = strstr(comment+1, sb_marker);
if (directive) {
directive += sizeof(sb_marker) - 1;
kind = pdk_sb;
} else
#endif
goto cleanup;
}
while (isspace(*directive))
directive += 1;
errcode = 0;
openpar = strchr(directive, '(');
if (!openpar) {
errcode = -err_missing_openpar;
st_print_err(st, "invalid syntax", errcode);
goto cleanup;
}
closepar = strchr(openpar, ')');
if (!closepar) {
errcode = -err_missing_closepar;
st_print_err(st, "invalid syntax", errcode);
goto cleanup;
}
*openpar = '\0';
*closepar = '\0';
payload = openpar+1;
errcode = pd_set(pd, kind, directive, payload);
cleanup:
free(line);
return errcode;
}
static const char bin_suffix[] = ".bin";
static const char lst_suffix[] = ".lst";
static const char path_separator = '/';
struct yasm *yasm_alloc(const char *pttfile)
{
char *tmp;
size_t flen, binsize, lstsize;
struct yasm *y;
int errcode, len;
if (bug_on(!pttfile))
return NULL;
y = calloc(1, sizeof(*y));
if (!y)
return NULL;
y->fl = fl_alloc();
if (!y->fl)
goto error;
y->st_asm = st_alloc();
if (!y->st_asm)
goto error;
errcode = duplicate_name(&y->fileroot, pttfile, FILENAME_MAX);
if (errcode < 0)
goto error;
errcode = duplicate_name(&y->pttfile, pttfile, FILENAME_MAX);
if (errcode < 0)
goto error;
tmp = strrchr(y->fileroot, '.');
if (tmp)
*tmp = '\0';
tmp = strrchr(y->fileroot, path_separator);
if (tmp) {
tmp += 1;
flen = strnlen(tmp, FILENAME_MAX);
if (FILENAME_MAX <= flen)
goto error;
memmove(y->fileroot, tmp, flen);
y->fileroot[flen] = '\0';
}
flen = strnlen(y->fileroot, FILENAME_MAX);
if (FILENAME_MAX <= flen)
goto error;
binsize = flen + sizeof(bin_suffix);
lstsize = flen + sizeof(lst_suffix);
y->binfile = malloc(binsize);
if (!y->binfile)
goto error;
len = snprintf(y->binfile, binsize, "%s%s", y->fileroot, bin_suffix);
if ((len < 0) || ((size_t) len != (binsize - 1)))
goto error;
y->lstfile = malloc(lstsize);
if (!y->lstfile)
goto error;
len = snprintf(y->lstfile, lstsize, "%s%s", y->fileroot, lst_suffix);
if ((len < 0) || ((size_t) len != (lstsize - 1)))
goto error;
y->l = l_alloc();
if (!y->l)
goto error;
return y;
error:
yasm_free(y);
return 0;
}
static int yasm_run(struct yasm *y)
{
char *argv[] = {
"yasm",
"<pttfile>",
"-f", "bin",
"-o", "<binfile>",
"-L", "nasm",
"-l", "<lstfile>",
NULL,
};
argv[1] = y->pttfile;
argv[5] = y->binfile;
argv[9] = y->lstfile;
return run(argv[0], argv);
}
int yasm_parse(struct yasm *y)
{
int errcode;
const struct text *t;
if (bug_on(!y))
return -err_internal;
errcode = yasm_run(y);
if (errcode < 0)
goto error;
errcode = fl_gettext(y->fl, &t, y->lstfile);
if (errcode < 0)
goto error;
errcode = parse_yasm_labels(y->l, t);
if (errcode < 0)
goto error;
error:
return errcode;
}
void yasm_free(struct yasm *y)
{
if (!y)
return;
free(y->fileroot);
free(y->pttfile);
free(y->lstfile);
free(y->binfile);
fl_free(y->fl);
st_free(y->st_asm);
l_free(y->l);
free(y);
}
int yasm_lookup_label(const struct yasm *y, uint64_t *addr,
const char *labelname)
{
if (bug_on(!y))
return -err_internal;
return l_lookup(y->l, addr, labelname);
}
static int yasm_advance_next_line(struct yasm *y)
{
char s[1024];
char filename[FILENAME_MAX];
int errcode;
int asm_line, asm_inc;
if (bug_on(!y))
return -err_internal;
for (;;) {
errcode = fl_getline(y->fl, s, sizeof(s), y->lstfile,
(size_t) y->lst_curr_line);
y->lst_curr_line += 1;
if (errcode < 0)
break;
if (sscanf(s, "%*d %%line %d+%d %1023[^\r\n]", &asm_line,
&asm_inc, filename) == 3) {
st_set_file(y->st_asm, filename, asm_line, asm_inc);
continue;
}
if (y->st_asm->n <= 0 || y->st_asm->inc <= 0)
continue;
errcode = fl_getline(y->fl, s, (size_t) sizeof(s),
y->st_asm->filename,
(size_t) y->st_asm->n - 1u);
if (errcode < 0)
break;
errcode = st_update(y->st_asm, s);
break;
}
return errcode;
}
int yasm_pd_parse(struct yasm *y, struct pt_directive *pd)
{
return pd_parse(pd, y->st_asm);
}
int yasm_next_pt_directive(struct yasm *y, struct pt_directive *pd)
{
int errcode;
for (;;) {
errcode = yasm_advance_next_line(y);
if (errcode < 0)
break;
errcode = pd_parse(pd, y->st_asm);
if (errcode != -err_no_directive)
return errcode;
}
if (errcode == -err_out_of_range)
errcode = -err_no_directive;
return errcode;
}
int yasm_next_line(struct yasm *y, char *dest, size_t destlen)
{
int errcode;
if (!destlen)
return 0;
if (bug_on(!dest))
return -err_internal;
errcode = yasm_advance_next_line(y);
if (errcode < 0)
return errcode;
strncpy(dest, y->st_asm->line, destlen);
dest[destlen-1] = '\0';
return 0;
}
int yasm_print_err(const struct yasm *y, const char *s, int errcode)
{
if (bug_on(!y))
return -err_internal;
return st_print_err(y->st_asm, s, errcode);
}
int yasm_lookup_section_label(const struct yasm *y, const char *name,
const char *attribute, uint64_t *value)
{
if (bug_on(!y))
return -err_internal;
return lookup_section_label(y->l, name, attribute, value);
}