The compiler may choose not to emit type information in DWARF for external symbols. Clang, for example, does this for symbols not defined in the current TU. To provide a way to work around this issue, add support for __gendwarfksyms_ptr_<symbol> pointers that force the compiler to emit the necessary type information in DWARF also for the missing symbols. Example usage: #define GENDWARFKSYMS_PTR(sym) \ static typeof(sym) *__gendwarfksyms_ptr_##sym __used \ __section(".discard.gendwarfksyms") = &sym; extern int external_symbol(void); GENDWARFKSYMS_PTR(external_symbol); Signed-off-by: Sami Tolvanen <samitolvanen@google.com> Reviewed-by: Petr Pavlu <petr.pavlu@suse.com> Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
296 lines
7.1 KiB
C
296 lines
7.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2024 Google LLC
|
|
*/
|
|
|
|
#include <dwarf.h>
|
|
#include <elfutils/libdw.h>
|
|
#include <elfutils/libdwfl.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include <hash.h>
|
|
#include <hashtable.h>
|
|
#include <xalloc.h>
|
|
|
|
#ifndef __GENDWARFKSYMS_H
|
|
#define __GENDWARFKSYMS_H
|
|
|
|
/*
|
|
* Options -- in gendwarfksyms.c
|
|
*/
|
|
extern int debug;
|
|
extern int dump_dies;
|
|
extern int dump_die_map;
|
|
extern int dump_types;
|
|
extern int dump_versions;
|
|
extern int stable;
|
|
extern int symtypes;
|
|
|
|
/*
|
|
* Output helpers
|
|
*/
|
|
#define __PREFIX "gendwarfksyms: "
|
|
#define __println(prefix, format, ...) \
|
|
fprintf(stderr, prefix __PREFIX "%s: " format "\n", __func__, \
|
|
##__VA_ARGS__)
|
|
|
|
#define debug(format, ...) \
|
|
do { \
|
|
if (debug) \
|
|
__println("", format, ##__VA_ARGS__); \
|
|
} while (0)
|
|
|
|
#define warn(format, ...) __println("warning: ", format, ##__VA_ARGS__)
|
|
#define error(format, ...) \
|
|
do { \
|
|
__println("error: ", format, ##__VA_ARGS__); \
|
|
exit(1); \
|
|
} while (0)
|
|
|
|
#define __die_debug(color, format, ...) \
|
|
do { \
|
|
if (dump_dies && dump_die_map) \
|
|
fprintf(stderr, \
|
|
"\033[" #color "m<" format ">\033[39m", \
|
|
__VA_ARGS__); \
|
|
} while (0)
|
|
|
|
#define die_debug_r(format, ...) __die_debug(91, format, __VA_ARGS__)
|
|
#define die_debug_g(format, ...) __die_debug(92, format, __VA_ARGS__)
|
|
#define die_debug_b(format, ...) __die_debug(94, format, __VA_ARGS__)
|
|
|
|
/*
|
|
* Error handling helpers
|
|
*/
|
|
#define __check(expr, test) \
|
|
({ \
|
|
int __res = expr; \
|
|
if (test) \
|
|
error("`%s` failed: %d", #expr, __res); \
|
|
__res; \
|
|
})
|
|
|
|
/* Error == non-zero values */
|
|
#define check(expr) __check(expr, __res)
|
|
/* Error == negative values */
|
|
#define checkp(expr) __check(expr, __res < 0)
|
|
|
|
/* Consistent aliases (DW_TAG_<type>_type) for DWARF tags */
|
|
#define DW_TAG_enumerator_type DW_TAG_enumerator
|
|
#define DW_TAG_formal_parameter_type DW_TAG_formal_parameter
|
|
#define DW_TAG_member_type DW_TAG_member
|
|
#define DW_TAG_template_type_parameter_type DW_TAG_template_type_parameter
|
|
#define DW_TAG_typedef_type DW_TAG_typedef
|
|
#define DW_TAG_variant_part_type DW_TAG_variant_part
|
|
#define DW_TAG_variant_type DW_TAG_variant
|
|
|
|
/*
|
|
* symbols.c
|
|
*/
|
|
|
|
/* See symbols.c:is_symbol_ptr */
|
|
#define SYMBOL_PTR_PREFIX "__gendwarfksyms_ptr_"
|
|
#define SYMBOL_PTR_PREFIX_LEN (sizeof(SYMBOL_PTR_PREFIX) - 1)
|
|
|
|
static inline unsigned int addr_hash(uintptr_t addr)
|
|
{
|
|
return hash_ptr((const void *)addr);
|
|
}
|
|
|
|
enum symbol_state {
|
|
SYMBOL_UNPROCESSED,
|
|
SYMBOL_MAPPED,
|
|
SYMBOL_PROCESSED
|
|
};
|
|
|
|
struct symbol_addr {
|
|
uint32_t section;
|
|
Elf64_Addr address;
|
|
};
|
|
|
|
struct symbol {
|
|
const char *name;
|
|
struct symbol_addr addr;
|
|
struct hlist_node addr_hash;
|
|
struct hlist_node name_hash;
|
|
enum symbol_state state;
|
|
uintptr_t die_addr;
|
|
uintptr_t ptr_die_addr;
|
|
unsigned long crc;
|
|
};
|
|
|
|
typedef void (*symbol_callback_t)(struct symbol *, void *arg);
|
|
|
|
bool is_symbol_ptr(const char *name);
|
|
void symbol_read_exports(FILE *file);
|
|
void symbol_read_symtab(int fd);
|
|
struct symbol *symbol_get(const char *name);
|
|
void symbol_set_ptr(struct symbol *sym, Dwarf_Die *ptr);
|
|
void symbol_set_die(struct symbol *sym, Dwarf_Die *die);
|
|
void symbol_set_crc(struct symbol *sym, unsigned long crc);
|
|
void symbol_for_each(symbol_callback_t func, void *arg);
|
|
void symbol_print_versions(void);
|
|
void symbol_free(void);
|
|
|
|
/*
|
|
* die.c
|
|
*/
|
|
|
|
enum die_state {
|
|
DIE_INCOMPLETE,
|
|
DIE_UNEXPANDED,
|
|
DIE_COMPLETE,
|
|
DIE_SYMBOL,
|
|
DIE_LAST = DIE_SYMBOL
|
|
};
|
|
|
|
enum die_fragment_type {
|
|
FRAGMENT_EMPTY,
|
|
FRAGMENT_STRING,
|
|
FRAGMENT_LINEBREAK,
|
|
FRAGMENT_DIE
|
|
};
|
|
|
|
struct die_fragment {
|
|
enum die_fragment_type type;
|
|
union {
|
|
char *str;
|
|
int linebreak;
|
|
uintptr_t addr;
|
|
} data;
|
|
struct list_head list;
|
|
};
|
|
|
|
#define CASE_CONST_TO_STR(name) \
|
|
case name: \
|
|
return #name;
|
|
|
|
static inline const char *die_state_name(enum die_state state)
|
|
{
|
|
switch (state) {
|
|
CASE_CONST_TO_STR(DIE_INCOMPLETE)
|
|
CASE_CONST_TO_STR(DIE_UNEXPANDED)
|
|
CASE_CONST_TO_STR(DIE_COMPLETE)
|
|
CASE_CONST_TO_STR(DIE_SYMBOL)
|
|
}
|
|
|
|
error("unexpected die_state: %d", state);
|
|
}
|
|
|
|
struct die {
|
|
enum die_state state;
|
|
bool mapped;
|
|
char *fqn;
|
|
int tag;
|
|
uintptr_t addr;
|
|
struct list_head fragments;
|
|
struct hlist_node hash;
|
|
};
|
|
|
|
typedef void (*die_map_callback_t)(struct die *, void *arg);
|
|
|
|
int __die_map_get(uintptr_t addr, enum die_state state, struct die **res);
|
|
struct die *die_map_get(Dwarf_Die *die, enum die_state state);
|
|
void die_map_add_string(struct die *pd, const char *str);
|
|
void die_map_add_linebreak(struct die *pd, int linebreak);
|
|
void die_map_for_each(die_map_callback_t func, void *arg);
|
|
void die_map_add_die(struct die *pd, struct die *child);
|
|
void die_map_free(void);
|
|
|
|
/*
|
|
* cache.c
|
|
*/
|
|
|
|
#define CACHE_HASH_BITS 10
|
|
|
|
/* A cache for addresses we've already seen. */
|
|
struct cache {
|
|
HASHTABLE_DECLARE(cache, 1 << CACHE_HASH_BITS);
|
|
};
|
|
|
|
void cache_set(struct cache *cache, unsigned long key, int value);
|
|
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);
|
|
}
|
|
|
|
static inline bool cache_was_expanded(struct cache *cache, void *addr)
|
|
{
|
|
return __cache_was_expanded(cache, (uintptr_t)addr);
|
|
}
|
|
|
|
/*
|
|
* dwarf.c
|
|
*/
|
|
|
|
struct expansion_state {
|
|
bool expand;
|
|
const char *current_fqn;
|
|
};
|
|
|
|
struct kabi_state {
|
|
int members;
|
|
Dwarf_Die placeholder;
|
|
const char *orig_name;
|
|
};
|
|
|
|
struct state {
|
|
struct symbol *sym;
|
|
Dwarf_Die die;
|
|
|
|
/* List expansion */
|
|
bool first_list_item;
|
|
|
|
/* Structure expansion */
|
|
struct expansion_state expand;
|
|
struct cache expansion_cache;
|
|
|
|
/* Reserved or ignored members */
|
|
struct kabi_state kabi;
|
|
};
|
|
|
|
typedef int (*die_callback_t)(struct state *state, struct die *cache,
|
|
Dwarf_Die *die);
|
|
typedef bool (*die_match_callback_t)(Dwarf_Die *die);
|
|
bool match_all(Dwarf_Die *die);
|
|
|
|
int process_die_container(struct state *state, struct die *cache,
|
|
Dwarf_Die *die, die_callback_t func,
|
|
die_match_callback_t match);
|
|
|
|
void process_cu(Dwarf_Die *cudie);
|
|
|
|
/*
|
|
* types.c
|
|
*/
|
|
|
|
void generate_symtypes_and_versions(FILE *file);
|
|
|
|
/*
|
|
* kabi.c
|
|
*/
|
|
|
|
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);
|
|
|
|
void kabi_read_rules(int fd);
|
|
void kabi_free(void);
|
|
|
|
#endif /* __GENDWARFKSYMS_H */
|