# HG changeset patch # User Taylor R Campbell # Date 1744596185 0 # Mon Apr 14 02:03:05 2025 +0000 # Branch trunk # Node ID 6595e473ec58e7d23a5a1c351c2fec8136cd9fd5 # Parent ac7c24d0e55da140e83ba403e888ecdd1b7c0323 # EXP-Topic riastradh-pr59296-trtldrdebugmips t_rtld_r_debug: Mark this xfail on mips. PR port-mips/59296: t_rtld_r_debug test is failing diff -r ac7c24d0e55d -r 6595e473ec58 tests/libexec/ld.elf_so/t_rtld_r_debug.c --- a/tests/libexec/ld.elf_so/t_rtld_r_debug.c Sun Apr 13 17:23:06 2025 +0000 +++ b/tests/libexec/ld.elf_so/t_rtld_r_debug.c Mon Apr 14 02:03:05 2025 +0000 @@ -116,6 +116,10 @@ check_r_debug_return_link_map(const char loader = NULL; debug = get_rtld_r_debug(); +#ifdef __mips__ + atf_tc_expect_fail("PR port-mips/59296:" + " t_rtld_r_debug test is failing"); +#endif ATF_REQUIRE(debug != NULL); ATF_CHECK_EQ_MSG(debug->r_version, R_DEBUG_VERSION, "debug->r_version=%d R_DEBUG_VERSION=%d", # HG changeset patch # User Taylor R Campbell # Date 1744596814 0 # Mon Apr 14 02:13:34 2025 +0000 # Branch trunk # Node ID e39733b6bc5cbaa630adffe32ca218f870045cd6 # Parent 6595e473ec58e7d23a5a1c351c2fec8136cd9fd5 # EXP-Topic riastradh-pr59296-trtldrdebugmips ld.elf_so: Teach this to handle MIPS PIE rtld debug data. Adapt t_rtld_r_debug to handle the two MIPS cases too. XXX t_rtld_r_debug should be tested both as PIE and non-PIE to exercise both cases. Context: The value of a DT_DEBUG .dynamic entry is initialized at load-time, by ld.elf_so, to a pointer to a data structure set up by ld.elf_so describing the shared objects loaded by the executable, so debuggers can find them from, e.g., core dumps. None of this is really documented anywhere that I can find. Best reference is this post on the gdb mailing list from a quarter century ago saying there's no real documentation: https://web.archive.org/web/20250414021320/https://sourceware.org/pipermail/gdb/2000-April/004509.html However, on MIPS, the .dynamic section is mapped read-only, so ld.elf_so can't properly modify it (I imagine technically it could with some mprotect shenanigans but that's not how it's done on MIPS). Instead, the linker reserves a location in read/write memory and uses a DT_MIPS_RLD_MAP entry with a pointer to that location. However, in position-independent executables, the .dynamic entry can't have an absolute pointer to that location because it's not known up front. Instead, the the linker uses a DT_MIPS_RLD_MAP_REL entry with the relative offset to that location from the Elf_Dyn entry itself. I would add a reference for this but it's basically a matter of UTSL plus some oblique mentions on the web and mailing list discussions: https://web.archive.org/web/20250414024823/https://cygwin.com/legacy-ml/binutils/2016-04/msg00244.html https://web.archive.org/web/20250403151803/https://maskray.me/blog/2023-09-04-toolchain-notes-on-mips https://web.archive.org/web/20211024050833/https://reviews.llvm.org/D12794?id=34533 https://web.archive.org/web/20250407052145/https://wiki.debian.org/MIPSPort https://web.archive.org/web/20250414024924/https://reviews.freebsd.org/D17867?id=50122 PR port-mips/59296: t_rtld_r_debug test is failing diff -r 6595e473ec58 -r e39733b6bc5c libexec/ld.elf_so/headers.c --- a/libexec/ld.elf_so/headers.c Mon Apr 14 02:03:05 2025 +0000 +++ b/libexec/ld.elf_so/headers.c Mon Apr 14 02:13:34 2025 +0000 @@ -333,8 +333,10 @@ void #endif /* - * Don't process DT_DEBUG on MIPS as the dynamic section - * is mapped read-only. DT_MIPS_RLD_MAP is used instead. + * Don't process DT_DEBUG on MIPS as the dynamic + * section is mapped read-only. DT_MIPS_RLD_MAP or + * DT_MIPS_RLD_MAP_REL is used instead. + * * XXX: n32/n64 may use DT_DEBUG, not sure yet. */ #ifndef __mips__ @@ -358,10 +360,38 @@ void obj->gotsym = dynp->d_un.d_val; break; + /* + * The .dynamic section is read-only, so the loader + * can't write to it; instead, the linker reserves + * space in a read/write .rld_map section for the + * loader write to, and leaves a pointer to that space + * in a DT_MIPS_RLD_MAP entry. + * + * Except pointers like that don't work for + * position-independent executables, which use + * DT_MIPS_RLD_MAP_REL instead. + */ case DT_MIPS_RLD_MAP: #ifdef RTLD_LOADER - *((Elf_Addr *)(dynp->d_un.d_ptr)) = (Elf_Addr) - &_rtld_debug; + *((Elf_Addr *)dynp->d_un.d_ptr) = + (Elf_Addr)&_rtld_debug; +#endif + break; + + /* + * The .dynamic section is read-only, so the loader + * can't write to it; instead, the linker reserves + * space in a read/write .rld_map section for the + * loader write to, which might be mapped anywhere in + * virtual address space for position-independent + * executables, so the linker leaves its offset + * relative to the .dynamic entry itself in the dynamic + * entry. + */ + case DT_MIPS_RLD_MAP_REL: +#ifdef RTLD_LOADER + *(Elf_Addr *)((Elf_Addr)dynp + dynp->d_un.d_val) = + (Elf_Addr)&_rtld_debug; #endif break; #endif diff -r 6595e473ec58 -r e39733b6bc5c sys/arch/mips/include/elf_machdep.h --- a/sys/arch/mips/include/elf_machdep.h Mon Apr 14 02:03:05 2025 +0000 +++ b/sys/arch/mips/include/elf_machdep.h Mon Apr 14 02:13:34 2025 +0000 @@ -152,6 +152,7 @@ #define DT_MIPS_RLD_MAP 0x70000016 /* address of loader map */ #define DT_MIPS_PLTGOT 0x70000032 #define DT_MIPS_RWPLT 0x70000034 +#define DT_MIPS_RLD_MAP_REL 0x70000035 /* * ELF Flags diff -r 6595e473ec58 -r e39733b6bc5c tests/libexec/ld.elf_so/t_rtld_r_debug.c --- a/tests/libexec/ld.elf_so/t_rtld_r_debug.c Mon Apr 14 02:03:05 2025 +0000 +++ b/tests/libexec/ld.elf_so/t_rtld_r_debug.c Mon Apr 14 02:13:34 2025 +0000 @@ -87,19 +87,30 @@ get_dynamic_section(void) return (Elf_Dyn *)((uint8_t *)dynphdr->p_vaddr + relocbase); } -static struct r_debug * +static const struct r_debug * get_rtld_r_debug(void) { - struct r_debug *debug = NULL; + const struct r_debug *debug = NULL; Elf_Dyn *dynp; for (dynp = get_dynamic_section(); dynp->d_tag != DT_NULL; dynp++) { printf("dynp %p: tag=%ld val=0x%lx\n", dynp, (long)dynp->d_tag, (long)dynp->d_un.d_val); +#ifdef __mips__ + if (dynp->d_tag == DT_MIPS_RLD_MAP) { + debug = (const void *)*(Elf_Addr *)dynp->d_un.d_ptr; + break; + } + if (dynp->d_tag == DT_MIPS_RLD_MAP_REL) { + debug = (const void *)*(Elf_Addr *)((Elf_Addr)dynp + + dynp->d_un.d_val); + } +#else if (dynp->d_tag == DT_DEBUG) { debug = (void *)dynp->d_un.d_val; break; } +#endif } ATF_REQUIRE(debug != NULL); @@ -107,19 +118,15 @@ get_rtld_r_debug(void) } static void -check_r_debug_return_link_map(const char *name, struct link_map **rmap) +check_r_debug_return_link_map(const char *name, const struct link_map **rmap) { - struct r_debug *debug; - struct link_map *map; + const struct r_debug *debug; + const struct link_map *map; void *loader; bool found; loader = NULL; debug = get_rtld_r_debug(); -#ifdef __mips__ - atf_tc_expect_fail("PR port-mips/59296:" - " t_rtld_r_debug test is failing"); -#endif ATF_REQUIRE(debug != NULL); ATF_CHECK_EQ_MSG(debug->r_version, R_DEBUG_VERSION, "debug->r_version=%d R_DEBUG_VERSION=%d", @@ -166,7 +173,7 @@ ATF_TC_HEAD(dlopen, tc) ATF_TC_BODY(dlopen, tc) { void *handle; - struct link_map *map, *r_map; + const struct link_map *r_map, *map; handle = dlopen("libutil.so", RTLD_LAZY); ATF_REQUIRE_MSG(handle, "dlopen: %s", dlerror());