/* Line breaks and indentation for pretty-printing */ staticvoid process_linebreak(struct die *cache, int n)
{
indentation_level += n;
do_linebreak = true;
die_map_add_linebreak(cache, n);
}
/* * Definitions in .c files cannot change the public ABI, * so consider them private.
*/ if (!get_udata_attr(die, DW_AT_decl_file, &filenum)) returnfalse;
res = cache_get(&srcfile_cache, filenum); if (res >= 0) return !!res;
int process_die_container(struct state *state, struct die *cache,
Dwarf_Die *die, die_callback_t func,
die_match_callback_t match)
{
Dwarf_Die current; int res;
/* Track the first item in lists. */ if (state)
state->first_list_item = true;
res = checkp(dwarf_child(die, ¤t)); while (!res) { if (match(¤t)) { /* <0 = error, 0 = continue, >0 = stop */
res = checkp(func(state, cache, ¤t)); if (res) goto out;
}
res = checkp(dwarf_siblingof(¤t, ¤t));
}
res = 0;
out: if (state)
state->first_list_item = false;
return res;
}
staticint process_type(struct state *state, struct die *parent,
Dwarf_Die *die);
staticvoid process_type_attr(struct state *state, struct die *cache,
Dwarf_Die *die)
{
Dwarf_Die type;
if (get_ref_die_attr(die, DW_AT_type, &type)) {
check(process_type(state, cache, &type)); return;
}
/* Compilers can omit DW_AT_type -- print out 'void' to clarify */
process(cache, "base_type void");
}
staticvoid process_list_comma(struct state *state, struct die *cache)
{ if (state->first_list_item) {
state->first_list_item = false;
} else {
process(cache, " ,");
process_linebreak(cache, 0);
}
}
/* Comma-separated with DW_AT_type */ staticvoid __process_list_type(struct state *state, struct die *cache,
Dwarf_Die *die, constchar *type)
{ constchar *name = get_name_attr(die);
if (stable) { if (is_kabi_prefix(name))
name = NULL;
state->kabi.orig_name = NULL;
}
if (is_kabi_prefix(name)) {
name += KABI_PREFIX_LEN;
if (!strncmp(name, KABI_RESERVED_PREFIX,
KABI_RESERVED_PREFIX_LEN)) return KABI_RESERVED; if (!strncmp(name, KABI_IGNORED_PREFIX,
KABI_IGNORED_PREFIX_LEN)) return KABI_IGNORED;
if (!strncmp(name, KABI_RENAMED_PREFIX,
KABI_RENAMED_PREFIX_LEN)) { if (suffix) {
name += KABI_RENAMED_PREFIX_LEN;
*suffix = name;
} return KABI_RESERVED;
}
}
return KABI_NORMAL;
}
staticint check_struct_member_kabi_status(struct state *state, struct die *__unused, Dwarf_Die *die)
{ int res;
assert(dwarf_tag(die) == DW_TAG_member_type);
/* * If the union member is a struct, expect the __kabi field to * be the first member of the structure, i.e..: * * union { * type new_member; * struct { * type __kabi_field; * } * };
*/
res = get_kabi_status(die, &state->kabi.orig_name);
if (res == KABI_RESERVED &&
!get_ref_die_attr(die, DW_AT_type, &state->kabi.placeholder))
error("structure member missing a type?");
return res;
}
staticint check_union_member_kabi_status(struct state *state, struct die *__unused, Dwarf_Die *die)
{
Dwarf_Die type; int res;
assert(dwarf_tag(die) == DW_TAG_member_type);
if (!get_ref_die_attr(die, DW_AT_type, &type))
error("union member missing a type?");
/* * We expect a union with two members. Check if either of them * has a __kabi name prefix, i.e.: * * union { * ... * type memberN; // <- type, N = {0,1} * ... * }; * * The member can also be a structure type, in which case we'll * check the first structure member. * * In any case, stop processing after we've seen two members.
*/
res = get_kabi_status(die, &state->kabi.orig_name);
if (res == KABI_RESERVED)
state->kabi.placeholder = type; if (res != KABI_NORMAL) return res;
if (dwarf_tag(&type) == DW_TAG_structure_type)
res = checkp(process_die_container(
state, NULL, &type, check_struct_member_kabi_status,
match_member_type));
staticint get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder, constchar **orig_name)
{ struct state state; int res;
if (!stable) return KABI_NORMAL;
/* * To maintain a stable kABI, distributions may choose to reserve * space in structs for later use by adding placeholder members, * for example: * * struct s { * u32 a; * // an 8-byte placeholder for future use * u64 __kabi_reserved_0; * }; * * When the reserved member is taken into use, the type change * would normally cause the symbol version to change as well, but * if the replacement uses the following convention, gendwarfksyms * continues to use the placeholder type for versioning instead, * thus maintaining the same symbol version: * * struct s { * u32 a; * union { * // placeholder replaced with a new member `b` * struct t b; * struct { * // the placeholder type that is still * // used for versioning * u64 __kabi_reserved_0; * }; * }; * }; * * I.e., as long as the replaced member is in a union, and the * placeholder has a __kabi_reserved name prefix, we'll continue * to use the placeholder type (here u64) for version calculation * instead of the union type. * * It's also possible to ignore new members from versioning if * they've been added to alignment holes, for example, by * including them in a union with another member that uses the * __kabi_ignored name prefix: * * struct s { * u32 a; * // an alignment hole is used to add `n` * union { * u32 n; * // hide the entire union member from versioning * u8 __kabi_ignored_0; * }; * u64 b; * }; * * Note that the user of this feature is responsible for ensuring * that the structure actually remains ABI compatible.
*/
memset(&state.kabi, 0, sizeof(state.kabi));
res = checkp(process_die_container(&state, NULL, die,
check_union_member_kabi_status,
match_member_type));
if (res == KABI_RESERVED) { if (placeholder)
*placeholder = state.kabi.placeholder; if (orig_name)
*orig_name = state.kabi.orig_name;
}
staticint ___process_structure_type(struct state *state, struct die *cache,
Dwarf_Die *die)
{ switch (dwarf_tag(die)) { case DW_TAG_member: if (is_kabi_ignored(die)) return 0; return check(process_type(state, cache, die)); case DW_TAG_variant_part: return check(process_type(state, cache, die)); case DW_TAG_class_type: case DW_TAG_enumeration_type: case DW_TAG_structure_type: case DW_TAG_template_type_parameter: case DW_TAG_union_type: case DW_TAG_subprogram: /* Skip non-member types, including member functions */ return 0; default:
error("unexpected structure_type child: %x", dwarf_tag(die));
}
}
staticvoid __process_structure_type(struct state *state, struct die *cache,
Dwarf_Die *die, constchar *type,
die_callback_t process_func,
die_match_callback_t match_func)
{ bool expand;
staticvoid process_unspecified_type(struct state *state, struct die *cache,
Dwarf_Die *die)
{ /* * These can be emitted for stand-alone assembly code, which means we * might run into them in vmlinux.o.
*/
process(cache, "unspecified_type");
}
staticvoid process_cached(struct state *state, struct die *cache,
Dwarf_Die *die)
{ struct die_fragment *df;
Dwarf_Die child;
list_for_each_entry(df, &cache->fragments, list) { switch (df->type) { case FRAGMENT_STRING:
die_debug_b("cache %p STRING '%s'", cache,
df->data.str);
process(NULL, df->data.str); break; case FRAGMENT_LINEBREAK:
process_linebreak(NULL, df->data.linebreak); break; case FRAGMENT_DIE: if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu),
(void *)df->data.addr, &child))
error("dwarf_die_addr_die failed");
die_debug_b("cache %p DIE addr %" PRIxPTR " tag %x",
cache, df->data.addr, dwarf_tag(&child));
check(process_type(state, NULL, &child)); break; default:
error("empty die_fragment");
}
}
}
staticint process_type(struct state *state, struct die *parent, Dwarf_Die *die)
{ enum die_state want_state = DIE_COMPLETE; struct die *cache; struct expansion_state saved; int tag = dwarf_tag(die);
expansion_state_save(&state->expand, &saved);
/* * Structures and enumeration types are expanded only once per * exported symbol. This is sufficient for detecting ABI changes * within the structure.
*/ if (is_expanded_type(tag)) { if (cache_was_expanded(&state->expansion_cache, die->addr))
state->expand.expand = false;
if (state->expand.expand)
cache_mark_expanded(&state->expansion_cache, die->addr); else
want_state = DIE_UNEXPANDED;
}
/* * If we have want_state already cached, use it instead of walking * through DWARF.
*/
cache = die_map_get(die, want_state);
if (cache->state == want_state) {
die_debug_g("cached addr %p tag %x -- %s", die->addr, tag,
die_state_name(cache->state));
staticvoid save_symbol_ptr(struct state *state)
{
Dwarf_Die ptr_type;
Dwarf_Die type;
if (!get_ref_die_attr(&state->die, DW_AT_type, &ptr_type) ||
dwarf_tag(&ptr_type) != DW_TAG_pointer_type)
error("%s must be a pointer type!",
get_symbol_name(&state->die));
if (!get_ref_die_attr(&ptr_type, DW_AT_type, &type))
error("%s pointer missing a type attribute?",
get_symbol_name(&state->die));
/* * Save the symbol pointer DIE in case the actual symbol is * missing from the DWARF. Clang, for example, intentionally * omits external symbols from the debugging information.
*/ if (dwarf_tag(&type) == DW_TAG_subroutine_type)
symbol_set_ptr(state->sym, &type); else
symbol_set_ptr(state->sym, &ptr_type);
}
staticint process_exported_symbols(struct state *unused, struct die *cache,
Dwarf_Die *die)
{ int tag = dwarf_tag(die);
switch (tag) { /* Possible containers of exported symbols */ case DW_TAG_namespace: case DW_TAG_class_type: case DW_TAG_structure_type: return check(process_die_container(
NULL, cache, die, process_exported_symbols, match_all));
/* Possible exported symbols */ case DW_TAG_subprogram: case DW_TAG_variable: { struct state state;
if (!dwarf_die_addr_die(dwarf, (void *)sym->ptr_die_addr, &state.die))
error("dwarf_die_addr_die failed for symbol ptr: '%s'",
sym->name);
if (dwarf_tag(&state.die) == DW_TAG_subroutine_type)
process_subprogram(&state, &state.die); else
process_variable(&state, &state.die);
cache_free(&state.expansion_cache);
}
staticint resolve_fqns(struct state *parent, struct die *unused,
Dwarf_Die *die)
{ struct state state; struct die *cache; constchar *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;
}
} elseif (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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.