l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
PKGDIR ?= ../..
L4DIR ?= $(PKGDIR)/../..
TARGET = libunwind.a libunwind.so
CONTRIB_INCDIR = libunwind
REQUIRES_LIBS = l4re_c l4re_c-util libbacktrace
PRIVATE_INCDIR = $(SRC_DIR)
SRC_C += libunwind-l4.c elf-image-cache.c helper.c
ARCH_MAP-x86 = x86
ARCH_MAP-amd64 = x86_64
ARCH_MAP-arm = arm
ARCH_MAP-arm64 = aarch64
ARCH_MAP-mips = mips
ARCH_MAP-riscv = riscv
ARCH_MAP-ppc32 = ppc32
# no sparc
PRIVATE_INCDIR += $(SRC_DIR)/../contrib/include
PRIVATE_INCDIR += $(SRC_DIR)/../contrib/src
PRIVATE_INCDIR += $(SRC_DIR)/../contrib/include/tdep
PRIVATE_INCDIR += $(SRC_DIR)/../contrib/include/tdep-$(ARCH_MAP-$(ARCH))
vpath %.c $(SRC_DIR)/../contrib/src
vpath %.S $(SRC_DIR)/../contrib/src
vpath %.c $(SRC_DIR)/../contrib/src/$(ARCH_MAP-$(ARCH))
vpath %.S $(SRC_DIR)/../contrib/src/$(ARCH_MAP-$(ARCH))
# ppc has a shared dir for ppc32+ppc64, for others we just have the include
# paths twice
vpath %.c $(SRC_DIR)/../contrib/src/$(if $(filter ppc32,$(ARCH)),ppc)
vpath %.S $(SRC_DIR)/../contrib/src/$(if $(filter ppc32,$(ARCH)),ppc))
vpath %.c $(SRC_DIR)/../contrib/src/dwarf
vpath %.c $(SRC_DIR)/../contrib/src/mi
SRC_C_UNWIND = os-l4.c Gapply_reg_state.c Gcreate_addr_space.c \
Gget_proc_info.c Gget_save_loc.c Gglobal.c \
Ginit_local.c Ginit.c Ginit_remote.c Gregs.c Greg_states_iterate.c \
Gresume.c Gstep.c is_fpreg.c regname.c Gexpr.c \
Gfde.c Gfind_proc_info-lsb.c Gfind_unwind_table.c global.c Gparser.c Gpe.c \
flush_cache.c Gdestroy_addr_space.c Gdyn-extract.c Gdyn-remote.c \
Gfind_dynamic_proc_info.c Gget_accessors.c Gget_fpreg.c \
Gget_proc_info_by_ip.c Gget_proc_name.c Gget_reg.c \
Gput_dynamic_unwind_info.c Gset_cache_size.c Gset_caching_policy.c \
Gaddress_validator.c \
Gset_fpreg.c Gset_reg.c init.c mempool.c strerror.c \
elf$(BITS).c
SRC_C_UNWIND-x86 = Gos-l4.c
SRC_C_UNWIND-amd64 = Gos-l4.c Gstash_frame.c Gtrace.c
SRC_C_UNWIND-arm = Gos-l4.c Gex_tables.c Gstash_frame.c Gtrace.c
SRC_C_UNWIND-arm64 = Gos-l4.c Gis_signal_frame.c Gstash_frame.c Gtrace.c Gstrip_ptrauth_insn_mask.c
SRC_C_UNWIND-mips = Gis_signal_frame.c
SRC_C_UNWIND-riscv = Gis_signal_frame.c
SRC_C_UNWIND-ppc32 = Gis_signal_frame.c get_func_addr.c
SRC_S_UNWIND-x86 = getcontext-linux.S
SRC_S_UNWIND-amd64 = getcontext.S setcontext.S
SRC_S_UNWIND-riscv = getcontext.S setcontext.S
SRC_C += $(SRC_C_UNWIND) $(SRC_C_UNWIND-$(ARCH))
SRC_S += $(SRC_S_UNWIND) $(SRC_S_UNWIND-$(ARCH))
NOT_HAVE_SYNC_FUNCTIONS-sparc = y
DEFINES = $(if $(NOT_HAVE_SYNC_FUNCTIONS-$(ARCH)),,-DHAVE_SYNC_FUNCTIONS=1)
DEFINES += -DHAVE_CONFIG_H -D_GNU_SOURCE
DEFINES += -D__UCLIBC_HAS_CONTEXT_FUNCS__
#DEFINES += -DDEBUG
include $(L4DIR)/mk/lib.mk
# target_is_big_endian() in libunwind triggers this warning
CFLAGS := $(filter-out -Wstrict-prototypes, $(CFLAGS))
CFLAGS += -Wno-strict-prototypes
# libunwind triggers this warning a lot
CFLAGS += -Wno-unused-parameter

View File

@@ -0,0 +1,99 @@
#include "elf-image-cache.h"
/*
* The following are internals of libbacktrace. It would be fantastic if we
* did not need to do this but could use official interfaces of
* libbacktrace only.
*/
#include <libbacktrace/internal.h>
static const char * const dwarf_section_names[DEBUG_MAX] =
{
".debug_info",
".debug_line",
".debug_abbrev",
".debug_ranges",
".debug_str",
".debug_addr",
".debug_str_offsets",
".debug_line_str",
".debug_rnglists"
};
static void setup_dwarf_state(struct backtrace_state *state,
struct elf_image *ei,
backtrace_error_callback error_cb, void *data)
{
if (state->fileline_fn)
return;
struct dwarf_sections dwarf_sections;
memset (&dwarf_sections, 0, sizeof(dwarf_sections));
for (int i = 0; i < DEBUG_MAX; i++)
{
Elf_W(Shdr) *shdr = elf_w(find_section)(ei, dwarf_section_names[i]);
if (shdr)
{
dwarf_sections.data[i] = ei->image + shdr->sh_offset;
dwarf_sections.size[i] = shdr->sh_size;
}
}
Elf_W(Ehdr) *ehdr = ei->image;
struct libbacktrace_base_address baddr = { .m = 0 };
backtrace_dwarf_add (state, baddr, &dwarf_sections,
ehdr->e_ident[EI_DATA] == ELFDATA2MSB,
NULL, error_cb, data, &state->fileline_fn, NULL);
}
typedef struct elf_image_cache_item
{
elf_image_handle_t elf;
struct backtrace_state state;
struct elf_image_cache_item *next;
} elf_image_cache_item_t;
static elf_image_cache_item_t *elf_image_cache = NULL;
elf_image_handle_t *get_elf_image(char const *path)
{
elf_image_cache_item_t *item = elf_image_cache;
while (item)
{
if (!strcmp(item->elf.path, path))
return &item->elf;
item = item->next;
}
item = malloc(sizeof(elf_image_cache_item_t));
if (!item)
return NULL;
memset(item, 0, sizeof(elf_image_cache_item_t));
strncpy(item->elf.path, path, sizeof(item->elf.path));
item->elf.path[sizeof(item->elf.path) - 1] = '\0';
if (elf_map_image(&item->elf.ei, item->elf.path) < 0)
{
free(item);
return NULL;
}
item->next = elf_image_cache;
elf_image_cache = item;
return &item->elf;
}
struct backtrace_state *get_backtrace_state(elf_image_handle_t *elf,
backtrace_error_callback error_cb,
void *data)
{
elf_image_cache_item_t *item = (elf_image_cache_item_t *)elf;
setup_dwarf_state(&item->state, &elf->ei, error_cb, data);
return &item->state;
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <libunwind_i.h>
#include <libbacktrace/backtrace.h>
// We have to cache these states, because libbacktrace offers no method to free once allocated states.
// We cannot also not unmap the elf image, as backtrace data structures still refer to it!
typedef struct elf_image_handle
{
struct elf_image ei;
char path[256];
} elf_image_handle_t;
elf_image_handle_t *get_elf_image(char const *path);
struct backtrace_state *get_backtrace_state(elf_image_handle_t *elf,
backtrace_error_callback error_cb,
void *data);

View File

@@ -0,0 +1,191 @@
#include <stdio.h>
#include <cxxabi.h>
#include <stdlib.h>
#include <errno.h>
#include <l4/libunwind/backtrace_util.h>
#include "libunwind-l4.h"
#include "os-l4.h"
#include "elf-image-cache.h"
static char *demangle(const char *function)
{
int status = -1;
char *demfunc = 0;
demfunc = __cxa_demangle(function, 0, 0, &status);
if (status == 0)
return demfunc;
return NULL;
}
typedef struct backtrace_cb_data
{
int counter;
elf_image_handle_t *elf;
unsigned long segbase;
unsigned long mapoff;
char path[256];
unsigned long vbase;
} backtrace_cb_data_t;
static void
print_func_name(const char *function)
{
char *demfunc = demangle(function);
if (demfunc)
{
printf("%s", demfunc);
free(demfunc);
}
else
printf("%s()", function);
}
static int
bt_cb_one(void *vdata, uintptr_t pc,
const char *filename, int lineno, const char *function)
{
backtrace_cb_data_t *data = vdata;
unw_word_t ip = (unw_word_t)pc - data->vbase + data->segbase;
printf("#%d 0x%08lx", data->counter, (l4_addr_t)ip);
// Print module and relative virtual address in module
if (data->path[0])
printf(" = 0x%lx@%s", (l4_addr_t)pc, data->path);
printf(": ");
if (function)
print_func_name(function);
else
{
char func_name[256];
unw_word_t off;
// Fallback to symbol name from (dynamic linker) symbol table.
if (data->elf &&
// get_proc_name_in_image(), or rather get_load_offset() which is used
// internally, expects mapoff to be page size aligned.
elf_w(get_proc_name_in_image)(unw_local_addr_space, &data->elf->ei,
data->segbase & L4_PAGEMASK,
ip, func_name, sizeof(func_name),
&off) >= 0)
{
print_func_name(func_name);
printf("+0x%lx", (unsigned long)off);
}
else
printf("unknown");
}
if (filename || lineno)
printf(" at %s:%d", filename, lineno);
printf("\n");
data->counter++;
return 1;
}
static void
bt_err(void *vdata, const char *msg, int errnum)
{
backtrace_cb_data_t *data = vdata;
(void) data; (void) msg; (void) errnum;
Debug(1, "#%d backtrace-error: %s (%d)\n", data->counter, msg, errnum);
}
static int
do_backtrace_pcinfo(unw_word_t ip, backtrace_cb_data_t *data)
{
if (!data->elf)
return 0;
// Get relocation offset... (use get_load_offset...)
Elf_W(Ehdr) *ehdr = data->elf->ei.image;
const ElfW(Phdr) *phdr = (Elf_W(Phdr) *) ((char *) data->elf->ei.image + ehdr->e_phoff);
/* See if PC falls into one of the loaded segments.*/
// TODO: Use get_load_offset?!
data->vbase = 0;
for (int n = 0; n < ehdr->e_phnum; n++, phdr++)
{
if (phdr->p_type == PT_LOAD && phdr->p_offset == data->mapoff)
{
data->vbase = phdr->p_vaddr;
break;
}
}
struct backtrace_state *state = get_backtrace_state(data->elf, bt_err, data);
// For relocated binaries ip has to be adjusted to the default
// virtual address, otherwise pcinfo will find nothing!
return state && backtrace_pcinfo(state, ip - data->segbase + data->vbase,
bt_cb_one, bt_err, data) == 1;
}
static void do_backtrace(unw_cursor_t *cursor, pid_t pid)
{
unw_word_t ip;
backtrace_cb_data_t data;
memset(&data, 0, sizeof(data));
data.counter = 0;
do
{
unw_get_reg(cursor, UNW_REG_IP, &ip);
if (l4_find_elf(pid_to_l4(pid), ip, &data.segbase, &data.mapoff,
data.path, sizeof(data.path)) >= 0)
{
data.elf = get_elf_image(data.path);
if (do_backtrace_pcinfo(ip, &data))
// Success...
continue;
}
else
{
data.elf = NULL;
data.segbase = 0;
data.mapoff = 0;
data.path[0] = 0;
data.vbase = 0;
}
// If unable to find binary or backtrace_pcinfo failed, invoke bt_cb_one manually.
bt_cb_one(&data, ip - data.segbase + data.vbase, NULL, 0, NULL);
}
while (unw_step (cursor) > 0);
}
void libunwind_do_local_backtrace(void)
{
unw_cursor_t cursor;
unw_context_t uc;
unw_getcontext(&uc);
int r = unw_init_local(&cursor, &uc);
if (r)
return;
do_backtrace(&cursor, getpid());
}
// TODO: Fall back on binary if the region manager does not provide info?
void libunwind_do_remote_backtrace(l4_exc_regs_t *regs, l4_cap_idx_t rm,
libunwind_access_remote_mem_f remote_mem,
void *remote_mem_arg)
{
unw_cursor_t cursor;
l4_unw_data_t data;
data.regs = regs;
data.rm = rm;
data.remote_mem = remote_mem;
data.remote_mem_arg = remote_mem_arg;
unw_addr_space_t as = unw_create_addr_space(&l4_unw_accessors, 0);
int r = unw_init_remote(&cursor, as, &data);
if (r)
return;
do_backtrace(&cursor, l4_to_pid(rm));
}

View File

@@ -0,0 +1,345 @@
#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,
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include <libunwind.h>
#include <l4/libunwind/backtrace_util.h>
typedef struct l4_unw_data
{
l4_exc_regs_t *regs;
l4_cap_idx_t rm;
libunwind_access_remote_mem_f remote_mem;
void *remote_mem_arg;
} l4_unw_data_t;
extern unw_accessors_t l4_unw_accessors;