]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: tools: improve symbol resolution without dl_addr
authorWilly Tarreau <w@1wt.eu>
Thu, 13 Mar 2025 16:21:24 +0000 (17:21 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 13 Mar 2025 16:30:48 +0000 (17:30 +0100)
When dl_addr is not usable or fails, better fall back to the closest
symbol among the known ones instead of providing everything relative
to main. Most often, the location of the function will give some hints
about what it can be. Thus now we can emit fct+0xXXX in addition to
main+0xXXX or main-0xXXX. We keep a margin of +256kB maximum after a
function for a match, which is around the maximum size met in an object
file, otherwise it becomes pointless again.

src/tools.c

index 9401337c2302384e30ead7ab25ca330629d8f7db..19fd2670f2ba6cde6516f03d608274d85560f687 100644 (file)
@@ -5549,18 +5549,31 @@ const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *ad
        size_t size;
        const char *fname, *p;
 #endif
-       int i;
+       size_t dist, best_dist;
+       int i, best_idx;
 
        if (pfx)
                chunk_appendf(buf, "%s", pfx);
 
+       best_idx = -1; best_dist = ~0;
        for (i = 0; i < sizeof(fcts) / sizeof(fcts[0]); i++) {
-               if (addr == fcts[i].func) {
-                       chunk_appendf(buf, "%s", fcts[i].name);
-                       return addr;
+               if (addr < (void*)fcts[i].func)
+                       continue;
+               dist = addr - (void*)fcts[i].func;
+               if (dist < (1<<18) && dist < best_dist) {
+                       best_dist = dist;
+                       best_idx = i;
+                       if (!dist)
+                               break;
                }
        }
 
+       /* if that's an exact match, no need to call dl_addr. This happends
+        * when showing callback pointers for example, but not in backtraces.
+        */
+       if (!best_dist)
+               goto use_array;
+
 #if (defined(__ELF__) && !defined(__linux__)) || defined(USE_DL)
        /* Now let's try to be smarter */
 
@@ -5577,14 +5590,14 @@ const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *ad
 
        if (!isolated &&
            HA_SPIN_TRYLOCK(OTHER_LOCK, &dladdr_lock) != 0)
-               goto unknown;
+               goto use_array;
 
        i = dladdr_and_size(addr, &dli, &size);
        if (!isolated)
                HA_SPIN_UNLOCK(OTHER_LOCK, &dladdr_lock);
 
        if (!i)
-               goto unknown;
+               goto use_array;
 
        /* 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 '/'
@@ -5635,9 +5648,15 @@ const void *resolve_sym_name(struct buffer *buf, const char *pfx, const void *ad
                return NULL;
        }
 #endif /* __ELF__ && !__linux__ || USE_DL */
- unknown:
-       /* unresolved symbol from the main file, report relative offset to main */
-       if ((void*)addr < (void*)main)
+ use_array:
+       /* either exact match from the array, or unresolved symbol for which we
+        * may have a close match. Otherwise we report an offset relative to main.
+        */
+       if (best_idx >= 0) {
+               chunk_appendf(buf, "%s+%#lx", fcts[best_idx].name, (long)best_dist);
+               return best_dist == 0 ? addr : NULL;
+       }
+       else if ((void*)addr < (void*)main)
                chunk_appendf(buf, "main-%#lx", (long)((void*)main - addr));
        else
                chunk_appendf(buf, "main+%#lx", (long)(addr - (void*)main));