xref: /illumos-gate/usr/src/lib/libdwarf/common/gennames.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2     Copyright 2009-2010 SN Systems Ltd. All rights reserved.
3     Portions Copyright 2009-2018 David Anderson. All rights reserved.
4 
5     This program is free software; you can redistribute it and/or modify it
6     under the terms of version 2.1 of the GNU Lesser General Public License
7     as published by the Free Software Foundation.
8 
9     This program is distributed in the hope that it would be useful, but
10     WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13     Further, this software is distributed without any warranty that it is
14     free of the rightful claim of any third person regarding infringement
15     or the like.  Any license provided herein, whether implied or
16     otherwise, applies only to this software file.  Patent licenses, if
17     any, provided herein do not apply to combinations of this program with
18     other software, or any other product whatsoever.
19 
20     You should have received a copy of the GNU Lesser General Public
21     License along with this program; if not, write the Free Software
22     Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301,
23     USA.
24 
25 */
26 
27 #include "config.h"
28 #ifdef _WIN32
29 #define _CRT_SECURE_NO_WARNINGS
30 #endif /* _WIN32 */
31 
32 #include <stdio.h>
33 #ifdef HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif /* HAVE_STDLIB_H */
36 #include <errno.h>   /* For errno declaration. */
37 #include <ctype.h>
38 #include <string.h>
39 #include "dwgetopt.h"
40 #include "libdwarf_version.h" /* for DW_VERSION_DATE_STR */
41 
42 /*  gennames.c
43     Prints routines to return constant name for the associated value
44     (such as the TAG name string for a particular tag).
45 
46     The input is dwarf.h
47     For each set of names with a common prefix, we create a routine
48     to return the name given the value.
49     Also print header file that gives prototypes of routines.
50     To handle cases where there are multiple names for a single
51     value (DW_AT_* has some due to ambiguities in the DWARF2 spec)
52     we take the first of a given value as the definitive name.
53     TAGs, Attributes, etc are given distinct checks.
54 
55     There are multiple output files as some people find one
56     form more pleasant than the other.
57 
58     The doprinting argument is so that when used by tag_tree.c,
59     and tag_attr.c that we don't get irritating messages on stderr
60     when those dwarfdump built-time applications are run.
61 
62     Some compilers generate better code for switch statements than
63     others, so the -s and -t options let the user decide which
64     is better for their compiler (when building dwarfdump):
65     a simple switch or code doing binary search.
66     This choice affects the runtime speed of dwarfdump.  */
67 
68 typedef int boolean;
69 #define TRUE 1
70 #define FALSE 0
71 #define FAILED 1
72 
73 static void OpenAllFiles(void);
74 static void WriteFileTrailers(void);
75 static void CloseAllFiles(void);
76 static void GenerateInitialFileLines(void);
77 static void GenerateOneSet(void);
78 #ifdef TRACE_ARRAY
79 static void PrintArray(void);
80 #endif /* TRACE_ARRAY */
81 static boolean is_skippable_line(char *pLine);
82 static void ParseDefinitionsAndWriteOutput(void);
83 
84 /* We don't need really long lines: the input file is simple. */
85 #define MAX_LINE_SIZE 1000
86 /* We don't need a variable array size, it just has to be big enough. */
87 #define ARRAY_SIZE 300
88 
89 #define MAX_NAME_LEN 64
90 
91 /* To store entries from dwarf.h */
92 typedef struct {
93     char     name[MAX_NAME_LEN];  /* short name */
94     unsigned value; /* value */
95     /* Original spot in array.   Lets us guarantee a stable sort. */
96     unsigned original_position;
97 } array_data;
98 
99 /*  A group_array is a grouping from dwarf.h.
100     All the TAGs are one group, all the
101     FORMs are another group, and so on. */
102 static array_data group_array[ARRAY_SIZE];
103 static unsigned array_count = 0;
104 
105 typedef int (*compfn)(const void *,const void *);
106 static int Compare(array_data *,array_data *);
107 
108 static const char *prefix_root = "DW_";
109 static const unsigned prefix_root_len = 3;
110 
111 /* f_dwarf_in is the input dwarf.h. The others are output files. */
112 static FILE *f_dwarf_in;
113 static FILE *f_names_h;
114 static FILE *f_names_c;
115 static FILE *f_names_enum_h;
116 static FILE *f_names_new_h;
117 
118 /* Size unchecked, but large enough. */
119 static char prefix[200] = "";
120 
121 static const char *usage[] = {
122     "Usage: gennames <options>",
123     "    -i input-table-path",
124     "    -o output-table-path",
125     "    -s use 'switch' in generation",
126     "    -t use 'tables' in generation",
127     "",
128 };
129 
130 static void
131 print_args(int argc, char *argv[])
132 {
133     int index;
134     printf("Arguments: ");
135     for (index = 1; index < argc; ++index) {
136         printf("%s ",argv[index]);
137     }
138     printf("\n");
139 }
140 
141 
142 char *program_name = 0;
143 static char *input_name = 0;
144 static char *output_name = 0;
145 static boolean use_switch = TRUE;
146 static boolean use_tables = FALSE;
147 
148 static void
149 print_version(const char * name)
150 {
151 #ifdef _DEBUG
152     const char *acType = "Debug";
153 #else
154     const char *acType = "Release";
155 #endif /* _DEBUG */
156 
157     printf("%s [%s %s]\n",name,DW_VERSION_DATE_STR,acType);
158 }
159 
160 
161 static void
162 print_usage_message(const char *options[])
163 {
164     int index;
165     for (index = 0; *options[index]; ++index) {
166         printf("%s\n",options[index]);
167     }
168 }
169 
170 
171 /* process arguments */
172 static void
173 process_args(int argc, char *argv[])
174 {
175     int c = 0;
176     boolean usage_error = FALSE;
177 
178     program_name = argv[0];
179 
180     while ((c = dwgetopt(argc, argv, "i:o:st")) != EOF) {
181         switch (c) {
182         case 'i':
183             input_name = dwoptarg;
184             break;
185         case 'o':
186             output_name = dwoptarg;
187             break;
188         case 's':
189             use_switch = TRUE;
190             use_tables = FALSE;
191             break;
192         case 't':
193             use_switch = FALSE;
194             use_tables = TRUE;
195             break;
196         default:
197             usage_error = TRUE;
198             break;
199         }
200     }
201 
202     if (usage_error || 1 == dwoptind || dwoptind != argc) {
203         print_usage_message(usage);
204         exit(FAILED);
205     }
206 }
207 
208 int
209 main(int argc,char **argv)
210 {
211     print_version(argv[0]);
212     print_args(argc,argv);
213     process_args(argc,argv);
214     OpenAllFiles();
215     GenerateInitialFileLines();
216     ParseDefinitionsAndWriteOutput();
217     WriteFileTrailers();
218     CloseAllFiles();
219     return 0;
220 }
221 
222 /* Print the array used to hold the tags, attributes values */
223 #ifdef TRACE_ARRAY
224 static void
225 PrintArray(void)
226 {
227     int i;
228     for (i = 0; i < array_count; ++i) {
229         printf("%d: Name %s_%s, Value 0x%04x\n",
230             i,prefix,
231             array[i].name,
232             array[i].value);
233     }
234 }
235 #endif /* TRACE_ARRAY */
236 
237 /* By including original position we force a stable sort */
238 static int
239 Compare(array_data *elem1,array_data *elem2)
240 {
241     if (elem1->value < elem2->value) {
242         return -1;
243     }
244     if (elem1->value > elem2->value) {
245         return 1;
246     }
247     if (elem1->original_position < elem2->original_position) {
248         return -1;
249     }
250     if (elem1->original_position > elem2->original_position) {
251         return 1;
252     }
253     return 0;
254 }
255 
256 static FILE *
257 open_path(const char *base, const char *file, const char *direction)
258 {
259     FILE *f = 0;
260     /*  POSIX PATH_MAX  would suffice, normally stdio BUFSIZ is larger
261         than PATH_MAX */
262     static char path_name[BUFSIZ];
263 
264     /* 2 == space for / and NUL */
265     size_t netlen = strlen(file) +strlen(base) + 2;
266 
267     if (netlen >= BUFSIZ) {
268         printf("Error opening '%s/%s', name too long\n",base,file);
269         exit(1);
270     }
271 
272     strcpy(path_name,base);
273     strcat(path_name,"/");
274     strcat(path_name,file);
275 
276     f = fopen(path_name,direction);
277     if (!f) {
278         printf("Error opening '%s'\n",path_name);
279         exit(1);
280     }
281     return f;
282 }
283 
284 /* Open files and write the basic headers */
285 static void
286 OpenAllFiles(void)
287 {
288     const char *dwarf_h      = "dwarf.h";
289     const char *names_h      = "dwarf_names.h";
290     const char *names_c      = "dwarf_names.c";
291     const char *names_enum_h = "dwarf_names_enum.h";
292     const char *names_new_h  = "dwarf_names_new.h";
293 
294     f_dwarf_in = open_path(input_name,dwarf_h,"r");
295     f_names_enum_h = open_path(output_name,names_enum_h,"w");
296     f_names_new_h = open_path(output_name,names_new_h,"w");
297     f_names_h = open_path(output_name,names_h,"w");
298     f_names_c = open_path(output_name,names_c,"w");
299 }
300 
301 static void
302 GenerateInitialFileLines(void)
303 {
304     /* Generate entries for 'dwarf_names_enum.h' */
305     fprintf(f_names_enum_h,"/* Automatically generated, do not edit. */\n");
306     fprintf(f_names_enum_h,"/* Generated sourcedate %s */\n",
307         DW_VERSION_DATE_STR);
308     fprintf(f_names_enum_h,"\n/* BEGIN FILE */\n\n");
309     fprintf(f_names_enum_h,"#ifndef __DWARF_NAMES_ENUM_H__\n");
310     fprintf(f_names_enum_h,"#define __DWARF_NAMES_ENUM_H__\n");
311 
312     /* Generate entries for 'dwarf_names_new.h' */
313     fprintf(f_names_new_h,"/* Automatically generated, do not edit. */\n");
314     fprintf(f_names_new_h,"/* Generated sourcedate %s */\n",
315         DW_VERSION_DATE_STR);
316     fprintf(f_names_new_h,"\n/* BEGIN FILE */\n\n");
317     fprintf(f_names_new_h,"/* define DWARF_PRINT_PREFIX before this\n");
318     fprintf(f_names_new_h,"   point if you wish to.  */\n");
319     fprintf(f_names_new_h,"#ifndef DWARF_PRINT_PREFIX\n");
320     fprintf(f_names_new_h,"#define DWARF_PRINT_PREFIX dwarf_\n");
321     fprintf(f_names_new_h,"#endif\n");
322     fprintf(f_names_new_h,"#define dw_glue(x,y) x##y\n");
323     fprintf(f_names_new_h,"#define dw_glue2(x,y) dw_glue(x,y)\n");
324     fprintf(f_names_new_h,"#define DWPREFIX(x) dw_glue2(DWARF_PRINT_PREFIX,x)\n");
325 
326     /* Generate entries for 'dwarf_names.h' */
327     fprintf(f_names_h,"/* Generated routines, do not edit. */\n");
328     fprintf(f_names_h,"/* Generated sourcedate %s */\n",
329         DW_VERSION_DATE_STR);
330     fprintf(f_names_h,"\n/* BEGIN FILE */\n\n");
331 
332     fprintf(f_names_h,"#ifndef DWARF_NAMES_H\n");
333     fprintf(f_names_h,"#define DWARF_NAMES_H\n\n");
334     fprintf(f_names_h,"#ifdef __cplusplus\n");
335     fprintf(f_names_h,"extern \"C\" {\n");
336     fprintf(f_names_h,"#endif /* __cplusplus */\n\n");
337 
338     /* Generate entries for 'dwarf_names.c' */
339     fprintf(f_names_c,"/* Generated routines, do not edit. */\n");
340     fprintf(f_names_c,"/* Generated sourcedate %s */\n",
341         DW_VERSION_DATE_STR);
342     fprintf(f_names_c,"\n/* BEGIN FILE */\n\n");
343     fprintf(f_names_c,"#include \"dwarf.h\"\n\n");
344     fprintf(f_names_c,"#include \"libdwarf.h\"\n\n");
345 
346     if (use_tables) {
347         fprintf(f_names_c,"typedef struct Names_Data {\n");
348         fprintf(f_names_c,"    const char *l_name; \n");
349         fprintf(f_names_c,"    unsigned    value;  \n");
350         fprintf(f_names_c,"} Names_Data;\n\n");
351 
352         /* Generate code to find an entry */
353         fprintf(f_names_c,"/* Use standard binary search to get entry */\n");
354         fprintf(f_names_c,"static int\nfind_entry(Names_Data *table,"
355             "const int last,unsigned value, const char **s_out)\n");
356         fprintf(f_names_c,"{\n");
357         fprintf(f_names_c,"    int low = 0;\n");
358         fprintf(f_names_c,"    int high = last;\n");
359         fprintf(f_names_c,"    int mid;\n");
360         fprintf(f_names_c,"    unsigned maxval = table[last-1].value;\n");
361         fprintf(f_names_c,"\n");
362         fprintf(f_names_c,"    if (value > maxval) {\n");
363         fprintf(f_names_c,"        return DW_DLV_NO_ENTRY;\n");
364         fprintf(f_names_c,"    }\n");
365         fprintf(f_names_c,"    while (low < high) {\n");
366         fprintf(f_names_c,"        mid = low + ((high - low) / 2);\n");
367         fprintf(f_names_c,"        if(mid == last) {\n");
368         fprintf(f_names_c,"            break;\n");
369         fprintf(f_names_c,"        }\n");
370         fprintf(f_names_c,"        if (table[mid].value < value) {\n");
371         fprintf(f_names_c,"            low = mid + 1;\n");
372         fprintf(f_names_c,"        }\n");
373         fprintf(f_names_c,"        else {\n");
374         fprintf(f_names_c,"              high = mid;\n");
375         fprintf(f_names_c,"        }\n");
376         fprintf(f_names_c,"    }\n");
377         fprintf(f_names_c,"\n");
378         fprintf(f_names_c,"    if (low < last && table[low].value == value) {\n");
379         fprintf(f_names_c,"        /* Found: low is the entry */\n");
380         fprintf(f_names_c,"      *s_out = table[low].l_name;\n");
381         fprintf(f_names_c,"      return DW_DLV_OK;\n");
382         fprintf(f_names_c,"    }\n");
383         fprintf(f_names_c,"    return DW_DLV_NO_ENTRY;\n");
384         fprintf(f_names_c,"}\n");
385         fprintf(f_names_c,"\n");
386     }
387 }
388 
389 /* Close files and write basic trailers */
390 static void
391 WriteFileTrailers(void)
392 {
393     /* Generate entries for 'dwarf_names_enum.h' */
394     fprintf(f_names_enum_h,"#endif /* __DWARF_NAMES_ENUM_H__ */\n");
395     fprintf(f_names_enum_h,"\n/* END FILE */\n");
396 
397     /* Generate entries for 'dwarf_names_new.h' */
398     fprintf(f_names_new_h,"\n/* END FILE */\n");
399 
400     /* Generate entries for 'dwarf_names.h' */
401 
402     fprintf(f_names_h,"\n#ifdef __cplusplus\n");
403     fprintf(f_names_h,"}\n");
404     fprintf(f_names_h,"#endif /* __cplusplus */\n\n");
405     fprintf(f_names_h,"#endif /* DWARF_NAMES_H */\n");
406     fprintf(f_names_h,"\n/* END FILE */\n");
407 
408     /* Generate entries for 'dwarf_names.c' */
409     fprintf(f_names_c,"\n/* END FILE */\n");
410 }
411 
412 static void
413 CloseAllFiles(void)
414 {
415     fclose(f_dwarf_in);
416     fclose(f_names_enum_h);
417     fclose(f_names_new_h);
418     fclose(f_names_h);
419     fclose(f_names_c);
420 }
421 
422 /* Write the table and code for a common set of names */
423 static void
424 GenerateOneSet(void)
425 {
426     unsigned u;
427     unsigned prev_value = 0;
428     size_t len;
429     char *prefix_id = prefix + prefix_root_len;
430     unsigned actual_array_count = 0;
431 
432 #ifdef TRACE_ARRAY
433     printf("List before sorting:\n");
434     PrintArray();
435 #endif /* TRACE_ARRAY */
436 
437     /*  Sort the array, because the values in 'libdwarf.h' are not in
438         ascending order; if we use '-t' we must be sure the values are
439         sorted, for the binary search to work properly.
440         We want a stable sort, hence mergesort.  */
441     qsort((void *)&group_array,array_count,sizeof(array_data),(compfn)Compare);
442 
443 #ifdef TRACE_ARRAY
444     printf("\nList after sorting:\n");
445     PrintArray();
446 #endif /* TRACE_ARRAY */
447 
448     /* Generate entries for 'dwarf_names_enum.h' */
449     fprintf(f_names_enum_h,"\nenum Dwarf_%s_e {\n",prefix_id);
450 
451     /* Generate entries for 'dwarf_names_new.h' */
452     fprintf(f_names_new_h,"int DWPREFIX(get_%s_name) (unsigned int, const char **);\n",prefix_id);
453 
454     /* Generate entries for 'dwarf_names.h' and libdwarf.h */
455     fprintf(f_names_h,"extern int dwarf_get_%s_name(unsigned int /*val_in*/, const char ** /*s_out */);\n",prefix_id);
456 
457     /* Generate code for 'dwarf_names.c' */
458     fprintf(f_names_c,"/* ARGSUSED */\n");
459     fprintf(f_names_c,"int\n");
460     fprintf(f_names_c,"dwarf_get_%s_name (unsigned int val,const char ** s_out)\n",prefix_id);
461     fprintf(f_names_c,"{\n");
462     if (use_tables) {
463         fprintf(f_names_c,"    static Names_Data Dwarf_%s_n[] = {\n",prefix_id);
464     } else {
465         fprintf(f_names_c,"    switch (val) {\n");
466     }
467 
468     for (u = 0; u < array_count; ++u) {
469         /* Check if value already dumped */
470         if (u > 0 && group_array[u].value == prev_value) {
471             fprintf(f_names_c,
472                 "    /* Skipping alternate spelling of value 0x%x. %s_%s */\n",
473                 (unsigned)prev_value,
474                 prefix,
475                 group_array[u].name);
476             continue;
477         }
478         prev_value = group_array[u].value;
479 
480         /*  Generate entries for 'dwarf_names_enum.h'.
481             The 39 just makes nice formatting in the output. */
482         len = 39 - strlen(prefix);
483         fprintf(f_names_enum_h,"    %s_%-*s = 0x%04x",
484             prefix,(int)len,group_array[u].name,group_array[u].value);
485         fprintf(f_names_enum_h,(u + 1 < array_count) ? ",\n" : "\n");
486 
487         /* Generate entries for 'dwarf_names.c' */
488         if (use_tables) {
489             fprintf(f_names_c,"    {/* %3u */ \"%s_%s\", ",
490                 actual_array_count,prefix,group_array[u].name);
491             fprintf(f_names_c," %s_%s}", prefix,group_array[u].name);
492             fprintf(f_names_c,(u + 1 < array_count) ? ",\n" : "\n");
493         } else {
494             fprintf(f_names_c,"    case %s_%s:\n",
495                 prefix,group_array[u].name);
496             fprintf(f_names_c,"        *s_out = \"%s_%s\";\n",
497                 prefix,group_array[u].name);
498             fprintf(f_names_c,"        return DW_DLV_OK;\n");
499         }
500         ++actual_array_count;
501     }
502 
503     /* Closing entries for 'dwarf_names_enum.h' */
504     fprintf(f_names_enum_h,"};\n");
505 
506     if (use_tables) {
507         /* Closing entries for 'dwarf_names.c' */
508         fprintf(f_names_c,"    };\n\n");
509 
510         /* Closing code for 'dwarf_names.c' */
511         fprintf(f_names_c,"    const int last_entry = %d;\n",actual_array_count);
512         fprintf(f_names_c,"    /* find the entry */\n");
513         fprintf(f_names_c,"    int r = find_entry(Dwarf_%s_n,last_entry,val,s_out);\n",prefix_id);
514         fprintf(f_names_c,"    return r;\n");
515         fprintf(f_names_c,"}\n");
516     } else {
517         fprintf(f_names_c,"    }\n");
518         fprintf(f_names_c,"    return DW_DLV_NO_ENTRY;\n");
519         fprintf(f_names_c,"}\n");
520     }
521 
522     /* Mark the group_array as empty */
523     array_count = 0;
524 }
525 
526 /*  Detect empty lines (and other lines we do not want to read) */
527 static boolean
528 is_skippable_line(char *pLine)
529 {
530     boolean empty = TRUE;
531 
532     for (; *pLine && empty; ++pLine) {
533         empty = isspace(*pLine);
534     }
535     return empty;
536 }
537 
538 static void
539 safe_strncpy(char *out, unsigned out_len,
540     char *in,unsigned in_len)
541 {
542     if(in_len >= out_len) {
543         fprintf(stderr,"Impossible input line from dwarf.h. Giving up. \n");
544         fprintf(stderr,"Length %u is too large, limited to %u.\n",
545             in_len,out_len);
546         exit(1);
547     }
548     strncpy(out,in,in_len);
549 }
550 
551 
552 /* Parse the 'dwarf.h' file and generate the tables */
553 static void
554 ParseDefinitionsAndWriteOutput(void)
555 {
556     char new_prefix[64];
557     char *second_underscore = NULL;
558     char type[1000];
559     char name[1000];
560     char value[1000];
561     char extra[1000];
562     char line_in[MAX_LINE_SIZE];
563     int pending = FALSE;
564     int prefix_len = 0;
565 
566     /* Process each line from 'dwarf.h' */
567     while (!feof(f_dwarf_in)) {
568         /*  errno is cleared here so printing errno after
569             the fgets is showing errno as set by fgets. */
570         char *fgbad = 0;
571         errno = 0;
572         fgbad = fgets(line_in,sizeof(line_in),f_dwarf_in);
573         if(!fgbad) {
574             if(feof(f_dwarf_in)) {
575                 break;
576             }
577             /*  Is error. errno must be set. */
578             fprintf(stderr,"Error reading dwarf.h!. Errno %d\n",errno);
579             exit(1);
580         }
581         if (is_skippable_line(line_in)) {
582             continue;
583         }
584         sscanf(line_in,"%s %s %s %s",type,name,value,extra);
585         if (strcmp(type,"#define") ||
586             strncmp(name,prefix_root,prefix_root_len)) {
587             continue;
588         }
589 
590         second_underscore = strchr(name + prefix_root_len,'_');
591         prefix_len = (int)(second_underscore - name);
592         safe_strncpy(new_prefix,sizeof(new_prefix),name,prefix_len);
593         new_prefix[prefix_len] = 0;
594 
595         /* Check for new prefix set */
596         if (strcmp(prefix,new_prefix)) {
597             if (pending) {
598                 /* Generate current prefix set */
599                 GenerateOneSet();
600             }
601             pending = TRUE;
602             strcpy(prefix,new_prefix);
603         }
604 
605         /* Be sure we have a valid entry */
606         if (array_count >= ARRAY_SIZE) {
607             printf("Too many entries for current group_array size of %d",ARRAY_SIZE);
608             exit(1);
609         }
610 
611         /* Move past the second underscore */
612         ++second_underscore;
613 
614         {
615             unsigned long v = strtoul(value,NULL,16);
616             /*  Some values are duplicated, that is ok.
617                 After the sort we will weed out the duplicate values,
618                 see GenerateOneSet(). */
619             /*  Record current entry */
620             if (strlen(second_underscore) >= MAX_NAME_LEN) {
621                 printf("Too long a name %s for max len %d\n",
622                     second_underscore,MAX_NAME_LEN);
623                 exit(1);
624             }
625             strcpy(group_array[array_count].name,second_underscore);
626             group_array[array_count].value = v;
627             group_array[array_count].original_position = array_count;
628             ++array_count;
629         }
630     }
631     if (pending) {
632         /* Generate final prefix set */
633         GenerateOneSet();
634     }
635 }
636