t_dladdr.c revision 1.2
1/*	$NetBSD: t_dladdr.c,v 1.2 2025/12/15 02:36:47 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.2 2025/12/15 02:36:47 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_CHECK_MSG(dladdr(page, &info) == 0,
158	    "dladdr returned %s @ %p (symbol %s @ %p) for bogus address %p",
159	    info.dli_fname, info.dli_fbase,
160	    info.dli_sname, info.dli_saddr,
161	    page);
162}
163
164ATF_TP_ADD_TCS(tp)
165{
166
167	ATF_TP_ADD_TC(tp, dladdr_after__end);
168	ATF_TP_ADD_TC(tp, dladdr_errno);
169	ATF_TP_ADD_TC(tp, dladdr_self);
170	return atf_no_error();
171}
172