/* Copyright (C) 2006 Silicon Graphics, Inc. All Rights Reserved. Portions Copyright 2011 David Anderson. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it would be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Further, this software is distributed without any warranty that it is free of the rightful claim of any third person regarding infringement or the like. Any license provided herein, whether implied or otherwise, applies only to this software file. Patent licenses, if any, provided herein do not apply to combinations of this program with other software, or any other product whatsoever. You should have received a copy of the GNU General Public License along with this program; if not, write the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301, USA. Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, Mountain View, CA 94043, or: http://www.sgi.com For further information regarding this notice, see: http://oss.sgi.com/projects/GenInfo/NoticeExplan $Header: /plroot/cmplrs.src/v7.4.5m/.RCS/PL/dwarfdump/RCS/dwconf.c,v 1.4 2006/04/18 18:05:57 davea Exp $ */ /* Windows specific */ #ifdef HAVE_STDAFX_H #include "stdafx.h" #endif /* HAVE_STDAFX_H */ #include "globals.h" #include "dwarf.h" #include "libdwarf.h" #include #include "dwconf.h" #include "makename.h" extern int verbose; /* The nesting level is arbitrary, 2 should suffice. But at least this prevents an infinite loop. */ #define MAX_NEST_LEVEL 3 struct token_s { unsigned tk_len; char *tk_data; }; enum linetype_e { LT_ERROR, LT_COMMENT, LT_BLANK, LT_BEGINABI, LT_REG, LT_FRAME_INTERFACE, LT_CFA_REG, LT_INITIAL_REG_VALUE, LT_SAME_VAL_REG, LT_UNDEFINED_VAL_REG, LT_REG_TABLE_SIZE, LT_ADDRESS_SIZE, LT_INCLUDEABI, LT_ENDABI }; struct comtable_s { enum linetype_e type; char *name; size_t namelen; }; static int errcount = 0; /* Count errors found in this scan of the configuration file. */ static char name_begin_abi[] = "beginabi:"; static char name_reg[] = "reg:"; static char name_frame_interface[] = "frame_interface:"; static char name_cfa_reg[] = "cfa_reg:"; static char name_initial_reg_value[] = "initial_reg_value:"; static char name_same_val_reg[] = "same_val_reg:"; static char name_undefined_val_reg[] = "undefined_val_reg:"; static char name_reg_table_size[] = "reg_table_size:"; static char name_address_size[] = "address_size:"; static char name_includeabi[] = "includeabi:"; static char name_endabi[] = "endabi:"; static struct comtable_s comtable[] = { {LT_BEGINABI, name_begin_abi}, {LT_REG, name_reg}, {LT_FRAME_INTERFACE, name_frame_interface}, {LT_CFA_REG, name_cfa_reg}, {LT_INITIAL_REG_VALUE, name_initial_reg_value}, {LT_SAME_VAL_REG, name_same_val_reg}, {LT_UNDEFINED_VAL_REG, name_undefined_val_reg}, {LT_REG_TABLE_SIZE, name_reg_table_size}, {LT_ADDRESS_SIZE, name_address_size}, {LT_INCLUDEABI, name_includeabi}, {LT_ENDABI, name_endabi}, }; struct conf_internal_s { unsigned long beginabi_lineno; unsigned long frame_interface_lineno; unsigned long initial_reg_value_lineno; unsigned long reg_table_size_lineno; unsigned long address_size_lineno; unsigned long same_val_reg_lineno; unsigned long undefined_val_reg_lineno; unsigned long cfa_reg_lineno; unsigned long regcount; struct dwconf_s * conf_out; const char * conf_name_used; char ** conf_defaults; }; static void init_conf_internal(struct conf_internal_s *s, struct dwconf_s * conf_out) { s->beginabi_lineno = 0; s->frame_interface_lineno = 0; s->initial_reg_value_lineno = 0; s->reg_table_size_lineno = 0; s->address_size_lineno = 0; s->same_val_reg_lineno = 0; s->undefined_val_reg_lineno = 0; s->cfa_reg_lineno = 0; s->cfa_reg_lineno = 0; s->conf_name_used = 0; s->conf_defaults = 0; s->regcount = 0; s->conf_out = conf_out; } static int size_of_comtable = sizeof(comtable) / sizeof(comtable[0]); static FILE *find_a_file(const char *named_file, char **defaults, const char** name_used); static int find_abi_start(FILE * stream, const char *abi_name, long *offset, unsigned long *lineno_out); static int parse_abi(FILE * stream, const char *fname, const char *abiname, struct conf_internal_s *out, unsigned long lineno, unsigned nest_level); static char *get_token(char *cp, struct token_s *outtok); /* This finds a dwarfdump.conf file and then parses it. It updates conf_out as appropriate. This finds the first file (looking in a set of places) with that name. It then looks for the right ABI entry. If the first file it finds does not have that ABI entry it gives up. It would also be reasonable to search every 'dwarfdump.conf' it finds for the abi. But we stop at the first dwarfdump.conf we find. This is the internal call to get the conf data to implement a crude 'includeabi' feature. Returns 0 if no errors found, else returns > 0. */ static int find_conf_file_and_read_config_inner(const char *named_file, const char *named_abi, struct conf_internal_s *conf_internal, unsigned nest_level) { FILE *conf_stream = 0; const char *name_used = 0; long offset = 0; int res = FALSE; unsigned long lineno = 0; errcount = 0; conf_stream = find_a_file(named_file, conf_internal->conf_defaults, &name_used); if (!conf_stream) { ++errcount; printf("dwarfdump found no file %s!\n", named_file ? named_file : "readable for configuration. " "(add options -v -v to see what file names tried)\n"); return errcount; } if (verbose > 1) { printf("dwarfdump using configuration file %s\n", name_used); } conf_internal->conf_name_used = name_used; res = find_abi_start(conf_stream, named_abi, &offset, &lineno); if (errcount > 0) { ++errcount; printf("dwarfdump found no ABI %s in file %s.\n", named_abi, name_used); return errcount; } res = fseek(conf_stream, offset, SEEK_SET); if (res != 0) { ++errcount; printf("dwarfdump seek to %ld offset in %s failed!\n", offset, name_used); return errcount; } parse_abi(conf_stream, name_used, named_abi, conf_internal, lineno, nest_level); fclose(conf_stream); return errcount; } /* This is the external-facing call to get the conf data. */ int find_conf_file_and_read_config(const char *named_file, const char *named_abi, char **defaults, struct dwconf_s *conf_out) { int res = 0; struct conf_internal_s conf_internal; init_conf_file_data(conf_out); init_conf_internal(&conf_internal,conf_out); conf_internal.conf_defaults = defaults; res = find_conf_file_and_read_config_inner(named_file, named_abi, &conf_internal,0); return res; } /* Given path strings, attempt to make a canonical file name: that is, avoid superfluous '/' so that no '//' (or worse) is created in the output. The path components are to be separated so at least one '/' is to appear between the two 'input strings' when creating the output. */ static char * canonical_append(char *target, unsigned int target_size, const char *first_string, const char *second_string) { size_t firstlen = strlen(first_string); /* +1 +1: Leave room for added "/" and final NUL, though that is overkill, as we drop a NUL byte too. */ if ((firstlen + strlen(second_string) + 1 + 1) >= target_size) { /* Not enough space. */ return NULL; } for (; *second_string == '/'; ++second_string) { } for (; firstlen > 0 && first_string[firstlen - 1] == '/'; --firstlen) { } target[0] = 0; if (firstlen > 0) { strncpy(target, first_string, firstlen); target[firstlen + 1] = 0; } target[firstlen] = '/'; firstlen++; target[firstlen] = 0; strcat(target, second_string); return target; } #ifdef BUILD_FOR_TEST #define CANBUF 25 struct canap_s { char *res_exp; char *first; char *second; } canap[] = { { "ab/c", "ab", "c"}, { "ab/c", "ab/", "c"}, { "ab/c", "ab", "/c"}, { "ab/c", "ab////", "/////c"}, { "ab/", "ab", ""}, { "ab/", "ab////", ""}, { "ab/", "ab////", ""}, { "/a", "", "a"}, { 0, "/abcdefgbijkl", "pqrstuvwxyzabcd"}, { 0, 0, 0} }; static void test_canonical_append(void) { /* Make buf big, this is test code, so be safe. */ char lbuf[1000]; unsigned i; unsigned failcount = 0; printf("Entry test_canonical_append\n"); for (i = 0;; ++i) { char *res = 0; if (canap[i].first == 0 && canap[i].second == 0) break; res = canonical_append(lbuf, CANBUF, canap[i].first, canap[i].second); if (res == 0) { if (canap[i].res_exp == 0) { /* GOOD */ printf("PASS %u\n", i); } else { ++failcount; printf("FAIL: entry %u wrong, expected %s, got NULL\n", i, canap[i].res_exp); } } else { if (canap[i].res_exp == 0) { ++failcount; printf("FAIL: entry %u wrong, got %s expected NULL\n", i, res); } else { if (strcmp(res, canap[i].res_exp) == 0) { printf("PASS %u\n", i); /* GOOD */ } else { ++failcount; printf("FAIL: entry %u wrong, expected %s got %s\n", i, canap[i].res_exp, res); } } } } printf("FAIL count %u\n", failcount); } #endif /* BUILD_FOR_TEST */ /* Try to find a file as named and open for read. We treat each name as a full name, we are not combining separate name and path components. This is an arbitrary choice... The defaults are listed in dwarfdump.c in the array config_file_defaults[]. */ static FILE * find_a_file(const char *named_file, char **defaults, const char ** name_used) { FILE *fin = 0; const char *lname = named_file; const char *type = "rw"; int i = 0; #ifdef BUILD_FOR_TEST test_canonical_append(); #endif /* BUILD_FOR_TEST */ if (lname) { /* Name given, just assume it is fully correct, try no other. */ if (verbose > 1) { printf("dwarfdump looking for configuration as %s\n", lname); } fin = fopen(lname, type); if (fin) { *name_used = lname; return fin; } return 0; } /* No name given, find a default, if we can. */ for (i = 0; defaults[i]; ++i) { lname = defaults[i]; #ifdef WIN32 /* Open the configuration file, located in the directory where the tool is loaded from */ { static char szPath[MAX_PATH]; if (GetModuleFileName(NULL,szPath,MAX_PATH)) { char *pDir = strrchr(szPath,'/'); if (!pDir) { pDir = strrchr(szPath,'\\'); if (!pDir) { /* No file was found */ return 0; } } /* Add the configuration name to the pathname */ ++pDir; strcpy(pDir,"dwarfdump.conf"); lname = szPath; } } #else /* non-Win */ if (strncmp(lname, "HOME/", 5) == 0) { /* arbitrary size */ char buf[2000]; char *homedir = getenv("HOME"); if (homedir) { char *cp = canonical_append(buf, sizeof(buf), homedir, lname + 5); if (!cp) { /* OOps, ignore this one. */ continue; } lname = makename(buf); } } #endif /* WIN32 */ if (verbose > 1) { printf("dwarfdump looking for configuration as %s\n", lname); } fin = fopen(lname, type); if (fin) { *name_used = lname; return fin; } } return 0; } /* Start at a token begin, see how long it is, return length. */ unsigned find_token_len(char *cp) { unsigned len = 0; for (; *cp; ++cp) { if (isspace(*cp)) { return len; } if (*cp == '#') { return len; /* begins comment */ } ++len; } return len; } /* Skip past all whitespace: the only code that even knows what whitespace is. */ static char * skipwhite(char *cp) { for (; *cp; ++cp) { if (!isspace(*cp)) { return cp; } } return cp; } /* Return TRUE if ok. FALSE if find more tokens. Emit error message if error. */ static int ensure_has_no_more_tokens(char *cp, const char *fname, unsigned long lineno) { struct token_s tok; cp = get_token(cp, &tok); if (tok.tk_len > 0) { printf("dwarfdump.conf error: " "extra characters after command operands, found " "\"%s\" in %s line %lu\n", tok.tk_data, fname, lineno); ++errcount; return FALSE; } return TRUE; } /* There may be many beginabi: lines in a dwarfdump.conf file, find the one we want and return its file offset. */ static int find_abi_start(FILE * stream, const char *abi_name, long *offset, unsigned long *lineno_out) { char buf[100]; unsigned long lineno = 0; for (; !feof(stream);) { struct token_s tok; char *line = 0; long loffset = ftell(stream); line = fgets(buf, sizeof(buf), stream); ++lineno; if (!line) { ++errcount; return FALSE; } line = get_token(buf, &tok); if (strcmp(tok.tk_data, name_begin_abi) != 0) { continue; } get_token(line, &tok); if (strcmp(tok.tk_data, abi_name) != 0) { continue; } *offset = loffset; *lineno_out = lineno; return TRUE; } ++errcount; return FALSE; } static char *tempstr = 0; static unsigned tempstr_len = 0; /* Use a global buffer (tempstr) to turn a non-delimited input char array into a NUL-terminated C string (with the help of makename() to get a permanent address for the result ing string). */ static char * build_string(unsigned tlen, char *cp) { if (tlen >= tempstr_len) { free(tempstr); tempstr = malloc(tlen + 100); } strncpy(tempstr, cp, tlen); tempstr[tlen] = 0; return makename(tempstr); } /* The tokenizer for our simple parser. */ static char * get_token(char *cp, struct token_s *outtok) { char *lcp = skipwhite(cp); unsigned tlen = find_token_len(lcp); outtok->tk_len = tlen; if (tlen > 0) { outtok->tk_data = build_string(tlen, lcp); } else { outtok->tk_data = ""; } return lcp + tlen; } /* We can't get all the field set up statically very easily, so we get the command string length set here. */ static void finish_comtable_setup(void) { unsigned i; for (i = 0; i < size_of_comtable; ++i) { comtable[i].namelen = strlen(comtable[i].name); } } /* Given a line of the table, determine if it is a command or not, and if a command, which one is it. Return LT_ERROR if it's not recognized. */ static enum linetype_e which_command(char *cp, struct comtable_s **tableentry) { int i; struct token_s tok; if (*cp == '#') return LT_COMMENT; if (!*cp) return LT_BLANK; get_token(cp, &tok); for (i = 0; i < size_of_comtable; ++i) { if (tok.tk_len == comtable[i].namelen && strcmp(comtable[i].name, tok.tk_data) == 0) { *tableentry = &comtable[i]; return comtable[i].type; } } return LT_ERROR; } /* We are promised it's an abiname: command find the name on the line. */ static int parsebeginabi(char *cp, const char *fname, const char *abiname, unsigned long lineno, struct comtable_s *comtab) { size_t clen = comtab->namelen; size_t abinamelen = strlen(abiname); struct token_s tok; cp = cp + clen + 1; cp = skipwhite(cp); get_token(cp, &tok); if (tok.tk_len != abinamelen || strncmp(cp, abiname, abinamelen) != 0) { printf("dwarfdump internal error: " "mismatch %s with %s %s line %lu\n", cp, tok.tk_data, fname, lineno); ++errcount; return FALSE; } ensure_has_no_more_tokens(cp + tok.tk_len, fname, lineno); return TRUE; } /* This expands register names as required, but does not ensure no names duplicated. */ #define CONF_TABLE_OVERSIZE 100 static void add_to_reg_table(struct dwconf_s *conf, char *rname, unsigned long rval, const char *fname, unsigned long lineno) { if (conf->cf_regs_malloced == 0) { conf->cf_regs = 0; conf->cf_named_regs_table_size = 0; } if (rval >= conf->cf_named_regs_table_size) { char **newregs = 0; unsigned long newtablen = rval + CONF_TABLE_OVERSIZE; unsigned long newtabsize = newtablen * sizeof(char *); unsigned long oldtabsize = conf->cf_named_regs_table_size * sizeof(char *); newregs = realloc(conf->cf_regs, newtabsize); if (!newregs) { printf("dwarfdump: unable to malloc table %lu bytes. " " %s line %lu\n", newtabsize, fname, lineno); exit(1); } /* Zero out the new entries. */ memset((char *) newregs + (oldtabsize), 0, (newtabsize - oldtabsize)); conf->cf_named_regs_table_size = (unsigned long) newtablen; conf->cf_regs = newregs; conf->cf_regs_malloced = 1; } conf->cf_regs[rval] = rname; return; } /* Our input is supposed to be a number. Determine the value (and return it) or generate an error message. */ static int make_a_number(char *cmd, const char *filename, unsigned long lineno, struct token_s *tok, unsigned long *val_out) { char *endnum = 0; unsigned long val = 0; val = strtoul(tok->tk_data, &endnum, 0); if (val == 0 && endnum == (tok->tk_data)) { printf("dwarfdump.conf error: " "%s missing register number (\"%s\" not valid) %s line %lu\n", cmd, tok->tk_data, filename, lineno); ++errcount; return FALSE; } if (endnum != (tok->tk_data + tok->tk_len)) { printf("dwarfdump.conf error: " "%s Missing register number (\"%s\" not valid) %s line %lu\n", cmd, tok->tk_data, filename, lineno); ++errcount; return FALSE; } *val_out = val; return TRUE; } /* We are guaranteed it's a reg: command, so parse that command and record the interesting data. */ static int parsereg(char *cp, const char *fname, unsigned long lineno, struct conf_internal_s *conf, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s regnum; struct token_s tokreg; unsigned long val = 0; int ok = FALSE; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tokreg); cp = get_token(cp, ®num); if (tokreg.tk_len == 0) { printf("dwarfdump.conf error: " "reg: missing register name %s line %lu", fname, lineno); ++errcount; return FALSE; } if (regnum.tk_len == 0) { printf("dwarfdump.conf error: " "reg: missing register number %s line %lu", fname, lineno); ++errcount; return FALSE; } ok = make_a_number(comtab->name, fname, lineno, ®num, &val); if (!ok) { ++errcount; return FALSE; } add_to_reg_table(conf->conf_out, tokreg.tk_data, val, fname, lineno); res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } /* We are guaranteed it's an frame_interface: command. Parse it and record the value data. */ static int parseframe_interface(char *cp, const char *fname, unsigned long lineno, struct conf_internal_s *conf, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; unsigned long val = 0; int ok = FALSE; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tok); if (tok.tk_len == 0) { printf("dwarfdump.conf error: " "%s missing interface number %s line %lu", comtab->name, fname, lineno); ++errcount; return FALSE; } ok = make_a_number(comtab->name, fname, lineno, &tok, &val); if (!ok) { ++errcount; return FALSE; } if (val != 2 && val != 3) { printf("dwarfdump.conf error: " "%s only interface numbers 2 or 3 are allowed, " " not %lu. %s line %lu", comtab->name, val, fname, lineno); ++errcount; return FALSE; } conf->conf_out->cf_interface_number = (int) val; res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } /* We are guaranteed it's a cfa_reg: command. Parse it and record the important data. */ static int parsecfa_reg(char *cp, const char *fname, unsigned long lineno, struct conf_internal_s *conf, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; unsigned long val = 0; int ok = FALSE; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tok); if (tok.tk_len == 0) { printf("dwarfdump.conf error: " "%s missing cfa_reg number %s line %lu", comtab->name, fname, lineno); ++errcount; return FALSE; } ok = make_a_number(comtab->name, fname, lineno, &tok, &val); if (!ok) { ++errcount; return FALSE; } conf->conf_out->cf_cfa_reg = (int) val; res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } /* We are guaranteed it's an initial_reg_value: command, parse it and put the reg value where it will be remembered. */ static int parseinitial_reg_value(char *cp, const char *fname, unsigned long lineno, struct conf_internal_s *conf, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; unsigned long val = 0; int ok = FALSE; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tok); if (tok.tk_len == 0) { printf("dwarfdump.conf error: " "%s missing initial reg value %s line %lu", comtab->name, fname, lineno); ++errcount; return FALSE; } ok = make_a_number(comtab->name, fname, lineno, &tok, &val); if (!ok) { ++errcount; return FALSE; } conf->conf_out->cf_initial_rule_value = (int) val; res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } static int parsesame_val_reg(char *cp, const char *fname, unsigned long lineno, struct conf_internal_s *conf, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; unsigned long val = 0; int ok = FALSE; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tok); if (tok.tk_len == 0) { printf("dwarfdump.conf error: " "%s missing same_reg value %s line %lu", comtab->name, fname, lineno); ++errcount; return FALSE; } ok = make_a_number(comtab->name, fname, lineno, &tok, &val); if (!ok) { ++errcount; return FALSE; } conf->conf_out->cf_same_val = (int) val; res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } static int parseundefined_val_reg(char *cp, const char *fname, unsigned long lineno, struct conf_internal_s *conf, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; unsigned long val = 0; int ok = FALSE; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tok); if (tok.tk_len == 0) { printf("dwarfdump.conf error: " "%s missing undefined_reg value %s line %lu", comtab->name, fname, lineno); ++errcount; return FALSE; } ok = make_a_number(comtab->name, fname, lineno, &tok, &val); if (!ok) { ++errcount; return FALSE; } conf->conf_out->cf_undefined_val = (int) val; res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } /* We are guaranteed it's a table size command, parse it and record the table size. */ static int parsereg_table_size(char *cp, const char *fname, unsigned long lineno, struct conf_internal_s *conf, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; unsigned long val = 0; int ok = FALSE; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tok); if (tok.tk_len == 0) { printf("dwarfdump.conf error: " "%s missing reg table size value %s line %lu", comtab->name, fname, lineno); ++errcount; return FALSE; } ok = make_a_number(comtab->name, fname, lineno, &tok, &val); if (!ok) { ++errcount; return FALSE; } conf->conf_out->cf_table_entry_count = (unsigned long) val; res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } /* We are guaranteed it's a table size command, parse it and record the table size. */ static int parseaddress_size(char *cp, const char *fname, unsigned long lineno, struct conf_internal_s *conf, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; unsigned long val = 0; int ok = FALSE; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tok); if (tok.tk_len == 0) { printf("dwarfdump.conf error: " "%s missing address size value %s line %lu", comtab->name, fname, lineno); ++errcount; return FALSE; } ok = make_a_number(comtab->name, fname, lineno, &tok, &val); if (!ok) { ++errcount; return FALSE; } conf->conf_out->cf_address_size = (unsigned long) val; res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } /* We are guaranteed it's an endabi: command, parse it and check we have the right abi. */ static int parseendabi(char *cp, const char *fname, const char *abiname, unsigned long lineno, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; int res = 0; cp = cp + clen + 1; cp = get_token(cp, &tok); if (strcmp(abiname, tok.tk_data) != 0) { printf("%s error: " "mismatch abi name %s (here) vs. %s (beginabi:) %s line %lu\n", comtab->name, tok.tk_data, abiname, fname, lineno); ++errcount; return FALSE; } res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } static int parseincludeabi(char *cp, const char *fname, unsigned long lineno, char **abiname_out, struct comtable_s *comtab) { size_t clen = comtab->namelen; struct token_s tok; char *name = 0; int res = FALSE; cp = cp + clen + 1; cp = get_token(cp, &tok); name = makename(tok.tk_data); *abiname_out = name; res = ensure_has_no_more_tokens(cp, fname, lineno); return res; } /* Return TRUE if we succeeded and filed in *out. Return FALSE if we failed (and fill in nothing). beginabi: reg: frame_interface: cfa_reg: initial_reg_value: reg_table_size: endabi: We are positioned at the start of a beginabi: line when called. */ static int parse_abi(FILE * stream, const char *fname, const char *abiname, struct conf_internal_s *conf_internal, unsigned long lineno, unsigned int nest_level) { struct dwconf_s *localconf = conf_internal->conf_out; char buf[1000]; int comtype = 0; static int first_time_done = 0; struct comtable_s *comtabp = 0; if( nest_level > MAX_NEST_LEVEL) { ++errcount; printf("dwarfdump.conf: includeabi nest too deep in %s at line %lu\n", fname, lineno); return FALSE; } if (first_time_done == 0) { finish_comtable_setup(); first_time_done = 1; } for (; !feof(stream);) { char *line = 0; /* long loffset = ftell(stream); */ line = fgets(buf, sizeof(buf), stream); if (!line) { ++errcount; printf ("dwarfdump: end of file or error before endabi: in %s, line %lu\n", fname, lineno); return FALSE; } ++lineno; line = skipwhite(line); comtype = which_command(line, &comtabp); switch (comtype) { case LT_ERROR: ++errcount; printf ("dwarfdump: Unknown text in %s is \"%s\" at line %lu\n", fname, line, lineno); break; case LT_COMMENT: break; case LT_BLANK: break; case LT_BEGINABI: if (conf_internal->beginabi_lineno > 0) { ++errcount; printf ("dwarfdump: Encountered beginabi: when not expected. " "%s line %lu previous beginabi line %lu\n", fname, lineno, conf_internal->beginabi_lineno); } conf_internal->beginabi_lineno = lineno; parsebeginabi(line, fname, abiname, lineno, comtabp); break; case LT_REG: parsereg(line, fname, lineno, conf_internal, comtabp); conf_internal->regcount++; break; case LT_FRAME_INTERFACE: if (conf_internal->frame_interface_lineno > 0) { ++errcount; printf ("dwarfdump: Encountered duplicate frame_interface: " "%s line %lu previous frame_interface: line %lu\n", fname, lineno, conf_internal->frame_interface_lineno); } conf_internal->frame_interface_lineno = lineno; parseframe_interface(line, fname, lineno, conf_internal, comtabp); break; case LT_CFA_REG: if (conf_internal->cfa_reg_lineno > 0) { printf("dwarfdump: Encountered duplicate cfa_reg: " "%s line %lu previous cfa_reg line %lu\n", fname, lineno, conf_internal->cfa_reg_lineno); ++errcount; } conf_internal->cfa_reg_lineno = lineno; parsecfa_reg(line, fname, lineno, conf_internal, comtabp); break; case LT_INITIAL_REG_VALUE: if (conf_internal->initial_reg_value_lineno > 0) { printf ("dwarfdump: Encountered duplicate initial_reg_value: " "%s line %lu previous initial_reg_value: line %lu\n", fname, lineno, conf_internal->initial_reg_value_lineno); ++errcount; } conf_internal->initial_reg_value_lineno = lineno; parseinitial_reg_value(line, fname, lineno, conf_internal, comtabp); break; case LT_SAME_VAL_REG: if (conf_internal->same_val_reg_lineno > 0) { ++errcount; printf ("dwarfdump: Encountered duplicate same_val_reg: " "%s line %lu previous initial_reg_value: line %lu\n", fname, lineno, conf_internal->initial_reg_value_lineno); } conf_internal->same_val_reg_lineno = lineno; parsesame_val_reg(line, fname, lineno, conf_internal, comtabp); break; case LT_UNDEFINED_VAL_REG: if (conf_internal->undefined_val_reg_lineno > 0) { ++errcount; printf ("dwarfdump: Encountered duplicate undefined_val_reg: " "%s line %lu previous initial_reg_value: line %lu\n", fname, lineno, conf_internal->initial_reg_value_lineno); } conf_internal->undefined_val_reg_lineno = lineno; parseundefined_val_reg(line, fname, lineno, conf_internal, comtabp); break; case LT_REG_TABLE_SIZE: if (conf_internal->reg_table_size_lineno > 0) { printf("dwarfdump: duplicate reg_table_size: " "%s line %lu previous reg_table_size: line %lu\n", fname, lineno, conf_internal->reg_table_size_lineno); ++errcount; } conf_internal->reg_table_size_lineno = lineno; parsereg_table_size(line, fname, lineno, conf_internal, comtabp); break; case LT_ENDABI: parseendabi(line, fname, abiname, lineno, comtabp); if (conf_internal->regcount > localconf->cf_table_entry_count) { printf("dwarfdump: more registers named than " " in %s ( %lu named vs %s %lu) %s line %lu\n", abiname, (unsigned long) conf_internal->regcount, name_reg_table_size, (unsigned long) localconf->cf_table_entry_count, fname, (unsigned long) lineno); ++errcount; } return TRUE; case LT_ADDRESS_SIZE: if (conf_internal->address_size_lineno > 0) { printf("dwarfdump: duplicate address_size: " "%s line %lu previous address_size: line %lu\n", fname, lineno, conf_internal->address_size_lineno); ++errcount; } conf_internal->address_size_lineno = lineno; parseaddress_size(line, fname, lineno, conf_internal, comtabp); break; case LT_INCLUDEABI: { char *abiname_inner = 0; unsigned long abilno = conf_internal->beginabi_lineno; int ires = 0; ires = parseincludeabi(line,fname,lineno, &abiname_inner,comtabp); if(ires == FALSE) { return FALSE; } /* For the nested abi read, the abi line number must be set as if not-yet-read, and then restored. */ conf_internal->beginabi_lineno = 0; find_conf_file_and_read_config_inner( conf_internal->conf_name_used, abiname_inner, conf_internal,nest_level+1); conf_internal->beginabi_lineno = abilno; } break; default: printf ("dwarfdump internal error, impossible line type %d %s %lu \n", (int) comtype, fname, lineno); exit(1); } } ++errcount; printf("End of file, no endabi: found. %s, line %lu\n", fname, lineno); return FALSE; } /* MIPS/IRIX frame register names. For alternate name sets, use dwarfdump.conf or revise dwarf.h and libdwarf.h and this table. */ static char *regnames[] = { "cfa", "r1/at", "r2/v0", "r3/v1", "r4/a0", "r5/a1", "r6/a2", "r7/a3", "r8/t0", "r9/t1", "r10/t2", "r11/t3", "r12/t4", "r13/t5", "r14/t6", "r15/t7", "r16/s0", "r17/s1", "r18/s2", "r19/s3", "r20/s4", "r21/s5", "r22/s6", "r23/s7", "r24/t8", "r25/t9", "r26/k0", "r27/k1", "r28/gp", "r29/sp", "r30/s8", "r31", "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", "ra", "slk", }; /* Naming a few registers makes printing these just a little bit faster. */ static char *genericregnames[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20" }; /* This is a simple generic set of registers. The table entry count is pretty arbitrary. */ void init_conf_file_data(struct dwconf_s *config_file_data) { unsigned generic_table_count; config_file_data->cf_abi_name = ""; config_file_data->cf_config_file_path = ""; config_file_data->cf_interface_number = 3; config_file_data->cf_table_entry_count = 100; config_file_data->cf_initial_rule_value = DW_FRAME_UNDEFINED_VAL; config_file_data->cf_cfa_reg = DW_FRAME_CFA_COL3; config_file_data->cf_address_size = 0; config_file_data->cf_same_val = DW_FRAME_SAME_VAL; config_file_data->cf_undefined_val = DW_FRAME_UNDEFINED_VAL; config_file_data->cf_regs = genericregnames; generic_table_count = sizeof(genericregnames) / sizeof(genericregnames[0]); config_file_data->cf_named_regs_table_size = generic_table_count; config_file_data->cf_regs_malloced = 0; } /* These defaults match MIPS/IRIX ABI defaults, but this function is not actually used. For a 'generic' ABI, see -R or init_conf_file_data(). To really get the old MIPS, use '-x abi=mips'. For other ABIs, see -x abi= to configure dwarfdump (and libdwarf) frame data reporting at runtime. */ void init_mips_conf_file_data(struct dwconf_s *config_file_data) { unsigned long base_table_count = sizeof(regnames) / sizeof(regnames[0]); memset(config_file_data, 0, sizeof(*config_file_data)); /* Interface 2 is deprecated, but for testing purposes is acceptable. */ config_file_data->cf_interface_number = 2; config_file_data->cf_table_entry_count = DW_REG_TABLE_SIZE; config_file_data->cf_initial_rule_value = DW_FRAME_REG_INITIAL_VALUE; config_file_data->cf_cfa_reg = DW_FRAME_CFA_COL; config_file_data->cf_address_size = 0; config_file_data->cf_same_val = DW_FRAME_SAME_VAL; config_file_data->cf_undefined_val = DW_FRAME_UNDEFINED_VAL; config_file_data->cf_regs = regnames; config_file_data->cf_named_regs_table_size = base_table_count; config_file_data->cf_regs_malloced = 0; if (config_file_data->cf_table_entry_count != base_table_count) { printf("dwarfdump: improper base table initization, " "header files wrong: " "DW_REG_TABLE_SIZE %u != string table size %lu\n", (unsigned) DW_REG_TABLE_SIZE, (unsigned long) base_table_count); exit(1); } return; } /* A 'generic' ABI. For up to 1200 registers. Perhaps cf_initial_rule_value should be d UNDEFINED VALUE (1034) instead, but for the purposes of getting the dwarfdump output correct either will work. */ void init_generic_config_1200_regs(struct dwconf_s *config_file_data) { unsigned long generic_table_count = sizeof(genericregnames) / sizeof(genericregnames[0]); config_file_data->cf_interface_number = 3; config_file_data->cf_table_entry_count = 1200; /* There is no defined name for cf_initial_rule_value, cf_same_val, or cf_undefined_val in libdwarf.h, these must just be high enough to be higher than any real register number. DW_FRAME_CFA_COL3 must also be higher than any real register number. */ config_file_data->cf_initial_rule_value = 1235; /* SAME VALUE */ config_file_data->cf_cfa_reg = DW_FRAME_CFA_COL3; config_file_data->cf_address_size = 0; config_file_data->cf_same_val = 1235; config_file_data->cf_undefined_val = 1234; config_file_data->cf_regs = genericregnames; config_file_data->cf_named_regs_table_size = generic_table_count; config_file_data->cf_regs_malloced = 0; } /* Print the 'right' string for the register we are given. Deal sensibly with the special regs as well as numbers we know and those we have not been told about. */ void print_reg_from_config_data(Dwarf_Signed reg, struct dwconf_s *config_data) { char *name = 0; if (reg == config_data->cf_cfa_reg) { fputs("cfa",stdout); return; } if (reg == config_data->cf_undefined_val) { fputs("u",stdout); return; } if (reg == config_data->cf_same_val) { fputs("s",stdout); return; } if (config_data->cf_regs == 0 || reg < 0 || reg >= config_data->cf_named_regs_table_size) { printf("r%" DW_PR_DSd "", (Dwarf_Signed) reg); return; } name = config_data->cf_regs[reg]; if (!name) { /* Can happen, the reg names table can be sparse. */ printf("r%" DW_PR_DSd "", (Dwarf_Signed) reg); return; } fputs(name,stdout); return; }