| /* |
| * Copyright (c) 2013-2017, 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 <ctype.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| const char *pt_suffix = ".pt"; |
| const char *exp_suffix = ".exp"; |
| |
| enum { |
| pd_len = 1024 |
| }; |
| |
| /* 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); |
| |
| 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; |
| |
| 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 i, 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; |
| long int n; |
| |
| line += 1; |
| |
| n = strtol(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; |
| } |
| addr &= (1ull << (n << 3)) - 1ull; |
| line = endptr; |
| |
| qmark_size = 8 - n; |
| } |
| |
| if (qmark_padding) { |
| int i; |
| |
| 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; |
| } |
| |
| /* 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) |
| { |
| int bytes_written; |
| int errcode; |
| char *directive, *payload, *pt_label_name, *tmp; |
| struct pt_directive *pd; |
| struct pt_packet packet; |
| |
| 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; |
| |
| pt_label_name = NULL; |
| bytes_written = 0; |
| errcode = 0; |
| |
| /* find a label name. */ |
| tmp = strchr(directive, ':'); |
| if (tmp) { |
| uint64_t x; |
| |
| pt_label_name = directive; |
| directive = tmp+1; |
| *tmp = '\0'; |
| |
| /* ignore whitespace between label and directive. */ |
| while (isspace(*directive)) |
| directive += 1; |
| |
| /* 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); |
| } |
| |
| /* now try to match the directive string and call the |
| * corresponding function that parses the payload and emits an |
| * according packet. |
| */ |
| if (strcmp(directive, "") == 0) |
| return yasm_print_err(p->y, "invalid syntax", |
| -err_parse_missing_directive); |
| else if (strcmp(directive, ".exp") == 0) { |
| /* 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; |
| } |
| |
| if (strcmp(directive, "psb") == 0) { |
| errcode = parse_empty(payload); |
| if (errcode < 0) { |
| yasm_print_err(p->y, "psb: parsing failed", errcode); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| } |
| } 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| 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); |
| goto error; |
| } |
| packet.type = ppt_mnt; |
| } else { |
| errcode = yasm_print_err(p->y, "invalid syntax", |
| -err_parse_unknown_directive); |
| goto error; |
| } |
| |
| bytes_written = pt_enc_next(e, &packet); |
| if (bytes_written < 0) { |
| const char *errstr, *format; |
| char *msg; |
| size_t n; |
| |
| errstr = pt_errstr(pt_errcode(bytes_written)); |
| format = "encoder error in directive %s (status %s)"; |
| /* the length of format includes the "%s" (-2) |
| * characters, we add errstr (+-0) and then we need |
| * space for a terminating null-byte (+1). |
| */ |
| n = strlen(format)-4 + strlen(directive) + strlen(errstr) + 1; |
| |
| msg = malloc(n); |
| if (!msg) |
| errcode = yasm_print_err(p->y, |
| "encoder error not enough memory to show error code", |
| -err_pt_lib); |
| else { |
| sprintf(msg, format, directive, errstr); |
| errcode = yasm_print_err(p->y, msg, -err_pt_lib); |
| free(msg); |
| } |
| } else { |
| if (pt_label_name) { |
| errcode = l_append(p->pt_labels, pt_label_name, |
| p->pt_bytes_written); |
| if (errcode < 0) |
| goto error; |
| } |
| p->pt_bytes_written += bytes_written; |
| } |
| |
| error: |
| if (errcode < 0) |
| bytes_written = errcode; |
| return bytes_written; |
| } |
| |
| /* 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. |
| */ |
| 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 check_ipc(enum pt_ip_compression ipc) |
| { |
| switch (ipc) { |
| 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: |
| return 0; |
| } |
| return -err_parse_ipc; |
| } |
| |
| int parse_ip(struct parser *p, uint64_t *ip, enum pt_ip_compression *ipc, |
| char *payload) |
| { |
| int errcode; |
| char *endptr; |
| |
| 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; |
| |
| *ipc = (enum pt_ip_compression) strtol(payload, &endptr, 0); |
| if (payload == endptr || *endptr != '\0') |
| return -err_parse_ipc; |
| |
| /* is ipc valid? */ |
| errcode = check_ipc(*ipc); |
| if (errcode < 0) |
| return errcode; |
| |
| payload = strtok(NULL, " :"); |
| if (!payload) |
| return -err_parse_ip_missing; |
| |
| /* can be resolved to a label? */ |
| if (*payload == '%') { |
| int errcode; |
| |
| 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? */ |
| int errcode; |
| |
| 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; |
| } |