#include "elf-image-cache.h" #include "libunwind-l4.h" #include "libunwind_i.h" #include "os-l4.h" // libunwind uses pipes to validate addresses before local memory accesses, // however it should not be relevant for remote unwinding. int pipe (int __pipedes[2]) { (void) __pipedes; Debug(1, "WARN: Pipe is unimplemented!\n"); return -1; } // Linear search is necessary because l4re binaries do not emit a .eh_frame_hdr section or program header. static int linear_eh_frame_search(struct elf_image *ei, unsigned long segbase, unsigned long mapoff, unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, void *arg) { Elf_W(Shdr) *shdr = elf_w(find_section) (ei, ".eh_frame"); if (!shdr) return -UNW_ENOINFO; // eh_frame address in remote process Elf_W(Addr) eh_frame_start = shdr->sh_offset + segbase - mapoff; Elf_W(Addr) eh_frame_end = eh_frame_start + shdr->sh_size; unw_accessors_t *a = unw_get_accessors_int (as); int ret = -UNW_ENOINFO; unw_word_t addr = eh_frame_start; while (addr < eh_frame_end) { unw_word_t fde_addr = addr; if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, segbase, 0, 0, arg)) < 0) return ret; if (ip >= pi->start_ip && ip < pi->end_ip) { addr = fde_addr; if ((ret = dwarf_extract_proc_info_from_fde (as, a, &addr, pi, segbase, 1, 0, arg)) < 0) return ret; // Have to manually clear the flags here, as dwarf_extract_proc_info_from_fde does not do it, // and thus from a failed dwarf_search_unwind_table run for .debug_frame the UNW_PI_FLAG_DEBUG_FRAME flag might be set, // which later breaks CFI program execution. pi->flags = 0; return UNW_ESUCCESS; } } return ret; } static int l4_find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, int need_unwind_info, void *arg) { l4_unw_data_t *data = arg; struct elf_dyn_info edi; memset(&edi, 0, sizeof(edi)); invalidate_edi(&edi); /* where it is mapped in virtual memory */ unsigned long segbase; /* offset in the file */ unsigned long mapoff; char path[256]; if (l4_find_elf(data->rm, ip, &segbase, &mapoff, path, sizeof(path))) return -UNW_ENOINFO; elf_image_handle_t *elf = get_elf_image(path); if (!elf) return -UNW_ENOINFO; edi.ei = elf->ei; int ret = -UNW_ENOINFO; if (tdep_find_unwind_table(&edi, as, path, segbase, mapoff, ip) >= 0) { if (edi.di_cache.format != -1) ret = tdep_search_unwind_table (as, ip, &edi.di_cache, pi, need_unwind_info, arg); if (ret == -UNW_ENOINFO && edi.di_debug.format != -1) ret = tdep_search_unwind_table (as, ip, &edi.di_debug, pi, need_unwind_info, arg); #if UNW_TARGET_ARM if (ret == -UNW_ENOINFO && edi.di_arm.format != -1) ret = tdep_search_unwind_table (as, ip, &edi.di_arm, pi, need_unwind_info, arg); #endif } // Fall back to lineary searching eh_frame if no binary lookup table (.eh_frame_hdr) or .debug_frame is present, // which is what is supported by tdep_find_unwind_table and tdep_search_unwind_table. if (ret == -UNW_ENOINFO && edi.di_cache.format == -1) ret = linear_eh_frame_search(&edi.ei, segbase, mapoff, as, ip, pi, arg); return ret; } static int l4_get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr, void *arg) { #ifndef UNW_LOCAL_ONLY # pragma weak _U_dyn_info_list_addr if (!_U_dyn_info_list_addr) return -UNW_ENOINFO; #endif // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so. *dyn_info_list_addr = _U_dyn_info_list_addr (); return 0; } static int l4_access_mem_remote(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp, int write, void *arg) { (void)as; l4_unw_data_t *data = arg; l4_umword_t val; int res = data->remote_mem(addr, &val, write, data->remote_mem_arg); if (res >= 0) *valp = val; return res; } static int l4_access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp, int write, void *arg) { (void)as; l4_unw_data_t *data = arg; l4_exc_regs_t *regs = data->regs; l4_umword_t *reg; Debug(16, "reg=%d write=%d\n", regnum, write); #ifdef ARCH_arm if (regnum >= UNW_ARM_R0 && regnum <= UNW_ARM_R12) reg = ®s->r[regnum]; else if (regnum == UNW_TDEP_SP) reg = ®s->sp; else if (regnum == UNW_ARM_R14) reg = ®s->ulr; else if (regnum == UNW_TDEP_IP) reg = ®s->pc; else { printf("Unknown/unhandled regnum %d\n", regnum); return -UNW_EBADREG; } #elif defined(ARCH_arm64) if (regnum >= UNW_AARCH64_X0 && regnum <= UNW_AARCH64_X30) reg = ®s->r[regnum - UNW_AARCH64_X0]; else if(regnum == UNW_AARCH64_SP) reg = ®s->sp; else if(regnum == UNW_AARCH64_PC) reg = ®s->pc; else { printf("Unknown/unhandled regnum %d\n", regnum); return -UNW_EBADREG; } #elif defined(ARCH_x86) switch (regnum) { case UNW_X86_EAX: reg = ®s->eax; break; case UNW_X86_EDX: reg = ®s->edx; break; case UNW_X86_ECX: reg = ®s->ecx; break; case UNW_X86_EBX: reg = ®s->ebx; break; case UNW_X86_ESI: reg = ®s->esi; break; case UNW_X86_EDI: reg = ®s->edi; break; case UNW_X86_EBP: reg = ®s->ebp; break; case UNW_X86_ESP: reg = ®s->sp; break; case UNW_X86_EIP: reg = ®s->ip; break; case UNW_X86_EFLAGS: reg = ®s->flags; break; case UNW_X86_TRAPNO: reg = ®s->trapno; break; default: printf("Unknown/unhandled regnum %d\n", regnum); return -UNW_EBADREG; }; #elif defined(ARCH_amd64) switch (regnum) { case UNW_X86_64_RAX: reg = ®s->rax; break; case UNW_X86_64_RDX: reg = ®s->rdx; break; case UNW_X86_64_RCX: reg = ®s->rcx; break; case UNW_X86_64_RBX: reg = ®s->rbx; break; case UNW_X86_64_RSI: reg = ®s->rsi; break; case UNW_X86_64_RDI: reg = ®s->rdi; break; case UNW_X86_64_RBP: reg = ®s->rbp; break; case UNW_X86_64_RSP: reg = ®s->sp; break; case UNW_X86_64_RIP: reg = ®s->ip; break; case UNW_X86_64_R8: reg = ®s->r8; break; case UNW_X86_64_R9: reg = ®s->r9; break; case UNW_X86_64_R10: reg = ®s->r10; break; case UNW_X86_64_R11: reg = ®s->r11; break; case UNW_X86_64_R12: reg = ®s->r12; break; case UNW_X86_64_R13: reg = ®s->r13; break; case UNW_X86_64_R14: reg = ®s->r14; break; case UNW_X86_64_R15: reg = ®s->r15; break; default: printf("Unknown/unhandled regnum %d\n", regnum); return -UNW_EBADREG; }; #elif defined(ARCH_riscv) if (regnum == UNW_RISCV_X0) { if (!write) *valp = 0; return 0; } else if (regnum >= UNW_RISCV_X1 && regnum <= UNW_RISCV_X31) reg = ®s->r[regnum - UNW_RISCV_X1]; else if(regnum == UNW_RISCV_PC) reg = ®s->pc; else { printf("Unknown/unhandled regnum %d\n", regnum); return -UNW_EBADREG; } #elif defined(ARCH_ppc32) if (regnum >= UNW_PPC32_R0 && regnum <= UNW_PPC32_R31) reg = ®s->r[regnum - UNW_PPC32_R0]; else { printf("Unknown/unhandled regnum %d\n", regnum); return -UNW_EBADREG; } #elif defined(ARCH_sparc) printf("Unknown/unhandled regnum %d\n", regnum); return -UNW_EBADREG; #elif defined(ARCH_mips) printf("Unknown/unhandled regnum %d\n", regnum); return -UNW_EBADREG; #else #error libunwind: Add missing architecture support #endif if (write) *reg = *valp; else *valp = *reg; Debug(16, "reg %d = 0x%lx\n", regnum, *valp); return 0; } static int l4_access_fpreg(unw_addr_space_t as, unw_regnum_t regnum, unw_fpreg_t *fwalp, int write, void *arg) { (void)as; (void)regnum; (void)fwalp; (void)write; (void)arg; printf("l4_access_fpreg(regnum=%d, write=%d): Not implemented\n", regnum, write); return -UNW_EINVAL; } static int l4_resume(unw_addr_space_t as, unw_cursor_t *cp, void *arg) { (void)as; (void)cp; (void)arg; printf("l4_resume(): Not implemented\n"); return -UNW_EINVAL; } static int l4_get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp, size_t buf_len, unw_word_t *offp, void *arg) { l4_unw_data_t *data = arg; return elf_w(get_proc_name) (as, l4_to_pid(data->rm), addr, bufp, buf_len, offp); } unw_accessors_t l4_unw_accessors = { .find_proc_info = l4_find_proc_info, .put_unwind_info = dwarf_put_unwind_info, .get_dyn_info_list_addr = l4_get_dyn_info_list_addr, .access_mem = l4_access_mem_remote, .access_reg = l4_access_reg, .access_fpreg = l4_access_fpreg, .resume = l4_resume, .get_proc_name = l4_get_proc_name, };