diff --git a/include/common/standard.h b/include/common/standard.h index 5cd056473..b278f088e 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -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) diff --git a/src/standard.c b/src/standard.c index bd148ee68..5c4daf2b5 100644 --- a/src/standard.c +++ b/src/standard.c @@ -10,6 +10,12 @@ * */ +#if defined(USE_DL) +#define _GNU_SOURCE +#include +#include +#endif + #include #include #include @@ -31,7 +37,15 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include +#include + #include #include @@ -4321,6 +4335,120 @@ void debug_hexdump(FILE *out, const char *pfx, const char *buf, } } +/* Tries to append to buffer some indications about the symbol at address + * 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 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 as address from string * made of integer sepereated by dot characters.