summaryrefslogtreecommitdiff
path: root/scripts/gendwarfksyms
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/gendwarfksyms')
-rw-r--r--scripts/gendwarfksyms/cache.c2
-rw-r--r--scripts/gendwarfksyms/die.c6
-rw-r--r--scripts/gendwarfksyms/dwarf.c168
-rw-r--r--scripts/gendwarfksyms/examples/kabi.h21
-rw-r--r--scripts/gendwarfksyms/examples/kabi_ex.c7
-rw-r--r--scripts/gendwarfksyms/examples/kabi_ex.h101
-rw-r--r--scripts/gendwarfksyms/gendwarfksyms.h18
-rw-r--r--scripts/gendwarfksyms/kabi.c145
-rw-r--r--scripts/gendwarfksyms/symbols.c2
-rw-r--r--scripts/gendwarfksyms/types.c214
10 files changed, 484 insertions, 200 deletions
diff --git a/scripts/gendwarfksyms/cache.c b/scripts/gendwarfksyms/cache.c
index c9c19b86a686..1c640db93db3 100644
--- a/scripts/gendwarfksyms/cache.c
+++ b/scripts/gendwarfksyms/cache.c
@@ -15,7 +15,7 @@ void cache_set(struct cache *cache, unsigned long key, int value)
{
struct cache_item *ci;
- ci = xmalloc(sizeof(struct cache_item));
+ ci = xmalloc(sizeof(*ci));
ci->key = key;
ci->value = value;
hash_add(cache->cache, &ci->hash, hash_32(key));
diff --git a/scripts/gendwarfksyms/die.c b/scripts/gendwarfksyms/die.c
index 66bd4c9bc952..052f7a3f975a 100644
--- a/scripts/gendwarfksyms/die.c
+++ b/scripts/gendwarfksyms/die.c
@@ -6,7 +6,7 @@
#include <string.h>
#include "gendwarfksyms.h"
-#define DIE_HASH_BITS 15
+#define DIE_HASH_BITS 16
/* {die->addr, state} -> struct die * */
static HASHTABLE_DEFINE(die_map, 1 << DIE_HASH_BITS);
@@ -33,7 +33,7 @@ static struct die *create_die(Dwarf_Die *die, enum die_state state)
{
struct die *cd;
- cd = xmalloc(sizeof(struct die));
+ cd = xmalloc(sizeof(*cd));
init_die(cd);
cd->addr = (uintptr_t)die->addr;
@@ -123,7 +123,7 @@ static struct die_fragment *append_item(struct die *cd)
{
struct die_fragment *df;
- df = xmalloc(sizeof(struct die_fragment));
+ df = xmalloc(sizeof(*df));
df->type = FRAGMENT_EMPTY;
list_add_tail(&df->list, &cd->fragments);
return df;
diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c
index 534d9aa7c114..3538a7d9cb07 100644
--- a/scripts/gendwarfksyms/dwarf.c
+++ b/scripts/gendwarfksyms/dwarf.c
@@ -3,6 +3,7 @@
* Copyright (C) 2024 Google LLC
*/
+#define _GNU_SOURCE
#include <assert.h>
#include <inttypes.h>
#include <stdarg.h>
@@ -193,79 +194,17 @@ static void process_fmt(struct die *cache, const char *fmt, ...)
va_end(args);
}
-#define MAX_FQN_SIZE 64
-
-/* Get a fully qualified name from DWARF scopes */
-static char *get_fqn(Dwarf_Die *die)
+static void update_fqn(struct die *cache, Dwarf_Die *die)
{
- const char *list[MAX_FQN_SIZE];
- Dwarf_Die *scopes = NULL;
- bool has_name = false;
- char *fqn = NULL;
- char *p;
- int count = 0;
- int len = 0;
- int res;
- int i;
-
- res = checkp(dwarf_getscopes_die(die, &scopes));
- if (!res) {
- list[count] = get_name_attr(die);
-
- if (!list[count])
- return NULL;
-
- len += strlen(list[count]);
- count++;
-
- goto done;
- }
-
- for (i = res - 1; i >= 0 && count < MAX_FQN_SIZE; i--) {
- if (dwarf_tag(&scopes[i]) == DW_TAG_compile_unit)
- continue;
+ struct die *fqn;
- list[count] = get_name_attr(&scopes[i]);
-
- if (list[count]) {
- has_name = true;
- } else {
- list[count] = "<anonymous>";
- has_name = false;
- }
-
- len += strlen(list[count]);
- count++;
-
- if (i > 0) {
- list[count++] = "::";
- len += 2;
- }
+ if (!cache->fqn) {
+ if (!__die_map_get((uintptr_t)die->addr, DIE_FQN, &fqn) &&
+ *fqn->fqn)
+ cache->fqn = xstrdup(fqn->fqn);
+ else
+ cache->fqn = "";
}
-
- free(scopes);
-
- if (count == MAX_FQN_SIZE)
- warn("increase MAX_FQN_SIZE: reached the maximum");
-
- /* Consider the DIE unnamed if the last scope doesn't have a name */
- if (!has_name)
- return NULL;
-done:
- fqn = xmalloc(len + 1);
- *fqn = '\0';
-
- p = fqn;
- for (i = 0; i < count; i++)
- p = stpcpy(p, list[i]);
-
- return fqn;
-}
-
-static void update_fqn(struct die *cache, Dwarf_Die *die)
-{
- if (!cache->fqn)
- cache->fqn = get_fqn(die) ?: "";
}
static void process_fqn(struct die *cache, Dwarf_Die *die)
@@ -289,12 +228,24 @@ static void process_fqn(struct die *cache, Dwarf_Die *die)
DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility)
DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
DEFINE_PROCESS_UDATA_ATTRIBUTE(bit_size)
-DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)
DEFINE_PROCESS_UDATA_ATTRIBUTE(data_bit_offset)
DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location)
DEFINE_PROCESS_UDATA_ATTRIBUTE(discr_value)
+static void process_byte_size_attr(struct die *cache, Dwarf_Die *die)
+{
+ Dwarf_Word value;
+ unsigned long override;
+
+ if (get_udata_attr(die, DW_AT_byte_size, &value)) {
+ if (stable && kabi_get_byte_size(cache->fqn, &override))
+ value = override;
+
+ process_fmt(cache, " byte_size(%" PRIu64 ")", value);
+ }
+}
+
/* Match functions -- die_match_callback_t */
#define DEFINE_MATCH(type) \
static bool match_##type##_type(Dwarf_Die *die) \
@@ -683,7 +634,7 @@ static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder,
* Note that the user of this feature is responsible for ensuring
* that the structure actually remains ABI compatible.
*/
- memset(&state.kabi, 0, sizeof(struct kabi_state));
+ memset(&state.kabi, 0, sizeof(state.kabi));
res = checkp(process_die_container(&state, NULL, die,
check_union_member_kabi_status,
@@ -1148,8 +1099,81 @@ static void process_symbol_ptr(struct symbol *sym, void *arg)
cache_free(&state.expansion_cache);
}
+static int resolve_fqns(struct state *parent, struct die *unused,
+ Dwarf_Die *die)
+{
+ struct state state;
+ struct die *cache;
+ const char *name;
+ bool use_prefix;
+ char *prefix = NULL;
+ char *fqn = "";
+ int tag;
+
+ if (!__die_map_get((uintptr_t)die->addr, DIE_FQN, &cache))
+ return 0;
+
+ tag = dwarf_tag(die);
+
+ /*
+ * Only namespaces and structures need to pass a prefix to the next
+ * scope.
+ */
+ use_prefix = tag == DW_TAG_namespace || tag == DW_TAG_class_type ||
+ tag == DW_TAG_structure_type;
+
+ state.expand.current_fqn = NULL;
+ name = get_name_attr(die);
+
+ if (parent && parent->expand.current_fqn && (use_prefix || name)) {
+ /*
+ * The fqn for the current DIE, and if needed, a prefix for the
+ * next scope.
+ */
+ if (asprintf(&prefix, "%s::%s", parent->expand.current_fqn,
+ name ? name : "<anonymous>") < 0)
+ error("asprintf failed");
+
+ if (use_prefix)
+ state.expand.current_fqn = prefix;
+
+ /*
+ * Use fqn only if the DIE has a name. Otherwise fqn will
+ * remain empty.
+ */
+ if (name) {
+ fqn = prefix;
+ /* prefix will be freed by die_map. */
+ prefix = NULL;
+ }
+ } else if (name) {
+ /* No prefix from the previous scope. Use only the name. */
+ fqn = xstrdup(name);
+
+ if (use_prefix)
+ state.expand.current_fqn = fqn;
+ }
+
+ /* If the DIE has a non-empty name, cache it. */
+ if (*fqn) {
+ cache = die_map_get(die, DIE_FQN);
+ /* Move ownership of fqn to die_map. */
+ cache->fqn = fqn;
+ cache->state = DIE_FQN;
+ }
+
+ check(process_die_container(&state, NULL, die, resolve_fqns,
+ match_all));
+
+ free(prefix);
+ return 0;
+}
+
void process_cu(Dwarf_Die *cudie)
{
+ check(process_die_container(NULL, NULL, cudie, resolve_fqns,
+ match_all));
+
check(process_die_container(NULL, NULL, cudie, process_exported_symbols,
match_all));
diff --git a/scripts/gendwarfksyms/examples/kabi.h b/scripts/gendwarfksyms/examples/kabi.h
index 97a5669b083d..170733a3fba4 100644
--- a/scripts/gendwarfksyms/examples/kabi.h
+++ b/scripts/gendwarfksyms/examples/kabi.h
@@ -37,11 +37,14 @@
#define __stringify(x...) __stringify_1(x)
#endif
-#define __KABI_RULE(hint, target, value) \
+#define ___KABI_RULE(hint, target, value) \
static const char __PASTE(__gendwarfksyms_rule_, \
__COUNTER__)[] __used __aligned(1) \
__section(".discard.gendwarfksyms.kabi_rules") = \
- "1\0" #hint "\0" #target "\0" #value
+ "1\0" #hint "\0" target "\0" value
+
+#define __KABI_RULE(hint, target, value) \
+ ___KABI_RULE(hint, #target, #value)
#define __KABI_NORMAL_SIZE_ALIGN(_orig, _new) \
union { \
@@ -90,6 +93,20 @@
__KABI_RULE(enumerator_value, fqn field, value)
/*
+ * KABI_BYTE_SIZE(fqn, value)
+ * Set the byte_size attribute for the struct/union/enum fqn to
+ * value bytes.
+ */
+#define KABI_BYTE_SIZE(fqn, value) __KABI_RULE(byte_size, fqn, value)
+
+/*
+ * KABI_TYPE_STRING(type, str)
+ * For the given type, override the type string used in symtypes
+ * output and version calculation with str.
+ */
+#define KABI_TYPE_STRING(type, str) ___KABI_RULE(type_string, type, str)
+
+/*
* KABI_RESERVE
* Reserve some "padding" in a structure for use by LTS backports.
* This is normally placed at the end of a structure.
diff --git a/scripts/gendwarfksyms/examples/kabi_ex.c b/scripts/gendwarfksyms/examples/kabi_ex.c
index 0b7ffd830541..1f799eb7c756 100644
--- a/scripts/gendwarfksyms/examples/kabi_ex.c
+++ b/scripts/gendwarfksyms/examples/kabi_ex.c
@@ -28,3 +28,10 @@ struct ex2c ex2c;
struct ex3a ex3a;
struct ex3b ex3b;
struct ex3c ex3c;
+
+struct ex4a ex4a;
+
+struct ex5a ex5a;
+struct ex5b ex5b;
+
+int ex6a;
diff --git a/scripts/gendwarfksyms/examples/kabi_ex.h b/scripts/gendwarfksyms/examples/kabi_ex.h
index 1736e0f65208..785b211d9c58 100644
--- a/scripts/gendwarfksyms/examples/kabi_ex.h
+++ b/scripts/gendwarfksyms/examples/kabi_ex.h
@@ -21,6 +21,12 @@
* ./gendwarfksyms --stable --dump-dies \
* examples/kabi_ex.o 2>&1 >/dev/null | \
* FileCheck examples/kabi_ex.h --check-prefix=STABLE
+
+ * $ nm examples/kabi_ex.o | awk '{ print $NF }' | \
+ * ./gendwarfksyms --stable --dump-versions \
+ * examples/kabi_ex.o 2>&1 >/dev/null | \
+ * sort | \
+ * FileCheck examples/kabi_ex.h --check-prefix=VERSIONS
*/
#ifndef __KABI_EX_H__
@@ -170,7 +176,7 @@ struct ex2a {
/*
* STABLE: variable structure_type ex2a {
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
- * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) b data_member_location(8)
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8)
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
* STABLE-NEXT: } byte_size(32)
@@ -227,7 +233,7 @@ struct ex3a {
/*
* STABLE: variable structure_type ex3a {
- * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0)
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
* STABLE-NEXT: } byte_size(16)
*/
@@ -260,4 +266,95 @@ _Static_assert(sizeof(struct ex3a) == sizeof(struct ex3c), "ex3a size doesn't ma
* STABLE-NEXT: } byte_size(16)
*/
+/*
+ * Example: An ignored field added to an end of a partially opaque struct,
+ * while keeping the byte_size attribute unchanged.
+ */
+
+struct ex4a {
+ unsigned long a;
+ KABI_IGNORE(0, unsigned long b);
+};
+
+/*
+ * This may be safe if the structure allocation is managed by the core kernel
+ * and the layout remains unchanged except for appended new members.
+ */
+KABI_BYTE_SIZE(ex4a, 8);
+
+/*
+ * STABLE: variable structure_type ex4a {
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
+ * STABLE-NEXT: } byte_size(8)
+ */
+
+/*
+ * Example: A type string override.
+ */
+
+struct ex5a {
+ unsigned long a;
+};
+
+/*
+ * This may be safe if the structure is fully opaque to modules, even though
+ * its definition has inadvertently become part of the ABI.
+ */
+KABI_TYPE_STRING(
+ "s#ex5a",
+ "structure_type ex5a { member pointer_type { s#ex4a } byte_size(8) p data_member_location(0) } byte_size(8)");
+
+/*
+ * Make sure the fully expanded type string includes ex4a.
+ *
+ * VERSIONS: ex5a variable structure_type ex5a {
+ * VERSIONS-SAME: member pointer_type {
+ * VERSIONS-SAME: structure_type ex4a {
+ * VERSIONS-SAME: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ * VERSIONS-SAME: } byte_size(8) p data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ */
+
+/*
+ * Example: A type string definition for a non-existent type.
+ */
+
+struct ex5b {
+ unsigned long a;
+};
+
+/* Replace the type string for struct ex5b */
+KABI_TYPE_STRING(
+ "s#ex5b",
+ "structure_type ex5b { member pointer_type { s#ex5c } byte_size(8) p data_member_location(0) } byte_size(8)");
+
+/* Define a type string for a non-existent struct ex5c */
+KABI_TYPE_STRING(
+ "s#ex5c",
+ "structure_type ex5c { member base_type int byte_size(4) encoding(5) n data_member_location(0) } byte_size(8)");
+
+/*
+ * Make sure the fully expanded type string includes the definition for ex5c.
+ *
+ * VERSIONS: ex5b variable structure_type ex5b {
+ * VERSIONS-SAME: member pointer_type {
+ * VERSIONS-SAME: structure_type ex5c {
+ * VERSIONS-SAME: member base_type int byte_size(4) encoding(5) n data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ * VERSIONS-SAME: } byte_size(8) p data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ */
+
+/*
+ * Example: A type string override for a symbol.
+ */
+
+KABI_TYPE_STRING("ex6a", "variable s#ex5c");
+
+/*
+ * VERSIONS: ex6a variable structure_type ex5c {
+ * VERSIONS-SAME: member base_type int byte_size(4) encoding(5) n data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ */
#endif /* __KABI_EX_H__ */
diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h
index 197a1a8123c6..d9c06d2cb1df 100644
--- a/scripts/gendwarfksyms/gendwarfksyms.h
+++ b/scripts/gendwarfksyms/gendwarfksyms.h
@@ -139,6 +139,7 @@ void symbol_free(void);
enum die_state {
DIE_INCOMPLETE,
+ DIE_FQN,
DIE_UNEXPANDED,
DIE_COMPLETE,
DIE_SYMBOL,
@@ -170,6 +171,7 @@ static inline const char *die_state_name(enum die_state state)
{
switch (state) {
CASE_CONST_TO_STR(DIE_INCOMPLETE)
+ CASE_CONST_TO_STR(DIE_FQN)
CASE_CONST_TO_STR(DIE_UNEXPANDED)
CASE_CONST_TO_STR(DIE_COMPLETE)
CASE_CONST_TO_STR(DIE_SYMBOL)
@@ -214,24 +216,14 @@ int cache_get(struct cache *cache, unsigned long key);
void cache_init(struct cache *cache);
void cache_free(struct cache *cache);
-static inline void __cache_mark_expanded(struct cache *cache, uintptr_t addr)
-{
- cache_set(cache, addr, 1);
-}
-
-static inline bool __cache_was_expanded(struct cache *cache, uintptr_t addr)
-{
- return cache_get(cache, addr) == 1;
-}
-
static inline void cache_mark_expanded(struct cache *cache, void *addr)
{
- __cache_mark_expanded(cache, (uintptr_t)addr);
+ cache_set(cache, (unsigned long)addr, 1);
}
static inline bool cache_was_expanded(struct cache *cache, void *addr)
{
- return __cache_was_expanded(cache, (uintptr_t)addr);
+ return cache_get(cache, (unsigned long)addr) == 1;
}
/*
@@ -285,10 +277,12 @@ void generate_symtypes_and_versions(FILE *file);
* kabi.c
*/
+bool kabi_get_byte_size(const char *fqn, unsigned long *value);
bool kabi_is_enumerator_ignored(const char *fqn, const char *field);
bool kabi_get_enumerator_value(const char *fqn, const char *field,
unsigned long *value);
bool kabi_is_declonly(const char *fqn);
+bool kabi_get_type_string(const char *type, const char **str);
void kabi_read_rules(int fd);
void kabi_free(void);
diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c
index 66f01fcd1607..e3c2a3ccf51a 100644
--- a/scripts/gendwarfksyms/kabi.c
+++ b/scripts/gendwarfksyms/kabi.c
@@ -54,11 +54,27 @@
*/
#define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value"
+/*
+ * Rule: byte_size
+ * - For the fqn_field in the target field, set the byte_size
+ * attribute to the value in the value field.
+ */
+#define KABI_RULE_TAG_BYTE_SIZE "byte_size"
+
+/*
+ * Rule: type_string
+ * - For the type reference in the fqn field, use the type string
+ * in the value field.
+ */
+#define KABI_RULE_TAG_TYPE_STRING "type_string"
+
enum kabi_rule_type {
KABI_RULE_TYPE_UNKNOWN,
KABI_RULE_TYPE_DECLONLY,
KABI_RULE_TYPE_ENUMERATOR_IGNORE,
KABI_RULE_TYPE_ENUMERATOR_VALUE,
+ KABI_RULE_TYPE_BYTE_SIZE,
+ KABI_RULE_TYPE_TYPE_STRING,
};
#define RULE_HASH_BITS 7
@@ -127,6 +143,14 @@ void kabi_read_rules(int fd)
.type = KABI_RULE_TYPE_ENUMERATOR_VALUE,
.tag = KABI_RULE_TAG_ENUMERATOR_VALUE,
},
+ {
+ .type = KABI_RULE_TYPE_BYTE_SIZE,
+ .tag = KABI_RULE_TAG_BYTE_SIZE,
+ },
+ {
+ .type = KABI_RULE_TYPE_TYPE_STRING,
+ .tag = KABI_RULE_TAG_TYPE_STRING,
+ },
};
if (!stable)
@@ -204,7 +228,7 @@ void kabi_read_rules(int fd)
if (type == KABI_RULE_TYPE_UNKNOWN)
error("unsupported kABI rule type: '%s'", field);
- rule = xmalloc(sizeof(struct rule));
+ rule = xmalloc(sizeof(*rule));
rule->type = type;
rule->target = xstrdup(get_rule_field(&rule_str, &left));
@@ -222,33 +246,55 @@ void kabi_read_rules(int fd)
check(elf_end(elf));
}
-bool kabi_is_declonly(const char *fqn)
+static char *get_enumerator_target(const char *fqn, const char *field)
+{
+ char *target = NULL;
+
+ if (asprintf(&target, "%s %s", fqn, field) < 0)
+ error("asprintf failed for '%s %s'", fqn, field);
+
+ return target;
+}
+
+static struct rule *find_rule(enum kabi_rule_type type, const char *target)
{
struct rule *rule;
if (!stable)
- return false;
- if (!fqn || !*fqn)
- return false;
+ return NULL;
+ if (!target || !*target)
+ return NULL;
hash_for_each_possible(rules, rule, hash,
- rule_values_hash(KABI_RULE_TYPE_DECLONLY, fqn)) {
- if (rule->type == KABI_RULE_TYPE_DECLONLY &&
- !strcmp(fqn, rule->target))
- return true;
+ rule_values_hash(type, target)) {
+ if (rule->type == type && !strcmp(target, rule->target))
+ return rule;
}
- return false;
+ return NULL;
}
-static char *get_enumerator_target(const char *fqn, const char *field)
+static struct rule *find_enumerator_rule(enum kabi_rule_type type,
+ const char *fqn, const char *field)
{
- char *target = NULL;
+ struct rule *rule;
+ char *target;
- if (asprintf(&target, "%s %s", fqn, field) < 0)
- error("asprintf failed for '%s %s'", fqn, field);
+ if (!stable)
+ return NULL;
+ if (!fqn || !*fqn || !field || !*field)
+ return NULL;
- return target;
+ target = get_enumerator_target(fqn, field);
+ rule = find_rule(type, target);
+
+ free(target);
+ return rule;
+}
+
+bool kabi_is_declonly(const char *fqn)
+{
+ return !!find_rule(KABI_RULE_TYPE_DECLONLY, fqn);
}
static unsigned long get_ulong_value(const char *value)
@@ -267,58 +313,49 @@ static unsigned long get_ulong_value(const char *value)
bool kabi_is_enumerator_ignored(const char *fqn, const char *field)
{
- bool match = false;
- struct rule *rule;
- char *target;
-
- if (!stable)
- return false;
- if (!fqn || !*fqn || !field || !*field)
- return false;
+ return !!find_enumerator_rule(KABI_RULE_TYPE_ENUMERATOR_IGNORE, fqn,
+ field);
+}
- target = get_enumerator_target(fqn, field);
+bool kabi_get_enumerator_value(const char *fqn, const char *field,
+ unsigned long *value)
+{
+ struct rule *rule;
- hash_for_each_possible(
- rules, rule, hash,
- rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_IGNORE, target)) {
- if (rule->type == KABI_RULE_TYPE_ENUMERATOR_IGNORE &&
- !strcmp(target, rule->target)) {
- match = true;
- break;
- }
+ rule = find_enumerator_rule(KABI_RULE_TYPE_ENUMERATOR_VALUE, fqn,
+ field);
+ if (rule) {
+ *value = get_ulong_value(rule->value);
+ return true;
}
- free(target);
- return match;
+ return false;
}
-bool kabi_get_enumerator_value(const char *fqn, const char *field,
- unsigned long *value)
+bool kabi_get_byte_size(const char *fqn, unsigned long *value)
{
- bool match = false;
struct rule *rule;
- char *target;
- if (!stable)
- return false;
- if (!fqn || !*fqn || !field || !*field)
- return false;
+ rule = find_rule(KABI_RULE_TYPE_BYTE_SIZE, fqn);
+ if (rule) {
+ *value = get_ulong_value(rule->value);
+ return true;
+ }
- target = get_enumerator_target(fqn, field);
+ return false;
+}
- hash_for_each_possible(rules, rule, hash,
- rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_VALUE,
- target)) {
- if (rule->type == KABI_RULE_TYPE_ENUMERATOR_VALUE &&
- !strcmp(target, rule->target)) {
- *value = get_ulong_value(rule->value);
- match = true;
- break;
- }
+bool kabi_get_type_string(const char *type, const char **str)
+{
+ struct rule *rule;
+
+ rule = find_rule(KABI_RULE_TYPE_TYPE_STRING, type);
+ if (rule) {
+ *str = rule->value;
+ return true;
}
- free(target);
- return match;
+ return false;
}
void kabi_free(void)
diff --git a/scripts/gendwarfksyms/symbols.c b/scripts/gendwarfksyms/symbols.c
index 327f87389c34..35ed594f0749 100644
--- a/scripts/gendwarfksyms/symbols.c
+++ b/scripts/gendwarfksyms/symbols.c
@@ -146,7 +146,7 @@ void symbol_read_exports(FILE *file)
continue;
}
- sym = xcalloc(1, sizeof(struct symbol));
+ sym = xcalloc(1, sizeof(*sym));
sym->name = name;
sym->addr.section = SHN_UNDEF;
sym->state = SYMBOL_UNPROCESSED;
diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c
index 6c03265f4d10..9c3b053bf061 100644
--- a/scripts/gendwarfksyms/types.c
+++ b/scripts/gendwarfksyms/types.c
@@ -6,6 +6,8 @@
#define _GNU_SOURCE
#include <inttypes.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <zlib.h>
#include "gendwarfksyms.h"
@@ -43,7 +45,7 @@ static int type_list_append(struct list_head *list, const char *s, void *owned)
if (!s)
return 0;
- entry = xmalloc(sizeof(struct type_list_entry));
+ entry = xmalloc(sizeof(*entry));
entry->str = s;
entry->owned = owned;
list_add_tail(&entry->list, list);
@@ -100,7 +102,7 @@ static void type_expansion_append(struct type_expansion *type, const char *s,
#define TYPE_HASH_BITS 12
static HASHTABLE_DEFINE(type_map, 1 << TYPE_HASH_BITS);
-static int type_map_get(const char *name, struct type_expansion **res)
+static int __type_map_get(const char *name, struct type_expansion **res)
{
struct type_expansion *e;
@@ -114,12 +116,13 @@ static int type_map_get(const char *name, struct type_expansion **res)
return -1;
}
-static void type_map_add(const char *name, struct type_expansion *type)
+static struct type_expansion *type_map_add(const char *name,
+ struct type_expansion *type)
{
struct type_expansion *e;
- if (type_map_get(name, &e)) {
- e = xmalloc(sizeof(struct type_expansion));
+ if (__type_map_get(name, &e)) {
+ e = xmalloc(sizeof(*e));
type_expansion_init(e);
e->name = xstrdup(name);
@@ -130,7 +133,7 @@ static void type_map_add(const char *name, struct type_expansion *type)
} else {
/* Use the longest available expansion */
if (type->len <= e->len)
- return;
+ return e;
type_list_free(&e->expanded);
@@ -148,22 +151,71 @@ static void type_map_add(const char *name, struct type_expansion *type)
type_list_write(&e->expanded, stderr);
checkp(fputs("\n", stderr));
}
+
+ return e;
+}
+
+static void type_parse(const char *name, const char *str,
+ struct type_expansion *type);
+
+static int type_map_get(const char *name, struct type_expansion **res)
+{
+ struct type_expansion type;
+ const char *override;
+
+ if (!__type_map_get(name, res))
+ return 0;
+
+ /*
+ * If die_map didn't contain a type, we might still have
+ * a type_string kABI rule that defines it.
+ */
+ if (stable && kabi_get_type_string(name, &override)) {
+ type_expansion_init(&type);
+ type_parse(name, override, &type);
+ *res = type_map_add(name, &type);
+ type_expansion_free(&type);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int cmp_expansion_name(const void *p1, const void *p2)
+{
+ struct type_expansion *const *e1 = p1;
+ struct type_expansion *const *e2 = p2;
+
+ return strcmp((*e1)->name, (*e2)->name);
}
static void type_map_write(FILE *file)
{
struct type_expansion *e;
struct hlist_node *tmp;
+ struct type_expansion **es;
+ size_t count = 0;
+ size_t i = 0;
if (!file)
return;
- hash_for_each_safe(type_map, e, tmp, hash) {
- checkp(fputs(e->name, file));
+ hash_for_each_safe(type_map, e, tmp, hash)
+ ++count;
+ es = xmalloc(count * sizeof(*es));
+ hash_for_each_safe(type_map, e, tmp, hash)
+ es[i++] = e;
+
+ qsort(es, count, sizeof(*es), cmp_expansion_name);
+
+ for (i = 0; i < count; ++i) {
+ checkp(fputs(es[i]->name, file));
checkp(fputs(" ", file));
- type_list_write(&e->expanded, file);
+ type_list_write(&es[i]->expanded, file);
checkp(fputs("\n", file));
}
+
+ free(es);
}
static void type_map_free(void)
@@ -248,7 +300,7 @@ static char *get_type_name(struct die *cache)
warn("found incomplete cache entry: %p", cache);
return NULL;
}
- if (cache->state == DIE_SYMBOL)
+ if (cache->state == DIE_SYMBOL || cache->state == DIE_FQN)
return NULL;
if (!cache->fqn || !*cache->fqn)
return NULL;
@@ -267,15 +319,18 @@ static char *get_type_name(struct die *cache)
return name;
}
-static void __calculate_version(struct version *version, struct list_head *list)
+static void __calculate_version(struct version *version,
+ struct type_expansion *type)
{
struct type_list_entry *entry;
struct type_expansion *e;
/* Calculate a CRC over an expanded type string */
- list_for_each_entry(entry, list, list) {
+ list_for_each_entry(entry, &type->expanded, list) {
if (is_type_prefix(entry->str)) {
- check(type_map_get(entry->str, &e));
+ if (type_map_get(entry->str, &e))
+ error("unknown type reference to '%s' when expanding '%s'",
+ entry->str, type->name);
/*
* It's sufficient to expand each type reference just
@@ -285,7 +340,7 @@ static void __calculate_version(struct version *version, struct list_head *list)
version_add(version, entry->str);
} else {
cache_mark_expanded(&expansion_cache, e);
- __calculate_version(version, &e->expanded);
+ __calculate_version(version, e);
}
} else {
version_add(version, entry->str);
@@ -293,44 +348,19 @@ static void __calculate_version(struct version *version, struct list_head *list)
}
}
-static void calculate_version(struct version *version, struct list_head *list)
+static void calculate_version(struct version *version,
+ struct type_expansion *type)
{
version_init(version);
- __calculate_version(version, list);
+ __calculate_version(version, type);
cache_free(&expansion_cache);
}
-static void __type_expand(struct die *cache, struct type_expansion *type,
- bool recursive);
-
-static void type_expand_child(struct die *cache, struct type_expansion *type,
- bool recursive)
-{
- struct type_expansion child;
- char *name;
-
- name = get_type_name(cache);
- if (!name) {
- __type_expand(cache, type, recursive);
- return;
- }
-
- if (recursive && !__cache_was_expanded(&expansion_cache, cache->addr)) {
- __cache_mark_expanded(&expansion_cache, cache->addr);
- type_expansion_init(&child);
- __type_expand(cache, &child, true);
- type_map_add(name, &child);
- type_expansion_free(&child);
- }
-
- type_expansion_append(type, name, name);
-}
-
-static void __type_expand(struct die *cache, struct type_expansion *type,
- bool recursive)
+static void __type_expand(struct die *cache, struct type_expansion *type)
{
struct die_fragment *df;
struct die *child;
+ char *name;
list_for_each_entry(df, &cache->fragments, list) {
switch (df->type) {
@@ -346,7 +376,12 @@ static void __type_expand(struct die *cache, struct type_expansion *type,
error("unknown child: %" PRIxPTR,
df->data.addr);
- type_expand_child(child, type, recursive);
+ name = get_type_name(child);
+ if (name)
+ type_expansion_append(type, name, name);
+ else
+ __type_expand(child, type);
+
break;
case FRAGMENT_LINEBREAK:
/*
@@ -364,12 +399,85 @@ static void __type_expand(struct die *cache, struct type_expansion *type,
}
}
-static void type_expand(struct die *cache, struct type_expansion *type,
- bool recursive)
+static void type_expand(const char *name, struct die *cache,
+ struct type_expansion *type)
{
+ const char *override;
+
type_expansion_init(type);
- __type_expand(cache, type, recursive);
- cache_free(&expansion_cache);
+
+ if (stable && kabi_get_type_string(name, &override))
+ type_parse(name, override, type);
+ else
+ __type_expand(cache, type);
+}
+
+static void type_parse(const char *name, const char *str,
+ struct type_expansion *type)
+{
+ char *fragment;
+ size_t start = 0;
+ size_t end;
+ size_t pos;
+
+ if (!*str)
+ error("empty type string override for '%s'", name);
+
+ for (pos = 0; str[pos]; ++pos) {
+ bool empty;
+ char marker = ' ';
+
+ if (!is_type_prefix(&str[pos]))
+ continue;
+
+ end = pos + 2;
+
+ /*
+ * Find the end of the type reference. If the type name contains
+ * spaces, it must be in single quotes.
+ */
+ if (str[end] == '\'') {
+ marker = '\'';
+ ++end;
+ }
+ while (str[end] && str[end] != marker)
+ ++end;
+
+ /* Check that we have a non-empty type name */
+ if (marker == '\'') {
+ if (str[end] != marker)
+ error("incomplete %c# type reference for '%s' (string : '%s')",
+ str[pos], name, str);
+ empty = end == pos + 3;
+ ++end;
+ } else {
+ empty = end == pos + 2;
+ }
+ if (empty)
+ error("empty %c# type name for '%s' (string: '%s')",
+ str[pos], name, str);
+
+ /* Append the part of the string before the type reference */
+ if (pos > start) {
+ fragment = xstrndup(&str[start], pos - start);
+ type_expansion_append(type, fragment, fragment);
+ }
+
+ /*
+ * Append the type reference -- note that if the reference
+ * is invalid, i.e. points to a non-existent type, we will
+ * print out an error when calculating versions.
+ */
+ fragment = xstrndup(&str[pos], end - pos);
+ type_expansion_append(type, fragment, fragment);
+
+ start = end;
+ pos = end - 1;
+ }
+
+ /* Append the rest of the type string, if there's any left */
+ if (str[start])
+ type_expansion_append(type, &str[start], NULL);
}
static void expand_type(struct die *cache, void *arg)
@@ -399,9 +507,9 @@ static void expand_type(struct die *cache, void *arg)
return;
debug("%s", name);
- type_expand(cache, &type, true);
- type_map_add(name, &type);
+ type_expand(name, cache, &type);
+ type_map_add(name, &type);
type_expansion_free(&type);
free(name);
}
@@ -423,11 +531,11 @@ static void expand_symbol(struct symbol *sym, void *arg)
if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache))
return; /* We'll warn about missing CRCs later. */
- type_expand(cache, &type, false);
+ type_expand(sym->name, cache, &type);
/* If the symbol already has a version, don't calculate it again. */
if (sym->state != SYMBOL_PROCESSED) {
- calculate_version(&version, &type.expanded);
+ calculate_version(&version, &type);
symbol_set_crc(sym, version.crc);
debug("%s = %lx", sym->name, version.crc);