|
1 /* |
|
2 Copyright (C) 2006 Silicon Graphics, Inc. All Rights Reserved. |
|
3 |
|
4 This program is free software; you can redistribute it and/or modify it |
|
5 under the terms of version 2 of the GNU General Public License as |
|
6 published by the Free Software Foundation. |
|
7 |
|
8 This program is distributed in the hope that it would be useful, but |
|
9 WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
11 |
|
12 Further, this software is distributed without any warranty that it is |
|
13 free of the rightful claim of any third person regarding infringement |
|
14 or the like. Any license provided herein, whether implied or |
|
15 otherwise, applies only to this software file. Patent licenses, if |
|
16 any, provided herein do not apply to combinations of this program with |
|
17 other software, or any other product whatsoever. |
|
18 |
|
19 You should have received a copy of the GNU General Public License along |
|
20 with this program; if not, write the Free Software Foundation, Inc., 51 |
|
21 Franklin Street - Fifth Floor, Boston MA 02110-1301, USA. |
|
22 |
|
23 Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, |
|
24 Mountain View, CA 94043, or: |
|
25 |
|
26 http://www.sgi.com |
|
27 |
|
28 For further information regarding this notice, see: |
|
29 |
|
30 http://oss.sgi.com/projects/GenInfo/NoticeExplan |
|
31 |
|
32 |
|
33 $Header: /plroot/cmplrs.src/v7.4.5m/.RCS/PL/dwarfdump/RCS/dwconf.c,v 1.4 2006/04/18 18:05:57 davea Exp $ */ |
|
34 |
|
35 |
|
36 #include "globals.h" |
|
37 #include <ctype.h> |
|
38 #include "dwconf.h" |
|
39 #include "makename.h" |
|
40 |
|
41 |
|
42 struct token_s { |
|
43 unsigned tk_len; |
|
44 char *tk_data; |
|
45 }; |
|
46 enum linetype_e { |
|
47 LT_ERROR, |
|
48 LT_COMMENT, |
|
49 LT_BLANK, |
|
50 LT_BEGINABI, |
|
51 LT_REG, |
|
52 LT_FRAME_INTERFACE, |
|
53 LT_CFA_REG, |
|
54 LT_INITIAL_REG_VALUE, |
|
55 LT_REG_TABLE_SIZE, |
|
56 LT_ENDABI |
|
57 }; |
|
58 |
|
59 struct comtable_s { |
|
60 enum linetype_e type; |
|
61 char *name; |
|
62 size_t namelen; |
|
63 }; |
|
64 |
|
65 static int errcount = 0; /* Count errors found in this scan of |
|
66 the configuration file. */ |
|
67 |
|
68 static char name_begin_abi[] = "beginabi:"; |
|
69 static char name_reg[] = "reg:"; |
|
70 static char name_frame_interface[] = "frame_interface:"; |
|
71 static char name_cfa_reg[] = "cfa_reg:"; |
|
72 static char name_initial_reg_value[] = "initial_reg_value:"; |
|
73 static char name_reg_table_size[] = "reg_table_size:"; |
|
74 static char name_endabi[] = "endabi:"; |
|
75 |
|
76 static struct comtable_s comtable[] = { |
|
77 {LT_BEGINABI, name_begin_abi}, |
|
78 {LT_REG, name_reg}, |
|
79 {LT_FRAME_INTERFACE, name_frame_interface}, |
|
80 {LT_CFA_REG, name_cfa_reg}, |
|
81 {LT_INITIAL_REG_VALUE, name_initial_reg_value}, |
|
82 {LT_REG_TABLE_SIZE, name_reg_table_size}, |
|
83 {LT_ENDABI, name_endabi}, |
|
84 }; |
|
85 static int size_of_comtable = sizeof(comtable) / sizeof(comtable[0]); |
|
86 |
|
87 |
|
88 static FILE *find_a_file(char *named_file, char **defaults, |
|
89 string * name_used); |
|
90 static int find_abi_start(FILE * stream, char *abi_name, long *offset, |
|
91 unsigned long *lineno_out); |
|
92 static int parse_abi(FILE * stream, char *fname, char *abiname, |
|
93 struct dwconf_s *out, unsigned long lineno); |
|
94 static char *get_token(char *cp, struct token_s *outtok); |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 /* This finds a dwarfdump.conf file and |
|
101 then parses it. It updates |
|
102 conf_out as appropriate. |
|
103 |
|
104 This finds the first file (looking in a set of places) |
|
105 with that name. It then looks for the right ABI entry. |
|
106 If the first file it finds does not have that ABI entry it |
|
107 gives up. |
|
108 |
|
109 It would also be reasonable to search every 'dwarfdump.conf' |
|
110 it finds for the abi. But we stop at the first dwarfdump.conf |
|
111 we find. |
|
112 */ |
|
113 int |
|
114 find_conf_file_and_read_config(char *named_file, |
|
115 char *named_abi, char **defaults, |
|
116 struct dwconf_s *conf_out) |
|
117 { |
|
118 |
|
119 FILE *conf_stream = 0; |
|
120 char *name_used = 0; |
|
121 long offset = 0; |
|
122 int res = FALSE; |
|
123 unsigned long lineno = 0; |
|
124 |
|
125 errcount = 0; |
|
126 |
|
127 conf_stream = find_a_file(named_file, defaults, &name_used); |
|
128 if (!conf_stream) { |
|
129 ++errcount; |
|
130 printf("dwarfdump found no file %s!\n", |
|
131 named_file ? named_file : "readable for configuration. " |
|
132 "(add options -v -v to see what file names tried)\n"); |
|
133 return errcount; |
|
134 } |
|
135 if (verbose > 1) { |
|
136 printf("dwarfdump using configuration file %s\n", name_used); |
|
137 } |
|
138 |
|
139 res = find_abi_start(conf_stream, named_abi, &offset, &lineno); |
|
140 if (errcount > 0) { |
|
141 ++errcount; |
|
142 printf("dwarfdump found no ABI %s in file %s.\n", |
|
143 named_abi, name_used); |
|
144 return errcount; |
|
145 } |
|
146 res = fseek(conf_stream, offset, SEEK_SET); |
|
147 if (res != 0) { |
|
148 ++errcount; |
|
149 printf("dwarfdump seek to %ld offset in %s failed!\n", |
|
150 offset, name_used); |
|
151 return errcount; |
|
152 } |
|
153 parse_abi(conf_stream, name_used, named_abi, conf_out, lineno); |
|
154 fclose(conf_stream); |
|
155 return errcount; |
|
156 } |
|
157 |
|
158 /* Given path strings, attempt to make a canonical file name: |
|
159 that is, avoid superfluous '/' so that no |
|
160 '//' (or worse) is created in the output. The path components |
|
161 are to be separated so at least one '/' |
|
162 is to appear between the two 'input strings' when |
|
163 creating the output. |
|
164 */ |
|
165 static char * |
|
166 canonical_append(char *target, unsigned int target_size, |
|
167 char *first_string, char *second_string) |
|
168 { |
|
169 size_t firstlen = strlen(first_string); |
|
170 |
|
171 /* +1 +1: Leave room for added "/" and final NUL, though that is |
|
172 overkill, as we drop a NUL byte too. */ |
|
173 if ((firstlen + strlen(second_string) + 1 + 1) >= target_size) { |
|
174 /* Not enough space. */ |
|
175 return NULL; |
|
176 } |
|
177 for (; *second_string == '/'; ++second_string) { |
|
178 } |
|
179 for (; firstlen > 0 && first_string[firstlen - 1] == '/'; |
|
180 --firstlen) { |
|
181 } |
|
182 target[0] = 0; |
|
183 if (firstlen > 0) { |
|
184 strncpy(target, first_string, firstlen); |
|
185 target[firstlen + 1] = 0; |
|
186 } |
|
187 target[firstlen] = '/'; |
|
188 firstlen++; |
|
189 target[firstlen] = 0; |
|
190 strcat(target, second_string); |
|
191 return target; |
|
192 } |
|
193 |
|
194 #ifdef BUILD_FOR_TEST |
|
195 #define CANBUF 25 |
|
196 struct canap_s { |
|
197 char *res_exp; |
|
198 char *first; |
|
199 char *second; |
|
200 } canap[] = { |
|
201 { |
|
202 "ab/c", "ab", "c"}, { |
|
203 "ab/c", "ab/", "c"}, { |
|
204 "ab/c", "ab", "/c"}, { |
|
205 "ab/c", "ab////", "/////c"}, { |
|
206 "ab/", "ab", ""}, { |
|
207 "ab/", "ab////", ""}, { |
|
208 "ab/", "ab////", ""}, { |
|
209 "/a", "", "a"}, { |
|
210 0, "/abcdefgbijkl", "pqrstuvwxyzabcd"}, { |
|
211 0, 0, 0} |
|
212 }; |
|
213 static void |
|
214 test_canonical_append(void) |
|
215 { |
|
216 /* Make buf big, this is test code, so be safe. */ |
|
217 char lbuf[1000]; |
|
218 unsigned i; |
|
219 unsigned failcount = 0; |
|
220 |
|
221 printf("Entry test_canonical_append\n"); |
|
222 for (i = 0;; ++i) { |
|
223 char *res = 0; |
|
224 |
|
225 if (canap[i].first == 0 && canap[i].second == 0) |
|
226 break; |
|
227 |
|
228 res = canonical_append(lbuf, CANBUF, canap[i].first, |
|
229 canap[i].second); |
|
230 if (res == 0) { |
|
231 if (canap[i].res_exp == 0) { |
|
232 /* GOOD */ |
|
233 printf("PASS %u\n", i); |
|
234 } else { |
|
235 ++failcount; |
|
236 printf("FAIL: entry %u wrong, expected %s, got NULL\n", |
|
237 i, canap[i].res_exp); |
|
238 } |
|
239 } else { |
|
240 if (canap[i].res_exp == 0) { |
|
241 ++failcount; |
|
242 printf("FAIL: entry %u wrong, got %s expected NULL\n", |
|
243 i, res); |
|
244 } else { |
|
245 if (strcmp(res, canap[i].res_exp) == 0) { |
|
246 printf("PASS %u\n", i); |
|
247 /* GOOD */ |
|
248 } else { |
|
249 ++failcount; |
|
250 printf("FAIL: entry %u wrong, expected %s got %s\n", |
|
251 i, canap[i].res_exp, res); |
|
252 } |
|
253 } |
|
254 } |
|
255 } |
|
256 printf("FAIL count %u\n", failcount); |
|
257 |
|
258 } |
|
259 #endif /* BUILD_FOR_TEST */ |
|
260 /* Try to find a file as named and open for read. |
|
261 We treat each name as a full name, we are not |
|
262 combining separate name and path components. |
|
263 This is an arbitrary choice... |
|
264 |
|
265 The defaults are listed in dwarfdump.c in the array |
|
266 config_file_defaults[]. |
|
267 */ |
|
268 static FILE * |
|
269 find_a_file(char *named_file, char **defaults, string * name_used) |
|
270 { |
|
271 FILE *fin = 0; |
|
272 char *lname = named_file; |
|
273 const char *type = "rw"; |
|
274 int i = 0; |
|
275 |
|
276 #ifdef BUILD_FOR_TEST |
|
277 test_canonical_append(); |
|
278 #endif /* BUILD_FOR_TEST */ |
|
279 |
|
280 if (lname) { |
|
281 /* Name given, just assume it is fully correct, try no other. */ |
|
282 if (verbose > 1) { |
|
283 printf("dwarfdump looking for configuration as %s\n", |
|
284 lname); |
|
285 } |
|
286 fin = fopen(lname, type); |
|
287 if (fin) { |
|
288 *name_used = lname; |
|
289 return fin; |
|
290 } |
|
291 return 0; |
|
292 } |
|
293 /* No name given, find a default, if we can. */ |
|
294 for (i = 0; defaults[i]; ++i) { |
|
295 lname = defaults[i]; |
|
296 if (strncmp(lname, "HOME/", 5) == 0) { |
|
297 /* arbitrary size */ |
|
298 char buf[2000]; |
|
299 char *homedir = getenv("HOME"); |
|
300 |
|
301 if (homedir) { |
|
302 char *cp = canonical_append(buf, sizeof(buf), |
|
303 homedir, lname + 5); |
|
304 |
|
305 if (!cp) { |
|
306 /* OOps, ignore this one. */ |
|
307 continue; |
|
308 } |
|
309 lname = makename(buf); |
|
310 } |
|
311 } |
|
312 if (verbose > 1) { |
|
313 printf("dwarfdump looking for configuration as %s\n", |
|
314 lname); |
|
315 } |
|
316 fin = fopen(lname, type); |
|
317 if (fin) { |
|
318 *name_used = lname; |
|
319 return fin; |
|
320 } |
|
321 } |
|
322 return 0; |
|
323 } |
|
324 |
|
325 /* Start at a token begin, see how long it is, |
|
326 return length. */ |
|
327 unsigned |
|
328 find_token_len(char *cp) |
|
329 { |
|
330 unsigned len = 0; |
|
331 |
|
332 for (; *cp; ++cp) { |
|
333 if (isspace(*cp)) { |
|
334 return len; |
|
335 } |
|
336 if (*cp == '#') { |
|
337 return len; /* begins comment */ |
|
338 } |
|
339 ++len; |
|
340 } |
|
341 return len; |
|
342 } |
|
343 |
|
344 /* |
|
345 Skip past all whitespace: the only code that even knows |
|
346 what whitespace is. |
|
347 */ |
|
348 static char * |
|
349 skipwhite(char *cp) |
|
350 { |
|
351 for (; *cp; ++cp) { |
|
352 if (!isspace(*cp)) { |
|
353 return cp; |
|
354 } |
|
355 } |
|
356 return cp; |
|
357 } |
|
358 |
|
359 /* Return TRUE if ok. FALSE if find more tokens. |
|
360 Emit error message if error. |
|
361 */ |
|
362 static int |
|
363 ensure_has_no_more_tokens(char *cp, char *fname, unsigned long lineno) |
|
364 { |
|
365 struct token_s tok; |
|
366 |
|
367 cp = get_token(cp, &tok); |
|
368 if (tok.tk_len > 0) { |
|
369 printf("dwarfdump.conf error: " |
|
370 "extra characters after command operands, found " |
|
371 "\"%s\" in %s line %lu\n", tok.tk_data, fname, lineno); |
|
372 ++errcount; |
|
373 return FALSE; |
|
374 } |
|
375 return TRUE; |
|
376 } |
|
377 |
|
378 |
|
379 /* |
|
380 There may be many beginabi: lines in a dwarfdump.conf file, |
|
381 find the one we want and return it's file offset. |
|
382 */ |
|
383 static int |
|
384 find_abi_start(FILE * stream, |
|
385 char *abi_name, long *offset, unsigned long *lineno_out) |
|
386 { |
|
387 char buf[100]; |
|
388 unsigned long lineno = 0; |
|
389 |
|
390 for (; !feof(stream);) { |
|
391 |
|
392 struct token_s tok; |
|
393 char *line = 0; |
|
394 long loffset = ftell(stream); |
|
395 |
|
396 line = fgets(buf, sizeof(buf), stream); |
|
397 ++lineno; |
|
398 if (!line) { |
|
399 ++errcount; |
|
400 return FALSE; |
|
401 } |
|
402 |
|
403 line = get_token(buf, &tok); |
|
404 |
|
405 if (strcmp(tok.tk_data, name_begin_abi) != 0) { |
|
406 continue; |
|
407 } |
|
408 get_token(line, &tok); |
|
409 if (strcmp(tok.tk_data, abi_name) != 0) { |
|
410 continue; |
|
411 } |
|
412 |
|
413 *offset = loffset; |
|
414 *lineno_out = lineno; |
|
415 return TRUE; |
|
416 } |
|
417 |
|
418 ++errcount; |
|
419 return FALSE; |
|
420 } |
|
421 |
|
422 static char *tempstr = 0; |
|
423 static unsigned tempstr_len = 0; |
|
424 |
|
425 /* |
|
426 Use a global buffer (tempstr) to turn a non-delimited |
|
427 input char array into a NUL-terminated C string |
|
428 (with the help of makename() to get a permanent |
|
429 address for the result ing string). |
|
430 */ |
|
431 static char * |
|
432 build_string(unsigned tlen, char *cp) |
|
433 { |
|
434 if (tlen >= tempstr_len) { |
|
435 free(tempstr); |
|
436 tempstr = malloc(tlen + 100); |
|
437 } |
|
438 strncpy(tempstr, cp, tlen); |
|
439 tempstr[tlen] = 0; |
|
440 return makename(tempstr); |
|
441 } |
|
442 |
|
443 /* |
|
444 The tokenizer for our simple parser. |
|
445 */ |
|
446 static char * |
|
447 get_token(char *cp, struct token_s *outtok) |
|
448 { |
|
449 char *lcp = skipwhite(cp); |
|
450 unsigned tlen = find_token_len(lcp); |
|
451 |
|
452 outtok->tk_len = tlen; |
|
453 if (tlen > 0) { |
|
454 outtok->tk_data = build_string(tlen, lcp); |
|
455 } else { |
|
456 outtok->tk_data = ""; |
|
457 } |
|
458 return lcp + tlen; |
|
459 |
|
460 } |
|
461 |
|
462 /* |
|
463 We can't get all the field set up statically very easily, |
|
464 so we get the command string length set here. |
|
465 */ |
|
466 static void |
|
467 finish_comtable_setup(void) |
|
468 { |
|
469 unsigned i; |
|
470 |
|
471 for (i = 0; i < size_of_comtable; ++i) { |
|
472 comtable[i].namelen = strlen(comtable[i].name); |
|
473 } |
|
474 } |
|
475 |
|
476 /* |
|
477 Given a line of the table, determine if it is a command |
|
478 or not, and if a command, which one is it. |
|
479 Return LT_ERROR if it's not recognized. |
|
480 */ |
|
481 static enum linetype_e |
|
482 which_command(char *cp, struct comtable_s **tableentry) |
|
483 { |
|
484 int i; |
|
485 struct token_s tok; |
|
486 |
|
487 if (*cp == '#') |
|
488 return LT_COMMENT; |
|
489 if (!*cp) |
|
490 return LT_BLANK; |
|
491 |
|
492 get_token(cp, &tok); |
|
493 |
|
494 for (i = 0; i < size_of_comtable; ++i) { |
|
495 if (tok.tk_len == comtable[i].namelen && |
|
496 strcmp(comtable[i].name, tok.tk_data) == 0) { |
|
497 |
|
498 *tableentry = &comtable[i]; |
|
499 return comtable[i].type; |
|
500 } |
|
501 } |
|
502 |
|
503 return LT_ERROR; |
|
504 } |
|
505 |
|
506 /* We are promised it's an abiname: command |
|
507 find the name on the line. |
|
508 */ |
|
509 static int |
|
510 parsebeginabi(char *cp, char *fname, char *abiname, |
|
511 unsigned long lineno, struct comtable_s *comtab) |
|
512 { |
|
513 size_t clen = comtab->namelen; |
|
514 size_t abinamelen = strlen(abiname); |
|
515 struct token_s tok; |
|
516 |
|
517 |
|
518 cp = cp + clen + 1; |
|
519 cp = skipwhite(cp); |
|
520 get_token(cp, &tok); |
|
521 if (tok.tk_len != abinamelen || |
|
522 strncmp(cp, abiname, abinamelen) != 0) { |
|
523 printf("dwarfdump internal error: " |
|
524 "mismatch %s with %s %s line %lu\n", |
|
525 cp, tok.tk_data, fname, lineno); |
|
526 ++errcount; |
|
527 return FALSE; |
|
528 } |
|
529 { |
|
530 int res = |
|
531 ensure_has_no_more_tokens(cp + tok.tk_len, fname, lineno); |
|
532 return res; |
|
533 } |
|
534 } |
|
535 |
|
536 /* This expands register names as required, but does not |
|
537 ensure no names duplicated. |
|
538 */ |
|
539 #define CONF_TABLE_OVERSIZE 100 |
|
540 static void |
|
541 add_to_reg_table(struct dwconf_s *conf, |
|
542 char *rname, unsigned long rval, char *fname, |
|
543 unsigned long lineno) |
|
544 { |
|
545 if (conf->cf_regs_malloced == 0) { |
|
546 conf->cf_regs = 0; |
|
547 conf->cf_named_regs_table_size = 0; |
|
548 } |
|
549 if (rval >= conf->cf_named_regs_table_size) { |
|
550 char **newregs = 0; |
|
551 unsigned long newtablen = rval + CONF_TABLE_OVERSIZE; |
|
552 unsigned long newtabsize = newtablen * sizeof(char *); |
|
553 unsigned long oldtabsize = |
|
554 conf->cf_named_regs_table_size * sizeof(char *); |
|
555 newregs = realloc(conf->cf_regs, newtabsize); |
|
556 if (!newregs) { |
|
557 printf("dwarfdump: unable to malloc table %lu bytes. " |
|
558 " %s line %lu\n", newtabsize, fname, lineno); |
|
559 exit(1); |
|
560 } |
|
561 /* Zero out the new entries. */ |
|
562 memset((char *) newregs + (oldtabsize), 0, |
|
563 (newtabsize - oldtabsize)); |
|
564 conf->cf_named_regs_table_size = (unsigned long) newtablen; |
|
565 conf->cf_regs = newregs; |
|
566 conf->cf_regs_malloced = 1; |
|
567 } |
|
568 conf->cf_regs[rval] = rname; |
|
569 return; |
|
570 } |
|
571 |
|
572 /* Our input is supposed to be a number. |
|
573 Determine the value (and return it) or generate an error message. |
|
574 */ |
|
575 static int |
|
576 make_a_number(char *cmd, char *filename, unsigned long |
|
577 lineno, struct token_s *tok, unsigned long *val_out) |
|
578 { |
|
579 char *endnum = 0; |
|
580 unsigned long val = 0; |
|
581 |
|
582 val = strtoul(tok->tk_data, &endnum, 0); |
|
583 if (val == 0 && endnum == (tok->tk_data)) { |
|
584 printf("dwarfdump.conf error: " |
|
585 "%s missing register number (\"%s\" not valid) %s line %lu", |
|
586 cmd, tok->tk_data, filename, lineno); |
|
587 ++errcount; |
|
588 return FALSE; |
|
589 } |
|
590 if (endnum != (tok->tk_data + tok->tk_len)) { |
|
591 printf("dwarfdump.conf error: " |
|
592 "%s Missing register number (\"%s\" not valid) %s line %lu", |
|
593 cmd, tok->tk_data, filename, lineno); |
|
594 ++errcount; |
|
595 return FALSE; |
|
596 } |
|
597 *val_out = val; |
|
598 return TRUE; |
|
599 |
|
600 |
|
601 |
|
602 } |
|
603 |
|
604 /* We are guaranteed it's a reg: command, so parse that command |
|
605 and record the interesting data. |
|
606 */ |
|
607 static int |
|
608 parsereg(char *cp, char *fname, unsigned long lineno, |
|
609 struct dwconf_s *conf, struct comtable_s *comtab) |
|
610 { |
|
611 size_t clen = comtab->namelen; |
|
612 struct token_s regnum; |
|
613 struct token_s tokreg; |
|
614 unsigned long val = 0; |
|
615 int ok = FALSE; |
|
616 |
|
617 cp = cp + clen + 1; |
|
618 cp = get_token(cp, &tokreg); |
|
619 cp = get_token(cp, ®num); |
|
620 if (tokreg.tk_len == 0) { |
|
621 printf("dwarfdump.conf error: " |
|
622 "reg: missing register name %s line %lu", |
|
623 fname, lineno); |
|
624 ++errcount; |
|
625 return FALSE; |
|
626 |
|
627 } |
|
628 if (regnum.tk_len == 0) { |
|
629 printf("dwarfdump.conf error: " |
|
630 "reg: missing register number %s line %lu", |
|
631 fname, lineno); |
|
632 ++errcount; |
|
633 return FALSE; |
|
634 } |
|
635 |
|
636 ok = make_a_number(comtab->name, fname, lineno, ®num, &val); |
|
637 |
|
638 if (!ok) { |
|
639 ++errcount; |
|
640 return FALSE; |
|
641 } |
|
642 |
|
643 add_to_reg_table(conf, tokreg.tk_data, val, fname, lineno); |
|
644 |
|
645 { |
|
646 int res = ensure_has_no_more_tokens(cp, fname, lineno); |
|
647 |
|
648 return res; |
|
649 } |
|
650 } |
|
651 |
|
652 /* |
|
653 We are guaranteed it's an frame_interface: command. |
|
654 Parse it and record the value data. |
|
655 */ |
|
656 static int |
|
657 parseframe_interface(char *cp, char *fname, unsigned long lineno, |
|
658 struct dwconf_s *conf, struct comtable_s *comtab) |
|
659 { |
|
660 size_t clen = comtab->namelen; |
|
661 struct token_s tok; |
|
662 unsigned long val = 0; |
|
663 int ok = FALSE; |
|
664 |
|
665 cp = cp + clen + 1; |
|
666 cp = get_token(cp, &tok); |
|
667 if (tok.tk_len == 0) { |
|
668 printf("dwarfdump.conf error: " |
|
669 "%s missing interface number %s line %lu", |
|
670 comtab->name, fname, lineno); |
|
671 ++errcount; |
|
672 return FALSE; |
|
673 } |
|
674 |
|
675 ok = make_a_number(comtab->name, fname, lineno, &tok, &val); |
|
676 |
|
677 if (!ok) { |
|
678 ++errcount; |
|
679 return FALSE; |
|
680 } |
|
681 if (val != 2 && val != 3) { |
|
682 printf("dwarfdump.conf error: " |
|
683 "%s only interface numbers 2 or 3 are allowed, " |
|
684 " not %lu. %s line %lu", |
|
685 comtab->name, val, fname, lineno); |
|
686 ++errcount; |
|
687 return FALSE; |
|
688 } |
|
689 |
|
690 conf->cf_interface_number = (int) val; |
|
691 { |
|
692 int res = ensure_has_no_more_tokens(cp, fname, lineno); |
|
693 |
|
694 return res; |
|
695 } |
|
696 } |
|
697 |
|
698 /* |
|
699 We are guaranteed it's a cfa_reg: command. Parse it |
|
700 and record the important data. |
|
701 */ |
|
702 static int |
|
703 parsecfa_reg(char *cp, char *fname, unsigned long lineno, |
|
704 struct dwconf_s *conf, struct comtable_s *comtab) |
|
705 { |
|
706 size_t clen = comtab->namelen; |
|
707 struct token_s tok; |
|
708 unsigned long val = 0; |
|
709 int ok = FALSE; |
|
710 |
|
711 cp = cp + clen + 1; |
|
712 cp = get_token(cp, &tok); |
|
713 if (tok.tk_len == 0) { |
|
714 printf("dwarfdump.conf error: " |
|
715 "%s missing cfa_reg number %s line %lu", |
|
716 comtab->name, fname, lineno); |
|
717 ++errcount; |
|
718 return FALSE; |
|
719 } |
|
720 |
|
721 ok = make_a_number(comtab->name, fname, lineno, &tok, &val); |
|
722 |
|
723 if (!ok) { |
|
724 ++errcount; |
|
725 return FALSE; |
|
726 } |
|
727 conf->cf_cfa_reg = (int) val; |
|
728 { |
|
729 int res = ensure_has_no_more_tokens(cp, fname, lineno); |
|
730 |
|
731 return res; |
|
732 } |
|
733 } |
|
734 |
|
735 |
|
736 /* We are guaranteed it's an initial_reg_value: command, |
|
737 parse it and put the reg value where it will be remembered. |
|
738 */ |
|
739 static int |
|
740 parseinitial_reg_value(char *cp, char *fname, |
|
741 unsigned long lineno, |
|
742 struct dwconf_s *conf, struct comtable_s *comtab) |
|
743 { |
|
744 size_t clen = comtab->namelen; |
|
745 struct token_s tok; |
|
746 unsigned long val = 0; |
|
747 int ok = FALSE; |
|
748 |
|
749 cp = cp + clen + 1; |
|
750 cp = get_token(cp, &tok); |
|
751 if (tok.tk_len == 0) { |
|
752 printf("dwarfdump.conf error: " |
|
753 "%s missing initial reg value %s line %lu", |
|
754 comtab->name, fname, lineno); |
|
755 ++errcount; |
|
756 return FALSE; |
|
757 } |
|
758 |
|
759 ok = make_a_number(comtab->name, fname, lineno, &tok, &val); |
|
760 |
|
761 if (!ok) { |
|
762 |
|
763 ++errcount; |
|
764 return FALSE; |
|
765 } |
|
766 conf->cf_initial_rule_value = (int) val; |
|
767 { |
|
768 int res = ensure_has_no_more_tokens(cp, fname, lineno); |
|
769 |
|
770 return res; |
|
771 } |
|
772 } |
|
773 |
|
774 |
|
775 /* We are guaranteed it's a table size command, parse it |
|
776 and record the table size. |
|
777 */ |
|
778 static int |
|
779 parsereg_table_size(char *cp, char *fname, unsigned long lineno, |
|
780 struct dwconf_s *conf, struct comtable_s *comtab) |
|
781 { |
|
782 size_t clen = comtab->namelen; |
|
783 struct token_s tok; |
|
784 unsigned long val = 0; |
|
785 int ok = FALSE; |
|
786 |
|
787 cp = cp + clen + 1; |
|
788 cp = get_token(cp, &tok); |
|
789 if (tok.tk_len == 0) { |
|
790 printf("dwarfdump.conf error: " |
|
791 "%s missing reg table size value %s line %lu", |
|
792 comtab->name, fname, lineno); |
|
793 ++errcount; |
|
794 return FALSE; |
|
795 } |
|
796 |
|
797 ok = make_a_number(comtab->name, fname, lineno, &tok, &val); |
|
798 |
|
799 if (!ok) { |
|
800 ++errcount; |
|
801 return FALSE; |
|
802 } |
|
803 conf->cf_table_entry_count = (unsigned long) val; |
|
804 { |
|
805 int res = ensure_has_no_more_tokens(cp, fname, lineno); |
|
806 |
|
807 return res; |
|
808 } |
|
809 |
|
810 } |
|
811 |
|
812 |
|
813 /* We are guaranteed it's an endabi: command, parse it and |
|
814 check we have the right abi. |
|
815 */ |
|
816 static int |
|
817 parseendabi(char *cp, char *fname, char *abiname, unsigned long lineno, |
|
818 struct comtable_s *comtab) |
|
819 { |
|
820 size_t clen = comtab->namelen; |
|
821 struct token_s tok; |
|
822 |
|
823 |
|
824 cp = cp + clen + 1; |
|
825 cp = get_token(cp, &tok); |
|
826 if (strcmp(abiname, tok.tk_data) != 0) { |
|
827 printf("%s error: " |
|
828 "mismatch abi name %s (here) vs. %s (beginabi:) %s line %lu\n", |
|
829 comtab->name, tok.tk_data, abiname, fname, lineno); |
|
830 ++errcount; |
|
831 return FALSE; |
|
832 } |
|
833 { |
|
834 int res = ensure_has_no_more_tokens(cp, fname, lineno); |
|
835 |
|
836 return res; |
|
837 } |
|
838 |
|
839 } |
|
840 |
|
841 |
|
842 |
|
843 /* Return TRUE if we succeeded and filed in *out. |
|
844 Return FALSE if we failed (and fill in nothing). |
|
845 beginabi: <abiname> |
|
846 reg: <regname> <dwarf regnumber> |
|
847 frame_interface: <integer value 2 or 3> |
|
848 cfa_reg: <number> |
|
849 initial_reg_value: <number: normally 1034 or 1035 > |
|
850 reg_table_size: <size of table> |
|
851 endabi: <abiname> |
|
852 |
|
853 We are positioned at the start of a beginabi: line when |
|
854 called. |
|
855 |
|
856 */ |
|
857 static int |
|
858 parse_abi(FILE * stream, char *fname, char *abiname, |
|
859 struct dwconf_s *out, unsigned long lineno) |
|
860 { |
|
861 struct dwconf_s localconf; |
|
862 char buf[1000]; |
|
863 int comtype = 0; |
|
864 long regcount = 0; |
|
865 |
|
866 unsigned long beginabi_lineno = 0; |
|
867 unsigned long frame_interface_lineno = 0; |
|
868 unsigned long initial_reg_value_lineno = 0; |
|
869 unsigned long reg_table_size_lineno = 0; |
|
870 unsigned long cfa_reg_lineno = 0; |
|
871 static int first_time_done = 0; |
|
872 struct comtable_s *comtabp = 0; |
|
873 |
|
874 |
|
875 if (first_time_done == 0) { |
|
876 finish_comtable_setup(); |
|
877 first_time_done = 1; |
|
878 } |
|
879 |
|
880 |
|
881 |
|
882 |
|
883 init_conf_file_data(&localconf); |
|
884 |
|
885 for (; !feof(stream);) { |
|
886 |
|
887 char *line = 0; |
|
888 |
|
889 /* long loffset = ftell(stream); */ |
|
890 line = fgets(buf, sizeof(buf), stream); |
|
891 if (!line) { |
|
892 ++errcount; |
|
893 printf |
|
894 ("dwarfdump: end of file or error before endabi: in %s, line %lu\n", |
|
895 fname, lineno); |
|
896 return FALSE; |
|
897 } |
|
898 ++lineno; |
|
899 line = skipwhite(line); |
|
900 comtype = which_command(line, &comtabp); |
|
901 switch (comtype) { |
|
902 case LT_ERROR: |
|
903 ++errcount; |
|
904 printf |
|
905 ("dwarfdump: Unknown text in %s is \"%s\" at line %lu\n", |
|
906 fname, line, lineno); |
|
907 break; |
|
908 case LT_COMMENT: |
|
909 break; |
|
910 case LT_BLANK: |
|
911 break; |
|
912 case LT_BEGINABI: |
|
913 if (beginabi_lineno > 0) { |
|
914 ++errcount; |
|
915 printf |
|
916 ("dwarfdump: Encountered beginabi: when not expected. " |
|
917 "%s line %lu previous beginabi line %lu\n", fname, |
|
918 lineno, beginabi_lineno); |
|
919 } |
|
920 beginabi_lineno = lineno; |
|
921 parsebeginabi(line, fname, abiname, lineno, comtabp); |
|
922 break; |
|
923 |
|
924 case LT_REG: |
|
925 parsereg(line, fname, lineno, &localconf, comtabp); |
|
926 ++regcount; |
|
927 break; |
|
928 case LT_FRAME_INTERFACE: |
|
929 if (frame_interface_lineno > 0) { |
|
930 ++errcount; |
|
931 printf |
|
932 ("dwarfdump: Encountered duplicate frame_interface: " |
|
933 "%s line %lu previous frame_interface: line %lu\n", |
|
934 fname, lineno, frame_interface_lineno); |
|
935 } |
|
936 frame_interface_lineno = lineno; |
|
937 parseframe_interface(line, fname, |
|
938 lineno, &localconf, comtabp); |
|
939 break; |
|
940 case LT_CFA_REG: |
|
941 if (cfa_reg_lineno > 0) { |
|
942 printf("dwarfdump: Encountered duplicate cfa_reg: " |
|
943 "%s line %lu previous cfa_reg line %lu\n", |
|
944 fname, lineno, cfa_reg_lineno); |
|
945 ++errcount; |
|
946 } |
|
947 cfa_reg_lineno = lineno; |
|
948 parsecfa_reg(line, fname, lineno, &localconf, comtabp); |
|
949 break; |
|
950 case LT_INITIAL_REG_VALUE: |
|
951 if (initial_reg_value_lineno > 0) { |
|
952 printf |
|
953 ("dwarfdump: Encountered duplicate initial_reg_value_lineno: " |
|
954 "%s line %lu previous initial_reg_value: line %lu\n", |
|
955 fname, lineno, initial_reg_value_lineno); |
|
956 ++errcount; |
|
957 } |
|
958 initial_reg_value_lineno = lineno; |
|
959 |
|
960 parseinitial_reg_value(line, fname, |
|
961 lineno, &localconf, comtabp); |
|
962 break; |
|
963 case LT_REG_TABLE_SIZE: |
|
964 if (reg_table_size_lineno > 0) { |
|
965 printf("dwarfdump: duplicate reg_table_size: " |
|
966 "%s line %lu previous reg_table_size: line %lu\n", |
|
967 fname, lineno, reg_table_size_lineno); |
|
968 ++errcount; |
|
969 } |
|
970 reg_table_size_lineno = lineno; |
|
971 parsereg_table_size(line, fname, |
|
972 lineno, &localconf, comtabp); |
|
973 break; |
|
974 case LT_ENDABI: |
|
975 parseendabi(line, fname, abiname, lineno, comtabp); |
|
976 |
|
977 if (regcount > localconf.cf_table_entry_count) { |
|
978 printf("dwarfdump: more registers named than " |
|
979 " in %s ( %lu named vs %s %lu) %s line %lu\n", |
|
980 abiname, (unsigned long) regcount, |
|
981 name_reg_table_size, |
|
982 (unsigned long) localconf.cf_table_entry_count, |
|
983 fname, (unsigned long) lineno); |
|
984 ++errcount; |
|
985 } |
|
986 |
|
987 *out = localconf; |
|
988 return TRUE; |
|
989 default: |
|
990 printf |
|
991 ("dwarfdump internal error, impossible line type %d %s %lu \n", |
|
992 (int) comtype, fname, lineno); |
|
993 exit(1); |
|
994 |
|
995 } |
|
996 } |
|
997 ++errcount; |
|
998 printf("End of file, no endabi: found. %s, line %lu\n", |
|
999 fname, lineno); |
|
1000 return FALSE; |
|
1001 } |
|
1002 |
|
1003 /* MIPS/IRIX frame register names. |
|
1004 For alternate name sets, use dwarfdump.conf or |
|
1005 revise dwarf.h and libdwarf.h and this table. |
|
1006 */ |
|
1007 static char *regnames[] = { |
|
1008 "cfa", |
|
1009 "r1/at", "r2/v0", "r3/v1", |
|
1010 "r4/a0", "r5/a1", "r6/a2", "r7/a3", |
|
1011 "r8/t0", "r9/t1", "r10/t2", "r11/t3", |
|
1012 "r12/t4", "r13/t5", "r14/t6", "r15/t7", |
|
1013 "r16/s0", "r17/s1", "r18/s2", "r19/s3", |
|
1014 "r20/s4", "r21/s5", "r22/s6", "r23/s7", |
|
1015 "r24/t8", "r25/t9", "r26/k0", "r27/k1", |
|
1016 "r28/gp", "r29/sp", "r30/s8", "r31", |
|
1017 |
|
1018 "$f0", "$f1", |
|
1019 "$f2", "$f3", |
|
1020 "$f4", "$f5", |
|
1021 "$f6", "$f7", |
|
1022 "$f8", "$f9", |
|
1023 "$f10", "$f11", |
|
1024 "$f12", "$f13", |
|
1025 "$f14", "$f15", |
|
1026 "$f16", "$f17", |
|
1027 "$f18", "$f19", |
|
1028 "$f20", "$f21", |
|
1029 "$f22", "$f23", |
|
1030 "$f24", "$f25", |
|
1031 "$f26", "$f27", |
|
1032 "$f28", "$f29", |
|
1033 "$f30", "$f31", |
|
1034 "ra", "slk", |
|
1035 }; |
|
1036 |
|
1037 |
|
1038 /* These defaults match MIPS/IRIX ABI defaults. |
|
1039 For a 'generic' ABI, see -R. |
|
1040 For other ABIs, see -x abi=<whatever> |
|
1041 to configure dwarfdump (and libdwarf) frame |
|
1042 data reporting at runtime. |
|
1043 */ |
|
1044 void |
|
1045 init_conf_file_data(struct dwconf_s *config_file_data) |
|
1046 { |
|
1047 unsigned long base_table_count = |
|
1048 sizeof(regnames) / sizeof(regnames[0]); |
|
1049 |
|
1050 memset(config_file_data, 0, sizeof(*config_file_data)); |
|
1051 config_file_data->cf_interface_number = 2; |
|
1052 config_file_data->cf_table_entry_count = DW_REG_TABLE_SIZE; |
|
1053 config_file_data->cf_initial_rule_value = |
|
1054 DW_FRAME_REG_INITIAL_VALUE; |
|
1055 config_file_data->cf_cfa_reg = DW_FRAME_CFA_COL; |
|
1056 config_file_data->cf_regs = regnames; |
|
1057 config_file_data->cf_named_regs_table_size = base_table_count; |
|
1058 config_file_data->cf_regs_malloced = 0; |
|
1059 if (config_file_data->cf_table_entry_count != base_table_count) { |
|
1060 printf("dwarfdump: improper base table initization, " |
|
1061 "header files wrong: " |
|
1062 "DW_REG_TABLE_SIZE %u != string table size %lu\n", |
|
1063 (unsigned) DW_REG_TABLE_SIZE, |
|
1064 (unsigned long) base_table_count); |
|
1065 exit(1); |
|
1066 } |
|
1067 |
|
1068 return; |
|
1069 } |
|
1070 |
|
1071 /* Naming a few registers makes printing these just |
|
1072 a little bit faster. |
|
1073 */ |
|
1074 static char *genericregnames[] = { |
|
1075 "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", |
|
1076 "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", |
|
1077 "r20", |
|
1078 }; |
|
1079 |
|
1080 /* A 'generic' ABI. For up to 1000 registers. |
|
1081 Perhaps cf_initial_rule_value should be d |
|
1082 UNDEFINED VALUE (1034) instead, but for the purposes of |
|
1083 getting the dwarfdump output correct |
|
1084 either will work. |
|
1085 */ |
|
1086 void |
|
1087 init_generic_config_1000_regs(struct dwconf_s *config_file_data) |
|
1088 { |
|
1089 unsigned long generic_table_count = |
|
1090 sizeof(genericregnames) / sizeof(genericregnames[0]); |
|
1091 config_file_data->cf_interface_number = 3; |
|
1092 config_file_data->cf_table_entry_count = 1000; |
|
1093 config_file_data->cf_initial_rule_value = 1035; /* SAME VALUE */ |
|
1094 config_file_data->cf_cfa_reg = 1036; |
|
1095 config_file_data->cf_regs = genericregnames; |
|
1096 config_file_data->cf_named_regs_table_size = generic_table_count; |
|
1097 config_file_data->cf_regs_malloced = 0; |
|
1098 } |
|
1099 |
|
1100 /* Print the 'right' string for the register we are given. |
|
1101 Deal sensibly with the special regs as well as numbers |
|
1102 we know and those we have not been told about. |
|
1103 |
|
1104 */ |
|
1105 void |
|
1106 print_reg_from_config_data(Dwarf_Signed reg, |
|
1107 struct dwconf_s *config_data) |
|
1108 { |
|
1109 char *name = 0; |
|
1110 |
|
1111 if (reg == config_data->cf_cfa_reg) { |
|
1112 fputs("cfa",stdout); |
|
1113 return; |
|
1114 } |
|
1115 if (reg == DW_FRAME_CFA_COL3) { |
|
1116 /* This should not be necessary, but sometimes one forgets to |
|
1117 do the cfa_reg: command in dwarfdump.conf */ |
|
1118 fputs("cfa",stdout); |
|
1119 return; |
|
1120 } |
|
1121 if (reg == DW_FRAME_UNDEFINED_VAL) { |
|
1122 fputs("u",stdout); |
|
1123 return; |
|
1124 } |
|
1125 if (reg == DW_FRAME_SAME_VAL) { |
|
1126 fputs("s",stdout); |
|
1127 return; |
|
1128 } |
|
1129 if (config_data->cf_regs == 0 || |
|
1130 reg < 0 || reg > config_data->cf_named_regs_table_size) { |
|
1131 printf("r%lld", (signed long long) reg); |
|
1132 return; |
|
1133 } |
|
1134 name = config_data->cf_regs[reg]; |
|
1135 if (!name) { |
|
1136 /* Can happen, the reg names table can be sparse. */ |
|
1137 printf("r%lld", (signed long long) reg); |
|
1138 return; |
|
1139 } |
|
1140 fputs(name,stdout); |
|
1141 return; |
|
1142 } |