1 /* Basic tests for _dl_find_object.
2    Copyright (C) 2021 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <dl-find_object.h>
20 #include <dlfcn.h>
21 #include <gnu/lib-names.h>
22 #include <ldsodefs.h>
23 #include <link.h>
24 #include <stdio.h>
25 #include <support/check.h>
26 #include <support/xdlfcn.h>
27 
28 /* Use data objects for testing, so that it is not necessary to decode
29    function descriptors on architectures that have them.  */
30 static char main_program_data;
31 
32 /* Computes the expected _dl_find_object result directly from the
33    map.  */
34 static void
from_map(struct link_map * l,struct dl_find_object * expected)35 from_map (struct link_map *l, struct dl_find_object *expected)
36 {
37   struct dl_find_object_internal internal;
38   _dl_find_object_from_map (l, &internal);
39   _dl_find_object_to_external (&internal, expected);
40 }
41 
42 /* Compare _dl_find_object result at ADDRESS with *EXPECTED.  */
43 static void
check(void * address,struct dl_find_object * expected,int line)44 check (void *address,
45        struct dl_find_object *expected, int line)
46 {
47   struct dl_find_object actual;
48   int ret = _dl_find_object (address, &actual);
49   if (expected == NULL)
50     {
51       if (ret != -1)
52         {
53           support_record_failure ();
54           printf ("%s:%d: unexpected success for %p\n",
55                   __FILE__, line, address);
56         }
57       return;
58     }
59   if (ret != 0)
60     {
61       support_record_failure ();
62       printf ("%s:%d: unexpected failure for %p\n",
63               __FILE__, line, address);
64       return;
65     }
66 
67   if (actual.dlfo_flags != expected->dlfo_flags)
68     {
69       support_record_failure ();
70       printf ("%s:%d: error: %p: flags is %llu, expected %llu\n",
71               __FILE__, line, address,
72               actual.dlfo_flags, expected->dlfo_flags);
73     }
74   if (actual.dlfo_flags != expected->dlfo_flags)
75     {
76       support_record_failure ();
77       printf ("%s:%d: error: %p: map start is %p, expected %p\n",
78               __FILE__, line,
79               address, actual.dlfo_map_start, expected->dlfo_map_start);
80     }
81   if (actual.dlfo_map_end != expected->dlfo_map_end)
82     {
83       support_record_failure ();
84       printf ("%s:%d: error: %p: map end is %p, expected %p\n",
85               __FILE__, line,
86               address, actual.dlfo_map_end, expected->dlfo_map_end);
87     }
88   if (actual.dlfo_link_map != expected->dlfo_link_map)
89     {
90       support_record_failure ();
91       printf ("%s:%d: error: %p: link map is %p, expected %p\n",
92               __FILE__, line,
93               address, actual.dlfo_link_map, expected->dlfo_link_map);
94     }
95   if (actual.dlfo_eh_frame != expected->dlfo_eh_frame)
96     {
97       support_record_failure ();
98       printf ("%s:%d: error: %p: EH frame is %p, expected %p\n",
99               __FILE__, line,
100               address, actual.dlfo_eh_frame, expected->dlfo_eh_frame);
101     }
102 #if DLFO_STRUCT_HAS_EH_DBASE
103   if (actual.dlfo_eh_dbase != expected->dlfo_eh_dbase)
104     {
105       support_record_failure ();
106       printf ("%s:%d: error: %p: data base is %p, expected %p\n",
107               __FILE__, line,
108               address, actual.dlfo_eh_dbase, expected->dlfo_eh_dbase);
109     }
110 #endif
111 #if DLFO_STRUCT_HAS_EH_COUNT
112   if (actual.dlfo_eh_count != expected->dlfo_eh_count)
113     {
114       support_record_failure ();
115       printf ("%s:%d: error: %p: count is %d, expected %d\n",
116               __FILE__, line,
117               address, actual.dlfo_eh_count, expected->dlfo_eh_count);
118     }
119 #endif
120 }
121 
122 /* Check that unwind data for the main executable and the dynamic
123    linker can be found.  */
124 static void
check_initial(void)125 check_initial (void)
126 {
127 #ifndef FOR_STATIC
128   /* Avoid direct reference, which could lead to copy relocations.  */
129   struct r_debug *debug = xdlsym (NULL, "_r_debug");
130   TEST_VERIFY_EXIT (debug != NULL);
131   char **tzname = xdlsym (NULL, "tzname");
132 
133   /* The main executable has an unnamed link map.  */
134   struct link_map *main_map = (struct link_map *) debug->r_map;
135   TEST_COMPARE_STRING (main_map->l_name, "");
136 
137   /* The link map of the dynamic linker.  */
138   struct link_map *rtld_map = xdlopen (LD_SO, RTLD_LAZY | RTLD_NOLOAD);
139   TEST_VERIFY_EXIT (rtld_map != NULL);
140 
141   /* The link map of libc.so.  */
142   struct link_map *libc_map = xdlopen (LIBC_SO, RTLD_LAZY | RTLD_NOLOAD);
143   TEST_VERIFY_EXIT (libc_map != NULL);
144 
145   struct dl_find_object expected;
146 
147   /* Data in the main program.  */
148   from_map (main_map, &expected);
149   check (&main_program_data, &expected, __LINE__);
150   /* Corner cases for the mapping.  */
151   check ((void *) main_map->l_map_start, &expected, __LINE__);
152   check ((void *) (main_map->l_map_end - 1), &expected, __LINE__);
153 
154   /* Data in the dynamic loader.  */
155   from_map (rtld_map, &expected);
156   check (debug, &expected, __LINE__);
157   check ((void *) rtld_map->l_map_start, &expected, __LINE__);
158   check ((void *) (rtld_map->l_map_end - 1), &expected, __LINE__);
159 
160   /* Data in libc.  */
161   from_map (libc_map, &expected);
162   check (tzname, &expected, __LINE__);
163   check ((void *) libc_map->l_map_start, &expected, __LINE__);
164   check ((void *) (libc_map->l_map_end - 1), &expected, __LINE__);
165 #endif
166 }
167 
168 static int
do_test(void)169 do_test (void)
170 {
171   {
172     struct dl_find_object dlfo = { };
173     int ret = _dl_find_object (&main_program_data, &dlfo);
174     printf ("info: main program unwind data: %p (%d)\n",
175             dlfo.dlfo_eh_frame, ret);
176     TEST_COMPARE (ret, 0);
177     TEST_VERIFY (dlfo.dlfo_eh_frame != NULL);
178   }
179 
180   check_initial ();
181 
182   /* dlopen-based test.  First an object that can be dlclosed.  */
183   struct link_map *mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW);
184   void *mod1_data = xdlsym (mod1, "mod1_data");
185   void *map_start = (void *) mod1->l_map_start;
186   void *map_end = (void *) (mod1->l_map_end - 1);
187   check_initial ();
188 
189   struct dl_find_object expected;
190   from_map (mod1, &expected);
191   check (mod1_data, &expected, __LINE__);
192   check (map_start, &expected, __LINE__);
193   check (map_end, &expected, __LINE__);
194 
195   /* Unloading must make the data unavailable.  */
196   xdlclose (mod1);
197   check_initial ();
198   check (mod1_data, NULL, __LINE__);
199   check (map_start, NULL, __LINE__);
200   check (map_end, NULL, __LINE__);
201 
202   /* Now try a NODELETE load.  */
203   struct link_map *mod2 = xdlopen ("tst-dl_find_object-mod2.so", RTLD_NOW);
204   void *mod2_data = xdlsym (mod1, "mod2_data");
205   map_start = (void *) mod2->l_map_start;
206   map_end = (void *) (mod2->l_map_end - 1);
207   check_initial ();
208   from_map (mod2, &expected);
209   check (mod2_data, &expected, __LINE__);
210   check (map_start, &expected, __LINE__);
211   check (map_end, &expected, __LINE__);
212   dlclose (mod2);               /* Does nothing due to NODELETE.  */
213   check_initial ();
214   check (mod2_data, &expected, __LINE__);
215   check (map_start, &expected, __LINE__);
216   check (map_end, &expected, __LINE__);
217 
218   /* Now load again the first module.  */
219   mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW);
220   mod1_data = xdlsym (mod1, "mod1_data");
221   map_start = (void *) mod1->l_map_start;
222   map_end = (void *) (mod1->l_map_end - 1);
223   check_initial ();
224   from_map (mod1, &expected);
225   check (mod1_data, &expected, __LINE__);
226   check (map_start, &expected, __LINE__);
227   check (map_end, &expected, __LINE__);
228 
229   /* Check that _dl_find_object works from a shared object (mostly for
230      static dlopen).  */
231   __typeof (_dl_find_object) *find_object
232     = *(void **) xdlsym (mod2, "find_object");
233   struct dl_find_object actual;
234   TEST_COMPARE (find_object (&main_program_data, &actual), 0);
235   check (&main_program_data, &actual, __LINE__); /* Reversed check.  */
236 
237   return 0;
238 }
239 
240 #include <support/test-driver.c>
241