t_dladdr.c revision 1.1
1/* $NetBSD: t_dladdr.c,v 1.1 2025/12/14 23:26:12 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2025 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 CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__RCSID("$NetBSD: t_dladdr.c,v 1.1 2025/12/14 23:26:12 riastradh Exp $"); 31 32#include <sys/mman.h> 33 34#include <atf-c.h> 35#include <dlfcn.h> 36#include <unistd.h> 37 38#include "h_macros.h" 39 40#define SELF "t_dladdr" 41 42/* 43 * Note: the symbols foo, bar, and baz must be exposed to dlfcn(3) by 44 * linking this with -export-dynamic. 45 */ 46int foo; /* something in this ELF object */ 47int bar; /* something in this ELF object */ 48int baz; /* something in this ELF object */ 49 50extern char _end[]; /* one past last byte of this ELF object */ 51 52ATF_TC(dladdr_self); 53ATF_TC_HEAD(dladdr_self, tc) 54{ 55 atf_tc_set_md_var(tc, "descr", 56 "Verify dladdr of data in this object returns self"); 57} 58ATF_TC_BODY(dladdr_self, tc) 59{ 60 Dl_info info; 61 const char *ptr = (char *)&bar + 1; 62 const char *p; 63 64 /* 65 * If we're statically linked, dladdr just fails (XXX is that 66 * right?). But we're not statically linked because these are 67 * the ld.elf_so tests (XXX should migrate this to 68 * tests/lib/libc/dlfcn/ and handle it there). 69 * 70 * If we're dynamically linked, then foo, bar, and baz should 71 * live in self. 72 */ 73 ATF_CHECK_MSG(dladdr(ptr, &info) != 0, 74 "[bar @ %p + [0,%zu)] dladdr(%p) failed: %s", 75 &bar, sizeof(bar), ptr, dlerror()); 76 p = strrchr(info.dli_fname, '/'); 77 if (p == NULL) 78 p = info.dli_fname; 79 else 80 p++; 81 ATF_CHECK_MSG(strcmp(p, SELF) == 0, 82 "[bar @ %p + [0,%zu)] dladdr found %p in %s, not self=%s", 83 &bar, sizeof(bar), ptr, info.dli_fname, SELF); 84 ATF_CHECK_MSG(strcmp(info.dli_sname, "bar") == 0, 85 "[bar @ %p + [0,%zu)] dladdr found %p in %s at %p, not bar", 86 &bar, sizeof(bar), ptr, info.dli_sname, info.dli_saddr); 87} 88 89ATF_TC(dladdr_errno); 90ATF_TC_HEAD(dladdr_errno, tc) 91{ 92 atf_tc_set_md_var(tc, "descr", 93 "Verify dladdr(errno) returns libc.so (or self if static)"); 94} 95ATF_TC_BODY(dladdr_errno, tc) 96{ 97 Dl_info info; 98 const char *p; 99 100 /* 101 * If we're statically linked, dladdr just fails (XXX is that 102 * right?). But we're not statically linked because these are 103 * the ld.elf_so tests (XXX should migrate this to 104 * tests/lib/libc/dlfcn/ and handle it there). 105 * 106 * If we're dynamically linked and single-threaded (no 107 * libpthread.so), &errno will be in libc. 108 * 109 * If we're dynamically linked and multi-threaded, &errno would 110 * be in a pthread_t object -- but we're not multithreaded, so 111 * it's not. Hence only two cases: static vs dynamic. 112 */ 113 ATF_CHECK_MSG(dladdr(&errno, &info) != 0, 114 "[errno @ %p] dladdr failed: %s", &errno, dlerror()); 115 p = strrchr(info.dli_fname, '/'); 116 if (p == NULL) 117 p = info.dli_fname; 118 else 119 p++; 120 ATF_CHECK_MSG(strcmp(p, SELF) != 0, 121 "[errno @ %p] dladdr found errno in self=%s, not in libc.so", 122 &errno, info.dli_fname); 123} 124 125ATF_TC(dladdr_after__end); 126ATF_TC_HEAD(dladdr_after__end, tc) 127{ 128 atf_tc_set_md_var(tc, "descr", 129 "Verify dladdr doesn't claim pages past _end for self"); 130} 131ATF_TC_BODY(dladdr_after__end, tc) 132{ 133 const size_t pagesize = sysconf(_SC_PAGESIZE); 134 uintptr_t endp, nextp; 135 void *page; 136 Dl_info info; 137 138 /* 139 * Round up to a page start. 140 */ 141 endp = (uintptr_t)(void *)_end; 142 nextp = pagesize*((endp + pagesize - 1)/pagesize); 143 144 /* 145 * Map the next page, or something after it. It just has to be 146 * not mapped by an existing object, but for the sake of 147 * testing for past bugs, we would like to make it as close to 148 * a real object as we can. 149 */ 150 REQUIRE_LIBC(page = mmap((void *)nextp, pagesize, PROT_NONE, MAP_ANON, 151 /*fd*/-1, /*off*/0), 152 MAP_FAILED); 153 154 /* 155 * Verify dladdr doesn't return anything for this page. 156 */ 157 atf_tc_expect_fail("PR lib/59567: dladdr(3) doesn't work properly" 158 " especially when main executable is loaded at" 159 " high memory address"); 160 ATF_CHECK_MSG(dladdr(page, &info) == 0, 161 "dladdr returned %s @ %p (symbol %s @ %p) for bogus address %p", 162 info.dli_fname, info.dli_fbase, 163 info.dli_sname, info.dli_saddr, 164 page); 165} 166 167ATF_TP_ADD_TCS(tp) 168{ 169 170 ATF_TP_ADD_TC(tp, dladdr_after__end); 171 ATF_TP_ADD_TC(tp, dladdr_errno); 172 ATF_TP_ADD_TC(tp, dladdr_self); 173 return atf_no_error(); 174} 175