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