From 5f440b4af2b5dcae2f15652999b9aca7db55d02c Mon Sep 17 00:00:00 2001 From: Konstantin Belousov Date: Mon, 23 Apr 2012 13:51:55 +0300 Subject: [PATCH] freebsd: Workaround for old FreeBSD kernels Older kernels interpret the pid argument of the process information sysctls as pid only. If libunwind UPT consumer passed tid to _UPT_create, tdep_get_elf_image() returns error due to sysctls failure. Provide a slow workaround by searching for a process owning the supplied tid if sysctl returned ESRCH. --- src/os-freebsd.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/os-freebsd.c b/src/os-freebsd.c index 203347ea..76cdd23c 100644 --- a/src/os-freebsd.c +++ b/src/os-freebsd.c @@ -28,6 +28,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include +#include #include "libunwind_i.h" @@ -48,6 +49,45 @@ free_mem(void *ptr, size_t sz) munmap(ptr, sz); } +static int +get_pid_by_tid(int tid) +{ + int mib[3], error; + size_t len, len1; + char *buf; + struct kinfo_proc *kv; + int i, pid; + + len = 0; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ALL; + + error = sysctl(mib, 3, NULL, &len, NULL, 0); + if (error == -1) + return (-1); + len1 = len * 4 / 3; + buf = get_mem(len1); + if (buf == NULL) + return (-1); + len = len1; + error = sysctl(mib, 3, buf, &len, NULL, 0); + if (error == -1) { + free_mem(buf, len1); + return (-1); + } + pid = -1; + for (i = 0, kv = (struct kinfo_proc *)buf; i < len / sizeof(*kv); + i++, kv++) { + if (kv->ki_tid == tid) { + pid = kv->ki_pid; + break; + } + } + free_mem(buf, len1); + return (pid); +} + PROTECTED int tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, unsigned long *segbase, unsigned long *mapoff, char *path, size_t pathlen) @@ -64,8 +104,16 @@ tdep_get_elf_image (struct elf_image *ei, pid_t pid, unw_word_t ip, mib[3] = pid; error = sysctl(mib, 4, NULL, &len, NULL, 0); - if (error) - return (-1); + if (error == -1) { + if (errno == ESRCH) { + mib[3] = get_pid_by_tid(pid); + if (mib[3] != -1) + error = sysctl(mib, 4, NULL, &len, NULL, 0); + if (error == -1) + return (-UNW_EUNSPEC); + } else + return (-UNW_EUNSPEC); + } len1 = len * 4 / 3; buf = get_mem(len1); if (buf == NULL)