diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index a0fb4663c1e54..643bba2aae694 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -1243,6 +1243,9 @@ if(LLVM_LIBC_FULL_BUILD) # sys/select.h entrypoints libc.src.sys.select.select + # link.h entrypoints + libc.src.link.dl_iterate_phdr + # wchar.h entrypoints # libc.src.wchar.fgetwc # libc.src.wchar.fgetws diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt index f67984d4f6484..bbb7aca7f39b6 100644 --- a/libc/config/linux/riscv/entrypoints.txt +++ b/libc/config/linux/riscv/entrypoints.txt @@ -1377,6 +1377,9 @@ if(LLVM_LIBC_FULL_BUILD) # sys/select.h entrypoints libc.src.sys.select.select + # link.h entrypoints + libc.src.link.dl_iterate_phdr + # wchar.h entrypoints # libc.src.wchar.fgetwc # libc.src.wchar.fgetws diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index b6247c1172150..2a0e43744ec0d 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -1478,6 +1478,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..ae2b1df6ea71a 100644 --- a/libc/src/link/CMakeLists.txt +++ b/libc/src/link/CMakeLists.txt @@ -6,4 +6,6 @@ add_entrypoint_object( dl_iterate_phdr.h DEPENDS libc.hdr.stdint_proxy + 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 7964411598d4a..166f50fdfee86 100644 --- a/libc/src/link/dl_iterate_phdr.cpp +++ b/libc/src/link/dl_iterate_phdr.cpp @@ -5,21 +5,68 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===---------------------------------------------------------------------===// +/// +/// \file +/// The dl_iterate_phdr implementation. +/// +//===----------------------------------------------------------------------===/ #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 + +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 = ""; + to_return.dlpi_phdr = reinterpret_cast( + reinterpret_cast(executable_header) + + executable_header->e_phoff); + 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)) { - // 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(&__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 vdso_start_address = auxv::get(AT_SYSINFO_EHDR); + if (!vdso_start_address) + return 0; + ElfW(Ehdr) *vdso_header = reinterpret_cast(*vdso_start_address); + if (vdso_header == nullptr) + return 0; + struct dl_phdr_info vdso_info = create_executable_info(vdso_header); + for (auto elf_headers : + cpp::span(vdso_info.dlpi_phdr, vdso_info.dlpi_phnum)) { + if (elf_headers.p_type == PT_LOAD) { + vdso_info.dlpi_addr = + reinterpret_cast(vdso_header) - elf_headers.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..c3c8a5e89663c --- /dev/null +++ b/libc/test/src/link/dl_iterate_phdr_test.cpp @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===/ +/// +/// \file +/// Tests for the dl_iterate_phdr implementation. +/// +//===----------------------------------------------------------------------===/ + +#include "hdr/types/size_t.h" +#include "src/link/dl_iterate_phdr.h" +#include "test/UnitTest/Test.h" + +int save_return_1(struct dl_phdr_info *info, [[maybe_unused]] size_t info_size, + void *arg) { + *static_cast(arg) = info->dlpi_phnum; + return 1; +} + +TEST(LlvmLibcLinkDlIteratePhdrTest, OnlyExecutable) { + 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 save_return_0(struct dl_phdr_info *info, [[maybe_unused]] size_t info_size, + void *arg) { + *static_cast(arg) = info->dlpi_phnum; + return 0; +} + +TEST(LlvmLibcLinkDlIteratePhdrTest, BothExecutableAndVDSO) { + 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); +}