source: mainline/uspace/lib/c/generic/rtld/module.c@ f215c6ef

Last change on this file since f215c6ef was f215c6ef, checked in by Matěj Volf <git@…>, 6 months ago

properly initialize RTLD runtime for static binaries

this is a better approach than what I did of previous commit

see https://github.com/HelenOS/helenos/pull/242 for a lot more context

  • Property mode set to 100644
File size: 10.0 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup rtld
30 * @brief
31 * @{
32 */
33/**
34 * @file
35 */
36
37#include <align.h>
38#include <adt/list.h>
39#include <elf/elf_load.h>
40#include <errno.h>
41#include <loader/pcb.h>
42#include <stdalign.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <str.h>
46#include <macros.h>
47
48#include <rtld/rtld.h>
49#include <rtld/rtld_debug.h>
50#include <rtld/dynamic.h>
51#include <rtld/rtld_arch.h>
52#include <rtld/module.h>
53#include <libarch/rtld/module.h>
54
55#include "../private/libc.h"
56
57/** Create module for static executable.
58 *
59 * @param rtld Run-time dynamic linker
60 * @param rmodule Place to store pointer to new module or @c NULL
61 * @return EOK on success, ENOMEM if out of memory
62 */
63errno_t module_create_static_exec(const void *elf, rtld_t *rtld)
64{
65 module_t *module;
66
67 module = calloc(1, sizeof(module_t));
68 if (module == NULL) {
69 DPRINTF("malloc failed\n");
70 return ENOMEM;
71 }
72
73 module->id = rtld_get_next_id(rtld);
74 module->dyn.soname = "[program]";
75
76 module->rtld = rtld;
77 module->exec = true;
78 module->local = true;
79
80 const elf_segment_header_t *tls =
81 elf_get_phdr(elf, PT_TLS);
82
83 if (tls) {
84 uintptr_t bias = elf_get_bias(elf);
85 module->tdata = (void *) (tls->p_vaddr + bias);
86 module->tdata_size = tls->p_filesz;
87 module->tbss_size = tls->p_memsz - tls->p_filesz;
88 module->tls_align = tls->p_align;
89 } else {
90 module->tdata = NULL;
91 module->tdata_size = 0;
92 module->tbss_size = 0;
93 module->tls_align = 1;
94 }
95
96 list_append(&module->modules_link, &rtld->modules);
97 return EOK;
98}
99
100/** (Eagerly) process all relocation tables in a module.
101 *
102 * Currently works as if LD_BIND_NOW was specified.
103 */
104void module_process_relocs(module_t *m)
105{
106 DPRINTF("module_process_relocs('%s')\n", m->dyn.soname);
107
108 /* Do not relocate twice. */
109 if (m->relocated)
110 return;
111
112 module_process_pre_arch(m);
113
114 /* jmp_rel table */
115 if (m->dyn.jmp_rel != NULL) {
116 DPRINTF("jmp_rel table\n");
117 if (m->dyn.plt_rel == DT_REL) {
118 DPRINTF("jmp_rel table type DT_REL\n");
119 rel_table_process(m, m->dyn.jmp_rel, m->dyn.plt_rel_sz);
120 } else {
121 assert(m->dyn.plt_rel == DT_RELA);
122 DPRINTF("jmp_rel table type DT_RELA\n");
123 rela_table_process(m, m->dyn.jmp_rel, m->dyn.plt_rel_sz);
124 }
125 }
126
127 /* rel table */
128 if (m->dyn.rel != NULL) {
129 DPRINTF("rel table\n");
130 rel_table_process(m, m->dyn.rel, m->dyn.rel_sz);
131 }
132
133 /* rela table */
134 if (m->dyn.rela != NULL) {
135 DPRINTF("rela table\n");
136 rela_table_process(m, m->dyn.rela, m->dyn.rela_sz);
137 }
138
139 m->relocated = true;
140}
141
142/** Find module structure by soname/pathname.
143 *
144 * Used primarily to see if a module has already been loaded.
145 * Modules are compared according to their soname, i.e. possible
146 * path components are ignored.
147 */
148module_t *module_find(rtld_t *rtld, const char *name)
149{
150 const char *p, *soname;
151
152 DPRINTF("module_find('%s')\n", name);
153
154 /*
155 * If name contains slashes, treat it as a pathname and
156 * construct soname by chopping off the path. Otherwise
157 * treat it as soname.
158 */
159 p = str_rchr(name, '/');
160 soname = p ? (p + 1) : name;
161
162 /* Traverse list of all modules. Not extremely fast, but simple */
163 list_foreach(rtld->modules, modules_link, module_t, m) {
164 DPRINTF("m = %p\n", m);
165 if (str_cmp(m->dyn.soname, soname) == 0) {
166 return m; /* Found */
167 }
168 }
169
170 return NULL; /* Not found */
171}
172
173#define NAME_BUF_SIZE 64
174
175/** Load a module.
176 *
177 * Currently this trivially tries to load '/<name>'.
178 */
179module_t *module_load(rtld_t *rtld, const char *name, mlflags_t flags)
180{
181 elf_finfo_t info;
182 char name_buf[NAME_BUF_SIZE];
183 module_t *m;
184 errno_t rc;
185
186 m = calloc(1, sizeof(module_t));
187 if (m == NULL) {
188 DPRINTF("malloc failed\n");
189 goto error;
190 }
191
192 m->rtld = rtld;
193 m->id = rtld_get_next_id(rtld);
194
195 if ((flags & mlf_local) != 0)
196 m->local = true;
197
198 if (str_size(name) > NAME_BUF_SIZE - 2) {
199 DPRINTF("soname too long. increase NAME_BUF_SIZE\n");
200 goto error;
201 }
202
203 /* Prepend soname with '/lib/' */
204 str_cpy(name_buf, NAME_BUF_SIZE, "/lib/");
205 str_cpy(name_buf + 5, NAME_BUF_SIZE - 5, name);
206
207 DPRINTF("filename:'%s'\n", name_buf);
208
209 rc = elf_load_file_name(name_buf, RTLD_MODULE_LDF, &info);
210 if (rc != EOK) {
211 DPRINTF("Failed to load '%s'\n", name_buf);
212 goto error;
213 }
214
215 m->bias = elf_get_bias(info.base);
216
217 DPRINTF("loaded '%s' at 0x%zx\n", name_buf, m->bias);
218
219 if (info.dynamic == NULL) {
220 DPRINTF("Error: '%s' is not a dynamically-linked object.\n",
221 name_buf);
222 goto error;
223 }
224
225 /* Pending relocation. */
226 m->relocated = false;
227
228 DPRINTF("parse dynamic section\n");
229 /* Parse ELF .dynamic section. Store info to m->dyn. */
230 dynamic_parse(info.dynamic, m->bias, &m->dyn);
231
232 /* Insert into the list of loaded modules */
233 list_append(&m->modules_link, &rtld->modules);
234
235 /* Copy TLS info */
236 m->tdata = info.tls.tdata;
237 m->tdata_size = info.tls.tdata_size;
238 m->tbss_size = info.tls.tbss_size;
239 m->tls_align = info.tls.tls_align;
240
241 DPRINTF("tdata at %p size %zu, tbss size %zu\n",
242 m->tdata, m->tdata_size, m->tbss_size);
243
244 return m;
245
246error:
247 if (m)
248 free(m);
249
250 return NULL;
251}
252
253/** Load all modules on which m (transitively) depends.
254 */
255errno_t module_load_deps(module_t *m, mlflags_t flags)
256{
257 elf_dyn_t *dp;
258 char *dep_name;
259 module_t *dm;
260 size_t n, i;
261
262 DPRINTF("module_load_deps('%s')\n", m->dyn.soname);
263
264 /* Count direct dependencies */
265
266 dp = m->dyn.dynamic;
267 n = 0;
268
269 while (dp->d_tag != DT_NULL) {
270 if (dp->d_tag == DT_NEEDED)
271 ++n;
272 ++dp;
273 }
274
275 /* Create an array of pointers to direct dependencies */
276
277 m->n_deps = n;
278
279 if (n == 0) {
280 /* There are no dependencies, so we are done. */
281 m->deps = NULL;
282 return EOK;
283 }
284
285 m->deps = malloc(n * sizeof(module_t *));
286 if (!m->deps) {
287 DPRINTF("malloc failed\n");
288 return ENOMEM;
289 }
290
291 i = 0; /* Current dependency index */
292 dp = m->dyn.dynamic;
293
294 while (dp->d_tag != DT_NULL) {
295 if (dp->d_tag == DT_NEEDED) {
296 dep_name = m->dyn.str_tab + dp->d_un.d_val;
297
298 DPRINTF("%s needs %s\n", m->dyn.soname, dep_name);
299 dm = module_find(m->rtld, dep_name);
300 if (!dm) {
301 dm = module_load(m->rtld, dep_name, flags);
302 if (!dm) {
303 return EINVAL;
304 }
305
306 errno_t rc = module_load_deps(dm, flags);
307 if (rc != EOK) {
308 return rc;
309 }
310 }
311
312 /* Save into deps table */
313 m->deps[i++] = dm;
314 }
315 ++dp;
316 }
317
318 return EOK;
319}
320
321/** Find module structure by ID. */
322module_t *module_by_id(rtld_t *rtld, unsigned long id)
323{
324 list_foreach(rtld->modules, modules_link, module_t, m) {
325 if (m->id == id)
326 return m;
327 }
328
329 return NULL;
330}
331
332/** Process relocations in modules.
333 *
334 * Processes relocations in @a start and all its dependencies.
335 * Modules that have already been relocated are unaffected.
336 *
337 * @param start The module where to start from.
338 */
339void modules_process_relocs(rtld_t *rtld, module_t *start)
340{
341 list_foreach(rtld->modules, modules_link, module_t, m) {
342 /*
343 * Skip rtld module, since it has already been processed.
344 * Skip start / main program -- leave it for later
345 */
346 if (m != &rtld->rtld && m != start) {
347 module_process_relocs(m);
348 }
349 }
350
351 /*
352 * Now that shared libraries have been processed and their variables
353 * are thus initialized, we can process the main program,
354 * which may contain COPY relocations that copy value from shared
355 * library variables to instances of those variables defined
356 * in the main program.
357 */
358 module_process_relocs(start);
359}
360
361void modules_process_tls(rtld_t *rtld)
362{
363#ifdef CONFIG_TLS_VARIANT_1
364 rtld->tls_size = sizeof(tcb_t);
365 rtld->tls_align = alignof(tcb_t);
366
367 list_foreach(rtld->modules, modules_link, module_t, m) {
368 list_append(&m->imodules_link, &rtld->imodules);
369 rtld->tls_align = max(rtld->tls_align, m->tls_align);
370
371 rtld->tls_size = ALIGN_UP(rtld->tls_size, m->tls_align);
372 m->tpoff = rtld->tls_size;
373 rtld->tls_size += m->tdata_size + m->tbss_size;
374 }
375
376#else
377 rtld->tls_size = 0;
378 rtld->tls_align = alignof(tcb_t);
379
380 list_foreach(rtld->modules, modules_link, module_t, m) {
381 list_append(&m->imodules_link, &rtld->imodules);
382 rtld->tls_align = max(rtld->tls_align, m->tls_align);
383
384 /*
385 * We are allocating spans "backwards", here,
386 * as described in U. Drepper's paper.
387 */
388 rtld->tls_size += m->tdata_size + m->tbss_size;
389 rtld->tls_size = ALIGN_UP(rtld->tls_size, m->tls_align);
390 m->tpoff = -(ptrdiff_t) rtld->tls_size;
391 }
392
393 /*
394 * We are in negative offsets. In order for the alignments to
395 * be correct, "zero" offset (i.e. the total size) must be aligned
396 * to the strictest alignment present.
397 * Note that the padding is actually in front of the TLS data,
398 * not after it.
399 */
400 rtld->tls_size = ALIGN_UP(rtld->tls_size, rtld->tls_align);
401
402 /* Space for the TCB. */
403 rtld->tls_size += sizeof(tcb_t);
404#endif
405}
406
407/** Clear BFS tags of all modules.
408 */
409void modules_untag(rtld_t *rtld)
410{
411 list_foreach(rtld->modules, modules_link, module_t, m) {
412 m->bfs_tag = false;
413 }
414}
415
416/** @}
417 */
Note: See TracBrowser for help on using the repository browser.