[libc-commits] [libc] [libc] Add barebones dl_iterate_phdr implementation (PR #194196)

Aiden Grossman via libc-commits libc-commits at lists.llvm.org
Sat May 9 22:36:43 PDT 2026


https://github.com/boomanaiden154 updated https://github.com/llvm/llvm-project/pull/194196

>From 8f102d069ba37a723f51ee1b33ba521f3efe8e0b Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 26 Apr 2026 00:08:44 +0000
Subject: [PATCH 1/3] [libc] Add barebones dl_iterate_phdr implementation

Add a basic dl_iterate_phdr implementation so that we can get libunwind
building. This implementation is bare and not fully compliant with the
man page for fully static binaries (which are all that we support
currently with the lack of a dynamic linker) due to the lack of TLS
info, but that can be added at a future date if it is needed, as it is
not needed by libunwind.

Add some very basic smoke tests.
---
 libc/config/linux/x86_64/entrypoints.txt    |  3 ++
 libc/src/link/CMakeLists.txt                |  1 +
 libc/src/link/dl_iterate_phdr.cpp           | 53 +++++++++++++++++++--
 libc/test/src/CMakeLists.txt                |  1 +
 libc/test/src/link/CMakeLists.txt           | 11 +++++
 libc/test/src/link/dl_iterate_phdr_test.cpp | 37 ++++++++++++++
 6 files changed, 101 insertions(+), 5 deletions(-)
 create mode 100644 libc/test/src/link/CMakeLists.txt
 create mode 100644 libc/test/src/link/dl_iterate_phdr_test.cpp

diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index d1c1d9496af67..9c796bf4ac94e 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1462,6 +1462,9 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.nl_types.catopen 
     libc.src.nl_types.catclose
     libc.src.nl_types.catgets
+
+    # link.h entrypoints
+    libc.src.link.dl_iterate_phdr
   )
 endif()
 
diff --git a/libc/src/link/CMakeLists.txt b/libc/src/link/CMakeLists.txt
index 55f5edfab7d93..47a7f5a2af605 100644
--- a/libc/src/link/CMakeLists.txt
+++ b/libc/src/link/CMakeLists.txt
@@ -6,4 +6,5 @@ add_entrypoint_object(
     dl_iterate_phdr.h
   DEPENDS
     libc.hdr.stdint_proxy
+    libc.hdr.sys_auxv_macros
 )
diff --git a/libc/src/link/dl_iterate_phdr.cpp b/libc/src/link/dl_iterate_phdr.cpp
index 7964411598d4a..bb0f0edb15fc4 100644
--- a/libc/src/link/dl_iterate_phdr.cpp
+++ b/libc/src/link/dl_iterate_phdr.cpp
@@ -8,18 +8,61 @@
 
 #include "dl_iterate_phdr.h"
 
+#include "llvm-libc-macros/link-macros.h"
+#include "src/__support/OSUtil/linux/auxv.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 
+#include <elf.h>
+
+extern "C" void *__executable_start;
+
 namespace LIBC_NAMESPACE_DECL {
 
 LLVM_LIBC_FUNCTION(int, dl_iterate_phdr,
                    (__dl_iterate_phdr_callback_t callback, void *arg)) {
-  // FIXME: For pure static linking, this can report just the executable with
-  // info from __ehdr_start or AT_{PHDR,PHNUM} decoding, and its PT_TLS; and it
-  // could report the vDSO.
-  (void)callback, (void)arg;
-  return 0;
+  ElfW(Ehdr) *executable_header =
+      reinterpret_cast<ElfW(Ehdr) *>(&__executable_start);
+  struct dl_phdr_info executable_info;
+  executable_info.dlpi_addr = 0;
+  executable_info.dlpi_name = nullptr;
+  executable_info.dlpi_phdr = reinterpret_cast<ElfW(Phdr) *>(
+      reinterpret_cast<uintptr_t>(executable_header) +
+      executable_header->e_phoff);
+  executable_info.dlpi_phnum = executable_header->e_phnum;
+  executable_info.dlpi_adds = 0;
+  executable_info.dlpi_subs = 0;
+  executable_info.dlpi_tls_modid = 0;
+  executable_info.dlpi_tls_data = nullptr;
+  int executable_return_code =
+      callback(&executable_info, sizeof(executable_info), arg);
+  if (executable_return_code != 0)
+    return executable_return_code;
+
+  cpp::optional<unsigned long> vdso_start_address =
+      LIBC_NAMESPACE::auxv::get(AT_SYSINFO_EHDR);
+  if (!vdso_start_address)
+    return 0;
+  ElfW(Ehdr) *vdso_header = reinterpret_cast<ElfW(Ehdr) *>(*vdso_start_address);
+  if (vdso_header == nullptr)
+    return 0;
+  struct dl_phdr_info vdso_info;
+  vdso_info.dlpi_addr = 0;
+  vdso_info.dlpi_phdr = reinterpret_cast<ElfW(Phdr) *>(
+      reinterpret_cast<char *>(vdso_header) + vdso_header->e_phoff);
+  vdso_info.dlpi_phnum = vdso_header->e_phnum;
+  vdso_info.dlpi_adds = 0;
+  vdso_info.dlpi_subs = 0;
+  vdso_info.dlpi_tls_modid = 0;
+  vdso_info.dlpi_tls_data = nullptr;
+  for (size_t i = 0; i < vdso_info.dlpi_phnum; ++i) {
+    if (vdso_info.dlpi_phdr[i].p_type == PT_LOAD) {
+      vdso_info.dlpi_addr =
+          (ElfW(Addr))vdso_header - vdso_info.dlpi_phdr[i].p_vaddr;
+      break;
+    }
+  }
+  return callback(&vdso_info, sizeof(vdso_info), arg);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 7bdafc6a85706..b877c7455fc34 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -62,6 +62,7 @@ add_subdirectory(complex)
 add_subdirectory(ctype)
 add_subdirectory(errno)
 add_subdirectory(fenv)
+add_subdirectory(link)
 add_subdirectory(math)
 add_subdirectory(search)
 add_subdirectory(setjmp)
diff --git a/libc/test/src/link/CMakeLists.txt b/libc/test/src/link/CMakeLists.txt
new file mode 100644
index 0000000000000..efae9cf7e3d9b
--- /dev/null
+++ b/libc/test/src/link/CMakeLists.txt
@@ -0,0 +1,11 @@
+add_custom_target(libc_link_unittests)
+
+add_libc_unittest(
+  dl_iterate_phdr_test
+  SUITE
+    libc_link_unittests
+  SRCS
+    dl_iterate_phdr_test.cpp
+  DEPENDS
+    libc.src.link.dl_iterate_phdr
+)
diff --git a/libc/test/src/link/dl_iterate_phdr_test.cpp b/libc/test/src/link/dl_iterate_phdr_test.cpp
new file mode 100644
index 0000000000000..4817b0fe7957b
--- /dev/null
+++ b/libc/test/src/link/dl_iterate_phdr_test.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===/
+
+#include "hdr/types/size_t.h"
+#include "src/link/dl_iterate_phdr.h"
+#include "test/UnitTest/Test.h"
+
+int SaveReturn1(struct dl_phdr_info *info, [[maybe_unused]] size_t info_size,
+                void *arg) {
+  *static_cast<void **>(arg) = info;
+  return 1;
+}
+
+TEST(LlvmLibcLinkDlIteratePhdrTest, OnlyExecutable) {
+  struct dl_phdr_info executable_info;
+  EXPECT_EQ(LIBC_NAMESPACE::dl_iterate_phdr(SaveReturn1, &executable_info), 1);
+  int program_header_count = executable_info.dlpi_phnum;
+  EXPECT_GT(program_header_count, 0);
+}
+
+int SaveReturn0(struct dl_phdr_info *info, [[maybe_unused]] size_t info_size,
+                void *arg) {
+  *static_cast<void **>(arg) = info;
+  return 0;
+}
+
+TEST(LlvmLibcLinkDlIteratePhdrTest, BothExecutableAndVDSO) {
+  struct dl_phdr_info executable_info;
+  EXPECT_EQ(LIBC_NAMESPACE::dl_iterate_phdr(SaveReturn0, &executable_info), 0);
+  int program_header_count = executable_info.dlpi_phnum;
+  EXPECT_GT(program_header_count, 0);
+}

>From c7621bf4f1efc7a72042c81d197c5d782c4099ac Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Fri, 8 May 2026 23:19:54 +0000
Subject: [PATCH 2/3] feedback

---
 libc/config/linux/aarch64/entrypoints.txt   |  3 ++
 libc/config/linux/riscv/entrypoints.txt     |  3 ++
 libc/src/link/CMakeLists.txt                |  3 +-
 libc/src/link/dl_iterate_phdr.cpp           | 54 ++++++++++-----------
 libc/test/src/link/dl_iterate_phdr_test.cpp | 24 ++++-----
 5 files changed, 46 insertions(+), 41 deletions(-)

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index b15edc5e3e102..837e03b4d30c8 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1229,6 +1229,9 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.sys.socket.setsockopt
     libc.src.sys.socket.shutdown
     libc.src.sys.socket.socket
+
+    # link.h entrypoints
+    libc.src.link.dl_iterate_phdr
   )
 endif()
 
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 6478aed3b0391..012fa5126996c 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1370,6 +1370,9 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.sys.socket.recv
     libc.src.sys.socket.recvfrom
     libc.src.sys.socket.recvmsg
+
+    # link.h entrypoints
+    libc.src.link.dl_iterate_phdr
   )
 endif()
 
diff --git a/libc/src/link/CMakeLists.txt b/libc/src/link/CMakeLists.txt
index 47a7f5a2af605..ae2b1df6ea71a 100644
--- a/libc/src/link/CMakeLists.txt
+++ b/libc/src/link/CMakeLists.txt
@@ -6,5 +6,6 @@ add_entrypoint_object(
     dl_iterate_phdr.h
   DEPENDS
     libc.hdr.stdint_proxy
-    libc.hdr.sys_auxv_macros
+    libc.src.__support.CPP.span
+    libc.src.__support.OSUtil.linux.auxv
 )
diff --git a/libc/src/link/dl_iterate_phdr.cpp b/libc/src/link/dl_iterate_phdr.cpp
index bb0f0edb15fc4..8b3c112b2a600 100644
--- a/libc/src/link/dl_iterate_phdr.cpp
+++ b/libc/src/link/dl_iterate_phdr.cpp
@@ -9,56 +9,54 @@
 #include "dl_iterate_phdr.h"
 
 #include "llvm-libc-macros/link-macros.h"
+#include "src/__support/CPP/span.h"
 #include "src/__support/OSUtil/linux/auxv.h"
 #include "src/__support/common.h"
 #include "src/__support/macros/config.h"
 
 #include <elf.h>
 
-extern "C" void *__executable_start;
+extern "C" void *__ehdr_start;
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(int, dl_iterate_phdr,
-                   (__dl_iterate_phdr_callback_t callback, void *arg)) {
-  ElfW(Ehdr) *executable_header =
-      reinterpret_cast<ElfW(Ehdr) *>(&__executable_start);
-  struct dl_phdr_info executable_info;
-  executable_info.dlpi_addr = 0;
-  executable_info.dlpi_name = nullptr;
-  executable_info.dlpi_phdr = reinterpret_cast<ElfW(Phdr) *>(
+struct dl_phdr_info create_executable_info(ElfW(Ehdr) * executable_header) {
+  struct dl_phdr_info to_return;
+  to_return.dlpi_addr = 0;
+  to_return.dlpi_name = "";
+  to_return.dlpi_phdr = reinterpret_cast<ElfW(Phdr) *>(
       reinterpret_cast<uintptr_t>(executable_header) +
       executable_header->e_phoff);
-  executable_info.dlpi_phnum = executable_header->e_phnum;
-  executable_info.dlpi_adds = 0;
-  executable_info.dlpi_subs = 0;
-  executable_info.dlpi_tls_modid = 0;
-  executable_info.dlpi_tls_data = nullptr;
+  to_return.dlpi_phnum = executable_header->e_phnum;
+  to_return.dlpi_adds = 0;
+  to_return.dlpi_subs = 0;
+  to_return.dlpi_tls_modid = 0;
+  to_return.dlpi_tls_data = nullptr;
+  return to_return;
+}
+
+LLVM_LIBC_FUNCTION(int, dl_iterate_phdr,
+                   (__dl_iterate_phdr_callback_t callback, void *arg)) {
+  ElfW(Ehdr) *executable_header = reinterpret_cast<ElfW(Ehdr) *>(&__ehdr_start);
+  struct dl_phdr_info executable_info =
+      create_executable_info(executable_header);
   int executable_return_code =
       callback(&executable_info, sizeof(executable_info), arg);
   if (executable_return_code != 0)
     return executable_return_code;
 
-  cpp::optional<unsigned long> vdso_start_address =
-      LIBC_NAMESPACE::auxv::get(AT_SYSINFO_EHDR);
+  cpp::optional<unsigned long> vdso_start_address = auxv::get(AT_SYSINFO_EHDR);
   if (!vdso_start_address)
     return 0;
   ElfW(Ehdr) *vdso_header = reinterpret_cast<ElfW(Ehdr) *>(*vdso_start_address);
   if (vdso_header == nullptr)
     return 0;
-  struct dl_phdr_info vdso_info;
-  vdso_info.dlpi_addr = 0;
-  vdso_info.dlpi_phdr = reinterpret_cast<ElfW(Phdr) *>(
-      reinterpret_cast<char *>(vdso_header) + vdso_header->e_phoff);
-  vdso_info.dlpi_phnum = vdso_header->e_phnum;
-  vdso_info.dlpi_adds = 0;
-  vdso_info.dlpi_subs = 0;
-  vdso_info.dlpi_tls_modid = 0;
-  vdso_info.dlpi_tls_data = nullptr;
-  for (size_t i = 0; i < vdso_info.dlpi_phnum; ++i) {
-    if (vdso_info.dlpi_phdr[i].p_type == PT_LOAD) {
+  struct dl_phdr_info vdso_info = create_executable_info(vdso_header);
+  for (auto elf_headers :
+       cpp::span<const ElfW(Phdr)>(vdso_info.dlpi_phdr, vdso_info.dlpi_phnum)) {
+    if (elf_headers.p_type == PT_LOAD) {
       vdso_info.dlpi_addr =
-          (ElfW(Addr))vdso_header - vdso_info.dlpi_phdr[i].p_vaddr;
+          reinterpret_cast<ElfW(Addr)>(vdso_header) - elf_headers.p_vaddr;
       break;
     }
   }
diff --git a/libc/test/src/link/dl_iterate_phdr_test.cpp b/libc/test/src/link/dl_iterate_phdr_test.cpp
index 4817b0fe7957b..4156f0c0e0dbc 100644
--- a/libc/test/src/link/dl_iterate_phdr_test.cpp
+++ b/libc/test/src/link/dl_iterate_phdr_test.cpp
@@ -10,28 +10,28 @@
 #include "src/link/dl_iterate_phdr.h"
 #include "test/UnitTest/Test.h"
 
-int SaveReturn1(struct dl_phdr_info *info, [[maybe_unused]] size_t info_size,
-                void *arg) {
-  *static_cast<void **>(arg) = info;
+int save_return_1(struct dl_phdr_info *info, [[maybe_unused]] size_t info_size,
+                  void *arg) {
+  *static_cast<int *>(arg) = info->dlpi_phnum;
   return 1;
 }
 
 TEST(LlvmLibcLinkDlIteratePhdrTest, OnlyExecutable) {
-  struct dl_phdr_info executable_info;
-  EXPECT_EQ(LIBC_NAMESPACE::dl_iterate_phdr(SaveReturn1, &executable_info), 1);
-  int program_header_count = executable_info.dlpi_phnum;
+  int program_header_count = 0;
+  EXPECT_EQ(
+      LIBC_NAMESPACE::dl_iterate_phdr(save_return_1, &program_header_count), 1);
   EXPECT_GT(program_header_count, 0);
 }
 
-int SaveReturn0(struct dl_phdr_info *info, [[maybe_unused]] size_t info_size,
-                void *arg) {
-  *static_cast<void **>(arg) = info;
+int save_return_0(struct dl_phdr_info *info, [[maybe_unused]] size_t info_size,
+                  void *arg) {
+  *static_cast<int *>(arg) = info->dlpi_phnum;
   return 0;
 }
 
 TEST(LlvmLibcLinkDlIteratePhdrTest, BothExecutableAndVDSO) {
-  struct dl_phdr_info executable_info;
-  EXPECT_EQ(LIBC_NAMESPACE::dl_iterate_phdr(SaveReturn0, &executable_info), 0);
-  int program_header_count = executable_info.dlpi_phnum;
+  int program_header_count = 0;
+  EXPECT_EQ(
+      LIBC_NAMESPACE::dl_iterate_phdr(save_return_0, &program_header_count), 0);
   EXPECT_GT(program_header_count, 0);
 }

>From 3323f7cfb9c67d1150edf624b657c804869dc774 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 10 May 2026 05:36:23 +0000
Subject: [PATCH 3/3] feedback

---
 libc/src/link/dl_iterate_phdr.cpp           | 6 ++++++
 libc/test/src/link/dl_iterate_phdr_test.cpp | 5 +++++
 2 files changed, 11 insertions(+)

diff --git a/libc/src/link/dl_iterate_phdr.cpp b/libc/src/link/dl_iterate_phdr.cpp
index 8b3c112b2a600..166f50fdfee86 100644
--- a/libc/src/link/dl_iterate_phdr.cpp
+++ b/libc/src/link/dl_iterate_phdr.cpp
@@ -5,6 +5,11 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===---------------------------------------------------------------------===//
+///
+/// \file
+/// The dl_iterate_phdr implementation.
+///
+//===----------------------------------------------------------------------===/
 
 #include "dl_iterate_phdr.h"
 
@@ -21,6 +26,7 @@ extern "C" void *__ehdr_start;
 namespace LIBC_NAMESPACE_DECL {
 
 struct dl_phdr_info create_executable_info(ElfW(Ehdr) * executable_header) {
+  // TODO: Calculate dlpi_addr in the PIE case and set dlpi_name for VDSO.
   struct dl_phdr_info to_return;
   to_return.dlpi_addr = 0;
   to_return.dlpi_name = "";
diff --git a/libc/test/src/link/dl_iterate_phdr_test.cpp b/libc/test/src/link/dl_iterate_phdr_test.cpp
index 4156f0c0e0dbc..c3c8a5e89663c 100644
--- a/libc/test/src/link/dl_iterate_phdr_test.cpp
+++ b/libc/test/src/link/dl_iterate_phdr_test.cpp
@@ -5,6 +5,11 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===/
+///
+/// \file
+/// Tests for the dl_iterate_phdr implementation.
+///
+//===----------------------------------------------------------------------===/
 
 #include "hdr/types/size_t.h"
 #include "src/link/dl_iterate_phdr.h"



More information about the libc-commits mailing list