1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <fcntl.h>
6 #include <inttypes.h>
7 #include <launchpad/vmo.h>
8 #include <loader-service/loader-service.h>
9 #include <ldmsg/ldmsg.h>
10 #include <zircon/dlfcn.h>
11 #include <zircon/processargs.h>
12 #include <zircon/syscalls.h>
13
14 #include <stdatomic.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <unistd.h>
18
19 #include <unittest/unittest.h>
20
21 #if __has_feature(address_sanitizer)
22 # define LIBPREFIX "/boot/lib/asan/"
23 #else
24 # define LIBPREFIX "/boot/lib/"
25 #endif
26
dlopen_vmo_test(void)27 bool dlopen_vmo_test(void) {
28 BEGIN_TEST;
29
30 zx_handle_t vmo = ZX_HANDLE_INVALID;
31 zx_status_t status = launchpad_vmo_from_file(LIBPREFIX "liblaunchpad.so", &vmo);
32 EXPECT_EQ(status, ZX_OK, "");
33 EXPECT_NE(vmo, ZX_HANDLE_INVALID, "launchpad_vmo_from_file");
34
35 void* obj = dlopen_vmo(vmo, RTLD_LOCAL);
36 EXPECT_NONNULL(obj, "dlopen_vmo");
37
38 zx_handle_close(vmo);
39
40 void* sym = dlsym(obj, "launchpad_create");
41 EXPECT_NONNULL(sym, "dlsym");
42
43 int ok = dlclose(obj);
44 EXPECT_EQ(ok, 0, "dlclose");
45
46 END_TEST;
47 }
48
49 // This should be some library that this program links against.
50 #define TEST_SONAME "libfdio.so"
51 #define TEST_NAME "foobar"
52 #define TEST_ACTUAL_NAME LIBPREFIX TEST_SONAME
53
54 static atomic_bool my_loader_service_ok = false;
55 static atomic_int my_loader_service_calls = 0;
56
my_load_object(void * ctx,const char * name,zx_handle_t * out)57 static zx_status_t my_load_object(void* ctx, const char* name, zx_handle_t* out) {
58 ++my_loader_service_calls;
59
60 int cmp = strcmp(name, TEST_NAME);
61 EXPECT_EQ(cmp, 0, "called with unexpected name");
62 if (cmp != 0) {
63 unittest_printf(" saw \"%s\", expected \"%s\"", name, TEST_NAME);
64 return ZX_HANDLE_INVALID;
65 }
66
67 zx_handle_t vmo = ZX_HANDLE_INVALID;
68 zx_status_t status = launchpad_vmo_from_file((char*) ctx, &vmo);
69 EXPECT_EQ(status, ZX_OK, "");
70 EXPECT_NE(vmo, ZX_HANDLE_INVALID, "launchpad_vmo_from_file");
71 if (status < 0) {
72 return status;
73 }
74
75 my_loader_service_ok = true;
76 *out = vmo;
77 return ZX_OK;
78 }
79
my_load_abspath(void * ctx,const char * name,zx_handle_t * vmo)80 static zx_status_t my_load_abspath(void* ctx, const char* name, zx_handle_t* vmo) {
81 return ZX_ERR_NOT_SUPPORTED;
82 }
83
my_publish_data_sink(void * ctx,const char * name,zx_handle_t vmo)84 static zx_status_t my_publish_data_sink(void* ctx, const char* name, zx_handle_t vmo) {
85 zx_handle_close(vmo);
86 return ZX_ERR_NOT_SUPPORTED;
87 }
88
89 static loader_service_ops_t my_loader_ops = {
90 .load_object = my_load_object,
91 .load_abspath = my_load_abspath,
92 .publish_data_sink = my_publish_data_sink,
93 };
94
show_dlerror(void)95 static void show_dlerror(void) {
96 unittest_printf_critical("dlerror: %s\n", dlerror());
97 }
98
loader_service_test(void)99 bool loader_service_test(void) {
100 BEGIN_TEST;
101
102 // Get a handle to an existing library with a known SONAME.
103 void* by_name = dlopen(TEST_SONAME, RTLD_NOLOAD);
104 EXPECT_NONNULL(by_name, "dlopen failed on " TEST_SONAME);
105 if (by_name == NULL)
106 show_dlerror();
107
108 // Spin up our test service.
109 loader_service_t* svc = NULL;
110 zx_status_t status = loader_service_create(NULL, &my_loader_ops, (void*)TEST_ACTUAL_NAME, &svc);
111 EXPECT_EQ(status, ZX_OK, "loader_service_create");
112
113 zx_handle_t my_service = ZX_HANDLE_INVALID;
114 status = loader_service_connect(svc, &my_service);
115 EXPECT_EQ(status, ZX_OK, "loader_service_connect");
116
117 // Install the service.
118 zx_handle_t old = dl_set_loader_service(my_service);
119 EXPECT_NE(old, ZX_HANDLE_INVALID, "dl_set_loader_service");
120
121 // Now to a lookup that should go through our service. It
122 // should load up the new copy of the file, find that its
123 // SONAME matches an existing library, and just return it.
124 void *via_service = dlopen(TEST_NAME, RTLD_LOCAL);
125
126 EXPECT_EQ(my_loader_service_calls, 1,
127 "loader-service not called exactly once");
128
129 EXPECT_NONNULL(via_service, "dlopen via service");
130 if (via_service == NULL)
131 show_dlerror();
132
133 EXPECT_TRUE(my_loader_service_ok, "loader service thread not happy");
134
135 // It should not just have succeeded, but gotten the very
136 // same handle as the by-name lookup.
137 EXPECT_TRUE(via_service == by_name, "dlopen via service");
138
139 int fail = dlclose(by_name);
140 EXPECT_EQ(fail, 0, "dlclose on by-name");
141 if (fail)
142 show_dlerror();
143
144 fail = dlclose(via_service);
145 EXPECT_EQ(fail, 0, "dlclose on via-service");
146 if (fail)
147 show_dlerror();
148
149 // Put things back to how they were.
150 zx_handle_t old2 = dl_set_loader_service(old);
151 EXPECT_EQ(old2, my_service, "unexpected previous service handle");
152 zx_handle_close(old2);
153
154 END_TEST;
155 }
156
clone_test(void)157 bool clone_test(void) {
158 BEGIN_TEST;
159
160 zx_handle_t h = ZX_HANDLE_INVALID;
161 zx_status_t s = dl_clone_loader_service(&h);
162 EXPECT_EQ(s, ZX_OK, "unexpected return value from ioctl");
163 EXPECT_NE(h, ZX_HANDLE_INVALID, "invalid handle from ioctl");
164
165 zx_handle_close(h);
166
167 END_TEST;
168 }
169
170 int main(int argc, char** argv);
dladdr_main_test(void)171 static bool dladdr_main_test(void) {
172 BEGIN_TEST;
173
174 Dl_info info;
175 ASSERT_NE(dladdr(&main, &info), 0, "dladdr failed");
176
177 // The "main" symbol is not exported to .dynsym, so it won't be found.
178 EXPECT_EQ(info.dli_sname, NULL, "unexpected symbol name");
179 EXPECT_EQ(info.dli_saddr, NULL, "unexpected symbol address");
180
181 END_TEST;
182 }
183
184 // TODO(dbort): Test that this process uses the system loader service by default
185
186 BEGIN_TEST_CASE(dlfcn_tests)
187 RUN_TEST(dlopen_vmo_test);
188 RUN_TEST(loader_service_test);
189 RUN_TEST(clone_test);
190 RUN_TEST(dladdr_main_test);
END_TEST_CASE(dlfcn_tests)191 END_TEST_CASE(dlfcn_tests)
192
193 int main(int argc, char** argv) {
194 bool success = unittest_run_all_tests(argc, argv);
195 return success ? 0 : -1;
196 }
197