From: Willy Tarreau Date: Wed, 18 Mar 2026 10:17:22 +0000 (+0100) Subject: MINOR: debug: opportunistically load libthread_db.so.1 with set-dumpable=libs X-Git-Tag: v3.4-dev7~17 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b93137ce678ec36be6f6e538218d0d45d77294f6;p=thirdparty%2Fhaproxy.git MINOR: debug: opportunistically load libthread_db.so.1 with set-dumpable=libs When loading libs into the core dump, let's also try to load libthread_db.so.1 that gdb usually requires. It can significantly help decoding the threads for systems which require it, and the file is quite small. It can appear at a few different locations and is generally next to libpthread.so, or alternately libc, so we first look where we found them, and fall back to a few other common places. The file is really small, a few tens of kB usually. --- diff --git a/src/tools.c b/src/tools.c index 879879c47..bae43643a 100644 --- a/src/tools.c +++ b/src/tools.c @@ -6038,6 +6038,7 @@ struct dl_collect_ctx { size_t size; char *prefix; int pos; + char libpthread_path[PATH_MAX]; }; static int dl_collect_libs_cb(struct dl_phdr_info *info, size_t size, void *data) @@ -6066,6 +6067,18 @@ static int dl_collect_libs_cb(struct dl_phdr_info *info, size_t size, void *data load_file_into_tar(&ctx->storage, &ctx->size, ctx->prefix, dbg, NULL, "haproxy-libs-dump"); } + /* check if we're loading libpthread or libc, and if so, keep a copy of its path */ + if (!ctx->libpthread_path[0]) { + const char *basename = strrchr(fname, '/'); + + if (basename && + (strncmp(basename, "/libpthread.so", 14) == 0 || + strncmp(basename, "/libc.so", 8) == 0)) { + /* Note: this will trim the trailing slash */ + strncpy(ctx->libpthread_path, fname, + MIN(basename - fname, sizeof(ctx->libpthread_path))); + } + } leave: /* increment the object's number */ ctx->pos++; @@ -6075,11 +6088,13 @@ static int dl_collect_libs_cb(struct dl_phdr_info *info, size_t size, void *data /* dumps lib names and optionally address ranges */ void collect_libs(void) { - struct dl_collect_ctx ctx = { .storage = NULL, .size = 0, .pos = 0 }; + struct dl_collect_ctx ctx = { .storage = NULL, .size = 0, .pos = 0, .libpthread_path = "" }; + const char *libthr_paths[] = { ctx.libpthread_path, "/usr/lib64", "/lib64", "/usr/lib", "/lib", NULL }; ulong pagesize = sysconf(_SC_PAGESIZE); char dir_name[16]; size_t new_size; void *page; + int i; /* prepend a directory named after the starting pid */ snprintf(dir_name, sizeof(dir_name), "core-%u", getpid()); @@ -6088,6 +6103,22 @@ void collect_libs(void) /* callbacks will (re-)allocate ctx->storage */ dl_iterate_phdr(dl_collect_libs_cb, &ctx); + /* if we've found libpthread, there's likely a libthread_db.so.1 next + * to it, for use with gdb, and ctx.libpthread_path will point to it, + * and with it, libthr_paths[0]. Otherwise we search in a few other + * common paths. + */ + for (i = 0; libthr_paths[i]; i++) { + char path[PATH_MAX]; + + if (!*libthr_paths[i]) + continue; + + snprintf(path, sizeof(path), "%s/libthread_db.so.1", DISGUISE(libthr_paths[i])); + if (load_file_into_tar(&ctx.storage, &ctx.size, ctx.prefix, path, NULL, "haproxy-libs-dump") == 0) + break; + } + /* now that the archive is complete, we need to close it by appending * two empty 512B blocks. We'll also place it aligned in an isolated * mapped area so that it uses its own segment in a core dump for