[PATCH v2] perf symbols: Fix wrong symbol offset according to p_addr
From: He Kuang
Date: Mon Jan 11 2016 - 22:05:35 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 calculates the right symbol offset.
Before this patch, some symbols are not parsed correctly 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>
---
Patch v1 to v2:
- Modify commit message by following Ingo's suggestion.
- Change ambiguous virable name 'vaddr' to 'load_virtaddr'.
---
tools/perf/util/dso.c | 6 ++++++
tools/perf/util/dso.h | 3 ++-
tools/perf/util/event.c | 6 +++++-
tools/perf/util/map.h | 7 +++++--
tools/perf/util/symbol-elf.c | 20 ++++++++++++++++++++
5 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index e8e9a9d..afc51ce 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->load_virtaddr = 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_load_virtaddr(struct dso *dso)
+{
+ return dso->load_virtaddr;
+}
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 45ec4d0..2a03b87 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -166,7 +166,8 @@ struct dso {
const char *long_name;
u16 long_name_len;
u16 short_name_len;
- void *dwfl; /* DWARF debug info */
+ u64 load_virtaddr; /* Load virtual address */
+ void *dwfl; /* DWARF debug info */
struct auxtrace_cache *auxtrace_cache;
/* dso data file */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index cd61bb1..7f1dbdf 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->load_virtaddr = 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..db284a4 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_load_virtaddr(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_load_virtaddr(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_load_virtaddr(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..9a02b3e 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->load_virtaddr = 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