[RFC PATCH 2/2] perf symbols: Try reading the symbol table with libbfd

From: RÃmi Bernon
Date: Mon Jun 01 2020 - 07:41:00 EST


Wine generates PE binaries for its code modules and also generates
debug files in PE or PDB formats, which perf cannot parse either.

Trying libbfd, when supported, if the default libelf symbol parsing
failed, makes it possible to read the symbol table from any binary
format supported by it, and lets perf report symbols and annotations
for Windows applications running under Wine.

Because libbfd doesn't provide symbol size (probably because of some
backends not supporting it), we compute it by first sorting the symbols
by addresses and then considering that they are sequential in a given
section.

Signed-off-by: RÃmi Bernon <rbernon@xxxxxxxxxxxxxxx>
Cc: Alexander Shishkin <alexander.shishkin@xxxxxxxxxxxxxxx>
Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
Cc: Mark Rutland <mark.rutland@xxxxxxx>
Cc: Namhyung Kim <namhyung@xxxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Jacek Caban <jacek@xxxxxxxxxxxxxxx>
---
tools/perf/util/symbol.c | 124 +++++++++++++++++++++++++++++++++++++++
1 file changed, 124 insertions(+)

diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 381da6b39f89..0352e97822ac 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1520,6 +1520,121 @@ static int dso__load_perf_map(const char *map_path, struct dso *dso)
return -1;
}

+#ifdef HAVE_LIBBFD_SUPPORT
+#define PACKAGE 'perf'
+#include <bfd.h>
+
+static int bfd_symbols__cmpvalue(const void *a, const void *b)
+{
+ const asymbol *as = *(const asymbol **)a, *bs = *(const asymbol **)b;
+
+ if (bfd_asymbol_value(as) != bfd_asymbol_value(bs))
+ return bfd_asymbol_value(as) - bfd_asymbol_value(bs);
+
+ return bfd_asymbol_name(as)[0] - bfd_asymbol_name(bs)[0];
+}
+
+static bool dso__load_bfd_symbols(struct dso *dso, const char *debugfile)
+{
+ bool ret = false;
+ long symbols_size, symbols_count;
+ asection *section;
+ asymbol **symbols, *sym;
+ struct symbol *symbol;
+ bfd *abfd;
+ u_int i;
+ u64 start, len;
+
+ abfd = bfd_openr(dso->long_name, NULL);
+ if (!abfd)
+ return -1;
+
+ if (!bfd_check_format(abfd, bfd_object)) {
+ pr_debug2("%s: cannot read %s bfd file.\n", __func__,
+ dso->long_name);
+ goto out_close;
+ }
+
+ section = bfd_get_section_by_name(abfd, ".text");
+ if (section)
+ dso->text_offset = section->vma - section->filepos;
+
+ bfd_close(abfd);
+
+ abfd = bfd_openr(debugfile, NULL);
+ if (!abfd)
+ return -1;
+
+ if (!bfd_check_format(abfd, bfd_object)) {
+ pr_debug2("%s: cannot read %s bfd file.\n", __func__,
+ debugfile);
+ goto out_close;
+ }
+
+ symbols_size = bfd_get_symtab_upper_bound(abfd);
+ if (symbols_size == 0) {
+ bfd_close(abfd);
+ return true;
+ }
+
+ if (symbols_size < 0)
+ goto out_close;
+
+ symbols = malloc(symbols_size);
+ if (!symbols)
+ goto out_close;
+
+ symbols_count = bfd_canonicalize_symtab(abfd, symbols);
+ if (symbols_count < 0)
+ goto out_free;
+
+ qsort(symbols, symbols_count, sizeof(asymbol *), bfd_symbols__cmpvalue);
+
+#ifdef bfd_get_section
+#define bfd_asymbol_section bfd_get_section
+#endif
+ for (i = 0; i < symbols_count; ++i) {
+ sym = symbols[i];
+ section = bfd_asymbol_section(sym);
+ if (!(sym->flags & (BSF_GLOBAL)))
+ continue;
+
+ while (i + 1 < symbols_count &&
+ bfd_asymbol_section(symbols[i + 1]) == section &&
+ !(symbols[i + 1]->flags & BSF_GLOBAL))
+ i++;
+
+ if (i + 1 < symbols_count &&
+ bfd_asymbol_section(symbols[i + 1]) == section)
+ len = symbols[i + 1]->value - sym->value;
+ else
+ len = section->size - sym->value;
+
+ start = bfd_asymbol_value(sym) - dso->text_offset;
+ symbol = symbol__new(start, len, STB_GLOBAL, STT_FUNC,
+ bfd_asymbol_name(sym));
+ if (!symbol)
+ goto out_free;
+
+ symbols__insert(&dso->symbols, symbol);
+ }
+#ifdef bfd_get_section
+#undef bfd_asymbol_section
+#endif
+
+ symbols__fixup_end(&dso->symbols);
+ symbols__fixup_duplicate(&dso->symbols);
+ dso->adjust_symbols = 1;
+
+ ret = true;
+out_free:
+ free(symbols);
+out_close:
+ bfd_close(abfd);
+ return ret;
+}
+#endif
+
static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
enum dso_binary_type type)
{
@@ -1691,6 +1806,7 @@ int dso__load(struct dso *dso, struct map *map)
bool next_slot = false;
bool is_reg;
bool nsexit;
+ bool bfd_syms = false;
int sirc = -1;

enum dso_binary_type symtab_type = binary_type_symtab[i];
@@ -1712,9 +1828,17 @@ int dso__load(struct dso *dso, struct map *map)
if (is_reg)
sirc = symsrc__init(ss, dso, name, symtab_type);

+#ifdef HAVE_LIBBFD_SUPPORT
+ if (is_reg && sirc < 0)
+ bfd_syms = dso__load_bfd_symbols(dso, name);
+#endif
+
if (nsexit)
nsinfo__mountns_enter(dso->nsinfo, &nsc);

+ if (bfd_syms)
+ break;
+
if (!is_reg || sirc < 0)
continue;

--
2.26.1