aboutsummaryrefslogtreecommitdiff
path: root/libexec/ld.elf_so/t_dladdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/ld.elf_so/t_dladdr.c')
-rw-r--r--libexec/ld.elf_so/t_dladdr.c171
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();
+}