1 /* Basic tests for _dl_find_object.
2    Copyright (C) 2021-2022 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 (expected->dlfo_link_map->l_contiguous)
75     {
76       /* If the mappings are not contiguous, the actual and execpted
77          mappings may differ, so this subtest will not work.  */
78       if (actual.dlfo_flags != expected->dlfo_flags)
79         {
80           support_record_failure ();
81           printf ("%s:%d: error: %p: map start is %p, expected %p\n",
82                   __FILE__, line,
83                   address, actual.dlfo_map_start, expected->dlfo_map_start);
84         }
85       if (actual.dlfo_map_end != expected->dlfo_map_end)
86         {
87           support_record_failure ();
88           printf ("%s:%d: error: %p: map end is %p, expected %p\n",
89                   __FILE__, line,
90                   address, actual.dlfo_map_end, expected->dlfo_map_end);
91         }
92     }
93   if (actual.dlfo_link_map != expected->dlfo_link_map)
94     {
95       support_record_failure ();
96       printf ("%s:%d: error: %p: link map is %p, expected %p\n",
97               __FILE__, line,
98               address, actual.dlfo_link_map, expected->dlfo_link_map);
99     }
100   if (actual.dlfo_eh_frame != expected->dlfo_eh_frame)
101     {
102       support_record_failure ();
103       printf ("%s:%d: error: %p: EH frame is %p, expected %p\n",
104               __FILE__, line,
105               address, actual.dlfo_eh_frame, expected->dlfo_eh_frame);
106     }
107 #if DLFO_STRUCT_HAS_EH_DBASE
108   if (actual.dlfo_eh_dbase != expected->dlfo_eh_dbase)
109     {
110       support_record_failure ();
111       printf ("%s:%d: error: %p: data base is %p, expected %p\n",
112               __FILE__, line,
113               address, actual.dlfo_eh_dbase, expected->dlfo_eh_dbase);
114     }
115 #endif
116 #if DLFO_STRUCT_HAS_EH_COUNT
117   if (actual.dlfo_eh_count != expected->dlfo_eh_count)
118     {
119       support_record_failure ();
120       printf ("%s:%d: error: %p: count is %d, expected %d\n",
121               __FILE__, line,
122               address, actual.dlfo_eh_count, expected->dlfo_eh_count);
123     }
124 #endif
125 }
126 
127 /* Check that unwind data for the main executable and the dynamic
128    linker can be found.  */
129 static void
check_initial(void)130 check_initial (void)
131 {
132 #ifndef FOR_STATIC
133   /* Avoid direct reference, which could lead to copy relocations.  */
134   struct r_debug *debug = xdlsym (NULL, "_r_debug");
135   TEST_VERIFY_EXIT (debug != NULL);
136   char **tzname = xdlsym (NULL, "tzname");
137 
138   /* The main executable has an unnamed link map.  */
139   struct link_map *main_map = (struct link_map *) debug->r_map;
140   TEST_COMPARE_STRING (main_map->l_name, "");
141 
142   /* The link map of the dynamic linker.  */
143   struct link_map *rtld_map = xdlopen (LD_SO, RTLD_LAZY | RTLD_NOLOAD);
144   TEST_VERIFY_EXIT (rtld_map != NULL);
145 
146   /* The link map of libc.so.  */
147   struct link_map *libc_map = xdlopen (LIBC_SO, RTLD_LAZY | RTLD_NOLOAD);
148   TEST_VERIFY_EXIT (libc_map != NULL);
149 
150   struct dl_find_object expected;
151 
152   /* Data in the main program.  */
153   from_map (main_map, &expected);
154   check (&main_program_data, &expected, __LINE__);
155   /* Corner cases for the mapping.  */
156   check ((void *) main_map->l_map_start, &expected, __LINE__);
157   check ((void *) (main_map->l_map_end - 1), &expected, __LINE__);
158 
159   /* Data in the dynamic loader.  */
160   from_map (rtld_map, &expected);
161   check (debug, &expected, __LINE__);
162   check ((void *) rtld_map->l_map_start, &expected, __LINE__);
163   check ((void *) (rtld_map->l_map_end - 1), &expected, __LINE__);
164 
165   /* Data in libc.  */
166   from_map (libc_map, &expected);
167   check (tzname, &expected, __LINE__);
168   check ((void *) libc_map->l_map_start, &expected, __LINE__);
169   check ((void *) (libc_map->l_map_end - 1), &expected, __LINE__);
170 #endif
171 }
172 
173 static int
do_test(void)174 do_test (void)
175 {
176   {
177     struct dl_find_object dlfo = { };
178     int ret = _dl_find_object (&main_program_data, &dlfo);
179     printf ("info: main program unwind data: %p (%d)\n",
180             dlfo.dlfo_eh_frame, ret);
181     TEST_COMPARE (ret, 0);
182     TEST_VERIFY (dlfo.dlfo_eh_frame != NULL);
183   }
184 
185   check_initial ();
186 
187   /* dlopen-based test.  First an object that can be dlclosed.  */
188   struct link_map *mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW);
189   void *mod1_data = xdlsym (mod1, "mod1_data");
190   void *map_start = (void *) mod1->l_map_start;
191   void *map_end = (void *) (mod1->l_map_end - 1);
192   check_initial ();
193 
194   struct dl_find_object expected;
195   from_map (mod1, &expected);
196   check (mod1_data, &expected, __LINE__);
197   check (map_start, &expected, __LINE__);
198   check (map_end, &expected, __LINE__);
199 
200   /* Unloading must make the data unavailable.  */
201   xdlclose (mod1);
202   check_initial ();
203   check (mod1_data, NULL, __LINE__);
204   check (map_start, NULL, __LINE__);
205   check (map_end, NULL, __LINE__);
206 
207   /* Now try a NODELETE load.  */
208   struct link_map *mod2 = xdlopen ("tst-dl_find_object-mod2.so", RTLD_NOW);
209   void *mod2_data = xdlsym (mod2, "mod2_data");
210   map_start = (void *) mod2->l_map_start;
211   map_end = (void *) (mod2->l_map_end - 1);
212   check_initial ();
213   from_map (mod2, &expected);
214   check (mod2_data, &expected, __LINE__);
215   check (map_start, &expected, __LINE__);
216   check (map_end, &expected, __LINE__);
217   dlclose (mod2);               /* Does nothing due to NODELETE.  */
218   check_initial ();
219   check (mod2_data, &expected, __LINE__);
220   check (map_start, &expected, __LINE__);
221   check (map_end, &expected, __LINE__);
222 
223   /* Now load again the first module.  */
224   mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW);
225   mod1_data = xdlsym (mod1, "mod1_data");
226   map_start = (void *) mod1->l_map_start;
227   map_end = (void *) (mod1->l_map_end - 1);
228   check_initial ();
229   from_map (mod1, &expected);
230   check (mod1_data, &expected, __LINE__);
231   check (map_start, &expected, __LINE__);
232   check (map_end, &expected, __LINE__);
233 
234   /* Check that _dl_find_object works from a shared object (mostly for
235      static dlopen).  */
236   __typeof (_dl_find_object) *find_object
237     = *(void **) xdlsym (mod2, "find_object");
238   struct dl_find_object actual;
239   TEST_COMPARE (find_object (&main_program_data, &actual), 0);
240   check (&main_program_data, &actual, __LINE__); /* Reversed check.  */
241 
242   return 0;
243 }
244 
245 #include <support/test-driver.c>
246