From 1493c0ca4b75f371ce749975132736cb95af6436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Mon, 11 Jun 2018 19:53:29 +0200 Subject: [PATCH] Add memory_map direct providing method Allow to give the memory map directly through the accessors, in case this is more handy than providing a PID for some reason (cf. perf integration) --- include/libunwind-common.h.in | 37 ++++++++- src/Makefile.am | 2 +- src/eh_elf/eh_elf.c | 6 ++ src/eh_elf/eh_elf.h | 5 ++ src/eh_elf/memory_map.c | 83 +++++++++++++++++-- src/eh_elf/memory_map.h | 7 ++ src/ptrace/_UPT_accessors.c | 7 +- .../{_UPT_get_pid.c => _UPT_eh_elf_init.c} | 0 src/x86_64/Ginit_remote.c | 20 ++++- 9 files changed, 152 insertions(+), 15 deletions(-) rename src/ptrace/{_UPT_get_pid.c => _UPT_eh_elf_init.c} (100%) diff --git a/include/libunwind-common.h.in b/include/libunwind-common.h.in index eb156018..2a95131c 100644 --- a/include/libunwind-common.h.in +++ b/include/libunwind-common.h.in @@ -138,6 +138,37 @@ typedef struct unw_proc_info } unw_proc_info_t; +/// A structure containing the informations gathererd about a line in the +/// memory map +typedef struct { + uintptr_t offset; ///< Total offset: ip + offset = ip in original ELF file + char* object_name; ///< Name of the object mapped here + uintptr_t beg_ip, end_ip; ///< Start and end IP of this object in memory +} unw_mmap_entry_t; + +typedef enum { + UNW_EH_ELF_INIT_PID, ///< Use PID to init eh_elf + UNW_EH_ELF_INIT_MMAP, ///< Directly provide a memory map yourself +} unw_eh_elf_init_mode; + +/** This structure is passed among the accessors, and contains what's needed to + * init eh_elf unwinding. */ +struct unw_eh_elf_init_acc { + unw_eh_elf_init_mode init_mode; + union { + /// Provide this with UNW_EH_ELF_INIT_PID. + /// int get_pid(void* arg) returns the process' PID + int (*get_pid) (void *); + + /// Provide this with UNW_EH_ELF_INIT_MMAP + void (*get_mmap) + (unw_mmap_entry_t**, ///< An array of mmap_entries + size_t*, ///< number of entries + void* ///< `arg` + ); + } init_data; +}; + /* These are backend callback routines that provide access to the state of a "remote" process. This can be used, for example, to unwind another process through the ptrace() interface. */ @@ -181,10 +212,8 @@ typedef struct unw_accessors int (*get_proc_name) (unw_addr_space_t, unw_word_t, char *, size_t, unw_word_t *, void *); - /* Retrieve the PID of the process this memory belongs to. - Returns 0 for local memory or when undefined. - This must be non-zero for init_remote. */ - int (*get_pid) (void *); + /* A substructure of accessors used to init eh_elf unwinding. */ + struct unw_eh_elf_init_acc eh_elf_init; } unw_accessors_t; diff --git a/src/Makefile.am b/src/Makefile.am index dd29456b..cb0fb1ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,7 +49,7 @@ libunwind_ptrace_la_SOURCES = \ ptrace/_UPT_find_proc_info.c ptrace/_UPT_get_dyn_info_list_addr.c \ ptrace/_UPT_put_unwind_info.c ptrace/_UPT_get_proc_name.c \ ptrace/_UPT_reg_offset.c ptrace/_UPT_resume.c \ - ptrace/_UPT_get_pid.c + ptrace/_UPT_eh_elf_init.c noinst_HEADERS += ptrace/_UPT_internal.h ### libunwind-coredump: diff --git a/src/eh_elf/eh_elf.c b/src/eh_elf/eh_elf.c index 9fc174c1..2a56254f 100644 --- a/src/eh_elf/eh_elf.c +++ b/src/eh_elf/eh_elf.c @@ -23,9 +23,15 @@ int eh_elf_init_local() { } int eh_elf_init_pid(pid_t pid) { + Debug(3, "Init with pid\n"); return mmap_init_pid(pid); } +int eh_elf_init_mmap(unw_mmap_entry_t* entries, size_t count) { + Debug(3, "Init with mmap\n"); + return mmap_init_mmap(entries, count); +} + void eh_elf_clear() { mmap_clear(); } diff --git a/src/eh_elf/eh_elf.h b/src/eh_elf/eh_elf.h index 6c18230d..32d18f89 100644 --- a/src/eh_elf/eh_elf.h +++ b/src/eh_elf/eh_elf.h @@ -27,6 +27,11 @@ int eh_elf_init_local(); **/ int eh_elf_init_pid(pid_t pid); +/** Initialize everything with the provided memory map + * @return 0 on success, or a negative value upon failure + **/ +int eh_elf_init_mmap(unw_mmap_entry_t* entries, size_t count); + /// Cleanup everything that was allocated by eh_elf_init_* void eh_elf_clear(); diff --git a/src/eh_elf/memory_map.c b/src/eh_elf/memory_map.c index e2c035a8..dc07c2de 100644 --- a/src/eh_elf/memory_map.c +++ b/src/eh_elf/memory_map.c @@ -13,6 +13,13 @@ static int _mmap_init_done = 0; /// Init the memory map with a given /proc/XX/ argument int mmap_init_procdir(const char* procdir); +/// Reorder the entries in `entries` by increasing (non-overlapping) +/// memory region +static int mmap_order_entries(mmap_entry_t* entries, size_t count); + +/// `dlopen`s the corresponding eh_elf for each entry in `entries`. +static int mmap_dlopen_eh_elfs(mmap_entry_t* entries, size_t count); + static int compare_mmap_entry(const void* _e1, const void* _e2) { // We can't return e1->beg_ip - e2->beg_ip because of int overflows @@ -101,22 +108,82 @@ int mmap_init_procdir(const char* procdir) { realloc(_memory_map, _memory_map_size * sizeof(mmap_entry_t)); // Ensure the entries are sorted by ascending ip range - qsort(_memory_map, _memory_map_size, sizeof(mmap_entry_t), - compare_mmap_entry); + if(mmap_order_entries(_memory_map, _memory_map_size) < 0) + return -3; // dlopen corresponding eh_elf objects - for(size_t id = 0; id < _memory_map_size; ++id) { - char eh_elf_path[256]; - char* obj_basename = basename(_memory_map[id].object_name); - sprintf(eh_elf_path, "%s.eh_elf.so", obj_basename); - _memory_map[id].eh_elf = dlopen(eh_elf_path, RTLD_LAZY); - } + if(mmap_dlopen_eh_elfs(_memory_map, _memory_map_size) < 0) + return -4; _mmap_init_done = 1; return 0; } +int mmap_init_mmap(unw_mmap_entry_t* entries, size_t count) { + Debug(3, "Start reading mmap (entries=%016lx)\n", (uintptr_t)entries); + Debug(3, "%lu entries\n", count); + _memory_map = (mmap_entry_t*) calloc(count, sizeof(mmap_entry_t)); + _memory_map_size = count; + + int mmap_pos = 0; + for(int pos=0; pos < (int)count; ++pos) { + Debug(3, "> MMAP %016lx-%016lx %s\n", + entries[pos].beg_ip, + entries[pos].end_ip, + entries[pos].object_name); + + if(entries[pos].object_name[0] == '[') { + // Special entry (stack,vdso, …) + continue; + } + + _memory_map[mmap_pos].id = pos; + _memory_map[mmap_pos].offset = entries[pos].offset; + _memory_map[mmap_pos].beg_ip = entries[pos].beg_ip; + _memory_map[mmap_pos].end_ip = entries[pos].end_ip; + _memory_map[mmap_pos].object_name = + malloc(strlen(entries[pos].object_name) + 1); + strcpy(_memory_map[mmap_pos].object_name, entries[pos].object_name); + + ++mmap_pos; + } + + // Shrink memory map + _memory_map_size = mmap_pos; + _memory_map = (mmap_entry_t*) + realloc(_memory_map, _memory_map_size * sizeof(mmap_entry_t)); + + if(mmap_order_entries(_memory_map, _memory_map_size) < 0) + return -3; + if(mmap_dlopen_eh_elfs(_memory_map, _memory_map_size) < 0) + return -4; + Debug(3, "Init complete"); + return 0; +} + +/// Ensure entries in the memory map are ordered by ascending beg_ip +static int mmap_order_entries(mmap_entry_t* entries, size_t count) { + qsort(entries, count, sizeof(mmap_entry_t), + compare_mmap_entry); + for(size_t pos = 0; pos < count; ++pos) + entries[pos].id = pos; + return 0; +} + +/// `dlopen` the needed eh_elf objects. +static int mmap_dlopen_eh_elfs(mmap_entry_t* entries, size_t count) { + for(size_t id = 0; id < count; ++id) { + char eh_elf_path[256]; + char* obj_basename = basename(entries[id].object_name); + sprintf(eh_elf_path, "%s.eh_elf.so", obj_basename); + entries[id].eh_elf = dlopen(eh_elf_path, RTLD_LAZY); + if(entries[id].eh_elf == NULL) + return -1; + } + return 0; +} + void mmap_clear() { _mmap_init_done = 0; diff --git a/src/eh_elf/memory_map.h b/src/eh_elf/memory_map.h index c5d8dd06..5f9a2235 100644 --- a/src/eh_elf/memory_map.h +++ b/src/eh_elf/memory_map.h @@ -18,6 +18,8 @@ #include #include +#include "libunwind.h" + /// A type representing a dlopen handle typedef void* dl_obj_t; @@ -44,6 +46,11 @@ int mmap_init_local(); **/ int mmap_init_pid(pid_t pid); +/** Init the memory map from a provided memory map + * @returns 0 upon success, or a negative value upon failure. + **/ +int mmap_init_mmap(unw_mmap_entry_t* entries, size_t count); + /** Get the `mmap_entry_t` corresponding to the given IP * @return a pointer to the corresponding memory map entry, or NULL upon * failure. diff --git a/src/ptrace/_UPT_accessors.c b/src/ptrace/_UPT_accessors.c index 4c2e7bbd..5c7f89a8 100644 --- a/src/ptrace/_UPT_accessors.c +++ b/src/ptrace/_UPT_accessors.c @@ -35,5 +35,10 @@ PROTECTED unw_accessors_t _UPT_accessors = .access_fpreg = _UPT_access_fpreg, .resume = _UPT_resume, .get_proc_name = _UPT_get_proc_name, - .get_pid = _UPT_get_pid + .eh_elf_init = { + .init_mode = UNW_EH_ELF_INIT_PID, + .init_data = { + .get_pid = _UPT_get_pid + } + } }; diff --git a/src/ptrace/_UPT_get_pid.c b/src/ptrace/_UPT_eh_elf_init.c similarity index 100% rename from src/ptrace/_UPT_get_pid.c rename to src/ptrace/_UPT_eh_elf_init.c diff --git a/src/x86_64/Ginit_remote.c b/src/x86_64/Ginit_remote.c index f9886322..93399143 100644 --- a/src/x86_64/Ginit_remote.c +++ b/src/x86_64/Ginit_remote.c @@ -36,13 +36,31 @@ unw_init_remote (unw_cursor_t *cursor, unw_addr_space_t as, void *as_arg) return -UNW_EINVAL; #else /* !UNW_LOCAL_ONLY */ struct cursor *c = (struct cursor *) cursor; + int ret; + struct unw_eh_elf_init_acc* eh_elf_acc = &as->acc.eh_elf_init; if (!tdep_init_done) tdep_init (); Debug (1, "(cursor=%p)\n", c); - eh_elf_init_pid(as->acc.get_pid(as_arg)); + switch(eh_elf_acc->init_mode) { + case UNW_EH_ELF_INIT_PID: + ret = eh_elf_init_pid(eh_elf_acc->init_data.get_pid(as_arg)); + if(ret < 0) + return ret; + break; + case UNW_EH_ELF_INIT_MMAP: { + unw_mmap_entry_t* entries; + size_t entries_count; + eh_elf_acc->init_data.get_mmap(&entries, &entries_count, as_arg); + ret = eh_elf_init_mmap(entries, entries_count); + free(entries); + if(ret < 0) + return ret; + break; + } + } c->dwarf.as = as; if (as == unw_local_addr_space)