[PATCH] perf symbols: Fix wrong symbol offset according to p_addr

From: He Kuang
Date: Wed Jan 06 2016 - 03:26:52 EST


Perf missed the 'VirtAddr' value in elf program_headers when adjusting
symbol address for dynamic libraries. This patch considers the p_addr
value and gets the right symbol offset.

Before this patch, some symbols can not be right parsed on android phone:

init 369 [002] 339.970607: raw_syscalls:sys_enter: NR 22 (b, 7fd9e360a0, 10, ffffffff, 0, 8)
...
230ac [unknown] (/system/lib64/libsurfaceflinger.so)
11a0 main (/system/bin/surfaceflinger)
1c3fc __libc_init (/system/lib64/libc.so)
fd0 _start (/system/bin/surfaceflinger)
29ec __dl__start (/system/bin/linker64)

After this patch:

init 369 [002] 339.970607: raw_syscalls:sys_enter: NR 22 (b, 7fd9e360a0, 10, ffffffff, 0, 8)
...
3a0ac _ZN7android14SurfaceFlinger3runEv (/system/lib64/libsurfaceflinger.so)
11a0 main (/system/bin/surfaceflinger)
1c3fc __libc_init (/system/lib64/libc.so)
fd0 _start (/system/bin/surfaceflinger)
29ec __dl__start (/system/bin/linker64)

Signed-off-by: He Kuang <hekuang@xxxxxxxxxx>
---
tools/perf/util/dso.c | 6 ++++++
tools/perf/util/dso.h | 1 +
tools/perf/util/event.c | 6 +++++-
tools/perf/util/map.h | 7 +++++--
tools/perf/util/symbol-elf.c | 20 ++++++++++++++++++++
5 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index e8e9a9d..93bf13a 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -1063,6 +1063,7 @@ struct dso *dso__new(const char *name)
dso->needs_swap = DSO_SWAP__UNSET;
RB_CLEAR_NODE(&dso->rb_node);
dso->root = NULL;
+ dso->vaddr = 0;
INIT_LIST_HEAD(&dso->node);
INIT_LIST_HEAD(&dso->data.open_entry);
pthread_mutex_init(&dso->lock, NULL);
@@ -1367,3 +1368,8 @@ int dso__strerror_load(struct dso *dso, char *buf, size_t buflen)
scnprintf(buf, buflen, "%s", dso_load__error_str[idx]);
return 0;
}
+
+u64 dso__get_vaddr(struct dso *dso)
+{
+ return dso->vaddr;
+}
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 45ec4d0..b6c894f 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -166,6 +166,7 @@ struct dso {
const char *long_name;
u16 long_name_len;
u16 short_name_len;
+ u64 vaddr;
void *dwfl; /* DWARF debug info */
struct auxtrace_cache *auxtrace_cache;

diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index cd61bb1..ef964c0 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -1273,8 +1273,12 @@ try_again:
* Kernel maps might be changed when loading symbols so loading
* must be done prior to using kernel maps.
*/
+ map__load(al->map, machine->symbol_filter);
+
+ /* exclude kernel kallsyms */
if (load_map)
- map__load(al->map, machine->symbol_filter);
+ al->map->dso->vaddr = 0;
+
al->addr = al->map->map_ip(al->map, al->addr);
}
}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 7309d64..68ab703 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -86,14 +86,17 @@ void map_groups__put(struct map_groups *mg);
struct kmap *map__kmap(struct map *map);
struct map_groups *map__kmaps(struct map *map);

+u64 dso__get_vaddr(struct dso *dso);
static inline u64 map__map_ip(struct map *map, u64 ip)
{
- return ip - map->start + map->pgoff;
+ return ip - map->start + map->pgoff +
+ dso__get_vaddr(map->dso);
}

static inline u64 map__unmap_ip(struct map *map, u64 ip)
{
- return ip + map->start - map->pgoff;
+ return ip + map->start - map->pgoff -
+ dso__get_vaddr(map->dso);
}

static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip)
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
index 562b8eb..de0129f 100644
--- a/tools/perf/util/symbol-elf.c
+++ b/tools/perf/util/symbol-elf.c
@@ -15,6 +15,8 @@
#define EM_AARCH64 183 /* ARM 64 bit */
#endif

+static int elf_read_maps_ex(Elf * elf, bool exe, mapfn_t mapfn, void *data,
+ struct map *map);

#ifdef HAVE_CPLUS_DEMANGLE_SUPPORT
extern char *cplus_demangle(const char *, int);
@@ -831,6 +833,10 @@ int dso__load_sym(struct dso *dso, struct map *map,
sec = syms_ss->symtab;
shdr = syms_ss->symshdr;

+ err = elf_read_maps_ex(elf, ehdr.e_type == ET_EXEC ||
+ ehdr.e_type == ET_REL,
+ NULL, NULL, map);
+
if (runtime_ss->opdsec)
opddata = elf_rawdata(runtime_ss->opdsec, NULL);

@@ -1116,6 +1122,13 @@ out_elf_end:

static int elf_read_maps(Elf *elf, bool exe, mapfn_t mapfn, void *data)
{
+
+ return elf_read_maps_ex(elf, exe, mapfn, data, NULL);
+}
+
+static int elf_read_maps_ex(Elf *elf, bool exe, mapfn_t mapfn, void *data,
+ struct map *map)
+{
GElf_Phdr phdr;
size_t i, phdrnum;
int err;
@@ -1135,10 +1148,17 @@ static int elf_read_maps(Elf *elf, bool exe, mapfn_t mapfn, void *data)
} else {
if (!(phdr.p_flags & PF_R))
continue;
+ if (map && (phdr.p_flags & PF_X))
+ map->dso->vaddr = phdr.p_vaddr;
}
+
sz = min(phdr.p_memsz, phdr.p_filesz);
if (!sz)
continue;
+
+ if (!mapfn)
+ continue;
+
err = mapfn(phdr.p_vaddr, sz, phdr.p_offset, data);
if (err)
return err;
--
1.8.5.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/