mirror of
https://github.com/pi-hole/FTL.git
synced 2026-07-12 22:26:35 +01:00
Add universal crash backtrace via _Unwind_Backtrace
Replace the glibc-only execinfo.h backtrace with _Unwind_Backtrace
from GCC's libgcc, which works on glibc and musl, static-pie and
dynamic, across all supported architectures.
Key design decisions:
- __ehdr_start (GNU ld linker symbol) gives the PIE load base
reliably on all targets, replacing dl_iterate_phdr which does not
enumerate the main executable consistently on musl static builds
- SOURCE_ROOT compile definition strips the build machine prefix so
backtraces show project-relative paths (src/signals.c:42)
- dladdr() resolves dynamic symbol names from .dynsym for stripped
shared-library frames; /proc/self/maps provides the library name
as fallback when no symbol is exported
- Each frame is logged as a single line:
#N func_name src/file.c:line (resolved)
#N 0xADDRESS (libc.so.6 sym+off) (unresolved)
New subcommands:
- crash: triggers a deterministic SIGSEGV via mmap(PROT_NONE)
(not UB, not elided by the optimizer, not caught by
sanitizers) to validate the crash handler end-to-end
- backtrace: prints a live backtrace without crashing, for manual
inspection on a given build or platform
CI: add check_crash() to test/arch_test.sh, called unconditionally
for every architecture to verify the handler fires and a backtrace
is produced.
Bug fixes:
- removePID(): guard config.files.pid.v.s != NULL before fopen()
to prevent a secondary SIGSEGV when config was never loaded
(e.g. crash/backtrace subcommands)
- signal_handler(): fflush(stdout) immediately after generate_backtrace()
so the output is preserved even if cleanup() faults on uninitialized
state before exit() can flush stdio
Signed-off-by: Dominik <dl6er@dl6er.de>
This commit is contained in:
+12
-5
@@ -357,14 +357,21 @@ if(HAVE_RANDOM_H)
|
||||
target_compile_definitions(core PRIVATE USE_GETRANDOM)
|
||||
endif()
|
||||
|
||||
check_include_file("execinfo.h" HAVE_EXECINFO_H)
|
||||
if(HAVE_EXECINFO_H)
|
||||
message(STATUS "Building FTL with unwind support: YES")
|
||||
target_compile_definitions(core PRIVATE USE_BACKTRACE)
|
||||
# <unwind.h> is part of GCC's libgcc — available on ALL targets (musl+glibc, static+dynamic)
|
||||
# _Unwind_Backtrace() is already linked via -lgcc/-static-libgcc; no find_library needed
|
||||
check_include_file("unwind.h" HAVE_UNWIND_H)
|
||||
if(HAVE_UNWIND_H)
|
||||
target_compile_definitions(core PRIVATE USE_UNWIND)
|
||||
message(STATUS "Building FTL with _Unwind_Backtrace support: YES")
|
||||
else()
|
||||
message(STATUS "Building FTL with unwind support: NO")
|
||||
message(STATUS "Building FTL with _Unwind_Backtrace support: NO")
|
||||
endif()
|
||||
|
||||
# Embed the source root so crash backtraces can show project-relative paths
|
||||
# (e.g. "src/args.c" instead of "/home/user/FTL/src/args.c").
|
||||
target_compile_definitions(core PRIVATE SOURCE_ROOT="${CMAKE_SOURCE_DIR}/")
|
||||
|
||||
|
||||
|
||||
option(USE_READLINE "Build FTL with readline support, if available" ON)
|
||||
|
||||
|
||||
+37
@@ -76,6 +76,9 @@
|
||||
#include "config/inotify.h"
|
||||
// get_all_supported_ciphersuites()
|
||||
#include "webserver/webserver.h"
|
||||
// mmap(), PROT_NONE, MAP_PRIVATE, MAP_ANONYMOUS — used for intentional crash
|
||||
// test
|
||||
#include <sys/mman.h>
|
||||
|
||||
// defined in dnsmasq.c
|
||||
extern void print_dnsmasq_version(const char *yellow, const char *green, const char *bold, const char *normal);
|
||||
@@ -253,6 +256,40 @@ void parse_args(int argc, char *argv[])
|
||||
if(argc == 2 && strcmp(argv[1], "sigrtmin") == 0)
|
||||
exit(sigrtmin());
|
||||
|
||||
// Intentional crash test — used in CI to verify the crash handler and
|
||||
// backtrace machinery work correctly.
|
||||
// We use mmap(PROT_NONE) + a write to produce SIGSEGV SEGV_ACCERR.
|
||||
// This is NOT undefined behaviour, NOT elided by the optimiser, and NOT
|
||||
// intercepted by ASan or UBSan — unlike a raw null/invalid-pointer cast.
|
||||
// handle_signals() and init_backtrace() are called here explicitly
|
||||
// because parse_args() runs before main() sets them up.
|
||||
if(argc == 2 && strcmp(argv[1], "crash") == 0)
|
||||
{
|
||||
cli_mode = true;
|
||||
log_ctrl(false, true);
|
||||
config.misc.addr2line.v.b = true; // Enable addr2line — config not loaded in subcommand context
|
||||
handle_signals();
|
||||
void *addr = mmap(NULL, 4096u, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if(addr != MAP_FAILED)
|
||||
{
|
||||
volatile char *ptr = (volatile char *)addr;
|
||||
*ptr = 'x'; // Write to PROT_NONE page — SIGSEGV SEGV_ACCERR
|
||||
}
|
||||
raise(SIGSEGV); // Fallback: mmap should never fail in practice
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Generate a backtrace without crashing — useful for manual inspection
|
||||
// of the backtrace output on a given build/platform.
|
||||
if(argc == 2 && strcmp(argv[1], "backtrace") == 0)
|
||||
{
|
||||
cli_mode = true;
|
||||
log_ctrl(false, true);
|
||||
config.misc.addr2line.v.b = true; // Enable addr2line — config not loaded in subcommand context
|
||||
generate_backtrace();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
// If the binary name is "sqlite3" (e.g., symlink /usr/bin/sqlite3 -> /usr/bin/pihole-FTL),
|
||||
// we operate in drop-in mode and consume all arguments for the embedded SQLite3 engine
|
||||
// Also, we do this if the first argument is a file with ".db" ending
|
||||
|
||||
@@ -156,6 +156,11 @@ void savePID(void)
|
||||
*/
|
||||
static void removePID(void)
|
||||
{
|
||||
// Config may not have been loaded (e.g. crash/backtrace subcommands),
|
||||
// in which case no PID file was ever written — nothing to remove.
|
||||
if(config.files.pid.v.s == NULL)
|
||||
return;
|
||||
|
||||
FILE *f = NULL;
|
||||
// Open file for writing to overwrite/empty it
|
||||
if((f = fopen(config.files.pid.v.s, "w")) == NULL)
|
||||
|
||||
@@ -55,6 +55,11 @@ int main (int argc, char *argv[])
|
||||
// Obtain log file location
|
||||
getLogFilePath(true);
|
||||
|
||||
// Store binary path and PIE load base address for crash-time backtrace.
|
||||
// Must be called before handle_signals() so bin_path is populated before
|
||||
// the first possible signal.
|
||||
init_backtrace(argc > 0 ? argv[0] : NULL);
|
||||
|
||||
// Parse arguments
|
||||
// We run this also for no direct arguments
|
||||
// to have arg{c,v}_dnsmasq initialized
|
||||
|
||||
+210
-81
@@ -9,9 +9,13 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
#include "FTL.h"
|
||||
#if defined(USE_BACKTRACE)
|
||||
// backtrace(), backtrace_symbols()
|
||||
#include <execinfo.h>
|
||||
// Universal unwind backtrace via GCC's libgcc — works on glibc AND musl, static AND dynamic
|
||||
#if defined(USE_UNWIND)
|
||||
# include <unwind.h>
|
||||
# include <limits.h> // PATH_MAX
|
||||
# include <inttypes.h> // SCNxPTR — portable sscanf format for uintptr_t
|
||||
# include <sys/mman.h> // mmap() — used for the intentional crash test subcommand
|
||||
# include <dlfcn.h> // dladdr() — dynamic symbol lookup from .dynsym
|
||||
#endif
|
||||
#include "signals.h"
|
||||
// logging routines
|
||||
@@ -34,6 +38,192 @@ static volatile pid_t mpid = 0;
|
||||
static time_t FTLstarttime = 0;
|
||||
volatile int exit_code = EXIT_SUCCESS;
|
||||
|
||||
// Binary path stored by init_backtrace() — signal-handler-safe static buffer,
|
||||
// never reallocated, safe to read from any context including signal handlers
|
||||
#if defined(USE_UNWIND)
|
||||
static char bin_path[PATH_MAX] = { 0 };
|
||||
#endif
|
||||
|
||||
#if defined(USE_UNWIND)
|
||||
// PIE load base address — set once at startup by init_backtrace() using the
|
||||
// __ehdr_start linker symbol (GNU ld / lld / mold). __ehdr_start points to the
|
||||
// ELF header in memory, which equals the ASLR slide for PIE binaries and the
|
||||
// static load address for non-PIE. Either way: file_vaddr = runtime_addr -
|
||||
// exe_load_addr is exactly what addr2line expects. This is a linker symbol, not
|
||||
// a libc function, so it is reliable on glibc AND musl, static AND dynamic —
|
||||
// unlike dl_iterate_phdr, whose behaviour for static executables varies across
|
||||
// musl versions and may leave exe_load_addr == 0.
|
||||
static uintptr_t exe_load_addr = 0;
|
||||
// Provided by the linker for every ELF executable (GNU ld, lld, mold, gold).
|
||||
extern const char __ehdr_start;
|
||||
#endif // USE_UNWIND
|
||||
|
||||
// Initialize the backtrace subsystem.
|
||||
// Must be called early in main() (before handle_signals()) so that bin_path
|
||||
// and exe_load_addr are ready when the first crash handler fires.
|
||||
void init_backtrace(const char *argv0)
|
||||
{
|
||||
#if defined(USE_UNWIND)
|
||||
// /proc/self/exe gives the canonical absolute path even when argv[0] is
|
||||
// a relative path, a bare binary name, or a symlink.
|
||||
ssize_t len = readlink("/proc/self/exe", bin_path, sizeof(bin_path) - 1u);
|
||||
if(len > 0)
|
||||
bin_path[len] = '\0';
|
||||
else if(argv0 != NULL)
|
||||
{
|
||||
strncpy(bin_path, argv0, sizeof(bin_path) - 1u);
|
||||
bin_path[sizeof(bin_path) - 1u] = '\0';
|
||||
}
|
||||
|
||||
// Cache the PIE load base address for addr2line offset adjustment.
|
||||
// __ehdr_start is a linker symbol pointing at the ELF header in memory,
|
||||
// which equals the ASLR load base for PIE binaries.
|
||||
exe_load_addr = (uintptr_t)&__ehdr_start;
|
||||
#else
|
||||
// Unused, cannot generate backtraces right now on non-gcc targets —
|
||||
// this silences a warning about unused parameters in this case
|
||||
(void)argv0;
|
||||
#endif // USE_UNWIND
|
||||
}
|
||||
|
||||
#if defined(USE_UNWIND)
|
||||
// State carried through each _Unwind_Backtrace callback invocation
|
||||
struct unwind_state {
|
||||
void **frames;
|
||||
int count;
|
||||
int max;
|
||||
};
|
||||
|
||||
// Callback invoked by _Unwind_Backtrace for each frame on the call stack.
|
||||
// Signal-handler-safe: no heap allocation, no stdio, no locks.
|
||||
static _Unwind_Reason_Code unwind_callback(struct _Unwind_Context *ctx, void *arg)
|
||||
{
|
||||
struct unwind_state *state = (struct unwind_state *)arg;
|
||||
if(state->count >= state->max)
|
||||
return _URC_END_OF_STACK;
|
||||
uintptr_t ip = _Unwind_GetIP(ctx);
|
||||
if(ip == 0)
|
||||
return _URC_END_OF_STACK;
|
||||
// Subtract 1 so the address points at the call instruction rather than
|
||||
// the return address — addr2line then resolves the correct source line.
|
||||
state->frames[state->count++] = (void *)(ip - 1u);
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
#endif // USE_UNWIND (unwind_callback)
|
||||
|
||||
#if defined(USE_UNWIND)
|
||||
// Look up which /proc/self/maps entry contains addr and copy the basename
|
||||
// of the mapped file (e.g. "libc.so.6", "[vdso]") into buf.
|
||||
// buf is left empty when the address is not found or has no path.
|
||||
static void find_mapping_name(const void *addr, char *buf, const size_t buflen)
|
||||
{
|
||||
FILE *maps = fopen("/proc/self/maps", "r");
|
||||
if(maps == NULL)
|
||||
return;
|
||||
|
||||
const uintptr_t a = (uintptr_t)addr;
|
||||
char line[512];
|
||||
while(fgets(line, sizeof(line), maps) != NULL)
|
||||
{
|
||||
uintptr_t start = 0, end = 0;
|
||||
char path[256] = { 0 };
|
||||
// Format: start-end perms offset dev inode [path]
|
||||
// Use %*s (not %*llx etc.) to avoid the GNU -Wformat= restriction
|
||||
// that forbids combining the assignment-suppression modifier with a
|
||||
// length modifier. We only care about start, end, and path.
|
||||
const int n = sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %*s %*s %*s %*s %255s",
|
||||
&start, &end, path);
|
||||
if(n >= 2 && a >= start && a < end && path[0] != '\0')
|
||||
{
|
||||
const char *base = strrchr(path, '/');
|
||||
strncpy(buf, base ? base + 1 : path, buflen - 1u);
|
||||
buf[buflen - 1u] = '\0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
fclose(maps);
|
||||
}
|
||||
|
||||
// Log one backtrace frame as a single line.
|
||||
// Resolved: " #N func_name src/file.c:line"
|
||||
// Unresolved: " #N 0xADDRESS (reason)"
|
||||
static void log_frame(const int idx, const void *addr, const void *rel_addr)
|
||||
{
|
||||
if(!config.misc.addr2line.v.b)
|
||||
{
|
||||
log_info(" #%-2i %p (addr2line disabled via config)", idx, addr);
|
||||
return;
|
||||
}
|
||||
if(bin_path[0] == '\0')
|
||||
{
|
||||
log_info(" #%-2i %p (binary path unknown)", idx, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
char cmd[512];
|
||||
snprintf(cmd, sizeof(cmd), "addr2line -f -e %s %p", bin_path, rel_addr);
|
||||
|
||||
FILE *fp = popen(cmd, "r");
|
||||
if(fp == NULL)
|
||||
{
|
||||
log_info(" #%-2i %p (addr2line not available)", idx, addr);
|
||||
return;
|
||||
}
|
||||
|
||||
char func[256] = { 0 }, loc[256] = { 0 };
|
||||
if(fgets(func, sizeof(func), fp) != NULL)
|
||||
{
|
||||
char *nl = strchr(func, '\n');
|
||||
if(nl != NULL) *nl = '\0';
|
||||
}
|
||||
if(fgets(loc, sizeof(loc), fp) != NULL)
|
||||
{
|
||||
char *nl = strchr(loc, '\n');
|
||||
if(nl != NULL) *nl = '\0';
|
||||
}
|
||||
pclose(fp);
|
||||
|
||||
if(strcmp(func, "??") == 0)
|
||||
{
|
||||
// addr2line found nothing — the frame is in a shared library or a
|
||||
// stripped section. Try dladdr() which reads .dynsym, the dynamic
|
||||
// symbol table present in every shared library (even stripped ones).
|
||||
Dl_info dl = { 0 };
|
||||
if(dladdr(addr, &dl) != 0 && dl.dli_sname != NULL)
|
||||
{
|
||||
// Library basename (e.g. "libc.so.6")
|
||||
const char *lib = dl.dli_fname ? strrchr(dl.dli_fname, '/') : NULL;
|
||||
const char *libname = lib ? lib + 1 : (dl.dli_fname ? dl.dli_fname : "?");
|
||||
// Byte offset from the nearest preceding symbol
|
||||
const uintptr_t offset = (uintptr_t)addr - (uintptr_t)dl.dli_saddr;
|
||||
log_info(" #%-2i %p (%s %s+0x%zx)", idx, addr, libname, dl.dli_sname, (size_t)offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
// dladdr() gave nothing — fall back to /proc/self/maps for at
|
||||
// least the library/mapping name ([vdso], [stack], etc.).
|
||||
char mapping[128] = { 0 };
|
||||
find_mapping_name(addr, mapping, sizeof(mapping));
|
||||
if(mapping[0] != '\0')
|
||||
log_info(" #%-2i %p (%s, no debug info)", idx, addr, mapping);
|
||||
else
|
||||
log_info(" #%-2i %p (no debug info)", idx, addr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Strip the compile-time source root to show project-relative paths
|
||||
// (e.g. "src/signals.c:42" instead of "/home/user/FTL/src/signals.c:42").
|
||||
const char *display_loc = loc;
|
||||
#if defined(SOURCE_ROOT)
|
||||
if(strncmp(loc, SOURCE_ROOT, sizeof(SOURCE_ROOT) - 1u) == 0)
|
||||
display_loc = loc + sizeof(SOURCE_ROOT) - 1u;
|
||||
#endif
|
||||
|
||||
log_info(" #%-2i %-30s %s", idx, func, display_loc);
|
||||
}
|
||||
#endif // USE_UNWIND
|
||||
|
||||
volatile sig_atomic_t thread_cancellable[THREADS_MAX] = { false };
|
||||
const char * const thread_names[THREADS_MAX] = {
|
||||
"database",
|
||||
@@ -54,91 +244,26 @@ static char * __attribute__ ((nonnull (1))) getthread_name(char buffer[16])
|
||||
return buffer;
|
||||
}
|
||||
|
||||
#if defined(USE_BACKTRACE)
|
||||
static void print_addr2line(const char *symbol, const void *address, const int j, const void *offset)
|
||||
{
|
||||
// Only do this analysis for our own binary (skip trying to analyse libc.so, etc.)
|
||||
if(strstr(symbol, BINARY_NAME) == NULL)
|
||||
return;
|
||||
|
||||
// Find first occurrence of '(' or ' ' in the obtaned symbol string and
|
||||
// assume everything before that is the file name. (Don't go beyond the
|
||||
// string terminator \0)
|
||||
int p = 0;
|
||||
while(symbol[p] != '(' && symbol[p] != ' ' && symbol[p] != '\0')
|
||||
p++;
|
||||
|
||||
// Compute address cleaned by binary offset
|
||||
void *addr = (void*)(address-offset);
|
||||
|
||||
// Invoke addr2line command and get result through pipe
|
||||
char addr2line_cmd[256];
|
||||
snprintf(addr2line_cmd, sizeof(addr2line_cmd), "addr2line %p -e %.*s", addr, p, symbol);
|
||||
FILE *addr2line = NULL;
|
||||
char linebuffer[512];
|
||||
if(config.misc.addr2line.v.b &&
|
||||
(addr2line = popen(addr2line_cmd, "r")) != NULL &&
|
||||
fgets(linebuffer, sizeof(linebuffer), addr2line) != NULL)
|
||||
{
|
||||
char *pos;
|
||||
// Strip possible newline at the end of the addr2line output
|
||||
if ((pos=strchr(linebuffer, '\n')) != NULL)
|
||||
*pos = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(linebuffer, sizeof(linebuffer), "N/A (%p -> %s)", addr, addr2line_cmd);
|
||||
}
|
||||
// Log result
|
||||
log_info("L[%04i]: %s", j, linebuffer);
|
||||
|
||||
// Close pipe
|
||||
if(addr2line != NULL)
|
||||
pclose(addr2line);
|
||||
}
|
||||
#endif // USE_BACKTRACE
|
||||
|
||||
// Log backtrace
|
||||
// Log backtrace to the FTL log.
|
||||
// Uses _Unwind_Backtrace (GCC libgcc) on all targets — glibc AND musl,
|
||||
// static-pie AND dynamic, all architectures.
|
||||
void generate_backtrace(void)
|
||||
{
|
||||
// Live backtrace generation is not supported by every C standard library
|
||||
#if defined(USE_BACKTRACE)
|
||||
// Try to obtain backtrace. This may not always be helpful, but it is better than nothing
|
||||
void *buffer[255];
|
||||
const int calls = backtrace(buffer, sizeof(buffer)/sizeof(void *));
|
||||
log_info("Backtrace:");
|
||||
#if defined(USE_UNWIND)
|
||||
void *frames[128];
|
||||
struct unwind_state state = { frames, 0, 128 };
|
||||
_Unwind_Backtrace(unwind_callback, &state);
|
||||
|
||||
char ** bcktrace = backtrace_symbols(buffer, calls);
|
||||
if(bcktrace == NULL)
|
||||
log_info("Backtrace (%d frames):", state.count);
|
||||
for(int i = 0; i < state.count; i++)
|
||||
{
|
||||
log_warn("Unable to obtain backtrace symbols!");
|
||||
return;
|
||||
void *rel = (void *)((uintptr_t)frames[i] - exe_load_addr);
|
||||
log_frame(i, frames[i], rel);
|
||||
}
|
||||
|
||||
// Try to compute binary offset from backtrace_symbols result
|
||||
void *offset = NULL;
|
||||
for(int j = 0; j < calls; j++)
|
||||
{
|
||||
void *p1 = NULL, *p2 = NULL;
|
||||
char *pend = NULL;
|
||||
if((pend = strrchr(bcktrace[j], '(')) != NULL &&
|
||||
strstr(bcktrace[j], BINARY_NAME) != NULL &&
|
||||
sscanf(pend, "(+%p) [%p]", &p1, &p2) == 2)
|
||||
offset = (void*)(p2-p1);
|
||||
}
|
||||
|
||||
for(int j = 0; j < calls; j++)
|
||||
{
|
||||
log_info("B[%04i]: %s", j,
|
||||
bcktrace != NULL ? bcktrace[j] : "---");
|
||||
|
||||
if(bcktrace != NULL)
|
||||
print_addr2line(bcktrace[j], buffer[j], j, offset);
|
||||
}
|
||||
free(bcktrace);
|
||||
#else
|
||||
log_info("!!! INFO: pihole-FTL has not been compiled with glibc/backtrace support, not generating one !!!");
|
||||
#endif // USE_BACKTRACE
|
||||
log_info("!!! INFO: pihole-FTL has not been compiled with unwinding support, cannot generate backtrace !!!");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,6 +393,10 @@ static void __attribute__((noreturn)) signal_handler(int sig, siginfo_t *si, voi
|
||||
|
||||
generate_backtrace();
|
||||
|
||||
// Flush stdout immediately so the backtrace is visible even if a
|
||||
// subsequent fault in cleanup() kills the process before exit() runs.
|
||||
fflush(stdout);
|
||||
|
||||
// Print content of /dev/shm
|
||||
ls_dir("/dev/shm");
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ void handle_signals(void);
|
||||
void handle_realtime_signals(void);
|
||||
pid_t main_pid(void);
|
||||
void thread_sleepms(const enum thread_types thread, const int milliseconds);
|
||||
void init_backtrace(const char *argv0);
|
||||
void generate_backtrace(void);
|
||||
int sigtest(void);
|
||||
int sigrtmin(void);
|
||||
|
||||
+22
-2
@@ -93,7 +93,25 @@ check_minimum_glibc_version() {
|
||||
echo "Minimum glibc version check: OK (${1})"
|
||||
}
|
||||
|
||||
if [[ "${CI_ARCH}" == "linux/amd64" ]]; then
|
||||
check_crash() {
|
||||
# Run the intentional-crash subcommand and capture combined stdout+stderr.
|
||||
# The process exits non-zero (killed by SIGSEGV), so we suppress the error.
|
||||
output="$(./pihole-FTL crash 2>&1 || true)"
|
||||
if echo "$output" | grep -q "FTL crashed"; then
|
||||
if echo "$output" | grep -q "Backtrace ("; then
|
||||
echo "Crash handler test: OK (crash handler invoked, backtrace generated)"
|
||||
else
|
||||
# Handler ran but no backtrace — warn rather than fail.
|
||||
# This can happen if addr2line is absent or the binary has no debug info.
|
||||
echo "Crash handler test: WARNING (crash handler invoked, no backtrace)"
|
||||
fi
|
||||
else
|
||||
echo "Crash handler test: FAILED (FTL crash handler was not invoked)"
|
||||
okay=false
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ "${CI_ARCH}" == "linux/amd64" || "${CI_ARCH}" == "" ]]; then
|
||||
|
||||
if [[ "${STATIC}" == "true" ]]; then
|
||||
check_machine "ELF64" "Advanced Micro Devices X86-64"
|
||||
@@ -102,7 +120,7 @@ if [[ "${CI_ARCH}" == "linux/amd64" ]]; then
|
||||
check_file "ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), static-pie linked, with debug_info, not stripped"
|
||||
else
|
||||
check_machine "ELF64" "Advanced Micro Devices X86-64"
|
||||
check_libs "[libgmp.so.10] [libidn2.so.0] [libc.musl-x86_64.so.1]"
|
||||
check_libs "[libgmp.so.10] [libidn2.so.0] [libgcc_s.so.1] [libc.musl-x86_64.so.1]"
|
||||
check_file "ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, with debug_info, not stripped"
|
||||
fi
|
||||
|
||||
@@ -168,6 +186,8 @@ else
|
||||
|
||||
fi
|
||||
|
||||
check_crash
|
||||
|
||||
if [[ "${okay}" == "false" ]]; then
|
||||
echo "Binary checks failed"
|
||||
exit 1
|
||||
|
||||
Reference in New Issue
Block a user