MINOR: debug: add -dA to dump an archive of all dependencies

This adds "-dA[file]" on the command line, which dumps an archive of all
dependencies detected at runtime into the designated file in tar format.
This is equivalent to "set-dumpable libs", but instead of keeping the libs
in memory, it dumps them into a file. This may be used after a core dump,
in order to provide all necessary libraries to developers to permit them
to exploit the core. This may not be available on all operating systems.
This commit is contained in:
Willy Tarreau 2026-05-29 11:39:05 +02:00
parent f8fd6d25d8
commit 030a2bfeeb
5 changed files with 82 additions and 0 deletions

View file

@ -215,6 +215,18 @@ list of options is :
in foreground and to show incoming and outgoing events. It must never be
used in an init script.
-dA[file] : dump an archive of all dependencies detected at boot time in the
designated file in tar format, immediately after the configuration is done
loading. This is equivalent to "set-dumpable libs", but instead of keeping
the libs in memory, it dumps them into a file. This may be used after a
core dump, in order to provide all necessary libraries to developers to
permit them to exploit the core. This may not be available on all operating
systems. It is highly recommended to use this with the regular
configuration files, and optionally with "-c" when used manually, to make
haproxy immediately exit after the dump, without starting. Example:
$ haproxy -dA/tmp/libs.tar -c -f /etc/haproxy/haproxy.cfg
-dC[key] : dump the configuration file. It is performed after the lines are
tokenized, so comments are stripped and indenting is forced. If a non-zero
key is specified, lines are truncated before sensitive/confidential fields,

View file

@ -61,6 +61,7 @@ extern struct cfgfile fileless_cfg;
/* storage for collected libs */
extern void *lib_storage;
extern size_t lib_size;
extern char *lib_output_file;
struct proxy;
struct server;

View file

@ -1155,6 +1155,7 @@ void *get_sym_next_addr(const char *name);
int dump_libs(struct buffer *output, int with_addr);
void collect_libs(void);
void free_collected_libs(void);
int copy_libs_to_file(void);
/* Note that this may result in opening libgcc() on first call, so it may need
* to have been called once before chrooting.

View file

@ -274,6 +274,7 @@ unsigned int deprecated_directives_allowed = 0;
/* mapped storage for collected libs */
void *lib_storage = NULL;
size_t lib_size = 0;
char *lib_output_file = NULL;
int check_kw_experimental(struct cfg_keyword *kw, const char *file, int linenum,
char **errmsg)
@ -784,6 +785,9 @@ static void usage(char *name)
#if defined(HA_HAVE_DUMP_LIBS)
" -dL dumps loaded object files after config checks\n"
#endif
#if defined(HA_HAVE_DUMP_LIBS) && defined(HA_HAVE_DL_ITERATE_PHDR)
" -dA[file] collects libs into a tar file at <file>\n"
#endif
#if defined(USE_CPU_AFFINITY)
" -dc dumps the list of selected and evicted CPUs\n"
#endif
@ -1627,6 +1631,16 @@ void haproxy_init_args(int argc, char **argv)
#if defined(HA_HAVE_DUMP_LIBS)
else if (*flag == 'd' && flag[1] == 'L')
arg_mode |= MODE_DUMP_LIBS;
# if defined(HA_HAVE_DL_ITERATE_PHDR)
else if (*flag == 'd' && flag[1] == 'A') {
lib_output_file = flag + 2;
if (!*lib_output_file) {
ha_alert("-dA: missing output file name\n");
exit(1);
}
arg_mode |= MODE_DUMP_LIBS; // stop on libs dump
}
# endif /* HA_HAVE_DL_ITERATE_PHDR */
#endif
else if (*flag == 'd' && flag[1] == 'K') {
arg_mode |= MODE_DUMP_KWD;
@ -2312,6 +2326,17 @@ static void step_init_2(int argc, char** argv)
#if defined(HA_HAVE_DUMP_LIBS)
if (global.mode & MODE_DUMP_LIBS && !master) {
# if defined(HA_HAVE_DL_ITERATE_PHDR)
if (lib_output_file) {
/* we'll dump everything to lib_output_file */
if (copy_libs_to_file() < 0)
deinit_and_exit(1);
/* release memory if no longer needed */
if ((global.tune.options & (GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS)) !=
(GTUNE_SET_DUMPABLE | GTUNE_COLLECT_LIBS))
free_collected_libs();
}
# endif
qfprintf(stdout, "List of loaded object files:\n");
chunk_reset(&trash);
if (dump_libs(&trash, ((arg_mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_VERBOSE)))

View file

@ -6101,6 +6101,10 @@ void collect_libs(void)
void *page;
int i;
/* already done */
if (lib_storage)
return;
/* prepend a directory named after the starting pid */
snprintf(dir_name, sizeof(dir_name), "core-%u", getpid());
ctx.prefix = dir_name;
@ -6162,6 +6166,39 @@ void free_collected_libs(void)
lib_size = 0;
}
/* Prepare the archive in RAM and copy it to a target file. Returns <0 upon error. */
int copy_libs_to_file(void)
{
ssize_t len;
int ret = -1;
int fd = -1;
fd = open(lib_output_file, O_CREAT | O_WRONLY, S_IRWXU);
if (fd < 0) {
ha_alert("Cannot create output file to dump dependencies: %s.\n", strerror(errno));
goto fail;
}
collect_libs();
if (!lib_storage || !lib_size) {
ha_alert("Failed to collect dependencies.\n");
goto fail;
}
len = write(fd, lib_storage, lib_size);
if (len != lib_size) {
ha_alert("Failed to write dependencies to output file: %s.\n", strerror(errno));
goto fail;
}
/* OK done */
ret = 0;
fail:
if (fd >= 0)
close(fd);
return ret;
}
# else // no DL_ITERATE_PHDR
# error "No dump_libs() function for this platform"
# endif
@ -6183,6 +6220,12 @@ void free_collected_libs(void)
{
}
/* unsupported platform: do not copy anything */
int copy_libs_to_file(void)
{
return -1;
}
#endif // HA_HAVE_DUMP_LIBS
/*