perf tools: Pass build_id object to dso__set_build_id()
[linux-2.6-microblaze.git] / tools / perf / util / symbol.c
index 5151a8c..976632d 100644 (file)
@@ -1526,6 +1526,138 @@ out_failure:
        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 int bfd2elf_binding(asymbol *symbol)
+{
+       if (symbol->flags & BSF_WEAK)
+               return STB_WEAK;
+       if (symbol->flags & BSF_GLOBAL)
+               return STB_GLOBAL;
+       if (symbol->flags & BSF_LOCAL)
+               return STB_LOCAL;
+       return -1;
+}
+
+int dso__load_bfd_symbols(struct dso *dso, const char *debugfile)
+{
+       int err = -1;
+       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;
+       }
+
+       if (bfd_get_flavour(abfd) == bfd_target_elf_flavour)
+               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;
+       }
+
+       if (bfd_get_flavour(abfd) == bfd_target_elf_flavour)
+               goto out_close;
+
+       symbols_size = bfd_get_symtab_upper_bound(abfd);
+       if (symbols_size == 0) {
+               bfd_close(abfd);
+               return 0;
+       }
+
+       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 (bfd2elf_binding(sym) < 0)
+                       continue;
+
+               while (i + 1 < symbols_count &&
+                      bfd_asymbol_section(symbols[i + 1]) == section &&
+                      bfd2elf_binding(symbols[i + 1]) < 0)
+                       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, bfd2elf_binding(sym), 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;
+
+       err = 0;
+out_free:
+       free(symbols);
+out_close:
+       bfd_close(abfd);
+       return err;
+}
+#endif
+
 static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
                                           enum dso_binary_type type)
 {
@@ -1623,7 +1755,7 @@ int dso__load(struct dso *dso, struct map *map)
        struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
        bool kmod;
        bool perfmap;
-       unsigned char build_id[BUILD_ID_SIZE];
+       struct build_id bid;
        struct nscookie nsc;
        char newmapname[PATH_MAX];
        const char *map_path = dso->long_name;
@@ -1685,8 +1817,8 @@ int dso__load(struct dso *dso, struct map *map)
        if (!dso->has_build_id &&
            is_regular_file(dso->long_name)) {
            __symbol__join_symfs(name, PATH_MAX, dso->long_name);
-           if (filename__read_build_id(name, build_id, BUILD_ID_SIZE) > 0)
-               dso__set_build_id(dso, build_id);
+               if (filename__read_build_id(name, &bid) > 0)
+                       dso__set_build_id(dso, &bid);
        }
 
        /*
@@ -1699,6 +1831,7 @@ int dso__load(struct dso *dso, struct map *map)
                bool next_slot = false;
                bool is_reg;
                bool nsexit;
+               int bfdrc = -1;
                int sirc = -1;
 
                enum dso_binary_type symtab_type = binary_type_symtab[i];
@@ -1717,12 +1850,19 @@ int dso__load(struct dso *dso, struct map *map)
                        nsinfo__mountns_exit(&nsc);
 
                is_reg = is_regular_file(name);
+#ifdef HAVE_LIBBFD_SUPPORT
                if (is_reg)
+                       bfdrc = dso__load_bfd_symbols(dso, name);
+#endif
+               if (is_reg && bfdrc < 0)
                        sirc = symsrc__init(ss, dso, name, symtab_type);
 
                if (nsexit)
                        nsinfo__mountns_enter(dso->nsinfo, &nsc);
 
+               if (bfdrc == 0)
+                       break;
+
                if (!is_reg || sirc < 0)
                        continue;
 
@@ -1982,7 +2122,7 @@ static bool filename__readable(const char *file)
 
 static char *dso__find_kallsyms(struct dso *dso, struct map *map)
 {
-       u8 host_build_id[BUILD_ID_SIZE];
+       struct build_id bid;
        char sbuild_id[SBUILD_ID_SIZE];
        bool is_host = false;
        char path[PATH_MAX];
@@ -1995,9 +2135,8 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
                goto proc_kallsyms;
        }
 
-       if (sysfs__read_build_id("/sys/kernel/notes", host_build_id,
-                                sizeof(host_build_id)) == 0)
-               is_host = dso__build_id_equal(dso, host_build_id);
+       if (sysfs__read_build_id("/sys/kernel/notes", &bid) == 0)
+               is_host = dso__build_id_equal(dso, bid.data);
 
        /* Try a fast path for /proc/kallsyms if possible */
        if (is_host) {
@@ -2013,7 +2152,7 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map)
                        goto proc_kallsyms;
        }
 
-       build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
+       build_id__sprintf(&dso->bid, sbuild_id);
 
        /* Find kallsyms in build-id cache with kcore */
        scnprintf(path, sizeof(path), "%s/%s/%s",