diff options
Diffstat (limited to 'libexec/ld.elf_so/t_dladdr.c')
| -rw-r--r-- | libexec/ld.elf_so/t_dladdr.c | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/libexec/ld.elf_so/t_dladdr.c b/libexec/ld.elf_so/t_dladdr.c new file mode 100644 index 000000000000..5cdb0148fecf --- /dev/null +++ b/libexec/ld.elf_so/t_dladdr.c @@ -0,0 +1,171 @@ +/* $NetBSD: t_dladdr.c,v 1.2 2025/12/15 02:36:47 riastradh Exp $ */ + +/*- + * Copyright (c) 2025 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: t_dladdr.c,v 1.2 2025/12/15 02:36:47 riastradh Exp $"); + +#include <sys/mman.h> + +#include <atf-c.h> +#include <dlfcn.h> +#include <unistd.h> + +#include "h_macros.h" + +#define SELF "t_dladdr" + +/* + * Note: the symbols foo, bar, and baz must be exposed to dlfcn(3) by + * linking this with -export-dynamic. + */ +int foo; /* something in this ELF object */ +int bar; /* something in this ELF object */ +int baz; /* something in this ELF object */ + +extern char _end[]; /* one past last byte of this ELF object */ + +ATF_TC(dladdr_self); +ATF_TC_HEAD(dladdr_self, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify dladdr of data in this object returns self"); +} +ATF_TC_BODY(dladdr_self, tc) +{ + Dl_info info; + const char *ptr = (char *)&bar + 1; + const char *p; + + /* + * If we're statically linked, dladdr just fails (XXX is that + * right?). But we're not statically linked because these are + * the ld.elf_so tests (XXX should migrate this to + * tests/lib/libc/dlfcn/ and handle it there). + * + * If we're dynamically linked, then foo, bar, and baz should + * live in self. + */ + ATF_CHECK_MSG(dladdr(ptr, &info) != 0, + "[bar @ %p + [0,%zu)] dladdr(%p) failed: %s", + &bar, sizeof(bar), ptr, dlerror()); + p = strrchr(info.dli_fname, '/'); + if (p == NULL) + p = info.dli_fname; + else + p++; + ATF_CHECK_MSG(strcmp(p, SELF) == 0, + "[bar @ %p + [0,%zu)] dladdr found %p in %s, not self=%s", + &bar, sizeof(bar), ptr, info.dli_fname, SELF); + ATF_CHECK_MSG(strcmp(info.dli_sname, "bar") == 0, + "[bar @ %p + [0,%zu)] dladdr found %p in %s at %p, not bar", + &bar, sizeof(bar), ptr, info.dli_sname, info.dli_saddr); +} + +ATF_TC(dladdr_errno); +ATF_TC_HEAD(dladdr_errno, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify dladdr(errno) returns libc.so (or self if static)"); +} +ATF_TC_BODY(dladdr_errno, tc) +{ + Dl_info info; + const char *p; + + /* + * If we're statically linked, dladdr just fails (XXX is that + * right?). But we're not statically linked because these are + * the ld.elf_so tests (XXX should migrate this to + * tests/lib/libc/dlfcn/ and handle it there). + * + * If we're dynamically linked and single-threaded (no + * libpthread.so), &errno will be in libc. + * + * If we're dynamically linked and multi-threaded, &errno would + * be in a pthread_t object -- but we're not multithreaded, so + * it's not. Hence only two cases: static vs dynamic. + */ + ATF_CHECK_MSG(dladdr(&errno, &info) != 0, + "[errno @ %p] dladdr failed: %s", &errno, dlerror()); + p = strrchr(info.dli_fname, '/'); + if (p == NULL) + p = info.dli_fname; + else + p++; + ATF_CHECK_MSG(strcmp(p, SELF) != 0, + "[errno @ %p] dladdr found errno in self=%s, not in libc.so", + &errno, info.dli_fname); +} + +ATF_TC(dladdr_after__end); +ATF_TC_HEAD(dladdr_after__end, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify dladdr doesn't claim pages past _end for self"); +} +ATF_TC_BODY(dladdr_after__end, tc) +{ + const size_t pagesize = sysconf(_SC_PAGESIZE); + uintptr_t endp, nextp; + void *page; + Dl_info info; + + /* + * Round up to a page start. + */ + endp = (uintptr_t)(void *)_end; + nextp = pagesize*((endp + pagesize - 1)/pagesize); + + /* + * Map the next page, or something after it. It just has to be + * not mapped by an existing object, but for the sake of + * testing for past bugs, we would like to make it as close to + * a real object as we can. + */ + REQUIRE_LIBC(page = mmap((void *)nextp, pagesize, PROT_NONE, MAP_ANON, + /*fd*/-1, /*off*/0), + MAP_FAILED); + + /* + * Verify dladdr doesn't return anything for this page. + */ + ATF_CHECK_MSG(dladdr(page, &info) == 0, + "dladdr returned %s @ %p (symbol %s @ %p) for bogus address %p", + info.dli_fname, info.dli_fbase, + info.dli_sname, info.dli_saddr, + page); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, dladdr_after__end); + ATF_TP_ADD_TC(tp, dladdr_errno); + ATF_TP_ADD_TC(tp, dladdr_self); + return atf_no_error(); +} |
