// SPDX-License-Identifier: GPL-2.0-or-later
/* Generate kernel symbol version hashes.
Copyright 1996, 1997 Linux International.
New implementation contributed by Richard Henderson <rth@tamu.edu>
Based on original work by Bjorn Ekwall <bj0rn@blox.se>
This file was part of the Linux modutils 2.4.22: moved back into the
kernel sources by Rusty Russell/Kai Germaschewski.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <stdarg.h>
#include <getopt.h>
#include <hashtable.h>
#include "genksyms.h"
/*----------------------------------------------------------------------*/
static HASHTABLE_DEFINE(symbol_hashtable, 1 U << 12 );
static FILE *debugfile;
int cur_line = 1 ;
char *cur_filename;
int in_source_file;
static int flag_debug, flag_dump_defs, flag_reference, flag_dump_types,
flag_preserve, flag_warnings;
static int errors;
static int nsyms;
static struct symbol *expansion_trail;
static struct symbol *visited_symbols;
static const struct {
int n;
const char *name;
} symbol_types[] = {
[SYM_NORMAL] = { 0 , NULL},
[SYM_TYPEDEF] = {'t' , "typedef" },
[SYM_ENUM] = {'e' , "enum" },
[SYM_STRUCT] = {'s' , "struct" },
[SYM_UNION] = {'u' , "union" },
[SYM_ENUM_CONST] = {'E' , "enum constant" },
};
static int equal_list(struct string_list *a, struct string_list *b);
static void print_list(FILE * f, struct string_list *list);
static struct string_list *concat_list(struct string_list *start, ...);
static struct string_list *mk_node(const char *string);
static void print_location(void );
static void print_type_name(enum symbol_type type, const char *name);
/*----------------------------------------------------------------------*/
static const uint32_t crctab32[] = {
0 x00000000U, 0 x77073096U, 0 xee0e612cU, 0 x990951baU, 0 x076dc419U,
0 x706af48fU, 0 xe963a535U, 0 x9e6495a3U, 0 x0edb8832U, 0 x79dcb8a4U,
0 xe0d5e91eU, 0 x97d2d988U, 0 x09b64c2bU, 0 x7eb17cbdU, 0 xe7b82d07U,
0 x90bf1d91U, 0 x1db71064U, 0 x6ab020f2U, 0 xf3b97148U, 0 x84be41deU,
0 x1adad47dU, 0 x6ddde4ebU, 0 xf4d4b551U, 0 x83d385c7U, 0 x136c9856U,
0 x646ba8c0U, 0 xfd62f97aU, 0 x8a65c9ecU, 0 x14015c4fU, 0 x63066cd9U,
0 xfa0f3d63U, 0 x8d080df5U, 0 x3b6e20c8U, 0 x4c69105eU, 0 xd56041e4U,
0 xa2677172U, 0 x3c03e4d1U, 0 x4b04d447U, 0 xd20d85fdU, 0 xa50ab56bU,
0 x35b5a8faU, 0 x42b2986cU, 0 xdbbbc9d6U, 0 xacbcf940U, 0 x32d86ce3U,
0 x45df5c75U, 0 xdcd60dcfU, 0 xabd13d59U, 0 x26d930acU, 0 x51de003aU,
0 xc8d75180U, 0 xbfd06116U, 0 x21b4f4b5U, 0 x56b3c423U, 0 xcfba9599U,
0 xb8bda50fU, 0 x2802b89eU, 0 x5f058808U, 0 xc60cd9b2U, 0 xb10be924U,
0 x2f6f7c87U, 0 x58684c11U, 0 xc1611dabU, 0 xb6662d3dU, 0 x76dc4190U,
0 x01db7106U, 0 x98d220bcU, 0 xefd5102aU, 0 x71b18589U, 0 x06b6b51fU,
0 x9fbfe4a5U, 0 xe8b8d433U, 0 x7807c9a2U, 0 x0f00f934U, 0 x9609a88eU,
0 xe10e9818U, 0 x7f6a0dbbU, 0 x086d3d2dU, 0 x91646c97U, 0 xe6635c01U,
0 x6b6b51f4U, 0 x1c6c6162U, 0 x856530d8U, 0 xf262004eU, 0 x6c0695edU,
0 x1b01a57bU, 0 x8208f4c1U, 0 xf50fc457U, 0 x65b0d9c6U, 0 x12b7e950U,
0 x8bbeb8eaU, 0 xfcb9887cU, 0 x62dd1ddfU, 0 x15da2d49U, 0 x8cd37cf3U,
0 xfbd44c65U, 0 x4db26158U, 0 x3ab551ceU, 0 xa3bc0074U, 0 xd4bb30e2U,
0 x4adfa541U, 0 x3dd895d7U, 0 xa4d1c46dU, 0 xd3d6f4fbU, 0 x4369e96aU,
0 x346ed9fcU, 0 xad678846U, 0 xda60b8d0U, 0 x44042d73U, 0 x33031de5U,
0 xaa0a4c5fU, 0 xdd0d7cc9U, 0 x5005713cU, 0 x270241aaU, 0 xbe0b1010U,
0 xc90c2086U, 0 x5768b525U, 0 x206f85b3U, 0 xb966d409U, 0 xce61e49fU,
0 x5edef90eU, 0 x29d9c998U, 0 xb0d09822U, 0 xc7d7a8b4U, 0 x59b33d17U,
0 x2eb40d81U, 0 xb7bd5c3bU, 0 xc0ba6cadU, 0 xedb88320U, 0 x9abfb3b6U,
0 x03b6e20cU, 0 x74b1d29aU, 0 xead54739U, 0 x9dd277afU, 0 x04db2615U,
0 x73dc1683U, 0 xe3630b12U, 0 x94643b84U, 0 x0d6d6a3eU, 0 x7a6a5aa8U,
0 xe40ecf0bU, 0 x9309ff9dU, 0 x0a00ae27U, 0 x7d079eb1U, 0 xf00f9344U,
0 x8708a3d2U, 0 x1e01f268U, 0 x6906c2feU, 0 xf762575dU, 0 x806567cbU,
0 x196c3671U, 0 x6e6b06e7U, 0 xfed41b76U, 0 x89d32be0U, 0 x10da7a5aU,
0 x67dd4accU, 0 xf9b9df6fU, 0 x8ebeeff9U, 0 x17b7be43U, 0 x60b08ed5U,
0 xd6d6a3e8U, 0 xa1d1937eU, 0 x38d8c2c4U, 0 x4fdff252U, 0 xd1bb67f1U,
0 xa6bc5767U, 0 x3fb506ddU, 0 x48b2364bU, 0 xd80d2bdaU, 0 xaf0a1b4cU,
0 x36034af6U, 0 x41047a60U, 0 xdf60efc3U, 0 xa867df55U, 0 x316e8eefU,
0 x4669be79U, 0 xcb61b38cU, 0 xbc66831aU, 0 x256fd2a0U, 0 x5268e236U,
0 xcc0c7795U, 0 xbb0b4703U, 0 x220216b9U, 0 x5505262fU, 0 xc5ba3bbeU,
0 xb2bd0b28U, 0 x2bb45a92U, 0 x5cb36a04U, 0 xc2d7ffa7U, 0 xb5d0cf31U,
0 x2cd99e8bU, 0 x5bdeae1dU, 0 x9b64c2b0U, 0 xec63f226U, 0 x756aa39cU,
0 x026d930aU, 0 x9c0906a9U, 0 xeb0e363fU, 0 x72076785U, 0 x05005713U,
0 x95bf4a82U, 0 xe2b87a14U, 0 x7bb12baeU, 0 x0cb61b38U, 0 x92d28e9bU,
0 xe5d5be0dU, 0 x7cdcefb7U, 0 x0bdbdf21U, 0 x86d3d2d4U, 0 xf1d4e242U,
0 x68ddb3f8U, 0 x1fda836eU, 0 x81be16cdU, 0 xf6b9265bU, 0 x6fb077e1U,
0 x18b74777U, 0 x88085ae6U, 0 xff0f6a70U, 0 x66063bcaU, 0 x11010b5cU,
0 x8f659effU, 0 xf862ae69U, 0 x616bffd3U, 0 x166ccf45U, 0 xa00ae278U,
0 xd70dd2eeU, 0 x4e048354U, 0 x3903b3c2U, 0 xa7672661U, 0 xd06016f7U,
0 x4969474dU, 0 x3e6e77dbU, 0 xaed16a4aU, 0 xd9d65adcU, 0 x40df0b66U,
0 x37d83bf0U, 0 xa9bcae53U, 0 xdebb9ec5U, 0 x47b2cf7fU, 0 x30b5ffe9U,
0 xbdbdf21cU, 0 xcabac28aU, 0 x53b39330U, 0 x24b4a3a6U, 0 xbad03605U,
0 xcdd70693U, 0 x54de5729U, 0 x23d967bfU, 0 xb3667a2eU, 0 xc4614ab8U,
0 x5d681b02U, 0 x2a6f2b94U, 0 xb40bbe37U, 0 xc30c8ea1U, 0 x5a05df1bU,
0 x2d02ef8dU
};
static uint32_t partial_crc32_one(uint8_t c, uint32_t crc)
{
return crctab32[(crc ^ c) & 0 xff] ^ (crc >> 8 );
}
static uint32_t partial_crc32(const char *s, uint32_t crc)
{
while (*s)
crc = partial_crc32_one(*s++, crc);
return crc;
}
static uint32_t crc32(const char *s)
{
return partial_crc32(s, 0 xffffffff) ^ 0 xffffffff;
}
/*----------------------------------------------------------------------*/
static enum symbol_type map_to_ns(enum symbol_type t)
{
switch (t) {
case SYM_ENUM_CONST:
case SYM_NORMAL:
case SYM_TYPEDEF:
return SYM_NORMAL;
case SYM_ENUM:
case SYM_STRUCT:
case SYM_UNION:
return SYM_STRUCT;
}
return t;
}
struct symbol *find_symbol(const char *name, enum symbol_type ns, int exact)
{
struct symbol *sym;
hash_for_each_possible(symbol_hashtable, sym, hnode, crc32(name)) {
if (map_to_ns(sym->type) == map_to_ns(ns) &&
strcmp(name, sym->name) == 0 &&
sym->is_declared)
break ;
}
if (exact && sym && sym->type != ns)
return NULL;
return sym;
}
static int is_unknown_symbol(struct symbol *sym)
{
struct string_list *defn;
return ((sym->type == SYM_STRUCT ||
sym->type == SYM_UNION ||
sym->type == SYM_ENUM) &&
(defn = sym->defn) && defn->tag == SYM_NORMAL &&
strcmp(defn->string, "}" ) == 0 &&
(defn = defn->next) && defn->tag == SYM_NORMAL &&
strcmp(defn->string, "UNKNOWN" ) == 0 &&
(defn = defn->next) && defn->tag == SYM_NORMAL &&
strcmp(defn->string, "{" ) == 0 );
}
static struct string_list *process_enum(const char *name, enum symbol_type type,
struct string_list *defn)
{
/* The parser adds symbols in the order their declaration completes,
* so it is safe to store the value of the previous enum constant in
* a static variable.
*/
static int enum_counter;
static struct string_list *last_enum_expr;
if (type == SYM_ENUM_CONST) {
if (defn) {
free_list(last_enum_expr, NULL);
last_enum_expr = copy_list_range(defn, NULL);
enum_counter = 1 ;
} else {
struct string_list *expr;
char buf[20 ];
snprintf(buf, sizeof (buf), "%d" , enum_counter++);
if (last_enum_expr) {
expr = copy_list_range(last_enum_expr, NULL);
defn = concat_list(mk_node("(" ),
expr,
mk_node(")" ),
mk_node("+" ),
mk_node(buf), NULL);
} else {
defn = mk_node(buf);
}
}
} else {
free_list(last_enum_expr, NULL);
last_enum_expr = NULL;
enum_counter = 0 ;
if (!name)
/* Anonymous enum definition, nothing more to do */
return NULL;
}
return defn;
}
static struct symbol *__add_symbol(const char *name, enum symbol_type type,
struct string_list *defn, int is_extern,
int is_reference)
{
unsigned long h;
struct symbol *sym;
enum symbol_status status = STATUS_UNCHANGED;
if ((type == SYM_ENUM_CONST || type == SYM_ENUM) && !is_reference) {
defn = process_enum(name, type, defn);
if (defn == NULL)
return NULL;
}
h = crc32(name);
hash_for_each_possible(symbol_hashtable, sym, hnode, h) {
if (map_to_ns(sym->type) != map_to_ns(type) ||
strcmp(name, sym->name))
continue ;
if (is_reference) {
break ;
} else if (sym->type == type && equal_list(sym->defn, defn)) {
if (!sym->is_declared && sym->is_override) {
print_location();
print_type_name(type, name);
fprintf(stderr, " modversion is unchanged\n" );
}
sym->is_declared = 1 ;
} else if (sym->is_declared) {
error_with_pos("redefinition of %s" , name);
} else if (sym->is_override && flag_preserve) {
print_location();
fprintf(stderr, "ignoring " );
print_type_name(type, name);
fprintf(stderr, " modversion change\n" );
sym->is_declared = 1 ;
} else {
status = is_unknown_symbol(sym) ?
STATUS_DEFINED : STATUS_MODIFIED;
break ;
}
free_list(defn, NULL);
return sym;
}
if (sym) {
hash_del(&sym->hnode);
free_list(sym->defn, NULL);
free(sym->name);
free(sym);
--nsyms;
}
sym = xmalloc(sizeof (*sym));
sym->name = xstrdup(name);
sym->type = type;
sym->defn = defn;
sym->expansion_trail = NULL;
sym->visited = NULL;
sym->is_extern = is_extern;
hash_add(symbol_hashtable, &sym->hnode, h);
sym->is_declared = !is_reference;
sym->status = status;
sym->is_override = 0 ;
if (flag_debug) {
if (symbol_types[type].name)
fprintf(debugfile, "Defn for %s %s == <" ,
symbol_types[type].name, name);
else
fprintf(debugfile, "Defn for type%d %s == <" ,
type, name);
if (is_extern)
fputs("extern " , debugfile);
print_list(debugfile, defn);
fputs(">\n" , debugfile);
}
++nsyms;
return sym;
}
struct symbol *add_symbol(const char *name, enum symbol_type type,
struct string_list *defn, int is_extern)
{
return __add_symbol(name, type, defn, is_extern, 0 );
}
static struct symbol *add_reference_symbol(const char *name, enum symbol_type type,
struct string_list *defn, int is_extern)
{
return __add_symbol(name, type, defn, is_extern, 1 );
}
/*----------------------------------------------------------------------*/
void free_node(struct string_list *node)
{
free(node->string);
free(node);
}
void free_list(struct string_list *s, struct string_list *e)
{
while (s != e) {
struct string_list *next = s->next;
free_node(s);
s = next;
}
}
static struct string_list *mk_node(const char *string)
{
struct string_list *newnode;
newnode = xmalloc(sizeof (*newnode));
newnode->string = xstrdup(string);
newnode->tag = SYM_NORMAL;
newnode->next = NULL;
return newnode;
}
static struct string_list *concat_list(struct string_list *start, ...)
{
va_list ap;
struct string_list *n, *n2;
if (!start)
return NULL;
for (va_start(ap, start); (n = va_arg(ap, struct string_list *));) {
for (n2 = n; n2->next; n2 = n2->next)
;
n2->next = start;
start = n;
}
va_end(ap);
return start;
}
struct string_list *copy_node(struct string_list *node)
{
struct string_list *newnode;
newnode = xmalloc(sizeof (*newnode));
newnode->string = xstrdup(node->string);
newnode->tag = node->tag;
return newnode;
}
struct string_list *copy_list_range(struct string_list *start,
struct string_list *end)
{
struct string_list *res, *n;
if (start == end)
return NULL;
n = res = copy_node(start);
for (start = start->next; start != end; start = start->next) {
n->next = copy_node(start);
n = n->next;
}
n->next = NULL;
return res;
}
static int equal_list(struct string_list *a, struct string_list *b)
{
while (a && b) {
if (a->tag != b->tag || strcmp(a->string, b->string))
return 0 ;
a = a->next;
b = b->next;
}
return !a && !b;
}
#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof ((arr)[0 ]))
static struct string_list *read_node(FILE *f)
{
char buffer[256 ];
struct string_list node = {
.string = buffer,
.tag = SYM_NORMAL };
int c, in_string = 0 ;
while ((c = fgetc(f)) != EOF) {
if (!in_string && c == ' ' ) {
if (node.string == buffer)
continue ;
break ;
} else if (c == '"' ) {
in_string = !in_string;
} else if (c == '\n' ) {
if (node.string == buffer)
return NULL;
ungetc(c, f);
break ;
}
if (node.string >= buffer + sizeof (buffer) - 1 ) {
fprintf(stderr, "Token too long\n" );
exit (1 );
}
*node.string++ = c;
}
if (node.string == buffer)
return NULL;
*node.string = 0 ;
node.string = buffer;
if (node.string[1 ] == '#' ) {
size_t n;
for (n = 0 ; n < ARRAY_SIZE(symbol_types); n++) {
if (node.string[0 ] == symbol_types[n].n) {
node.tag = n;
node.string += 2 ;
return copy_node(&node);
}
}
fprintf(stderr, "Unknown type %c\n" , node.string[0 ]);
exit (1 );
}
return copy_node(&node);
}
static void read_reference(FILE *f)
{
while (!feof(f)) {
struct string_list *defn = NULL;
struct string_list *sym, *def;
int is_extern = 0 , is_override = 0 ;
struct symbol *subsym;
sym = read_node(f);
if (sym && sym->tag == SYM_NORMAL &&
!strcmp(sym->string, "override" )) {
is_override = 1 ;
free_node(sym);
sym = read_node(f);
}
if (!sym)
continue ;
def = read_node(f);
if (def && def->tag == SYM_NORMAL &&
!strcmp(def->string, "extern" )) {
is_extern = 1 ;
free_node(def);
def = read_node(f);
}
while (def) {
def->next = defn;
defn = def;
def = read_node(f);
}
subsym = add_reference_symbol(sym->string, sym->tag,
defn, is_extern);
subsym->is_override = is_override;
free_node(sym);
}
}
static void print_node(FILE * f, struct string_list *list)
{
if (symbol_types[list->tag].n) {
putc(symbol_types[list->tag].n, f);
putc('#' , f);
}
fputs(list->string, f);
}
static void print_list(FILE * f, struct string_list *list)
{
struct string_list **e, **b;
struct string_list *tmp, **tmp2;
int elem = 1 ;
if (list == NULL) {
fputs("(nil)" , f);
return ;
}
tmp = list;
while ((tmp = tmp->next) != NULL)
elem++;
b = alloca(elem * sizeof (*e));
e = b + elem;
tmp2 = e - 1 ;
(*tmp2--) = list;
while ((list = list->next) != NULL)
*(tmp2--) = list;
while (b != e) {
print_node(f, *b++);
putc(' ' , f);
}
}
static uint32_t expand_and_crc_sym(struct symbol *sym, uint32_t crc)
{
struct string_list *list = sym->defn;
struct string_list **e, **b;
struct string_list *tmp, **tmp2;
int elem = 1 ;
if (!list)
return crc;
tmp = list;
while ((tmp = tmp->next) != NULL)
elem++;
b = alloca(elem * sizeof (*e));
e = b + elem;
tmp2 = e - 1 ;
*(tmp2--) = list;
while ((list = list->next) != NULL)
*(tmp2--) = list;
while (b != e) {
struct string_list *cur;
struct symbol *subsym;
cur = *(b++);
switch (cur->tag) {
case SYM_NORMAL:
if (flag_dump_defs)
fprintf(debugfile, "%s " , cur->string);
crc = partial_crc32(cur->string, crc);
crc = partial_crc32_one(' ' , crc);
break ;
case SYM_ENUM_CONST:
case SYM_TYPEDEF:
subsym = find_symbol(cur->string, cur->tag, 0 );
/* FIXME: Bad reference files can segfault here. */
if (subsym->expansion_trail) {
if (flag_dump_defs)
fprintf(debugfile, "%s " , cur->string);
crc = partial_crc32(cur->string, crc);
crc = partial_crc32_one(' ' , crc);
} else {
subsym->expansion_trail = expansion_trail;
expansion_trail = subsym;
crc = expand_and_crc_sym(subsym, crc);
}
break ;
case SYM_STRUCT:
case SYM_UNION:
case SYM_ENUM:
subsym = find_symbol(cur->string, cur->tag, 0 );
if (!subsym) {
struct string_list *n;
error_with_pos("expand undefined %s %s" ,
symbol_types[cur->tag].name,
cur->string);
n = concat_list(mk_node
(symbol_types[cur->tag].name),
mk_node(cur->string),
mk_node("{" ),
mk_node("UNKNOWN" ),
mk_node("}" ), NULL);
subsym =
add_symbol(cur->string, cur->tag, n, 0 );
}
if (subsym->expansion_trail) {
if (flag_dump_defs) {
fprintf(debugfile, "%s %s " ,
symbol_types[cur->tag].name,
cur->string);
}
crc = partial_crc32(symbol_types[cur->tag].name,
crc);
crc = partial_crc32_one(' ' , crc);
crc = partial_crc32(cur->string, crc);
crc = partial_crc32_one(' ' , crc);
} else {
subsym->expansion_trail = expansion_trail;
expansion_trail = subsym;
crc = expand_and_crc_sym(subsym, crc);
}
break ;
}
}
{
static struct symbol **end = &visited_symbols;
if (!sym->visited) {
*end = sym;
end = &sym->visited;
sym->visited = (struct symbol *)-1 L;
}
}
return crc;
}
void export_symbol(const char *name)
{
struct symbol *sym;
uint32_t crc;
int has_changed = 0 ;
sym = find_symbol(name, SYM_NORMAL, 0 );
if (!sym) {
error_with_pos("export undefined symbol %s" , name);
return ;
}
if (flag_dump_defs)
fprintf(debugfile, "Export %s == <" , name);
expansion_trail = (struct symbol *)-1 L;
sym->expansion_trail = expansion_trail;
expansion_trail = sym;
crc = expand_and_crc_sym(sym, 0 xffffffff) ^ 0 xffffffff;
sym = expansion_trail;
while (sym != (struct symbol *)-1 L) {
struct symbol *n = sym->expansion_trail;
if (sym->status != STATUS_UNCHANGED) {
if (!has_changed) {
print_location();
fprintf(stderr,
"%s: %s: modversion changed because of changes in " ,
flag_preserve ? "error" : "warning" ,
name);
} else {
fprintf(stderr, ", " );
}
print_type_name(sym->type, sym->name);
if (sym->status == STATUS_DEFINED)
fprintf(stderr, " (became defined)" );
has_changed = 1 ;
if (flag_preserve)
errors++;
}
sym->expansion_trail = 0 ;
sym = n;
}
if (has_changed)
fprintf(stderr, "\n" );
if (flag_dump_defs)
fputs(">\n" , debugfile);
printf("#SYMVER %s 0x%08lx\n" , name, (unsigned long )crc);
}
/*----------------------------------------------------------------------*/
static void print_location(void )
{
fprintf(stderr, "%s:%d: " , cur_filename ? : "<stdin>" , cur_line);
}
static void print_type_name(enum symbol_type type, const char *name)
{
if (symbol_types[type].name)
fprintf(stderr, "%s %s" , symbol_types[type].name, name);
else
fprintf(stderr, "%s" , name);
}
void error_with_pos(const char *fmt, ...)
{
va_list args;
if (flag_warnings) {
print_location();
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
putc('\n' , stderr);
errors++;
}
}
static void genksyms_usage(void )
{
fputs("Usage:\n" "genksyms [-adDTwqhVR] > /path/to/.tmp_obj.ver\n" "\n"
" -d, --debug Increment the debug level (repeatable)\n"
" -D, --dump Dump expanded symbol defs (for debugging only)\n"
" -r, --reference file Read reference symbols from a file\n"
" -T, --dump-types file Dump expanded types into file\n"
" -p, --preserve Preserve reference modversions or fail\n"
" -w, --warnings Enable warnings\n"
" -q, --quiet Disable warnings (default)\n"
" -h, --help Print this message\n"
" -V, --version Print the release version\n"
, stderr);
}
int main(int argc, char **argv)
{
FILE *dumpfile = NULL, *ref_file = NULL;
int o;
struct option long_opts[] = {
{"debug" , 0 , 0 , 'd' },
{"warnings" , 0 , 0 , 'w' },
{"quiet" , 0 , 0 , 'q' },
{"dump" , 0 , 0 , 'D' },
{"reference" , 1 , 0 , 'r' },
{"dump-types" , 1 , 0 , 'T' },
{"preserve" , 0 , 0 , 'p' },
{"version" , 0 , 0 , 'V' },
{"help" , 0 , 0 , 'h' },
{0 , 0 , 0 , 0 }
};
while ((o = getopt_long(argc, argv, "dwqVDr:T:ph" ,
&long_opts[0 ], NULL)) != EOF)
switch (o) {
case 'd' :
flag_debug++;
break ;
case 'w' :
flag_warnings = 1 ;
break ;
case 'q' :
flag_warnings = 0 ;
break ;
case 'V' :
fputs("genksyms version 2.5.60\n" , stderr);
break ;
case 'D' :
flag_dump_defs = 1 ;
break ;
case 'r' :
flag_reference = 1 ;
ref_file = fopen(optarg, "r" );
if (!ref_file) {
perror(optarg);
return 1 ;
}
break ;
case 'T' :
flag_dump_types = 1 ;
dumpfile = fopen(optarg, "w" );
if (!dumpfile) {
perror(optarg);
return 1 ;
}
break ;
case 'p' :
flag_preserve = 1 ;
break ;
case 'h' :
genksyms_usage();
return 0 ;
default :
genksyms_usage();
return 1 ;
}
{
extern int yydebug;
extern int yy_flex_debug;
yydebug = (flag_debug > 1 );
yy_flex_debug = (flag_debug > 2 );
debugfile = stderr;
/* setlinebuf(debugfile); */
}
if (flag_reference) {
read_reference(ref_file);
fclose(ref_file);
}
yyparse();
if (flag_dump_types && visited_symbols) {
while (visited_symbols != (struct symbol *)-1 L) {
struct symbol *sym = visited_symbols;
if (sym->is_override)
fputs("override " , dumpfile);
if (symbol_types[sym->type].n) {
putc(symbol_types[sym->type].n, dumpfile);
putc('#' , dumpfile);
}
fputs(sym->name, dumpfile);
putc(' ' , dumpfile);
if (sym->is_extern)
fputs("extern " , dumpfile);
print_list(dumpfile, sym->defn);
putc('\n' , dumpfile);
visited_symbols = sym->visited;
sym->visited = NULL;
}
}
if (flag_debug) {
fprintf(debugfile, "Hash table occupancy %d/%zd = %g\n" ,
nsyms, HASH_SIZE(symbol_hashtable),
(double )nsyms / HASH_SIZE(symbol_hashtable));
}
if (dumpfile)
fclose(dumpfile);
return errors != 0 ;
}
Messung V0.5 in Prozent C=96 H=93 G=94
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland