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