Files
moslab-code/src/l4/pkg/libunwind/lib/build/libunwind-l4.c
2025-09-12 15:55:45 +02:00

346 lines
9.3 KiB
C

#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 = &regs->r[regnum];
else if (regnum == UNW_TDEP_SP)
reg = &regs->sp;
else if (regnum == UNW_ARM_R14)
reg = &regs->ulr;
else if (regnum == UNW_TDEP_IP)
reg = &regs->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 = &regs->r[regnum - UNW_AARCH64_X0];
else if(regnum == UNW_AARCH64_SP)
reg = &regs->sp;
else if(regnum == UNW_AARCH64_PC)
reg = &regs->pc;
else
{
printf("Unknown/unhandled regnum %d\n", regnum);
return -UNW_EBADREG;
}
#elif defined(ARCH_x86)
switch (regnum)
{
case UNW_X86_EAX:
reg = &regs->eax;
break;
case UNW_X86_EDX:
reg = &regs->edx;
break;
case UNW_X86_ECX:
reg = &regs->ecx;
break;
case UNW_X86_EBX:
reg = &regs->ebx;
break;
case UNW_X86_ESI:
reg = &regs->esi;
break;
case UNW_X86_EDI:
reg = &regs->edi;
break;
case UNW_X86_EBP:
reg = &regs->ebp;
break;
case UNW_X86_ESP:
reg = &regs->sp;
break;
case UNW_X86_EIP:
reg = &regs->ip;
break;
case UNW_X86_EFLAGS:
reg = &regs->flags;
break;
case UNW_X86_TRAPNO:
reg = &regs->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 = &regs->rax;
break;
case UNW_X86_64_RDX:
reg = &regs->rdx;
break;
case UNW_X86_64_RCX:
reg = &regs->rcx;
break;
case UNW_X86_64_RBX:
reg = &regs->rbx;
break;
case UNW_X86_64_RSI:
reg = &regs->rsi;
break;
case UNW_X86_64_RDI:
reg = &regs->rdi;
break;
case UNW_X86_64_RBP:
reg = &regs->rbp;
break;
case UNW_X86_64_RSP:
reg = &regs->sp;
break;
case UNW_X86_64_RIP:
reg = &regs->ip;
break;
case UNW_X86_64_R8:
reg = &regs->r8;
break;
case UNW_X86_64_R9:
reg = &regs->r9;
break;
case UNW_X86_64_R10:
reg = &regs->r10;
break;
case UNW_X86_64_R11:
reg = &regs->r11;
break;
case UNW_X86_64_R12:
reg = &regs->r12;
break;
case UNW_X86_64_R13:
reg = &regs->r13;
break;
case UNW_X86_64_R14:
reg = &regs->r14;
break;
case UNW_X86_64_R15:
reg = &regs->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 = &regs->r[regnum - UNW_RISCV_X1];
else if(regnum == UNW_RISCV_PC)
reg = &regs->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 = &regs->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,
};