Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAFS5DSH=ihMhB2ELo40NghevFyOXOsP46pTDmvhmiU9z-bWDCQ@mail.gmail.com>
Date: Wed, 4 Feb 2026 20:23:04 -0300
From: Luciano Lo Giudice <lmlogiudice@...il.com>
To: musl@...ts.openwall.com
Subject: [RFC] Implement the _dl_find_object interface

If we look at  the unwinder logic in libgcc, we will notice that outside of
glibc, it uses `dl_iterate_phdr` to locate the .eh_frame section for a
specific program counter value [1].

The issue with their approach is that they use a global cache [2], which is
not protected by any locks. This is presumably because they assume that
`dl_iterate_phdr` provides mutual exclusion by itself. In musl, however,
the dynamic linker doesn't exactly do that, and as such, it appears that
C++ exceptions (and anything that uses the `_Unwind_Find_FDE` interface)
are unsafe in a multi-threaded scenario.

The obvious fix to this would be to file a bug for libgcc and get it fixed
there, but there's another option that also doesn't involve changing the
locking protocol in the dynamic linker: Some time ago, glibc implemented a
new interface called `_dl_find_object` [3], whose main purpose was to
improve the performance of the unwinder used in C++ programs. As such, musl
itself can do the same thing and in addition to fixing this bug, also
obtain a slight performance boost for the described scenario, since this
new interface would only acquire the dynamic linker's read lock once
instead of multiple times.

I'm attaching the patch inline in this message as it's a short one. Any
questions, comments or critiques are welcome.

[1]:
https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2-fde-dip.c#L569
[2]:
https://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-dw2-fde-dip.c#L156
[3]:
https://git.zx2c4.com/glibc/commit/elf?id=5d28a8962dcb6ec056b81d730e3c6fb57185a210

--------------------------------------
diff --git a/include/dlfcn.h b/include/dlfcn.h
index 13ab71dd..4d794cbd 100644
--- a/include/dlfcn.h
+++ b/include/dlfcn.h
@@ -31,8 +31,35 @@ typedef struct {
  const char *dli_sname;
  void *dli_saddr;
 } Dl_info;
+
+#ifdef __x86_64__
+# define DLFO_STRUCT_HAS_EH_DBASE 0
+#else
+# define DLFO_STRUCT_HAS_EH_DBASE 1
+#endif
+
+#include <bits/alltypes.h>
+
+#define DLFO_FLAG_SFRAME   1
+
+struct dl_find_object {
+    unsigned long long dlfo_flags;
+    void *dlfo_map_start;
+    void *dlfo_map_end;
+    struct link_map *dlfo_link_map;
+    void *dlfo_eh_frame;
+#if DLFO_STRUCT_HAS_EH_DBASE
+    void *dlfo_eh_dbase;
+#  if __LONG_MAX == 0x7fffffffL
+    unsigned int __pad1;
+#  endif
+#endif
+    unsigned long long __extra[7];
+};
+
 int dladdr(const void *, Dl_info *);
 int dlinfo(void *, int, void *);
+int _dl_find_object(void *, struct dl_find_object *);
 #endif

 #if _REDIR_TIME64
diff --git a/include/elf.h b/include/elf.h
index d6ae539a..ff196ffa 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -612,6 +612,7 @@ typedef struct {
 #define PT_GNU_STACK 0x6474e551
 #define PT_GNU_RELRO 0x6474e552
 #define PT_GNU_PROPERTY 0x6474e553
+#define PT_GNU_SFRAME 0x6474e554
 #define PT_LOSUNW 0x6ffffffa
 #define PT_SUNWBSS 0x6ffffffa
 #define PT_SUNWSTACK 0x6ffffffb
diff --git a/ldso/dynlink.c b/ldso/dynlink.c
index 715948f4..2195017c 100644
--- a/ldso/dynlink.c
+++ b/ldso/dynlink.c
@@ -2437,3 +2437,44 @@ static void error_impl(const char *fmt, ...)
 static void error_noop(const char *fmt, ...)
 {
 }
+
+int _dl_find_object(void *addr, struct dl_find_object *obj)
+{
+ struct dso *p;
+ Phdr *phdr;
+ size_t cnt, esize;
+ unsigned int read;
+
+ pthread_rwlock_rdlock(&lock);
+ p = addr2dso((size_t)addr);
+ pthread_rwlock_unlock(&lock);
+
+ if (!p) return -1;
+
+ memset(obj, 0, sizeof(*obj));
+ obj->dlfo_map_start = p->map;
+ obj->dlfo_map_end = p->map + p->map_len;
+ obj->dlfo_link_map = (struct link_map *)p;
+#if DLFO_STRUCT_HAS_EH_DBASE
+ obj->dlfo_eh_dbase = p->got;
+#endif
+
+ phdr = p->phdr;
+ cnt = p->phnum;
+ esize = p->phentsize;
+ read = 0;
+
+ for (; cnt--; phdr = (void *)((char *)phdr + esize)) {
+ if (phdr->p_type == PT_GNU_EH_FRAME) {
+ obj->dlfo_eh_frame = laddr(p, phdr->p_vaddr);
+ read |= 1;
+ } else if (phdr->p_type == PT_GNU_SFRAME) {
+ obj->dlfo_flags |= DLFO_FLAG_SFRAME;
+ read |= 2;
+ }
+
+ if (read == 3) break;
+ }
+
+ return 0;
+}
diff --git a/src/dl_find_object.c b/src/dl_find_object.c
new file mode 100644
index 00000000..918d76a7
--- /dev/null
+++ b/src/dl_find_object.c
@@ -0,0 +1,10 @@
+#define _GNU_SOURCE
+#include <dlfcn.h>
+
+static int
+stub_dl_find_object (void *a, struct dl_find_object *o)
+{
+  return (-1);
+}
+
+weak_alias (stub_dl_find_object, _dl_find_object);

Content of type "text/html" skipped

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.