mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-19 04:00:46 +00:00
MINOR: tools: add resolve_sym_name() to resolve function pointers
We use various hacks at a few places to try to identify known function pointers in debugging outputs (show threads & show fd). Let's centralize this into a new function dedicated to this. It already knows about the functions matched by "show threads" and "show fd", and when built with USE_DL, it can rely on dladdr1() to resolve other functions. There are some limitations, as static functions are not resolved, linking with -rdynamic is mandatory, and even then some functions will not necessarily appear. It's possible to do a better job by rebuilding the whole symbol table from the ELF headers in memory but it's less portable and the gains are still limited, so this solution remains a reasonable tradeoff.
This commit is contained in:
parent
762fb3ec8e
commit
eb8b1ca3eb
@ -1485,6 +1485,7 @@ int dump_text_line(struct buffer *out, const char *buf, int bsize, int len,
|
||||
void dump_addr_and_bytes(struct buffer *buf, const char *pfx, const void *addr, int n);
|
||||
void dump_hex(struct buffer *out, const char *pfx, const void *buf, int len, int unsafe);
|
||||
int may_access(const void *ptr);
|
||||
void *resolve_sym_name(struct buffer *buf, const char *pfx, void *addr);
|
||||
|
||||
/* same as realloc() except that ptr is also freed upon failure */
|
||||
static inline void *my_realloc2(void *ptr, size_t size)
|
||||
|
128
src/standard.c
128
src/standard.c
@ -10,6 +10,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(USE_DL)
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <link.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
@ -31,7 +37,15 @@
|
||||
#include <common/standard.h>
|
||||
#include <common/tools.h>
|
||||
#include <types/global.h>
|
||||
#include <proto/applet.h>
|
||||
#include <proto/dns.h>
|
||||
#include <proto/hlua.h>
|
||||
#include <proto/listener.h>
|
||||
#include <proto/proto_udp.h>
|
||||
#include <proto/ssl_sock.h>
|
||||
#include <proto/stream_interface.h>
|
||||
#include <proto/task.h>
|
||||
|
||||
#include <eb32tree.h>
|
||||
#include <eb32sctree.h>
|
||||
|
||||
@ -4321,6 +4335,120 @@ void debug_hexdump(FILE *out, const char *pfx, const char *buf,
|
||||
}
|
||||
}
|
||||
|
||||
/* Tries to append to buffer <buf> some indications about the symbol at address
|
||||
* <addr> using the following form:
|
||||
* lib:+0xoffset (unresolvable address from lib's base)
|
||||
* main+0xoffset (unresolvable address from main (+/-))
|
||||
* lib:main+0xoffset (unresolvable lib address from main (+/-))
|
||||
* name (resolved exact exec address)
|
||||
* lib:name (resolved exact lib address)
|
||||
* name+0xoffset/0xsize (resolved address within exec symbol)
|
||||
* lib:name+0xoffset/0xsize (resolved address within lib symbol)
|
||||
*
|
||||
* The file name (lib or executable) is limited to what lies between the last
|
||||
* '/' and the first following '.'. An optional prefix <pfx> is prepended before
|
||||
* the output if not null. The file is not dumped when it's the same as the one
|
||||
* that contains the "main" symbol, or when USE_DL is not set.
|
||||
*
|
||||
* The symbol's base address is returned, or NULL when unresolved, in order to
|
||||
* allow the caller to match it against known ones.
|
||||
*/
|
||||
void *resolve_sym_name(struct buffer *buf, const char *pfx, void *addr)
|
||||
{
|
||||
const struct {
|
||||
const void *func;
|
||||
const char *name;
|
||||
} fcts[] = {
|
||||
{ .func = process_stream, .name = "process_stream" },
|
||||
{ .func = task_run_applet, .name = "task_run_applet" },
|
||||
{ .func = si_cs_io_cb, .name = "si_cs_io_cb" },
|
||||
{ .func = conn_fd_handler, .name = "conn_fd_handler" },
|
||||
{ .func = dgram_fd_handler, .name = "dgram_fd_handler" },
|
||||
{ .func = listener_accept, .name = "listener_accept" },
|
||||
{ .func = poller_pipe_io_handler, .name = "poller_pipe_io_handler" },
|
||||
{ .func = mworker_accept_wrapper, .name = "mworker_accept_wrapper" },
|
||||
#ifdef USE_LUA
|
||||
{ .func = hlua_process_task, .name = "hlua_process_task" },
|
||||
#endif
|
||||
#if defined(USE_OPENSSL) && (HA_OPENSSL_VERSION_NUMBER >= 0x1010000fL) && !defined(OPENSSL_NO_ASYNC)
|
||||
{ .func = ssl_async_fd_free, .name = "ssl_async_fd_free" },
|
||||
{ .func = ssl_async_fd_handler, .name = "ssl_async_fd_handler" },
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef USE_DL
|
||||
Dl_info dli, dli_main;
|
||||
const ElfW(Sym) *sym;
|
||||
const char *fname, *p;
|
||||
#endif
|
||||
int i;
|
||||
|
||||
if (pfx)
|
||||
chunk_appendf(buf, "%s", pfx);
|
||||
|
||||
for (i = 0; i < sizeof(fcts) / sizeof(fcts[0]); i++) {
|
||||
if (addr == fcts[i].func) {
|
||||
chunk_appendf(buf, "%s", fcts[i].name);
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_DL
|
||||
/* Now let's try to be smarter */
|
||||
#ifdef __USE_GNU // most detailed one
|
||||
if (!dladdr1(addr, &dli, (void **)&sym, RTLD_DL_SYMENT))
|
||||
goto unknown;
|
||||
#else
|
||||
if (!dladdr(addr, &dli))
|
||||
goto unknown;
|
||||
sym = NULL;
|
||||
#endif
|
||||
|
||||
/* 1. prefix the library name if it's not the same object as the one
|
||||
* that contains the main function. The name is picked between last '/'
|
||||
* and first following '.'.
|
||||
*/
|
||||
if (!dladdr(main, &dli_main))
|
||||
dli_main.dli_fbase = NULL;
|
||||
|
||||
if (dli_main.dli_fbase != dli.dli_fbase) {
|
||||
fname = dli.dli_fname;
|
||||
p = strrchr(fname, '/');
|
||||
if (p++)
|
||||
fname = p;
|
||||
p = strchr(fname, '.');
|
||||
if (!p)
|
||||
p = fname + strlen(fname);
|
||||
|
||||
chunk_appendf(buf, "%.*s:", (int)(long)(p - fname), fname);
|
||||
}
|
||||
|
||||
/* 2. symbol name */
|
||||
if (dli.dli_sname) {
|
||||
/* known, dump it and return symbol's address (exact or relative) */
|
||||
chunk_appendf(buf, "%s", dli.dli_sname);
|
||||
if (addr != dli.dli_saddr) {
|
||||
chunk_appendf(buf, "+%#lx", (long)(addr - dli.dli_saddr));
|
||||
if (sym)
|
||||
chunk_appendf(buf, "/%#lx", (long)sym->st_size);
|
||||
}
|
||||
return dli.dli_saddr;
|
||||
}
|
||||
else if (dli_main.dli_fbase != dli.dli_fbase) {
|
||||
/* unresolved symbol from a known library, report relative offset */
|
||||
chunk_appendf(buf, "+%#lx", (long)(addr - dli.dli_fbase));
|
||||
return NULL;
|
||||
}
|
||||
#endif /* USE_DL */
|
||||
unknown:
|
||||
/* unresolved symbol from the main file, report relative offset to main */
|
||||
if ((void*)addr < (void*)main)
|
||||
chunk_appendf(buf, "main-%#lx", (long)((void*)main - addr));
|
||||
else
|
||||
chunk_appendf(buf, "main+%#lx", (long)(addr - (void*)main));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate an array of unsigned int with <nums> as address from <str> string
|
||||
* made of integer sepereated by dot characters.
|
||||
|
Loading…
Reference in New Issue
Block a user