* ./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__
/*
* 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)
/*
* 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)
*/
* 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__ */
#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;
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)) {
+ if (__type_map_get(name, &e)) {
e = xmalloc(sizeof(struct type_expansion));
type_expansion_init(e);
e->name = xstrdup(name);
} else {
/* Use the longest available expansion */
if (type->len <= e->len)
- return;
+ return e;
type_list_free(&e->expanded);
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 void type_map_write(FILE *file)
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
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);
}
}
-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);
}
cache_free(&expansion_cache);
}
+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);
+
+ type_expansion_init(type);
+
+ 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)
{
struct type_expansion type;
+ const char *override;
char *name;
if (cache->mapped)
return;
debug("%s", name);
- type_expand(cache, &type, true);
- type_map_add(name, &type);
+ if (stable && kabi_get_type_string(name, &override))
+ type_parse(name, override, &type);
+ else
+ type_expand(cache, &type, true);
+
+ type_map_add(name, &type);
type_expansion_free(&type);
free(name);
}
{
struct type_expansion type;
struct version version;
+ const char *override;
struct die *cache;
/*
if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache))
return; /* We'll warn about missing CRCs later. */
- type_expand(cache, &type, false);
+ if (stable && kabi_get_type_string(sym->name, &override))
+ type_parse(sym->name, override, &type);
+ else
+ type_expand(cache, &type, false);
/* 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);