diff options
Diffstat (limited to 'pttc/src')
| -rw-r--r-- | pttc/src/errcode.c | 85 | ||||
| -rw-r--r-- | pttc/src/file.c | 314 | ||||
| -rw-r--r-- | pttc/src/main.c | 137 | ||||
| -rw-r--r-- | pttc/src/parse.c | 2779 | ||||
| -rw-r--r-- | pttc/src/posix/util.c | 67 | ||||
| -rw-r--r-- | pttc/src/pttc.c | 62 | ||||
| -rw-r--r-- | pttc/src/util.c | 240 | ||||
| -rw-r--r-- | pttc/src/windows/util.c | 137 | ||||
| -rw-r--r-- | pttc/src/yasm.c | 848 |
9 files changed, 4669 insertions, 0 deletions
diff --git a/pttc/src/errcode.c b/pttc/src/errcode.c new file mode 100644 index 0000000000000..1253b0eaa0aa5 --- /dev/null +++ b/pttc/src/errcode.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "errcode.h" + +const char *errstr[] = { + "success", + + "cannot open file", + "cannot read file", + "cannot get file size", + "cannot write file", + "out of range", + + "label has no address", + "yasm directive 'org' is required", + "no pt directive", + "no such label", + "label name is too long", + "label name is not unique", + + "failed to find section name", + "failed to find value for section attribute", + "unknown section attribute", + + "missing ')'", + "missing '('", + + "parse error", + "integer cannot be parsed", + "integer too big", + "ipc missing or has invalid value", + "ip missing", + "no arguments", + "trailing tokens", + "unknown character", + "unknown directive", + "missing directive", + + "unexpected sub C-state", + "invalid C-state", + + "no open sideband file", + "sideband format error", + "configuration error", + + "pt library error", + + "run failed", + + "unspecified error", + + "out of memory", + + "internal error", + + "processing stopped", + + "max error code", +}; diff --git a/pttc/src/file.c b/pttc/src/file.c new file mode 100644 index 0000000000000..a7ce1f3e953b5 --- /dev/null +++ b/pttc/src/file.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "errcode.h" +#include "file.h" +#include "util.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +struct text *text_alloc(const char *s) +{ + size_t n, i; + char **line; + struct text *t; + + t = calloc(1, sizeof(struct text)); + if (!t) + return NULL; + + /* If s is NULL or empty, there is nothing to do. */ + if (!s || *s == '\0') + return t; + + /* beginning of s is the first line. */ + t->n = 1; + t->line = calloc(1, sizeof(*t->line)); + if (!t->line) + goto error; + + t->line[0] = duplicate_str(s); + if (!t->line[0]) + goto error; + + /* iterate through all chars and make \r?\n to \0. */ + n = strlen(t->line[0]); + for (i = 0; i < n; i++) { + if (t->line[0][i] == '\r') { + if (i+1 >= n) { + /* the file ends with \r. */ + t->line[0][i] = '\0'; + break; + } + /* terminate the line string if it's a line end. */ + if (t->line[0][i+1] == '\n') + t->line[0][i] = '\0'; + + } else if (t->line[0][i] == '\n') { + /* set newline character always to \0. */ + t->line[0][i] = '\0'; + if (i+1 >= n) { + /* the file ends with \n. */ + break; + } + /* increase line pointer buffer. */ + line = realloc(t->line, (t->n+1) * sizeof(*t->line)); + if (!line) + goto error; + t->line = line; + /* point to the next character after the + * newline and increment the number of lines. + */ + t->line[t->n++] = &(t->line[0][i+1]); + } + } + + return t; + +error: + text_free(t); + return NULL; +} + +void text_free(struct text *t) +{ + if (!t) + return; + + if (t->line) + free(t->line[0]); + free(t->line); + free(t); +} + +int text_line(const struct text *t, char *dest, size_t destlen, size_t n) +{ + if (bug_on(!t)) + return -err_internal; + + if (bug_on(!dest && destlen)) + return -err_internal; + + if (n >= t->n) + return -err_out_of_range; + + if (!dest) + return 0; + + if (!destlen) + return -err_internal; + + strncpy(dest, t->line[n], destlen); + + /* Make sure the string is terminated. */ + dest[destlen-1] = '\0'; + return 0; +} + +struct file_list *fl_alloc(void) +{ + return calloc(1, sizeof(struct file_list)); +} + +void fl_free(struct file_list *fl) +{ + if (!fl) + return; + + fl_free(fl->next); + text_free(fl->text); + free(fl->filename); + free(fl); +} + +/* Appends the @filename to @fl and stores a pointer to the internal + * text structure in @t. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @fl or @t is the NULL pointer. + * Returns -err_file_stat if @filename could not be found. + * Returns -err_file_open if @filename could not be opened. + * Returns -err_file_read if the content of @filename could not be fully + * read. + */ +static int fl_append(struct file_list *fl, struct text **t, + const char *filename) +{ + int errcode; + FILE *f; + char *s; + long pos; + size_t fsize; + size_t read; + + if (bug_on(!fl)) + return -err_internal; + + if (bug_on(!t)) + return -err_internal; + + if (bug_on(!filename)) + return -err_internal; + + s = NULL; + *t = NULL; + + while (fl->next) + fl = fl->next; + + fl->next = fl_alloc(); + if (!fl->next) { + errcode = -err_no_mem; + goto error; + } + + fl->next->filename = duplicate_str(filename); + if (!fl->next->filename) { + errcode = -err_no_mem; + goto error; + } + + errno = 0; + f = fopen(filename, "rb"); + if (!f) { + fprintf(stderr, "open %s failed: %s\n", + filename, strerror(errno)); + errcode = -err_file_open; + goto error; + } + + errcode = fseek(f, 0, SEEK_END); + if (errcode) { + fprintf(stderr, "%s: failed to seek end: %s\n", + filename, strerror(errno)); + errcode = -err_file_size; + goto error_file; + } + + pos = ftell(f); + if (pos < 0) { + fprintf(stderr, "%s: failed to determine file size: %s\n", + filename, strerror(errno)); + errcode = -err_file_size; + goto error_file; + } + fsize = (size_t) pos; + + errcode = fseek(f, 0, SEEK_SET); + if (errcode) { + fprintf(stderr, "%s: failed to seek begin: %s\n", + filename, strerror(errno)); + errcode = -err_file_size; + goto error_file; + } + + s = calloc(fsize+1, 1); /* size + 1: space for last null byte. */ + if (!s) { + errcode = -err_no_mem; + goto error_file; + } + + read = fread(s, 1, fsize, f); + fclose(f); + if (read != fsize) { + fprintf(stderr, "read %s failed\n", filename); + errcode = -err_file_read; + goto error; + } + + *t = text_alloc(s); + if (!*t) { + errcode = -err_no_mem; + goto error; + } + + free(s); + fl->next->text = *t; + + return 0; + +error_file: + fclose(f); +error: + /* filename is closed after reading before handling error. */ + fl_free(fl->next); + fl->next = NULL; + free(s); + text_free(*t); + *t = NULL; + return errcode; +} + +int fl_getline(struct file_list *fl, char *dest, size_t destlen, + const char *filename, size_t n) +{ + int errcode; + const struct text *t; + + if (bug_on(!fl)) + return -err_internal; + + errcode = fl_gettext(fl, &t, filename); + if (errcode < 0) + return errcode; + + return text_line(t, dest, destlen, n); +} + +int fl_gettext(struct file_list *fl, const struct text **t, + const char *filename) +{ + struct text *tmp; + int errcode; + + if (bug_on(!fl)) + return -err_internal; + + if (bug_on(!t)) + return -err_internal; + + if (bug_on(!filename)) + return -err_internal; + + while (fl->next) { + fl = fl->next; + if (strcmp(fl->filename, filename) == 0) { + *t = fl->text; + return 0; + } + } + errcode = fl_append(fl, &tmp, filename); + if (errcode < 0) + return errcode; + + *t = tmp; + return 0; +} diff --git a/pttc/src/main.c b/pttc/src/main.c new file mode 100644 index 0000000000000..ee3baae43a8ec --- /dev/null +++ b/pttc/src/main.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "pttc.h" + +#include "pt_cpu.h" + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> + +/* Prints this tools version number and libipt version number on stdout. */ +static void version(const char *prog) +{ + struct pt_version v; + + v = pt_library_version(); + printf("%s-%d.%d.%d%s / libipt-%" PRIu8 ".%" PRIu8 ".%" PRIu32 "%s\n", + prog, PT_VERSION_MAJOR, PT_VERSION_MINOR, PT_VERSION_BUILD, + PT_VERSION_EXT, v.major, v.minor, v.build, v.ext); +} + +/* Prints usage information to stdout. */ +static void help(const char *prog) +{ + printf("usage: %s [<options>] <pttfile>\n\n" + "options:\n" + " --help|-h this text.\n" + " --version display version information and exit.\n" + " --cpu none|auto|f/m[/s] set cpu to the given value and encode according to:\n" + " none spec (default)\n" + " auto current cpu\n" + " f/m[/s] family/model[/stepping]\n" + " <pttfile> the annotated yasm input file.\n", + prog); +} + +int main(int argc, char *argv[]) +{ + struct pttc_options options; + const char *prog; + int errcode, i; + + prog = argv[0]; + memset(&options, 0, sizeof(options)); + + for (i = 1; i < argc;) { + const char *arg; + + arg = argv[i++]; + + if (strcmp(arg, "--help") == 0 || strcmp(arg, "-h") == 0) { + help(prog); + return 0; + } + if (strcmp(arg, "--version") == 0) { + version(prog); + return 0; + } + if (strcmp(arg, "--cpu") == 0) { + arg = argv[i++]; + + if (strcmp(arg, "auto") == 0) { + errcode = pt_cpu_read(&options.cpu); + if (errcode < 0) { + fprintf(stderr, + "%s: error reading cpu: %s.\n", + prog, + pt_errstr(pt_errcode(errcode))); + return 1; + } + continue; + } + + if (strcmp(arg, "none") == 0) { + memset(&options.cpu, 0, sizeof(options.cpu)); + continue; + } + + errcode = pt_cpu_parse(&options.cpu, arg); + if (errcode < 0) { + fprintf(stderr, + "%s: cpu must be specified as f/m[/s].\n", + prog); + return 1; + } + continue; + } + + if (arg[0] == '-') { + fprintf(stderr, "%s: unrecognized option '%s'.\n", + prog, arg); + return 1; + } + + if (options.pttfile) { + fprintf(stderr, + "%s: only one pttfile can be specified.\n", + prog); + return 1; + } + options.pttfile = arg; + } + + if (!options.pttfile) { + fprintf(stderr, "%s: no pttfile specified.\n", prog); + fprintf(stderr, "Try '%s -h' for more information.\n", prog); + return 1; + } + + return pttc_main(&options); +} diff --git a/pttc/src/parse.c b/pttc/src/parse.c new file mode 100644 index 0000000000000..339d159b466b5 --- /dev/null +++ b/pttc/src/parse.c @@ -0,0 +1,2779 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "errcode.h" +#include "parse.h" +#include "util.h" +#include "pt_compiler.h" + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#if defined(_MSC_VER) && (_MSC_VER < 1900) +# define snprintf _snprintf_c +#endif + + +static const char *pt_suffix = ".pt"; +static const char *exp_suffix = ".exp"; + +#if defined(FEATURE_SIDEBAND) +static const char *sb_suffix = ".sb"; +#endif + +enum { + pd_len = 1024 +}; + +#if defined(FEATURE_SIDEBAND) + +static void sb_rename_file(struct sb_file *sb) +{ + char filename[FILENAME_MAX]; + + /* We encode the configuration in the sideband filename. */ + switch (sb->format) { + case sbf_raw: + strncpy(filename, sb->name, sizeof(filename)); + + /* Make sure @filename is terminated. */ + filename[sizeof(filename) - 1] = 0; + break; + +#if defined(FEATURE_PEVENT) + case sbf_pevent: { + const struct pev_config *config; + size_t base_len, ext_len, suffix_len, total_len; + int errcode, printed; + char extension[256]; + + config = &sb->variant.pevent.config; + + printed = snprintf(extension, sizeof(extension), + ",sample-type=0x%" PRIx64 ",time-zero=0x%" + PRIx64 ",time-shift=0x%u" ",time-mult=0x%u", + config->sample_type, config->time_zero, + config->time_shift, config->time_mult); + if (printed < 0) { + fprintf(stderr, "error renaming %s.\n", sb->name); + return; + } + + ext_len = (size_t) printed; + suffix_len = strnlen(sb_suffix, sizeof(filename)); + + base_len = strnlen(sb->name, sizeof(filename)); + base_len -= suffix_len; + + total_len = base_len + ext_len + suffix_len + 1; + if (sizeof(filename) <= total_len) { + fprintf(stderr, "warning: %s could not be renamed.\n", + sb->name); + return; + } + + strncpy(filename, sb->name, base_len); + + printed = snprintf(filename + base_len, + sizeof(filename) - base_len, "%s%s", + extension, sb_suffix); + if (printed < 0) { + fprintf(stderr, "error renaming %s.\n", sb->name); + return; + } + + errno = 0; + errcode = rename(sb->name, filename); + if (errcode < 0) + fprintf(stderr, "error renaming %s: %s.\n", + sb->name, strerror(errno)); + } + break; +#endif /* defined(FEATURE_PEVENT) */ + } + + /* Print the name of the sideband file for test.bash. */ + printf("%s\n", filename); +} + +#endif /* defined(FEATURE_SIDEBAND) */ + +/* Deallocates the memory used by @p, closes all files, clears and + * zeroes the fields. + */ +static void p_free(struct parser *p) +{ + if (!p) + return; + + yasm_free(p->y); + pd_free(p->pd); + l_free(p->pt_labels); + free(p->ptfilename); + +#if defined(FEATURE_SIDEBAND) + { + struct sb_filelist *sb; + + sb = p->sbfiles; + while (sb) { + struct sb_filelist *trash; + + trash = sb; + sb = sb->next; + + fclose(trash->sbfile.file); + + sb_rename_file(&trash->sbfile); + + free(trash->sbfile.name); + free(trash); + } + } +#endif /* defined(FEATURE_SIDEBAND) */ + + free(p); +} + +/* Initializes @p with @pttfile and @conf. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p is the NULL pointer. + */ +static struct parser *p_alloc(const char *pttfile, const struct pt_config *conf) +{ + size_t n; + struct parser *p; + + if (!conf) + return NULL; + + if (!pttfile) + return NULL; + + p = calloc(1, sizeof(*p)); + if (!p) + return NULL; + + p->y = yasm_alloc(pttfile); + if (!p->y) + goto error; + + n = strlen(p->y->fileroot) + 1; + + p->ptfilename = malloc(n+strlen(pt_suffix)); + if (!p->ptfilename) + goto error; + + strcpy(p->ptfilename, p->y->fileroot); + strcat(p->ptfilename, pt_suffix); + + p->pd = pd_alloc(pd_len); + if (!p->pd) + goto error; + + p->pt_labels = l_alloc(); + if (!p->pt_labels) + goto error; + + p->conf = conf; + +#if defined(FEATURE_SIDEBAND) + p->sbfiles = NULL; + p->current_sbfile = NULL; +#endif + + return p; + +error: + p_free(p); + return NULL; +} + +/* Generates an .exp filename following the scheme: + * <fileroot>[-<extra>].exp + */ +static char *expfilename(struct parser *p, const char *extra) +{ + char *filename; + /* reserve enough space to hold the string + * "-cpu_fffff_mmm_sss" + 1 for the trailing null character. + */ + char cpu_suffix[19]; + size_t n; + + if (!extra) + extra = ""; + *cpu_suffix = '\0'; + + /* determine length of resulting filename, which looks like: + * <fileroot>[-<extra>][-cpu_<f>_<m>_<s>].exp + */ + n = strlen(p->y->fileroot); + + if (*extra != '\0') + /* the extra string is prepended with a -. */ + n += 1 + strlen(extra); + + if (p->conf->cpu.vendor != pcv_unknown) { + struct pt_cpu cpu; + + cpu = p->conf->cpu; + if (cpu.stepping) + n += sprintf(cpu_suffix, + "-cpu_%" PRIu16 "_%" PRIu8 "_%" PRIu8 "", + cpu.family, cpu.model, cpu.stepping); + else + n += sprintf(cpu_suffix, + "-cpu_%" PRIu16 "_%" PRIu8 "", cpu.family, + cpu.model); + } + + n += strlen(exp_suffix); + + /* trailing null character. */ + n += 1; + + filename = malloc(n); + if (!filename) + return NULL; + + strcpy(filename, p->y->fileroot); + if (*extra != '\0') { + strcat(filename, "-"); + strcat(filename, extra); + } + strcat(filename, cpu_suffix); + strcat(filename, exp_suffix); + + return filename; +} + +/* Returns true if @c is part of a label; false otherwise. */ +static int islabelchar(int c) +{ + if (isalnum(c)) + return 1; + + switch (c) { + case '_': + return 1; + } + + return 0; +} + +/* Generates the content of the .exp file by printing all lines with + * everything up to and including the first comment semicolon removed. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p is the NULL pointer. + * Returns -err_file_write if the .exp file could not be fully written. + */ +static int p_gen_expfile(struct parser *p) +{ + int errcode; + enum { slen = 1024 }; + char s[slen]; + struct pt_directive *pd; + char *filename; + FILE *f; + + if (bug_on(!p)) + return -err_internal; + + pd = p->pd; + + /* the directive in the current line must be the .exp directive. */ + errcode = yasm_pd_parse(p->y, pd); + if (bug_on(errcode < 0)) + return -err_internal; + + if (bug_on(strcmp(pd->name, ".exp") != 0)) + return -err_internal; + + filename = expfilename(p, pd->payload); + if (!filename) + return -err_no_mem; + f = fopen(filename, "w"); + if (!f) { + free(filename); + return -err_file_open; + } + + for (;;) { + int i; + char *line, *comment; + + errcode = yasm_next_line(p->y, s, slen); + if (errcode < 0) + break; + + errcode = yasm_pd_parse(p->y, pd); + if (errcode < 0 && errcode != -err_no_directive) + break; + + if (errcode == 0 && strcmp(pd->name, ".exp") == 0) { + fclose(f); + printf("%s\n", filename); + free(filename); + filename = expfilename(p, pd->payload); + if (!filename) + return -err_no_mem; + f = fopen(filename, "w"); + if (!f) { + free(filename); + return -err_file_open; + } + continue; + } + + line = strchr(s, ';'); + if (!line) + continue; + + line += 1; + + comment = strchr(line, '#'); + if (comment) + *comment = '\0'; + + /* remove trailing spaces. */ + for (i = (int) strlen(line)-1; i >= 0 && isspace(line[i]); i--) + line[i] = '\0'; + + for (;;) { + char *tmp, label[256]; + uint64_t addr; + int zero_padding, qmark_padding, qmark_size, status; + + zero_padding = 0; + qmark_padding = 0; + qmark_size = 0; + status = 0; + + /* find the label character in the string. + * if there is no label character, we just print + * the rest of the line and end. + */ + tmp = strchr(line, '%'); + if (!tmp) { + if (fprintf(f, "%s", line) < 0) { + errcode = -err_file_write; + goto error; + } + break; + } + + /* make the label character a null byte and + * print the first portion, which does not + * belong to the label into the file. + */ + *tmp = '\0'; + if (fprintf(f, "%s", line) < 0) { + errcode = -err_file_write; + goto error; + } + + /* test if there is a valid label name after the %. */ + line = tmp+1; + if (*line == '\0' || isspace(*line)) { + errcode = -err_no_label; + goto error; + } + + /* check if zero padding is requested. */ + if (*line == '0') { + zero_padding = 1; + line += 1; + } + /* chek if ? padding is requested. */ + else if (*line == '?') { + qmark_padding = 1; + zero_padding = 1; + qmark_size = 0; + line += 1; + } + + /* advance i to the first non alpha-numeric + * character. all characters everything from + * line[0] to line[i-1] belongs to the label + * name. + */ + for (i = 0; islabelchar(line[i]); i++) + ; + + if (i > 255) { + errcode = -err_label_name; + goto error; + } + strncpy(label, line, i); + label[i] = '\0'; + + /* advance to next character. */ + line = &line[i]; + + /* lookup the label name and print it to the + * output file. + */ + errcode = yasm_lookup_label(p->y, &addr, label); + if (errcode < 0) { + errcode = l_lookup(p->pt_labels, &addr, label); + if (errcode < 0) + goto error; + + if (zero_padding) + status = fprintf(f, "%016" PRIx64, addr); + else + status = fprintf(f, "%" PRIx64, addr); + + if (status < 0) { + errcode = -err_file_write; + goto error; + } + + continue; + } + + /* check if masking is requested. */ + if (*line == '.') { + char *endptr; + unsigned long int n; + + line += 1; + + n = strtoul(line, &endptr, 0); + /* check if strtol made progress and + * stops on a space or null byte. + * otherwise the int could not be + * parsed. + */ + if (line == endptr || + (*endptr != '\0' && !isspace(*endptr) + && !ispunct(*endptr))) { + errcode = -err_parse_int; + goto error; + } + if (8 < n) { + errcode = -err_parse_int; + goto error; + } + + addr &= (1ull << (n << 3)) - 1ull; + line = endptr; + + qmark_size = (int) (8 - n); + } + + if (qmark_padding) { + for (i = 0; i < qmark_size; ++i) { + status = fprintf(f, "??"); + if (status < 0) { + errcode = -err_file_write; + goto error; + } + } + + for (; i < 8; ++i) { + uint8_t byte; + + byte = (uint8_t)(addr >> ((7 - i) * 8)); + + status = fprintf(f, "%02" PRIx8, byte); + if (status < 0) { + errcode = -err_file_write; + goto error; + } + } + } else if (zero_padding) + status = fprintf(f, "%016" PRIx64, addr); + else + status = fprintf(f, "%" PRIx64, addr); + + if (status < 0) { + errcode = -err_file_write; + goto error; + } + + } + + if (fprintf(f, "\n") < 0) { + errcode = -err_file_write; + goto error; + } + } + +error: + + fclose(f); + if (errcode < 0 && errcode != -err_out_of_range) { + fprintf(stderr, "fatal: %s could not be created:\n", filename); + yasm_print_err(p->y, "", errcode); + remove(filename); + } else + printf("%s\n", filename); + free(filename); + + /* If there are no lines left, we are done. */ + if (errcode == -err_out_of_range) + return 0; + + return errcode; +} + +static void p_close_files(struct parser *p) +{ + if (p->ptfile) { + fclose(p->ptfile); + p->ptfile = NULL; + } +} + +static int p_open_files(struct parser *p) +{ + p->ptfile = fopen(p->ptfilename, "wb"); + if (!p->ptfile) { + fprintf(stderr, "open %s failed\n", p->ptfilename); + goto error; + } + return 0; + +error: + p_close_files(p); + return -err_file_open; +} + +static int parse_mwait(uint32_t *hints, uint32_t *ext, char *payload) +{ + char *endptr; + unsigned long i; + + if (bug_on(!hints || !ext)) + return -err_internal; + + payload = strtok(payload, ","); + if (!payload || *payload == '\0') + return -err_parse_no_args; + + i = strtoul(payload, &endptr, 0); + if (payload == endptr || *endptr != '\0') + return -err_parse_int; + + if (UINT32_MAX < i) + return -err_parse_int_too_big; + + *hints = (uint32_t)i; + + payload = strtok(NULL, " ,"); + if (!payload) + return -err_parse_no_args; + + i = strtoul(payload, &endptr, 0); + if (payload == endptr || *endptr != '\0') + return -err_parse_int; + + if (UINT32_MAX < i) + return -err_parse_int_too_big; + + *ext = (uint32_t)i; + + /* no more tokens left. */ + payload = strtok(NULL, " "); + if (payload) + return -err_parse_trailing_tokens; + + return 0; +} + +static int parse_c_state(uint8_t *state, uint8_t *sub_state, const char *input) +{ + unsigned int maj, min; + int matches; + + if (!input) + return -err_parse_no_args; + + maj = 0; + min = 0; + matches = sscanf(input, " c%u.%u", &maj, &min); + switch (matches) { + case 0: + return -err_parse_no_args; + + case 2: + if (!sub_state) + return -err_parse_c_state_sub; + + if (0xf <= min) + return -err_parse_c_state_invalid; + + fallthrough; + case 1: + if (!state) + return -err_internal; + + if (0xf <= maj) + return -err_parse_c_state_invalid; + + break; + } + + *state = (uint8_t) ((maj - 1) & 0xf); + if (sub_state) + *sub_state = (uint8_t) ((min - 1) & 0xf); + + return 0; +} + +/* Processes the current directive. + * If the encoder returns an error, a message including current file and + * line number together with the pt error string is printed on stderr. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p or @e is the NULL pointer. + * Returns -err_pt_lib if the pt encoder returned an error. + * Returns -err_parse if a general parsing error was encountered. + * Returns -err_parse_unknown_directive if there was an unknown pt directive. + */ +static int p_process_pt(struct parser *p, struct pt_encoder *e) +{ + struct pt_directive *pd; + struct pt_packet packet; + char *directive, *payload; + int bytes_written, errcode; + + if (bug_on(!p)) + return -err_internal; + + if (bug_on(!e)) + return -err_internal; + + pd = p->pd; + if (!pd) + return -err_internal; + + directive = pd->name; + payload = pd->payload; + + if (strcmp(directive, "psb") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "psb: parsing failed", errcode); + return errcode; + } + packet.type = ppt_psb; + } else if (strcmp(directive, "psbend") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "psbend: parsing failed", errcode); + return errcode; + } + packet.type = ppt_psbend; + } else if (strcmp(directive, "pad") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "pad: parsing failed", errcode); + return errcode; + } + packet.type = ppt_pad; + } else if (strcmp(directive, "ovf") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "ovf: parsing failed", errcode); + return errcode; + } + packet.type = ppt_ovf; + } else if (strcmp(directive, "stop") == 0) { + errcode = parse_empty(payload); + if (errcode < 0) { + yasm_print_err(p->y, "stop: parsing failed", errcode); + return errcode; + } + packet.type = ppt_stop; + } else if (strcmp(directive, "tnt") == 0) { + errcode = parse_tnt(&packet.payload.tnt.payload, + &packet.payload.tnt.bit_size, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tnt: parsing failed", errcode); + return errcode; + } + packet.type = ppt_tnt_8; + } else if (strcmp(directive, "tnt64") == 0) { + errcode = parse_tnt(&packet.payload.tnt.payload, + &packet.payload.tnt.bit_size, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tnt64: parsing failed", errcode); + return errcode; + } + packet.type = ppt_tnt_64; + } else if (strcmp(directive, "tip") == 0) { + errcode = parse_ip(p, &packet.payload.ip.ip, + &packet.payload.ip.ipc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tip: parsing failed", errcode); + return errcode; + } + packet.type = ppt_tip; + } else if (strcmp(directive, "tip.pge") == 0) { + errcode = parse_ip(p, &packet.payload.ip.ip, + &packet.payload.ip.ipc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tip.pge: parsing failed", + errcode); + return errcode; + } + packet.type = ppt_tip_pge; + } else if (strcmp(directive, "tip.pgd") == 0) { + errcode = parse_ip(p, &packet.payload.ip.ip, + &packet.payload.ip.ipc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tip.pgd: parsing failed", + errcode); + return errcode; + } + packet.type = ppt_tip_pgd; + } else if (strcmp(directive, "fup") == 0) { + errcode = parse_ip(p, &packet.payload.ip.ip, + &packet.payload.ip.ipc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "fup: parsing failed", errcode); + return errcode; + } + packet.type = ppt_fup; + } else if (strcmp(directive, "mode.exec") == 0) { + if (strcmp(payload, "16bit") == 0) { + packet.payload.mode.bits.exec.csl = 0; + packet.payload.mode.bits.exec.csd = 0; + } else if (strcmp(payload, "64bit") == 0) { + packet.payload.mode.bits.exec.csl = 1; + packet.payload.mode.bits.exec.csd = 0; + } else if (strcmp(payload, "32bit") == 0) { + packet.payload.mode.bits.exec.csl = 0; + packet.payload.mode.bits.exec.csd = 1; + } else { + errcode = yasm_print_err(p->y, + "mode.exec: argument must be one of \"16bit\", \"64bit\" or \"32bit\"", + -err_parse); + return errcode; + } + packet.payload.mode.leaf = pt_mol_exec; + packet.type = ppt_mode; + } else if (strcmp(directive, "mode.tsx") == 0) { + if (strcmp(payload, "begin") == 0) { + packet.payload.mode.bits.tsx.intx = 1; + packet.payload.mode.bits.tsx.abrt = 0; + } else if (strcmp(payload, "abort") == 0) { + packet.payload.mode.bits.tsx.intx = 0; + packet.payload.mode.bits.tsx.abrt = 1; + } else if (strcmp(payload, "commit") == 0) { + packet.payload.mode.bits.tsx.intx = 0; + packet.payload.mode.bits.tsx.abrt = 0; + } else { + errcode = yasm_print_err(p->y, + "mode.tsx: argument must be one of \"begin\", \"abort\" or \"commit\"", + -err_parse); + return errcode; + } + packet.payload.mode.leaf = pt_mol_tsx; + packet.type = ppt_mode; + } else if (strcmp(directive, "pip") == 0) { + const char *modifier; + + errcode = parse_uint64(&packet.payload.pip.cr3, payload); + if (errcode < 0) { + yasm_print_err(p->y, "pip: parsing failed", errcode); + return errcode; + } + packet.type = ppt_pip; + packet.payload.pip.nr = 0; + + modifier = strtok(NULL, " ,"); + if (modifier) { + if (strcmp(modifier, "nr") == 0) + packet.payload.pip.nr = 1; + else { + yasm_print_err(p->y, "pip: parsing failed", + -err_parse_trailing_tokens); + return errcode; + } + } + } else if (strcmp(directive, "tsc") == 0) { + errcode = parse_uint64(&packet.payload.tsc.tsc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tsc: parsing failed", errcode); + return errcode; + } + packet.type = ppt_tsc; + } else if (strcmp(directive, "cbr") == 0) { + errcode = parse_uint8(&packet.payload.cbr.ratio, payload); + if (errcode < 0) { + yasm_print_err(p->y, "cbr: parsing cbr failed", + errcode); + return errcode; + } + packet.type = ppt_cbr; + } else if (strcmp(directive, "tma") == 0) { + errcode = parse_tma(&packet.payload.tma.ctc, + &packet.payload.tma.fc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "tma: parsing tma failed", + errcode); + return errcode; + } + packet.type = ppt_tma; + } else if (strcmp(directive, "mtc") == 0) { + errcode = parse_uint8(&packet.payload.mtc.ctc, payload); + if (errcode < 0) { + yasm_print_err(p->y, "mtc: parsing mtc failed", + errcode); + return errcode; + } + packet.type = ppt_mtc; + } else if (strcmp(directive, "cyc") == 0) { + errcode = parse_uint64(&packet.payload.cyc.value, payload); + if (errcode < 0) { + yasm_print_err(p->y, "cyc: parsing cyc failed", + errcode); + return errcode; + } + packet.type = ppt_cyc; + } else if (strcmp(directive, "vmcs") == 0) { + errcode = parse_uint64(&packet.payload.vmcs.base, payload); + if (errcode < 0) { + yasm_print_err(p->y, "vmcs: parsing failed", errcode); + return errcode; + } + packet.type = ppt_vmcs; + } else if (strcmp(directive, "mnt") == 0) { + errcode = parse_uint64(&packet.payload.mnt.payload, payload); + if (errcode < 0) { + yasm_print_err(p->y, "mnt: parsing failed", errcode); + return errcode; + } + packet.type = ppt_mnt; + } else if (strcmp(directive, "exstop") == 0) { + packet.type = ppt_exstop; + memset(&packet.payload.exstop, 0, + sizeof(packet.payload.exstop)); + + if (strcmp(payload, "ip") == 0) + packet.payload.exstop.ip = 1; + else if (*payload) { + yasm_print_err(p->y, "exstop: parsing failed", + -err_parse_trailing_tokens); + return -err_parse_trailing_tokens; + } + } else if (strcmp(directive, "mwait") == 0) { + errcode = parse_mwait(&packet.payload.mwait.hints, + &packet.payload.mwait.ext, payload); + if (errcode < 0) { + yasm_print_err(p->y, "mwait: parsing failed", errcode); + return errcode; + } + + packet.type = ppt_mwait; + } else if (strcmp(directive, "pwre") == 0) { + char *token; + + packet.type = ppt_pwre; + memset(&packet.payload.pwre, 0, sizeof(packet.payload.pwre)); + + token = strtok(payload, " , "); + errcode = parse_c_state(&packet.payload.pwre.state, + &packet.payload.pwre.sub_state, token); + if (errcode < 0) { + yasm_print_err(p->y, "pwre: bad C-state", errcode); + return errcode; + } + + token = strtok(NULL, " ,"); + if (token) { + if (strcmp(token, "hw") == 0) + packet.payload.pwre.hw = 1; + else { + yasm_print_err(p->y, "pwre: parsing failed", + -err_parse_trailing_tokens); + return -err_parse_trailing_tokens; + } + } + } else if (strcmp(directive, "pwrx") == 0) { + char *token; + + packet.type = ppt_pwrx; + memset(&packet.payload.pwrx, 0, sizeof(packet.payload.pwrx)); + + token = strtok(payload, ":"); + if (!token) { + yasm_print_err(p->y, "pwrx: parsing failed", + -err_parse_no_args); + return -err_parse_no_args; + } + + if (strcmp(token, "int") == 0) + packet.payload.pwrx.interrupt = 1; + else if (strcmp(token, "st") == 0) + packet.payload.pwrx.store = 1; + else if (strcmp(token, "hw") == 0) + packet.payload.pwrx.autonomous = 1; + else { + yasm_print_err(p->y, "pwrx: bad wake reason", + -err_parse); + return -err_parse; + } + + token = strtok(NULL, " ,"); + errcode = parse_c_state(&packet.payload.pwrx.last, NULL, token); + if (errcode < 0) { + yasm_print_err(p->y, "pwrx: bad last C-state", errcode); + return errcode; + } + + token = strtok(NULL, " ,"); + errcode = parse_c_state(&packet.payload.pwrx.deepest, NULL, + token); + if (errcode < 0) { + yasm_print_err(p->y, "pwrx: bad deepest C-state", + errcode); + return errcode; + } + } else if (strcmp(directive, "ptw") == 0) { + char *token; + + packet.type = ppt_ptw; + memset(&packet.payload.ptw, 0, sizeof(packet.payload.ptw)); + + token = strtok(payload, ":"); + if (!token) { + yasm_print_err(p->y, "ptw: parsing failed", + -err_parse_no_args); + return -err_parse_no_args; + } + + errcode = str_to_uint8(token, &packet.payload.ptw.plc, 0); + if (errcode < 0) { + yasm_print_err(p->y, "ptw: bad payload size", errcode); + return errcode; + } + + token = strtok(NULL, ", "); + if (!token) { + yasm_print_err(p->y, "ptw: no payload", + -err_parse_no_args); + return -err_parse_no_args; + } + + errcode = str_to_uint64(token, &packet.payload.ptw.payload, 0); + if (errcode < 0) { + yasm_print_err(p->y, "ptw: bad payload", errcode); + return errcode; + } + + token = strtok(NULL, " "); + if (token) { + if (strcmp(token, "ip") != 0) { + yasm_print_err(p->y, "ptw: parse error", + -err_parse_trailing_tokens); + return -err_parse_trailing_tokens; + } + + packet.payload.ptw.ip = 1; + } + } else { + errcode = yasm_print_err(p->y, "invalid syntax", + -err_parse_unknown_directive); + return errcode; + } + + bytes_written = pt_enc_next(e, &packet); + if (bytes_written < 0) { + char msg[128]; + + snprintf(msg, sizeof(msg), + "encoder error in directive %s (status %s)", directive, + pt_errstr(pt_errcode(bytes_written))); + + yasm_print_err(p->y, msg, -err_pt_lib); + } else + p->pt_bytes_written += bytes_written; + + return bytes_written; +} + +#if defined(FEATURE_SIDEBAND) + +static int sb_open(struct parser *p, const char *fmt, const char *src, + const char *prio) +{ + struct sb_filelist *sbfiles; + const char *root; + char name[FILENAME_MAX]; + FILE *file; + + if (bug_on(!p) || bug_on(!p->y) || bug_on(!prio)) + return -err_internal; + + root = p->y->fileroot; + if (!root) { + yasm_print_err(p->y, "open - name root", -err_internal); + return -err_internal; + } + + if (src && *src) + snprintf(name, sizeof(name), "%s-%s-%s-%s%s", root, src, fmt, + prio, sb_suffix); + else + snprintf(name, sizeof(name), "%s-%s-%s%s", root, fmt, prio, + sb_suffix); + + for (sbfiles = p->sbfiles; sbfiles; sbfiles = sbfiles->next) { + if (strncmp(sbfiles->sbfile.name, name, sizeof(name)) == 0) + break; + } + + if (!sbfiles) { + file = fopen(name, "w"); + if (!file) { + yasm_print_err(p->y, name, -err_file_open); + return -err_file_open; + } + + sbfiles = malloc(sizeof(*sbfiles)); + if (!sbfiles) { + yasm_print_err(p->y, "open", -err_no_mem); + fclose(file); + return -err_no_mem; + } + + memset(&sbfiles->sbfile, 0, sizeof(sbfiles->sbfile)); + + sbfiles->sbfile.name = duplicate_str(name); + if (!sbfiles->sbfile.name) { + yasm_print_err(p->y, "open", -err_no_mem); + fclose(file); + free(sbfiles); + return -err_no_mem; + } + + sbfiles->sbfile.file = file; + sbfiles->sbfile.format = sbf_raw; + + sbfiles->next = p->sbfiles; + p->sbfiles = sbfiles; + } + + p->current_sbfile = &sbfiles->sbfile; + return 0; +} + +static struct sb_file *p_get_current_sbfile(struct parser *p) +{ + struct sb_file *sb; + + if (bug_on(!p)) + return NULL; + + sb = p->current_sbfile; + if (!sb) { + yasm_print_err(p->y, "no sideband file", -err_sb_missing); + return NULL; + } + + if (bug_on(!sb->file)) { + yasm_print_err(p->y, "corrupt sideband file", -err_internal); + return NULL; + } + + return sb; +} + +static int sb_set_format(struct parser *p, struct sb_file *sb, + enum sb_format format) +{ + if (bug_on(!p)) + return -err_internal; + + if (!sb) + return -err_sb_missing; + + switch (format) { + case sbf_raw: + /* Raw sideband directives are allowed for all formats. */ + return 0; + +#if defined(FEATURE_PEVENT) + case sbf_pevent: + switch (sb->format) { + case sbf_pevent: + return 0; + + case sbf_raw: + sb->format = sbf_pevent; + + memset(&sb->variant.pevent, 0, + sizeof(sb->variant.pevent)); + sb->variant.pevent.config.size = + sizeof(sb->variant.pevent.config); + sb->variant.pevent.config.time_shift = 0; + sb->variant.pevent.config.time_mult = 1; + sb->variant.pevent.config.time_zero = 0ull; + return 0; + + default: + yasm_print_err(p->y, "mixing sideband formats", + -err_sb_mix); + return -err_sb_mix; + } +#endif /* defined(FEATURE_PEVENT) */ + } + + yasm_print_err(p->y, "unknown sideband format", -err_internal); + return -err_internal; +} + +static int sb_raw(struct parser *p, const void *buffer, size_t size) +{ + struct sb_file *sb; + size_t written; + int errcode; + + if (bug_on(!p)) + return -err_internal; + + sb = p_get_current_sbfile(p); + if (!sb) + return -err_sb_missing; + + errcode = sb_set_format(p, sb, sbf_raw); + if (errcode < 0) + return errcode; + + written = fwrite(buffer, size, 1, sb->file); + if (written != 1) { + yasm_print_err(p->y, "write failed", -err_file_write); + return -err_file_write; + } + + sb->bytes_written += (int) size; + return 0; +} + +static int sb_raw_8(struct parser *p, uint8_t value) +{ + return sb_raw(p, &value, sizeof(value)); +} + +static int sb_raw_16(struct parser *p, uint16_t value) +{ + return sb_raw(p, &value, sizeof(value)); +} + +static int sb_raw_32(struct parser *p, uint32_t value) +{ + return sb_raw(p, &value, sizeof(value)); +} + +static int sb_raw_64(struct parser *p, uint64_t value) +{ + return sb_raw(p, &value, sizeof(value)); +} + +#if defined(FEATURE_PEVENT) + +/* A buffer to hold sample values to which a pev_event can point. */ + +struct pev_sample_buffer { + uint32_t pid; + uint32_t tid; + uint64_t time; + uint64_t id; + uint64_t stream_id; + uint32_t cpu; + uint64_t identifier; +}; + +static int pevent_sample_type(struct parser *p, uint64_t sample_type) +{ + struct sb_file *sb; + int errcode; + + sb = p_get_current_sbfile(p); + if (!sb) + return -err_sb_missing; + + errcode = sb_set_format(p, sb, sbf_pevent); + if (errcode < 0) + return errcode; + + if (sb->variant.pevent.is_final) { + yasm_print_err(p->y, + "the sideband configuration can no longer be " + "modified", -err_sb_final); + return -err_sb_final; + } + + sb->variant.pevent.config.sample_type = sample_type; + return 0; +} + +static int pevent_process_samples(struct pev_event *event, + struct pev_sample_buffer *samples, + struct parser *p, + const struct pev_config *config, + char *payload) +{ + char *token; + + if (bug_on(!event) || bug_on(!samples) || bug_on(!config)) + return -err_internal; + + if (config->sample_type & PERF_SAMPLE_TID) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint32(token, &samples->pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad pid", errcode); + return errcode; + } + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + errcode = str_to_uint32(token, &samples->tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad tid", errcode); + return errcode; + } + + event->sample.pid = &samples->pid; + event->sample.tid = &samples->tid; + } + + if (config->sample_type & PERF_SAMPLE_TIME) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "tsc missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint64(token, &event->sample.tsc, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad tsc", errcode); + return errcode; + } + + errcode = pev_time_from_tsc(&samples->time, event->sample.tsc, + config); + if (errcode < 0) { + fprintf(stderr, "error converting tsc %"PRIx64": %s\n", + event->sample.tsc, + pt_errstr(pt_errcode(errcode))); + return -err_pt_lib; + } + + event->sample.time = &samples->time; + } + + if (config->sample_type & PERF_SAMPLE_ID) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "id missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint64(token, &samples->id, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad id", errcode); + return errcode; + } + + event->sample.id = &samples->id; + } + + if (config->sample_type & PERF_SAMPLE_CPU) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "cpu missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint32(token, &samples->cpu, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad cpu", errcode); + return errcode; + } + + event->sample.cpu = &samples->cpu; + } + + if (config->sample_type & PERF_SAMPLE_STREAM_ID) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "stream missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint64(token, &samples->stream_id, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad stream", errcode); + return errcode; + } + + event->sample.stream_id = &samples->stream_id; + } + + if (config->sample_type & PERF_SAMPLE_IDENTIFIER) { + int errcode; + + token = strtok(payload, " ,"); + if (!token) { + yasm_print_err(p->y, "identifier missing", -err_parse); + return -err_parse; + } + + payload = NULL; + + errcode = str_to_uint64(token, &samples->identifier, 0); + if (errcode < 0) { + yasm_print_err(p->y, "bad identifier", errcode); + return errcode; + } + + event->sample.identifier = &samples->identifier; + } + + token = strtok(payload, " ,"); + if (token) { + yasm_print_err(p->y, "unexpected samples", -err_parse); + return -err_parse; + } + + return 0; +} + +static int sb_pevent(struct parser *p, struct pev_event *event, char *payload) +{ + const struct pev_config *config; + struct pev_sample_buffer samples; + struct sb_file *sb; + uint8_t raw[FILENAME_MAX]; + int errcode, size; + + memset(raw, 0, sizeof(raw)); + + sb = p_get_current_sbfile(p); + if (!sb) + return -err_sb_missing; + + errcode = sb_set_format(p, sb, sbf_pevent); + if (errcode < 0) + return errcode; + + config = &sb->variant.pevent.config; + + errcode = pevent_process_samples(event, &samples, p, config, payload); + if (errcode < 0) + return errcode; + + size = pev_write(event, raw, raw + sizeof(raw), config); + if (size < 0) { + fprintf(stderr, "error writing pevent sample: %s\n", + pt_errstr(pt_errcode(size))); + return -err_pt_lib; + } + + /* Emitting a pevent sideband event finalizes the configuration. */ + sb->variant.pevent.is_final = 1; + + return sb_raw(p, raw, size); +} + +static int pevent_mmap_section(struct parser *p, const char *section, + const char *pid, const char *tid) +{ + union { + struct pev_record_mmap mmap; + uint8_t buffer[FILENAME_MAX]; + } record; + struct pev_event event; + const char *filename; + uint64_t start, org; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + filename = p->y->binfile; + if (!filename) { + yasm_print_err(p->y, "pevent-mmap-section - filename", + -err_internal); + return -err_internal; + } + + strncpy(record.mmap.filename, filename, + sizeof(record.buffer) - sizeof(record.mmap)); + + errcode = str_to_uint32(pid, &record.mmap.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.mmap.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - tid", errcode); + return errcode; + } + + errcode = yasm_lookup_section_label(p->y, section, "vstart", + &record.mmap.addr); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - section vstart", + errcode); + return errcode; + } + + errcode = yasm_lookup_section_label(p->y, section, "length", + &record.mmap.len); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - section length", + errcode); + return errcode; + } + + errcode = yasm_lookup_section_label(p->y, section, "start", + &start); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - section start", + errcode); + return errcode; + } + + errcode = yasm_lookup_label(p->y, &org, "org"); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - org", + errcode); + return errcode; + } + + if (start < org) { + yasm_print_err(p->y, "corrupt section labels", -err_internal); + return -err_internal; + } + + record.mmap.pgoff = start - org; + + event.type = PERF_RECORD_MMAP; + event.record.mmap = &record.mmap; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_mmap(struct parser *p, const char *pid, const char *tid, + const char *addr, const char *len, const char *pgoff, + const char *filename) +{ + union { + struct pev_record_mmap mmap; + uint8_t buffer[FILENAME_MAX]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.mmap.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.mmap.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - tid", errcode); + return errcode; + } + + errcode = str_to_uint64(addr, &record.mmap.addr, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - addr", errcode); + return errcode; + } + + errcode = str_to_uint64(len, &record.mmap.len, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - len", errcode); + return errcode; + } + + errcode = str_to_uint64(pgoff, &record.mmap.pgoff, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-mmap-section - pgoff", errcode); + return errcode; + } + + strncpy(record.mmap.filename, filename, + sizeof(record.buffer) - sizeof(record.mmap)); + + event.type = PERF_RECORD_MMAP; + event.record.mmap = &record.mmap; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_lost(struct parser *p, const char *id, const char *lost) +{ + union { + struct pev_record_lost lost; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint64(id, &record.lost.id, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-lost - id", errcode); + return errcode; + } + + errcode = str_to_uint64(lost, &record.lost.lost, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-lost - lost", errcode); + return errcode; + } + + event.type = PERF_RECORD_LOST; + event.record.lost = &record.lost; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_comm(struct parser *p, const char *pid, const char *tid, + const char *comm, uint16_t misc) +{ + union { + struct pev_record_comm comm; + uint8_t buffer[FILENAME_MAX]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.comm.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-comm - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.comm.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-comm - tid", errcode); + return errcode; + } + + strcpy(record.comm.comm, comm); + + event.type = PERF_RECORD_COMM; + event.misc = misc; + event.record.comm = &record.comm; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_exit(struct parser *p, const char *pid, const char *ppid, + const char *tid, const char *ptid, const char *time) +{ + union { + struct pev_record_exit exit; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.exit.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(ppid, &record.exit.ppid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - ppid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.exit.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - tid", errcode); + return errcode; + } + + errcode = str_to_uint32(ptid, &record.exit.ptid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - ptid", errcode); + return errcode; + } + + + errcode = str_to_uint64(time, &record.exit.time, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-exit - time", errcode); + return errcode; + } + + event.type = PERF_RECORD_EXIT; + event.record.exit = &record.exit; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_fork(struct parser *p, const char *pid, const char *ppid, + const char *tid, const char *ptid, const char *time) +{ + union { + struct pev_record_fork fork; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.fork.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(ppid, &record.fork.ppid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - ppid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.fork.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - tid", errcode); + return errcode; + } + + errcode = str_to_uint32(ptid, &record.fork.ptid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - ptid", errcode); + return errcode; + } + + + errcode = str_to_uint64(time, &record.fork.time, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-fork - time", errcode); + return errcode; + } + + event.type = PERF_RECORD_FORK; + event.record.fork = &record.fork; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_aux(struct parser *p, const char *offset, const char *size, + const char *flags) +{ + union { + struct pev_record_aux aux; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint64(offset, &record.aux.aux_offset, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-aux - offset", errcode); + return errcode; + } + + errcode = str_to_uint64(size, &record.aux.aux_size, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-aux - size", errcode); + return errcode; + } + + errcode = str_to_uint64(flags, &record.aux.flags, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-aux - flags", errcode); + return errcode; + } + + event.type = PERF_RECORD_AUX; + event.record.aux = &record.aux; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_itrace_start(struct parser *p, const char *pid, + const char *tid) +{ + union { + struct pev_record_itrace_start itrace_start; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint32(pid, &record.itrace_start.pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-itrace-start - pid", errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.itrace_start.tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-itrace-start - tid", errcode); + return errcode; + } + + event.type = PERF_RECORD_ITRACE_START; + event.record.itrace_start = &record.itrace_start; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_lost_samples(struct parser *p, const char *lost) +{ + union { + struct pev_record_lost_samples lost_samples; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + errcode = str_to_uint64(lost, &record.lost_samples.lost, 0); + if (errcode < 0) { + yasm_print_err(p->y, "pevent-lost-samples - lost", errcode); + return errcode; + } + + event.type = PERF_RECORD_LOST_SAMPLES; + event.record.lost_samples = &record.lost_samples; + + return sb_pevent(p, &event, NULL); +} + +static int pevent_switch(struct parser *p, uint16_t misc, char *payload) +{ + struct pev_event event; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(&event, 0, sizeof(event)); + + event.type = PERF_RECORD_SWITCH; + event.misc = misc; + + return sb_pevent(p, &event, payload); +} + +static int pevent_switch_cpu_wide(struct parser *p, const char *pid, + const char *tid, uint16_t misc) +{ + union { + struct pev_record_switch_cpu_wide switch_cpu_wide; + uint8_t buffer[1024]; + } record; + struct pev_event event; + int errcode; + + if (bug_on(!p) || bug_on(!p->y)) + return -err_internal; + + memset(record.buffer, 0, sizeof(record.buffer)); + memset(&event, 0, sizeof(event)); + + event.misc = misc; + + errcode = str_to_uint32(pid, &record.switch_cpu_wide.next_prev_pid, 0); + if (errcode < 0) { + yasm_print_err(p->y, + "pevent-switch-cpu-wide - next_prev_pid", + errcode); + return errcode; + } + + errcode = str_to_uint32(tid, &record.switch_cpu_wide.next_prev_tid, 0); + if (errcode < 0) { + yasm_print_err(p->y, + "pevent-switch-cpu-wide - next_prev_tid", + errcode); + return errcode; + } + + event.type = PERF_RECORD_SWITCH_CPU_WIDE; + event.record.switch_cpu_wide = &record.switch_cpu_wide; + + return sb_pevent(p, &event, NULL); +} +#endif /* defined(FEATURE_PEVENT) */ + +/* Process a @sb directive. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p is the NULL pointer. + * Returns -err_parse if a general parsing error was encountered. + * Returns -err_parse_unknown_directive if there was an unknown pt directive. + */ +static int p_process_sb(struct parser *p) +{ + struct pt_directive *pd; + char *directive, *payload; + + if (bug_on(!p)) + return -err_internal; + + pd = p->pd; + if (!pd) + return -err_internal; + + directive = pd->name; + payload = pd->payload; + + if (strcmp(directive, "primary") == 0) { + char *fmt, *src; + + fmt = strtok(payload, " ,"); + if (!fmt) { + yasm_print_err(p->y, "primary - format missing", + -err_parse_no_args); + return -err_parse_no_args; + } + + src = strtok(NULL, " "); + + return sb_open(p, fmt, src, "primary"); + } else if (strcmp(directive, "secondary") == 0) { + char *fmt, *src; + + fmt = strtok(payload, " ,"); + if (!fmt) { + yasm_print_err(p->y, "secondary - format missing", + -err_parse_no_args); + return -err_parse_no_args; + } + + src = strtok(NULL, " "); + + return sb_open(p, fmt, src, "secondary"); + } else if (strcmp(directive, "raw-8") == 0) { + uint8_t value; + int errcode; + + errcode = parse_uint8(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, payload, errcode); + return errcode; + } + + return sb_raw_8(p, value); + } else if (strcmp(directive, "raw-16") == 0) { + uint16_t value; + int errcode; + + errcode = parse_uint16(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, payload, errcode); + return errcode; + } + + return sb_raw_16(p, value); + } else if (strcmp(directive, "raw-32") == 0) { + uint32_t value; + int errcode; + + errcode = parse_uint32(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, payload, errcode); + return errcode; + } + + return sb_raw_32(p, value); + } else if (strcmp(directive, "raw-64") == 0) { + uint64_t value; + int errcode; + + errcode = parse_uint64(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, payload, errcode); + return errcode; + } + + return sb_raw_64(p, value); +#if defined(FEATURE_PEVENT) + } else if (strcmp(directive, "pevent-sample_type") == 0) { + uint64_t sample_type; + char *token; + + sample_type = 0ull; + + for (token = strtok(payload, " ,"); token; + token = strtok(NULL, " ,")) { + + if (strcmp(token, "tid") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_TID; + else if (strcmp(token, "time") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_TIME; + else if (strcmp(token, "id") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_ID; + else if (strcmp(token, "stream") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_STREAM_ID; + else if (strcmp(token, "cpu") == 0) + sample_type |= (uint64_t) PERF_SAMPLE_CPU; + else if (strcmp(token, "identifier") == 0) + sample_type |= + (uint64_t) PERF_SAMPLE_IDENTIFIER; + else { + uint64_t value; + int errcode; + + errcode = parse_uint64(&value, payload); + if (errcode < 0) { + yasm_print_err(p->y, token, errcode); + return errcode; + } + + sample_type |= value; + } + } + + return pevent_sample_type(p, sample_type); + + } else if (strcmp(directive, "pevent-mmap-section") == 0) { + char *section, *pid, *tid; + + section = strtok(payload, " ,"); + if (!section) { + yasm_print_err(p->y, "section missing", -err_parse); + return -err_parse; + } + + pid = strtok(NULL, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + return pevent_mmap_section(p, section, pid, tid); + } else if (strcmp(directive, "pevent-mmap") == 0) { + char *pid, *tid, *addr, *len, *pgoff, *filename; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + addr = strtok(NULL, " ,"); + if (!addr) { + yasm_print_err(p->y, "addr missing", -err_parse); + return -err_parse; + } + + len = strtok(NULL, " ,"); + if (!len) { + yasm_print_err(p->y, "len missing", -err_parse); + return -err_parse; + } + + pgoff = strtok(NULL, " ,"); + if (!pgoff) { + yasm_print_err(p->y, "pgoff missing", -err_parse); + return -err_parse; + } + + filename = strtok(NULL, " ,"); + if (!filename) { + yasm_print_err(p->y, "filename missing", -err_parse); + return -err_parse; + } + + return pevent_mmap(p, pid, tid, addr, len, pgoff, filename); + } else if (strcmp(directive, "pevent-lost") == 0) { + char *id, *lost; + + id = strtok(payload, " ,"); + if (!id) { + yasm_print_err(p->y, "id missing", -err_parse); + return -err_parse; + } + + lost = strtok(NULL, " ,"); + if (!lost) { + yasm_print_err(p->y, "lost missing", -err_parse); + return -err_parse; + } + + return pevent_lost(p, id, lost); + } else if (strcmp(directive, "pevent-comm") == 0) { + char *pid, *tid, *comm; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + comm = strtok(NULL, " ,"); + if (!comm) { + yasm_print_err(p->y, "comm missing", -err_parse); + return -err_parse; + } + + return pevent_comm(p, pid, tid, comm, 0); + } else if (strcmp(directive, "pevent-comm.exec") == 0) { + char *pid, *tid, *comm; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + comm = strtok(NULL, " ,"); + if (!comm) { + yasm_print_err(p->y, "comm missing", -err_parse); + return -err_parse; + } + + return pevent_comm(p, pid, tid, comm, + PERF_RECORD_MISC_COMM_EXEC); + } else if (strcmp(directive, "pevent-exit") == 0) { + char *pid, *ppid, *tid, *ptid, *time; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + ppid = strtok(NULL, " ,"); + if (!ppid) { + yasm_print_err(p->y, "ppid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + ptid = strtok(NULL, " ,"); + if (!ptid) { + yasm_print_err(p->y, "ptid missing", -err_parse); + return -err_parse; + } + + time = strtok(NULL, " ,"); + if (!time) { + yasm_print_err(p->y, "time missing", -err_parse); + return -err_parse; + } + + return pevent_exit(p, pid, ppid, tid, ptid, time); + } else if (strcmp(directive, "pevent-fork") == 0) { + char *pid, *ppid, *tid, *ptid, *time; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + ppid = strtok(NULL, " ,"); + if (!ppid) { + yasm_print_err(p->y, "ppid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + ptid = strtok(NULL, " ,"); + if (!ptid) { + yasm_print_err(p->y, "ptid missing", -err_parse); + return -err_parse; + } + + time = strtok(NULL, " ,"); + if (!time) { + yasm_print_err(p->y, "time missing", -err_parse); + return -err_parse; + } + + return pevent_fork(p, pid, ppid, tid, ptid, time); + } else if (strcmp(directive, "pevent-aux") == 0) { + char *offset, *size, *flags; + + offset = strtok(payload, " ,"); + if (!offset) { + yasm_print_err(p->y, "offset missing", -err_parse); + return -err_parse; + } + + size = strtok(NULL, " ,"); + if (!size) { + yasm_print_err(p->y, "size missing", -err_parse); + return -err_parse; + } + + flags = strtok(NULL, " ,"); + if (!flags) { + yasm_print_err(p->y, "flags missing", -err_parse); + return -err_parse; + } + + return pevent_aux(p, offset, size, flags); + } else if (strcmp(directive, "pevent-itrace-start") == 0) { + char *pid, *tid; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + return pevent_itrace_start(p, pid, tid); + } else if (strcmp(directive, "pevent-lost-samples") == 0) { + char *lost; + + lost = strtok(payload, " ,"); + if (!lost) { + yasm_print_err(p->y, "lost missing", -err_parse); + return -err_parse; + } + + return pevent_lost_samples(p, lost); + } else if (strcmp(directive, "pevent-switch.in") == 0) + return pevent_switch(p, 0u, payload); + else if (strcmp(directive, "pevent-switch.out") == 0) + return pevent_switch(p, PERF_RECORD_MISC_SWITCH_OUT, payload); + else if (strcmp(directive, "pevent-switch-cpu-wide.in") == 0) { + char *pid, *tid; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + return pevent_switch_cpu_wide(p, pid, tid, 0u); + } else if (strcmp(directive, "pevent-switch-cpu-wide.out") == 0) { + char *pid, *tid; + + pid = strtok(payload, " ,"); + if (!pid) { + yasm_print_err(p->y, "pid missing", -err_parse); + return -err_parse; + } + + tid = strtok(NULL, " ,"); + if (!tid) { + yasm_print_err(p->y, "tid missing", -err_parse); + return -err_parse; + } + + return pevent_switch_cpu_wide(p, pid, tid, + PERF_RECORD_MISC_SWITCH_OUT); +#endif /* defined(FEATURE_PEVENT) */ + } else { + yasm_print_err(p->y, "syntax error", + -err_parse_unknown_directive); + return -err_parse_unknown_directive; + } +} + +#endif /* defined(FEATURE_SIDEBAND) */ + +/* Processes the current directive. + * If the encoder returns an error, a message including current file and + * line number together with the pt error string is printed on stderr. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_internal if @p or @e is the NULL pointer. + * Returns -err_parse_missing_directive if there was a pt directive marker, + * but no directive. + * Returns -stop_process if the .exp directive was encountered. + * Returns -err_pt_lib if the pt encoder returned an error. + * Returns -err_parse if a general parsing error was encountered. + * Returns -err_parse_unknown_directive if there was an unknown pt directive. + */ +static int p_process(struct parser *p, struct pt_encoder *e) +{ + char *directive, *tmp; + struct pt_directive *pd; + + if (bug_on(!p)) + return -err_internal; + + pd = p->pd; + if (!pd) + return -err_internal; + + directive = pd->name; + + /* We must have a directive. */ + if (!directive || (strcmp(directive, "") == 0)) + return yasm_print_err(p->y, "invalid syntax", + -err_parse_missing_directive); + + /* Check for special directives - they won't contain labels. */ + if (strcmp(directive, ".exp") == 0) { + int errcode; + + /* this is the end of processing pt directives, so we + * add a p_last label to the pt directive labels. + */ + errcode = l_append(p->pt_labels, "eos", p->pt_bytes_written); + if (errcode < 0) + return yasm_print_err(p->y, "append label", errcode); + + return -stop_process; + } + + /* find a label name. */ + tmp = strchr(directive, ':'); + if (tmp) { + char *pt_label_name; + uint64_t x; + int errcode, bytes_written; + size_t len; + + pt_label_name = directive; + directive = tmp+1; + *tmp = '\0'; + + /* ignore whitespace between label and directive. */ + while (isspace(*directive)) + directive += 1; + + /* we must have a directive, not just a label. */ + if (strcmp(directive, "") == 0) + return yasm_print_err(p->y, "invalid syntax", + -err_parse_missing_directive); + + /* if we can lookup a yasm label with the same name, the + * current pt directive label is invalid. */ + errcode = yasm_lookup_label(p->y, &x, pt_label_name); + if (errcode == 0) + errcode = -err_label_not_unique; + + if (errcode != -err_no_label) + return yasm_print_err(p->y, "label lookup", + errcode); + + /* if we can lookup a pt directive label with the same + * name, the current pt directive label is invalid. */ + errcode = l_lookup(p->pt_labels, &x, pt_label_name); + if (errcode == 0) + errcode = -err_label_not_unique; + + if (errcode != -err_no_label) + return yasm_print_err(p->y, "label lookup", + -err_label_not_unique); + + bytes_written = -pte_internal; + switch (pd->kind) { + case pdk_pt: + bytes_written = p->pt_bytes_written; + break; + +#if defined(FEATURE_SIDEBAND) + case pdk_sb: { + struct sb_file *sb; + + sb = p_get_current_sbfile(p); + if (!sb) + return yasm_print_err(p->y, "sideband label", + -err_sb_missing); + + bytes_written = sb->bytes_written; + } + break; +#endif /* defined(FEATURE_SIDEBAND) */ + } + + if (bytes_written < 0) + return bytes_written; + + errcode = l_append(p->pt_labels, pt_label_name, bytes_written); + if (errcode < 0) + return errcode; + + /* Update the directive name in the parser. */ + len = strlen(directive) + 1; + memmove(pd->name, directive, len); + } + + switch (pd->kind) { + case pdk_pt: + return p_process_pt(p, e); + +#if defined(FEATURE_SIDEBAND) + case pdk_sb: + return p_process_sb(p); +#endif + } + + return -err_internal; +} + +/* Starts the parsing process. + * + * Returns 0 on success; a negative enum errcode otherwise. + * Returns -err_pt_lib if the pt encoder could not be initialized. + * Returns -err_file_write if the .pt or .exp file could not be fully + * written. + */ +static int p_start(struct parser *p) +{ + int errcode; + + if (bug_on(!p)) + return -err_internal; + + errcode = yasm_parse(p->y); + if (errcode < 0) + return errcode; + + for (;;) { + int bytes_written; + struct pt_encoder *e; + + errcode = yasm_next_pt_directive(p->y, p->pd); + if (errcode < 0) + break; + + e = pt_alloc_encoder(p->conf); + if (!e) { + fprintf(stderr, "pt_alloc_encoder failed\n"); + errcode = -err_pt_lib; + break; + } + + bytes_written = p_process(p, e); + + pt_free_encoder(e); + + if (bytes_written == -stop_process) { + errcode = p_gen_expfile(p); + break; + } + if (bytes_written < 0) { + errcode = bytes_written; + break; + } + if (fwrite(p->conf->begin, 1, bytes_written, p->ptfile) + != (size_t)bytes_written) { + fprintf(stderr, "write %s failed", p->ptfilename); + errcode = -err_file_write; + break; + } + } + + /* If there is no directive left, there's nothing more to do. */ + if (errcode == -err_no_directive) + return 0; + + return errcode; +} + +int parse(const char *pttfile, const struct pt_config *conf) +{ + int errcode; + struct parser *p; + + p = p_alloc(pttfile, conf); + if (!p) + return -err_no_mem; + + errcode = p_open_files(p); + if (errcode < 0) + goto error; + + errcode = p_start(p); + p_close_files(p); + +error: + p_free(p); + return errcode; +} + +int parse_empty(char *payload) +{ + if (!payload) + return 0; + + strtok(payload, " "); + if (!payload || *payload == '\0') + return 0; + + return -err_parse_trailing_tokens; +} + +int parse_tnt(uint64_t *tnt, uint8_t *size, char *payload) +{ + char c; + + if (bug_on(!size)) + return -err_internal; + + if (bug_on(!tnt)) + return -err_internal; + + *size = 0; + *tnt = 0ull; + + if (!payload) + return 0; + + while (*payload != '\0') { + c = *payload; + payload++; + if (isspace(c) || c == '.') + continue; + *size += 1; + *tnt <<= 1; + switch (c) { + case 'n': + break; + case 't': + *tnt |= 1; + break; + default: + return -err_parse_unknown_char; + } + } + + return 0; +} + +static int ipc_from_uint32(enum pt_ip_compression *ipc, uint32_t val) +{ + switch (val) { + case pt_ipc_suppressed: + case pt_ipc_update_16: + case pt_ipc_update_32: + case pt_ipc_update_48: + case pt_ipc_sext_48: + case pt_ipc_full: + *ipc = (enum pt_ip_compression) val; + return 0; + } + return -err_parse_ipc; +} + +int parse_ip(struct parser *p, uint64_t *ip, enum pt_ip_compression *ipc, + char *payload) +{ + uint32_t ipcval; + int errcode; + + if (bug_on(!ip)) + return -err_internal; + + if (bug_on(!ipc)) + return -err_internal; + + *ipc = pt_ipc_suppressed; + *ip = 0; + + payload = strtok(payload, " :"); + if (!payload || *payload == '\0') + return -err_parse_no_args; + + errcode = str_to_uint32(payload, &ipcval, 0); + if (errcode < 0) + return errcode; + + errcode = ipc_from_uint32(ipc, ipcval); + if (errcode < 0) + return errcode; + + payload = strtok(NULL, " :"); + if (!payload) + return -err_parse_ip_missing; + + /* can be resolved to a label? */ + if (*payload == '%') { + if (!p) + return -err_internal; + + errcode = yasm_lookup_label(p->y, ip, payload + 1); + if (errcode < 0) + return errcode; + } else { + /* can be parsed as address? */ + errcode = str_to_uint64(payload, ip, 0); + if (errcode < 0) + return errcode; + } + + /* no more tokens left. */ + payload = strtok(NULL, " "); + if (payload) + return -err_parse_trailing_tokens; + + return 0; +} + +int parse_uint64(uint64_t *x, char *payload) +{ + int errcode; + + if (bug_on(!x)) + return -err_internal; + + payload = strtok(payload, " ,"); + if (!payload) + return -err_parse_no_args; + + errcode = str_to_uint64(payload, x, 0); + if (errcode < 0) + return errcode; + + return 0; +} + +int parse_uint32(uint32_t *x, char *payload) +{ + int errcode; + + if (bug_on(!x)) + return -err_internal; + + payload = strtok(payload, " ,"); + if (!payload) + return -err_parse_no_args; + + errcode = str_to_uint32(payload, x, 0); + if (errcode < 0) + return errcode; + + return 0; +} + +int parse_uint16(uint16_t *x, char *payload) +{ + int errcode; + + if (bug_on(!x)) + return -err_internal; + + payload = strtok(payload, " ,"); + if (!payload) + return -err_parse_no_args; + + errcode = str_to_uint16(payload, x, 0); + if (errcode < 0) + return errcode; + + return 0; +} + +int parse_uint8(uint8_t *x, char *payload) +{ + int errcode; + + if (bug_on(!x)) + return -err_internal; + + payload = strtok(payload, " ,"); + if (!payload) + return -err_parse_no_args; + + errcode = str_to_uint8(payload, x, 0); + if (errcode < 0) + return errcode; + + return 0; +} + +int parse_tma(uint16_t *ctc, uint16_t *fc, char *payload) +{ + char *endptr; + long int i; + + if (bug_on(!ctc || !fc)) + return -err_internal; + + payload = strtok(payload, ","); + if (!payload || *payload == '\0') + return -err_parse_no_args; + + i = strtol(payload, &endptr, 0); + if (payload == endptr || *endptr != '\0') + return -err_parse_int; + + if (i > 0xffffl) + return -err_parse_int_too_big; + + *ctc = (uint16_t)i; + + payload = strtok(NULL, " ,"); + if (!payload) + return -err_parse_no_args; + + i = strtol(payload, &endptr, 0); + if (payload == endptr || *endptr != '\0') + return -err_parse_int; + + if (i > 0xffffl) + return -err_parse_int_too_big; + + *fc = (uint16_t)i; + + /* no more tokens left. */ + payload = strtok(NULL, " "); + if (payload) + return -err_parse_trailing_tokens; + + return 0; +} diff --git a/pttc/src/posix/util.c b/pttc/src/posix/util.c new file mode 100644 index 0000000000000..02ad207c11136 --- /dev/null +++ b/pttc/src/posix/util.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "errcode.h" +#include "util.h" + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +int run(const char *file, char *const argv[]) +{ + pid_t pid; + int status; + + if (bug_on(!file)) + return -err_internal; + + if (bug_on(!argv)) + return -err_internal; + + pid = fork(); + + if (!pid) { + execvp(file, argv); + perror(argv[0]); + exit(1); + } + if (waitpid(pid, &status, 0) < 0) + return -err_other; + + if (!WIFEXITED(status)) + return -err_other; + + if (WEXITSTATUS(status)) + return -err_run; + + return 0; +} diff --git a/pttc/src/pttc.c b/pttc/src/pttc.c new file mode 100644 index 0000000000000..4250c595e0b2c --- /dev/null +++ b/pttc/src/pttc.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "errcode.h" +#include "pttc.h" +#include "parse.h" + +int pttc_main(const struct pttc_options *options) +{ + int errcode; + enum { buflen = 1024 }; + uint8_t buf[buflen]; + struct pt_config conf; + + pt_config_init(&conf); + conf.cpu = options->cpu; + conf.begin = buf; + conf.end = buf+buflen; + + /* apply errata for the chosen cpu. */ + if (conf.cpu.vendor) { + errcode = pt_cpu_errata(&conf.errata, &conf.cpu); + if (errcode < 0) { + fprintf(stderr, + "fatal: errata configuration failed %d: %s\n", + errcode, pt_errstr(pt_errcode(errcode))); + return errcode; + } + } + + errcode = parse(options->pttfile, &conf); + if (errcode < 0 && errcode != -err_run) + fprintf(stderr, "fatal: %s\n", errstr[-errcode]); + + return -errcode; + +} diff --git a/pttc/src/util.c b/pttc/src/util.c new file mode 100644 index 0000000000000..517280e403d27 --- /dev/null +++ b/pttc/src/util.c @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "errcode.h" +#include "util.h" + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +char *duplicate_str(const char *s) +{ + char *dup; + + if (!s) + return NULL; + + dup = malloc(strlen(s)+1); + if (!dup) + return NULL; + return strcpy(dup, s); +} + +int str_to_uint64(const char *str, uint64_t *val, int base) +{ + char *endptr; + uint64_t x; + + if (!str || !val) + return -err_internal; + + errno = 0; + x = strtoull(str, &endptr, base); + + if (errno == EINVAL) + return -err_parse_int; + + if (errno == ERANGE) + return -err_parse_int_too_big; + + if (str == endptr || *endptr != '\0') + return -err_parse_int; + + *val = x; + + return 0; +} + +int str_to_uint32(const char *str, uint32_t *val, int base) +{ + uint64_t x; + int errcode; + + if (!str || !val) + return -err_internal; + + errcode = str_to_uint64(str, &x, base); + if (errcode < 0) + return errcode; + + if (UINT32_MAX < x) + return -err_parse_int_too_big; + + *val = (uint32_t) x; + return 0; +} + +int str_to_uint16(const char *str, uint16_t *val, int base) +{ + uint64_t x; + int errcode; + + if (!str || !val) + return -err_internal; + + errcode = str_to_uint64(str, &x, base); + if (errcode < 0) + return errcode; + + if (UINT16_MAX < x) + return -err_parse_int_too_big; + + *val = (uint16_t) x; + return 0; +} + +int str_to_uint8(const char *str, uint8_t *val, int base) +{ + uint64_t x; + int errcode; + + if (!str || !val) + return -err_internal; + + errcode = str_to_uint64(str, &x, base); + if (errcode < 0) + return errcode; + + if (UINT8_MAX < x) + return -err_parse_int_too_big; + + *val = (uint8_t) x; + return 0; +} + +int do_bug_on(int cond, const char *condstr, const char *file, int line) +{ + if (cond) + fprintf(stderr, "%s:%d: internal error: %s\n", file, line, + condstr); + return cond; +} +struct label *l_alloc(void) +{ + return calloc(1, sizeof(struct label)); +} + +void l_free(struct label *l) +{ + if (!l) + return; + + l_free(l->next); + free(l->name); + free(l); +} + +int l_append(struct label *l, const char *name, uint64_t addr) +{ + int errcode; + + if (bug_on(!l)) + return -err_internal; + + if (bug_on(!name)) + return -err_internal; + + /* skip to the last label. */ + while (l->next) { + l = l->next; + + /* ignore the first label, which has no name. */ + if (strcmp(l->name, name) == 0) + return -err_label_not_unique; + } + + /* append a new label. */ + l->next = l_alloc(); + if (!l->next) + return -err_no_mem; + + /* save the name. */ + l->next->name = duplicate_str(name); + if (!l->next->name) { + errcode = -err_no_mem; + goto error; + } + + /* save the address. */ + l->next->addr = addr; + + return 0; +error: + free(l->next->name); + free(l->next); + l->next = NULL; + return errcode; +} + +int l_lookup(const struct label *l, uint64_t *addr, + const char *name) +{ + if (bug_on(!l)) + return -err_internal; + + if (bug_on(!addr)) + return -err_internal; + + if (bug_on(!name)) + return -err_internal; + + + *addr = 0; + while (l->next) { + l = l->next; + if (strcmp(l->name, name) == 0) { + *addr = l->addr; + return 0; + } + } + return -err_no_label; +} + +struct label *l_find(struct label *l, const char *name) +{ + if (bug_on(!l)) + return NULL; + + if (bug_on(!name)) + return NULL; + + + while (l->next) { + l = l->next; + + if (bug_on(!l->name)) + continue; + + if (strcmp(l->name, name) == 0) + return l; + } + return NULL; +} diff --git a/pttc/src/windows/util.c b/pttc/src/windows/util.c new file mode 100644 index 0000000000000..f24fbf12834b0 --- /dev/null +++ b/pttc/src/windows/util.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "errcode.h" +#include "util.h" + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <windows.h> + + +int run(const char *file, char *const argv[]) +{ + int errcode; + + int i; + size_t size; + char *args; + + STARTUPINFO si; + PROCESS_INFORMATION pi; + DWORD exit_code; + + DWORD dwret; + BOOL bret; + + + errcode = 0; + + if (bug_on(!file)) { + errcode = -err_internal; + goto out; + } + + if (bug_on(!argv)) { + errcode = -err_internal; + goto out; + } + + + /* calculate length of command line - this is the cumulative length of + * all arguments, plus two quotation marks (to make it quoted strings + * and allow for spaces in file/path names), plus a space after each + * arguments as delimiter (after the last arguments it's a terminating + * zero-byte instead of the space). * + */ + size = 0; + for (i = 0; argv[i]; ++i) + size += strlen(argv[i]) + 3; + + /* allocate command line string */ + args = calloc(size, 1); + if (!args) + return -err_no_mem; + + /* construct command line string, putting quotation marks + * around every argument of the vector and a space after it + */ + size = 0; + for (i = 0; argv[i]; ++i) { + args[size++] = '"'; + strcpy(args + size, argv[i]); + size += strlen(argv[i]); + args[size++] = '"'; + args[size++] = ' '; + } + /* transform last space into a terminating zero-byte and fix up size */ + args[--size] = '\0'; + + + /* initialize process/startup info */ + memset(&pi, 0, sizeof(pi)); + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + + /* create process - since the first parameter is NULL, the + * second parameter represents a command as it would behave + * on a command shell + */ + bret = CreateProcess(NULL, args, + NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + if (!bret) { + errcode = -err_other; + goto out_args; + } + + dwret = WaitForSingleObject(pi.hProcess, INFINITE); + if (dwret == WAIT_FAILED) { + errcode = -err_other; + goto out_handles; + } + + bret = GetExitCodeProcess(pi.hProcess, &exit_code); + if (!bret) { + errcode = -err_other; + goto out_handles; + } + + if (exit_code != 0) + errcode = -err_run; + + +out_handles: + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); +out_args: + free(args); +out: + return errcode; +} diff --git a/pttc/src/yasm.c b/pttc/src/yasm.c new file mode 100644 index 0000000000000..284f225d18390 --- /dev/null +++ b/pttc/src/yasm.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) 2013-2018, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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 "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, int size, const char *name, + const char *attribute) +{ + int written; + + written = snprintf(label, size, "section_%s_%s", name, attribute); + if (size <= 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; + + /* we initialize the section's length to zero - it will be updated + * when we process the section's content. + */ + 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); +} + +int parse_yasm_labels(struct label *l, const struct text *t) +{ + int errcode, no_org_directive; + size_t i; + uint64_t base_addr; + enum { linelen = 1024 }; + char line[linelen]; + struct label *length; + + if (bug_on(!t)) + return -err_internal; + + base_addr = 0; + no_org_directive = 1; + length = NULL; + + /* determine base address from org directive and insert special + * section labels. + */ + for (i = 0; i < t->n; i++) { + char *tmp; + + errcode = text_line(t, line, linelen, i); + if (errcode < 0) + return errcode; + + tmp = strstr(line, "[section"); + if (tmp) { + tmp += strlen("[section"); + errcode = parse_section(tmp, l, &length); + if (errcode < 0) + return errcode; + continue; + } + + tmp = strstr(line, "[org"); + if (tmp) { + base_addr = strtol(tmp+strlen("[org"), NULL, 0); + + errcode = l_append(l, "org", base_addr); + if (errcode < 0) + return errcode; + + no_org_directive = 0; + continue; + } + + /* update the section_<name>_length label, if we have one. + * + * this must be last; it destroys @line. + */ + if (length) { + uint64_t value, size; + + tmp = strtok(line, " "); + if (!tmp) + continue; + + /* we expect a line number. */ + errcode = str_to_uint64(tmp, &value, 10); + if (errcode < 0) + continue; + + tmp = strtok(NULL, " "); + if (!tmp) + continue; + + /* we expect an address. */ + errcode = str_to_uint64(tmp, &value, 16); + if (errcode < 0) + continue; + + tmp = strtok(NULL, " "); + if (!tmp) + continue; + + /* we expect an opcode. */ + errcode = str_to_uint64(tmp, &value, 16); + if (errcode < 0) + continue; + + /* we got an opcode - let's compute it's size. */ + for (size = 0; value != 0; value >>= 8) + size += 1; + + /* update the section_<name>_length label. */ + length->addr += size; + } + } + + if (no_org_directive) + return -err_no_org_directive; + + for (i = 0; i < t->n; i++) { + char *tmp, *name; + uint64_t addr; + + errcode = text_line(t, line, linelen, i); + if (errcode < 0) + goto error; + + /* Change the base on section switches. */ + tmp = strstr(line, "[section"); + if (tmp) { + tmp += strlen("[section"); + errcode = lookup_section_vstart(l, tmp, &base_addr); + if (errcode < 0) + return errcode; + continue; + } + + /* skip line number count. */ + tmp = strtok(line, " "); + if (!tmp) + continue; + + /* the label can now be on the same line as the memory + * address or on a line by its own. + * we look at the next token and (1) if it looks like a + * label, we search in the following lines for the + * corresponding address; or (2) if it looks like an + * address, we store it and see if the token after the + * opcode looks like a token; or (3) none of the above, + * we continue with the next line. + */ + + /* second token after the line number count. it's + * either an address; or a label. + */ + tmp = strtok(NULL, " "); + if (!tmp) + continue; + + if (!make_label(tmp)) { + /* get address in case we find a label later. */ + if (sscanf(tmp, "%" PRIx64, &addr) != 1) + continue; + + /* skip the opcode token. */ + tmp = strtok(NULL, " "); + if (!tmp) + continue; + + /* this might be a label now. */ + tmp = strtok(NULL, " "); + if (!make_label(tmp)) + continue; + + errcode = l_append(l, tmp, addr + base_addr); + if (errcode < 0) + goto error; + continue; + } + name = duplicate_str(tmp); + if (!name) { + errcode = -err_no_mem; + goto error; + } + + /* there was a label so now an address needs to + * be found. + */ + errcode = -err_label_addr; + for (i += 1; i < t->n; i++) { + int errcode_text; + + errcode_text = text_line(t, line, linelen, i); + if (errcode_text < 0) { + errcode = errcode_text; + break; + } + if (sscanf(line, "%*d %" PRIx64 " %*x %*s", &addr) + == 1) { + errcode = l_append(l, name, addr + base_addr); + break; + } + } + if (errcode == -err_label_addr) + fprintf(stderr, "label '%s' has no address\n", name); + free(name); + 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) +{ + size_t n; + + if (bug_on(!s)) + return -err_internal; + + n = strlen(s); + 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; +} + +/* Sets current @filename, increment (@inc) and line number (@n) in @st. + * + * Note that @filename, @inc and @n correspond to the yasm .lst file + * source file information. + * + * Returns 0 on success; a negative enum errcode otherwise. + */ +static int st_set_file(struct state *st, const char *filename, int inc, int n) +{ + if (bug_on(!st)) + return -err_internal; + + if (bug_on(!filename)) + return -err_internal; + + free(st->filename); + st->filename = duplicate_str(filename); + if (!st->filename) + return -err_no_mem; + st->inc = inc; + st->n = n; + return 0; +} + +/* Sets current line in @st to @s and increases the line number. + * + * Returns 0 on success; a negative enum errcode otherwise. + */ +static int st_update(struct state *st, const char *s) +{ + free(st->line); + st->line = duplicate_str(s); + if (!st->line) + return -err_no_mem; + + 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; +} + +/* Magic annotation markers. */ +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; + + + line = duplicate_str(st->line); + if (!line) + return -err_no_mem; + + /* make line lower case. */ + for (c = line; *c; ++c) + *c = (char) tolower(*c); + + /* if the current line is not a comment or contains no magic marker + * -err_no_directive is returned. + */ + errcode = -err_no_directive; + + /* search where the comment begins. */ + comment = strchr(line, ';'); + + /* if there is no comment in the line, we don't have anything to + * do. + */ + if (!comment) + goto cleanup; + + /* search for @pt marker. */ + directive = strstr(comment+1, pt_marker); + if (directive) { + directive += strlen(pt_marker); + kind = pdk_pt; + } else { +#if defined(FEATURE_SIDEBAND) + /* search for @sb marker. */ + directive = strstr(comment+1, sb_marker); + if (directive) { + directive += strlen(sb_marker); + kind = pdk_sb; + } else +#endif + goto cleanup; + } + + /* skip leading whitespace. */ + while (isspace(*directive)) + directive += 1; + + /* directive found, now parse the payload. */ + errcode = 0; + + /* find position of next '(', separating the directive and the + * payload. + */ + openpar = strchr(directive, '('); + if (!openpar) { + errcode = -err_missing_openpar; + st_print_err(st, "invalid syntax", errcode); + goto cleanup; + } + + /* find position of next ')', marking the end of the payload */ + closepar = strchr(openpar, ')'); + if (!closepar) { + errcode = -err_missing_closepar; + st_print_err(st, "invalid syntax", errcode); + goto cleanup; + } + + /* make "multiple" strings by artifically terminating them with + * '\0' then get directive and payload substrings, which will + * have leading and trailing whitespace "removed". + */ + *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 = '/'; +enum { + max_filename_len = 1024 +}; + +struct yasm *yasm_alloc(const char *pttfile) +{ + char *tmp; + size_t n; + struct yasm *y; + + 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; + + y->fileroot = duplicate_str(pttfile); + if (!y->fileroot) + goto error; + + y->pttfile = duplicate_str(pttfile); + if (!y->pttfile) + goto error; + + tmp = strrchr(y->fileroot, '.'); + if (tmp) + *tmp = '\0'; + + tmp = strrchr(y->fileroot, path_separator); + if (tmp) { + tmp += 1; + memmove(y->fileroot, tmp, strlen(tmp)+1); + } + + y->binfile = malloc(strlen(y->fileroot)+strlen(bin_suffix)+1); + if (!y->binfile) + goto error; + + y->lstfile = malloc(strlen(y->fileroot)+strlen(lst_suffix)+1); + if (!y->lstfile) + goto error; + + n = strlen(y->fileroot); + + strcpy(y->binfile, y->fileroot); + strcpy(y->binfile+n, bin_suffix); + strcpy(y->lstfile, y->fileroot); + strcpy(y->lstfile+n, lst_suffix); + + 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) +{ + enum { slen = 1024 }; + char s[slen]; + char filename[max_filename_len]; + int errcode; + int asm_line, asm_inc; + + if (bug_on(!y)) + return -err_internal; + + + for (;;) { + errcode = fl_getline(y->fl, s, slen, y->lstfile, + y->lst_curr_line); + /* always advance in lst file. */ + y->lst_curr_line += 1; + + if (errcode < 0) + break; + + /* if the current lst file line is a line directive, set + * state information to this file, line and increment + * and continue. + */ + 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 line number or increment in the previous line + * directive is <= 0, the current lst line has no + * corresponding line in the source file. + */ + if (y->st_asm->n <= 0 || y->st_asm->inc <= 0) + continue; + + /* finally the current line in the lst file can be + * correlated to the source file, so we retrieve the + * line from it and update the state. + */ + errcode = fl_getline(y->fl, s, slen, y->st_asm->filename, + y->st_asm->n-1); + 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); +} |
