11.2Sriastrad/* $NetBSD: t_dladdr.c,v 1.2 2025/12/15 02:36:47 riastradh Exp $ */ 21.1Sriastrad 31.1Sriastrad/*- 41.1Sriastrad * Copyright (c) 2025 The NetBSD Foundation, Inc. 51.1Sriastrad * All rights reserved. 61.1Sriastrad * 71.1Sriastrad * Redistribution and use in source and binary forms, with or without 81.1Sriastrad * modification, are permitted provided that the following conditions 91.1Sriastrad * are met: 101.1Sriastrad * 1. Redistributions of source code must retain the above copyright 111.1Sriastrad * notice, this list of conditions and the following disclaimer. 121.1Sriastrad * 2. Redistributions in binary form must reproduce the above copyright 131.1Sriastrad * notice, this list of conditions and the following disclaimer in the 141.1Sriastrad * documentation and/or other materials provided with the distribution. 151.1Sriastrad * 161.1Sriastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 171.1Sriastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 181.1Sriastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 191.1Sriastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 201.1Sriastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 211.1Sriastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 221.1Sriastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 231.1Sriastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 241.1Sriastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 251.1Sriastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 261.1Sriastrad * POSSIBILITY OF SUCH DAMAGE. 271.1Sriastrad */ 281.1Sriastrad 291.1Sriastrad#include <sys/cdefs.h> 301.2Sriastrad__RCSID("$NetBSD: t_dladdr.c,v 1.2 2025/12/15 02:36:47 riastradh Exp $"); 311.1Sriastrad 321.1Sriastrad#include <sys/mman.h> 331.1Sriastrad 341.1Sriastrad#include <atf-c.h> 351.1Sriastrad#include <dlfcn.h> 361.1Sriastrad#include <unistd.h> 371.1Sriastrad 381.1Sriastrad#include "h_macros.h" 391.1Sriastrad 401.1Sriastrad#define SELF "t_dladdr" 411.1Sriastrad 421.1Sriastrad/* 431.1Sriastrad * Note: the symbols foo, bar, and baz must be exposed to dlfcn(3) by 441.1Sriastrad * linking this with -export-dynamic. 451.1Sriastrad */ 461.1Sriastradint foo; /* something in this ELF object */ 471.1Sriastradint bar; /* something in this ELF object */ 481.1Sriastradint baz; /* something in this ELF object */ 491.1Sriastrad 501.1Sriastradextern char _end[]; /* one past last byte of this ELF object */ 511.1Sriastrad 521.1SriastradATF_TC(dladdr_self); 531.1SriastradATF_TC_HEAD(dladdr_self, tc) 541.1Sriastrad{ 551.1Sriastrad atf_tc_set_md_var(tc, "descr", 561.1Sriastrad "Verify dladdr of data in this object returns self"); 571.1Sriastrad} 581.1SriastradATF_TC_BODY(dladdr_self, tc) 591.1Sriastrad{ 601.1Sriastrad Dl_info info; 611.1Sriastrad const char *ptr = (char *)&bar + 1; 621.1Sriastrad const char *p; 631.1Sriastrad 641.1Sriastrad /* 651.1Sriastrad * If we're statically linked, dladdr just fails (XXX is that 661.1Sriastrad * right?). But we're not statically linked because these are 671.1Sriastrad * the ld.elf_so tests (XXX should migrate this to 681.1Sriastrad * tests/lib/libc/dlfcn/ and handle it there). 691.1Sriastrad * 701.1Sriastrad * If we're dynamically linked, then foo, bar, and baz should 711.1Sriastrad * live in self. 721.1Sriastrad */ 731.1Sriastrad ATF_CHECK_MSG(dladdr(ptr, &info) != 0, 741.1Sriastrad "[bar @ %p + [0,%zu)] dladdr(%p) failed: %s", 751.1Sriastrad &bar, sizeof(bar), ptr, dlerror()); 761.1Sriastrad p = strrchr(info.dli_fname, '/'); 771.1Sriastrad if (p == NULL) 781.1Sriastrad p = info.dli_fname; 791.1Sriastrad else 801.1Sriastrad p++; 811.1Sriastrad ATF_CHECK_MSG(strcmp(p, SELF) == 0, 821.1Sriastrad "[bar @ %p + [0,%zu)] dladdr found %p in %s, not self=%s", 831.1Sriastrad &bar, sizeof(bar), ptr, info.dli_fname, SELF); 841.1Sriastrad ATF_CHECK_MSG(strcmp(info.dli_sname, "bar") == 0, 851.1Sriastrad "[bar @ %p + [0,%zu)] dladdr found %p in %s at %p, not bar", 861.1Sriastrad &bar, sizeof(bar), ptr, info.dli_sname, info.dli_saddr); 871.1Sriastrad} 881.1Sriastrad 891.1SriastradATF_TC(dladdr_errno); 901.1SriastradATF_TC_HEAD(dladdr_errno, tc) 911.1Sriastrad{ 921.1Sriastrad atf_tc_set_md_var(tc, "descr", 931.1Sriastrad "Verify dladdr(errno) returns libc.so (or self if static)"); 941.1Sriastrad} 951.1SriastradATF_TC_BODY(dladdr_errno, tc) 961.1Sriastrad{ 971.1Sriastrad Dl_info info; 981.1Sriastrad const char *p; 991.1Sriastrad 1001.1Sriastrad /* 1011.1Sriastrad * If we're statically linked, dladdr just fails (XXX is that 1021.1Sriastrad * right?). But we're not statically linked because these are 1031.1Sriastrad * the ld.elf_so tests (XXX should migrate this to 1041.1Sriastrad * tests/lib/libc/dlfcn/ and handle it there). 1051.1Sriastrad * 1061.1Sriastrad * If we're dynamically linked and single-threaded (no 1071.1Sriastrad * libpthread.so), &errno will be in libc. 1081.1Sriastrad * 1091.1Sriastrad * If we're dynamically linked and multi-threaded, &errno would 1101.1Sriastrad * be in a pthread_t object -- but we're not multithreaded, so 1111.1Sriastrad * it's not. Hence only two cases: static vs dynamic. 1121.1Sriastrad */ 1131.1Sriastrad ATF_CHECK_MSG(dladdr(&errno, &info) != 0, 1141.1Sriastrad "[errno @ %p] dladdr failed: %s", &errno, dlerror()); 1151.1Sriastrad p = strrchr(info.dli_fname, '/'); 1161.1Sriastrad if (p == NULL) 1171.1Sriastrad p = info.dli_fname; 1181.1Sriastrad else 1191.1Sriastrad p++; 1201.1Sriastrad ATF_CHECK_MSG(strcmp(p, SELF) != 0, 1211.1Sriastrad "[errno @ %p] dladdr found errno in self=%s, not in libc.so", 1221.1Sriastrad &errno, info.dli_fname); 1231.1Sriastrad} 1241.1Sriastrad 1251.1SriastradATF_TC(dladdr_after__end); 1261.1SriastradATF_TC_HEAD(dladdr_after__end, tc) 1271.1Sriastrad{ 1281.1Sriastrad atf_tc_set_md_var(tc, "descr", 1291.1Sriastrad "Verify dladdr doesn't claim pages past _end for self"); 1301.1Sriastrad} 1311.1SriastradATF_TC_BODY(dladdr_after__end, tc) 1321.1Sriastrad{ 1331.1Sriastrad const size_t pagesize = sysconf(_SC_PAGESIZE); 1341.1Sriastrad uintptr_t endp, nextp; 1351.1Sriastrad void *page; 1361.1Sriastrad Dl_info info; 1371.1Sriastrad 1381.1Sriastrad /* 1391.1Sriastrad * Round up to a page start. 1401.1Sriastrad */ 1411.1Sriastrad endp = (uintptr_t)(void *)_end; 1421.1Sriastrad nextp = pagesize*((endp + pagesize - 1)/pagesize); 1431.1Sriastrad 1441.1Sriastrad /* 1451.1Sriastrad * Map the next page, or something after it. It just has to be 1461.1Sriastrad * not mapped by an existing object, but for the sake of 1471.1Sriastrad * testing for past bugs, we would like to make it as close to 1481.1Sriastrad * a real object as we can. 1491.1Sriastrad */ 1501.1Sriastrad REQUIRE_LIBC(page = mmap((void *)nextp, pagesize, PROT_NONE, MAP_ANON, 1511.1Sriastrad /*fd*/-1, /*off*/0), 1521.1Sriastrad MAP_FAILED); 1531.1Sriastrad 1541.1Sriastrad /* 1551.1Sriastrad * Verify dladdr doesn't return anything for this page. 1561.1Sriastrad */ 1571.1Sriastrad ATF_CHECK_MSG(dladdr(page, &info) == 0, 1581.1Sriastrad "dladdr returned %s @ %p (symbol %s @ %p) for bogus address %p", 1591.1Sriastrad info.dli_fname, info.dli_fbase, 1601.1Sriastrad info.dli_sname, info.dli_saddr, 1611.1Sriastrad page); 1621.1Sriastrad} 1631.1Sriastrad 1641.1SriastradATF_TP_ADD_TCS(tp) 1651.1Sriastrad{ 1661.1Sriastrad 1671.1Sriastrad ATF_TP_ADD_TC(tp, dladdr_after__end); 1681.1Sriastrad ATF_TP_ADD_TC(tp, dladdr_errno); 1691.1Sriastrad ATF_TP_ADD_TC(tp, dladdr_self); 1701.1Sriastrad return atf_no_error(); 1711.1Sriastrad} 172