1 /* $NetBSD: t_rtld_r_debug.c,v 1.12 2025/04/16 12:05:52 riastradh Exp $ */ 2 3 /* 4 * Copyright (c) 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND 17 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __RCSID("$NetBSD: t_rtld_r_debug.c,v 1.12 2025/04/16 12:05:52 riastradh Exp $"); 32 33 #include <sys/types.h> 34 35 #include <atf-c.h> 36 #include <dlfcn.h> 37 #include <link_elf.h> 38 #include <stdbool.h> 39 40 #include "h_macros.h" 41 42 static long int 43 getauxval(unsigned int type) 44 { 45 const AuxInfo *aux; 46 47 for (aux = _dlauxinfo(); aux->a_type != AT_NULL; ++aux) { 48 if (type == aux->a_type) 49 return aux->a_v; 50 } 51 52 return 0; 53 } 54 55 static Elf_Dyn * 56 get_dynamic_section(void) 57 { 58 uintptr_t relocbase = (uintptr_t)~0U; 59 const Elf_Phdr *phdr; 60 Elf_Half phnum; 61 const Elf_Phdr *phlimit, *dynphdr; 62 63 phdr = (void *)getauxval(AT_PHDR); 64 phnum = (Elf_Half)getauxval(AT_PHNUM); 65 66 printf("AT_PHDR=%p\n", phdr); 67 printf("AT_PHNUM=%d\n", phnum); 68 69 ATF_REQUIRE(phdr != NULL); 70 ATF_REQUIRE(phnum != (Elf_Half)~0); 71 72 phlimit = phdr + phnum; 73 dynphdr = NULL; 74 75 for (; phdr < phlimit; ++phdr) { 76 printf("phdr %p: type=%d flags=0x%x" 77 " vaddr=0x%lx paddr=0x%lx" 78 " filesz=0x%lx memsz=0x%lx" 79 " align=0x%lx\n", 80 phdr, phdr->p_type, phdr->p_flags, 81 (long)phdr->p_vaddr, (long)phdr->p_paddr, 82 (long)phdr->p_filesz, (long)phdr->p_memsz, 83 (long)phdr->p_align); 84 if (phdr->p_type == PT_DYNAMIC) 85 dynphdr = phdr; 86 if (phdr->p_type == PT_PHDR) 87 relocbase = (uintptr_t)phdr - phdr->p_vaddr; 88 } 89 90 return (Elf_Dyn *)((uint8_t *)dynphdr->p_vaddr + relocbase); 91 } 92 93 static const struct r_debug * 94 get_rtld_r_debug(void) 95 { 96 const struct r_debug *debug = NULL; 97 Elf_Dyn *dynp; 98 99 for (dynp = get_dynamic_section(); dynp->d_tag != DT_NULL; dynp++) { 100 printf("dynp %p: tag=%ld val=0x%lx\n", dynp, 101 (long)dynp->d_tag, (long)dynp->d_un.d_val); 102 #ifdef __mips__ 103 if (dynp->d_tag == DT_MIPS_RLD_MAP) { 104 debug = (const void *)*(Elf_Addr *)dynp->d_un.d_ptr; 105 break; 106 } 107 if (dynp->d_tag == DT_MIPS_RLD_MAP_REL) { 108 debug = (const void *)*(Elf_Addr *)((Elf_Addr)dynp + 109 dynp->d_un.d_val); 110 break; 111 } 112 #else 113 if (dynp->d_tag == DT_DEBUG) { 114 debug = (void *)dynp->d_un.d_val; 115 break; 116 } 117 #endif 118 } 119 ATF_REQUIRE(debug != NULL); 120 121 return debug; 122 } 123 124 static void 125 check_r_debug_return_link_map(const char *name, const struct link_map **rmap) 126 { 127 const struct r_debug *debug; 128 const struct link_map *map; 129 void *loader; 130 bool found; 131 132 loader = NULL; 133 debug = get_rtld_r_debug(); 134 ATF_REQUIRE(debug != NULL); 135 ATF_CHECK_EQ_MSG(debug->r_version, R_DEBUG_VERSION, 136 "debug->r_version=%d R_DEBUG_VERSION=%d", 137 debug->r_version, R_DEBUG_VERSION); 138 map = debug->r_map; 139 ATF_CHECK(map != NULL); 140 141 for (found = false; map; map = map->l_next) { 142 if (strstr(map->l_name, name) != NULL) { 143 if (rmap) 144 *rmap = map; 145 found = true; 146 } else if (strstr(map->l_name, "ld.elf_so") != NULL) { 147 loader = (void *)map->l_addr; 148 } 149 } 150 ATF_CHECK(found); 151 ATF_CHECK(loader != NULL); 152 ATF_CHECK(debug->r_brk != NULL); 153 ATF_CHECK_EQ_MSG(debug->r_state, RT_CONSISTENT, 154 "debug->r_state=%d RT_CONSISTENT=%d", 155 debug->r_state, RT_CONSISTENT); 156 ATF_CHECK_EQ_MSG(debug->r_ldbase, loader, 157 "debug->r_ldbase=%p loader=%p", 158 debug->r_ldbase, loader); 159 } 160 161 ATF_TC(self); 162 ATF_TC_HEAD(self, tc) 163 { 164 atf_tc_set_md_var(tc, "descr", "check whether r_debug is well-formed"); 165 } 166 ATF_TC_BODY(self, tc) 167 { 168 check_r_debug_return_link_map("t_rtld_r_debug", NULL); 169 } 170 171 ATF_TC(dlopen); 172 ATF_TC_HEAD(dlopen, tc) 173 { 174 atf_tc_set_md_var(tc, "descr", 175 "check whether r_debug is well-formed after a dlopen(3) call"); 176 } 177 ATF_TC_BODY(dlopen, tc) 178 { 179 void *handle; 180 const struct link_map *r_map, *map; 181 182 handle = dlopen("libutil.so", RTLD_LAZY); 183 ATF_REQUIRE_MSG(handle, "dlopen: %s", dlerror()); 184 185 check_r_debug_return_link_map("libutil.so", &r_map); 186 187 ATF_REQUIRE_EQ_MSG(dlinfo(handle, RTLD_DI_LINKMAP, &map), 0, 188 "dlinfo: %s", dlerror()); 189 190 ATF_CHECK_EQ_MSG(map, r_map, "map=%p r_map=%p", map, r_map); 191 ATF_CHECK_EQ_MSG(dlclose(handle), 0, "dlclose: %s", dlerror()); 192 } 193 194 ATF_TP_ADD_TCS(tp) 195 { 196 ATF_TP_ADD_TC(tp, self); 197 ATF_TP_ADD_TC(tp, dlopen); 198 return atf_no_error(); 199 } 200