-
Notifications
You must be signed in to change notification settings - Fork 367
/
rtld.c
2983 lines (2570 loc) · 90.7 KB
/
rtld.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Run time dynamic linker.
Copyright (C) 1995-2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <ldsodefs.h>
#include <_itoa.h>
#include <entry.h>
#include <fpu_control.h>
#include <hp-timing.h>
#include <libc-lock.h>
#include <dl-librecon.h>
#include <unsecvars.h>
#include <dl-cache.h>
#include <dl-osinfo.h>
#include <dl-procinfo.h>
#include <dl-prop.h>
#include <dl-vdso.h>
#include <dl-vdso-setup.h>
#include <tls.h>
#include <stap-probe.h>
#include <stackinfo.h>
#include <not-cancel.h>
#include <array_length.h>
#include <libc-early-init.h>
#include <dl-main.h>
#include <gnu/lib-names.h>
#include <dl-tunables.h>
#include <get-dynamic-info.h>
#include <dl-execve.h>
#include <dl-find_object.h>
#include <dl-audit-check.h>
#include <assert.h>
/* This #define produces dynamic linking inline functions for
bootstrap relocation instead of general-purpose relocation.
Since ld.so must not have any undefined symbols the result
is trivial: always the map of ld.so itself. */
#define RTLD_BOOTSTRAP
#define RESOLVE_MAP(map, scope, sym, version, flags) map
#include "dynamic-link.h"
/* Must include after <dl-machine.h> for DT_MIPS definition. */
#include <dl-debug.h>
/* Only enables rtld profiling for architectures which provides non generic
hp-timing support. The generic support requires either syscall
(clock_gettime), which will incur in extra overhead on loading time.
Using vDSO is also an option, but it will require extra support on loader
to setup the vDSO pointer before its usage. */
#if HP_TIMING_INLINE
# define RLTD_TIMING_DECLARE(var, classifier,...) \
classifier hp_timing_t var __VA_ARGS__
# define RTLD_TIMING_VAR(var) RLTD_TIMING_DECLARE (var, )
# define RTLD_TIMING_SET(var, value) (var) = (value)
# define RTLD_TIMING_REF(var) &(var)
static inline void
rtld_timer_start (hp_timing_t *var)
{
HP_TIMING_NOW (*var);
}
static inline void
rtld_timer_stop (hp_timing_t *var, hp_timing_t start)
{
hp_timing_t stop;
HP_TIMING_NOW (stop);
HP_TIMING_DIFF (*var, start, stop);
}
static inline void
rtld_timer_accum (hp_timing_t *sum, hp_timing_t start)
{
hp_timing_t stop;
rtld_timer_stop (&stop, start);
HP_TIMING_ACCUM_NT(*sum, stop);
}
#else
# define RLTD_TIMING_DECLARE(var, classifier...)
# define RTLD_TIMING_SET(var, value)
# define RTLD_TIMING_VAR(var)
# define RTLD_TIMING_REF(var) 0
# define rtld_timer_start(var)
# define rtld_timer_stop(var, start)
# define rtld_timer_accum(sum, start)
#endif
/* Avoid PLT use for our local calls at startup. */
extern __typeof (__mempcpy) __mempcpy attribute_hidden;
/* GCC has mental blocks about _exit. */
extern __typeof (_exit) exit_internal asm ("_exit") attribute_hidden;
#define _exit exit_internal
/* Helper function to handle errors while resolving symbols. */
static void print_unresolved (int errcode, const char *objname,
const char *errsting);
/* Helper function to handle errors when a version is missing. */
static void print_missing_version (int errcode, const char *objname,
const char *errsting);
/* Print the various times we collected. */
static void print_statistics (const hp_timing_t *total_timep);
/* Creates an empty audit list. */
static void audit_list_init (struct audit_list *);
/* Add a string to the end of the audit list, for later parsing. Must
not be called after audit_list_next. */
static void audit_list_add_string (struct audit_list *, const char *);
/* Add the audit strings from the link map, found in the dynamic
segment at TG (either DT_AUDIT and DT_DEPAUDIT). Must be called
before audit_list_next. */
static void audit_list_add_dynamic_tag (struct audit_list *,
struct link_map *,
unsigned int tag);
/* Extract the next audit module from the audit list. Only modules
for which dso_name_valid_for_suid is true are returned. Must be
called after all the audit_list_add_string,
audit_list_add_dynamic_tags calls. */
static const char *audit_list_next (struct audit_list *);
/* Initialize *STATE with the defaults. */
static void dl_main_state_init (struct dl_main_state *state);
/* Process all environments variables the dynamic linker must recognize.
Since all of them start with `LD_' we are a bit smarter while finding
all the entries. */
extern char **_environ attribute_hidden;
static void process_envvars (struct dl_main_state *state);
#ifdef DL_ARGV_NOT_RELRO
int _dl_argc attribute_hidden;
char **_dl_argv = NULL;
/* Nonzero if we were run directly. */
unsigned int _dl_skip_args attribute_hidden;
#else
int _dl_argc attribute_relro attribute_hidden;
char **_dl_argv attribute_relro = NULL;
unsigned int _dl_skip_args attribute_relro attribute_hidden;
#endif
rtld_hidden_data_def (_dl_argv)
#ifndef THREAD_SET_STACK_GUARD
/* Only exported for architectures that don't store the stack guard canary
in thread local area. */
uintptr_t __stack_chk_guard attribute_relro;
#endif
/* Only exported for architectures that don't store the pointer guard
value in thread local area. */
uintptr_t __pointer_chk_guard_local attribute_relro attribute_hidden;
#ifndef THREAD_SET_POINTER_GUARD
strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
#endif
/* Check that AT_SECURE=0, or that the passed name does not contain
directories and is not overly long. Reject empty names
unconditionally. */
static bool
dso_name_valid_for_suid (const char *p)
{
if (__glibc_unlikely (__libc_enable_secure))
{
/* Ignore pathnames with directories for AT_SECURE=1
programs, and also skip overlong names. */
size_t len = strlen (p);
if (len >= SECURE_NAME_LIMIT || memchr (p, '/', len) != NULL)
return false;
}
return *p != '\0';
}
static void
audit_list_init (struct audit_list *list)
{
list->length = 0;
list->current_index = 0;
list->current_tail = NULL;
}
static void
audit_list_add_string (struct audit_list *list, const char *string)
{
/* Empty strings do not load anything. */
if (*string == '\0')
return;
if (list->length == array_length (list->audit_strings))
_dl_fatal_printf ("Fatal glibc error: Too many audit modules requested\n");
list->audit_strings[list->length++] = string;
/* Initialize processing of the first string for
audit_list_next. */
if (list->length == 1)
list->current_tail = string;
}
static void
audit_list_add_dynamic_tag (struct audit_list *list, struct link_map *main_map,
unsigned int tag)
{
ElfW(Dyn) *info = main_map->l_info[ADDRIDX (tag)];
const char *strtab = (const char *) D_PTR (main_map, l_info[DT_STRTAB]);
if (info != NULL)
audit_list_add_string (list, strtab + info->d_un.d_val);
}
static const char *
audit_list_next (struct audit_list *list)
{
if (list->current_tail == NULL)
return NULL;
while (true)
{
/* Advance to the next string in audit_strings if the current
string has been exhausted. */
while (*list->current_tail == '\0')
{
++list->current_index;
if (list->current_index == list->length)
{
list->current_tail = NULL;
return NULL;
}
list->current_tail = list->audit_strings[list->current_index];
}
/* Split the in-string audit list at the next colon colon. */
size_t len = strcspn (list->current_tail, ":");
if (len > 0 && len < sizeof (list->fname))
{
memcpy (list->fname, list->current_tail, len);
list->fname[len] = '\0';
}
else
/* Mark the name as unusable for dso_name_valid_for_suid. */
list->fname[0] = '\0';
/* Skip over the substring and the following delimiter. */
list->current_tail += len;
if (*list->current_tail == ':')
++list->current_tail;
/* If the name is valid, return it. */
if (dso_name_valid_for_suid (list->fname))
return list->fname;
/* Otherwise wrap around to find the next list element. . */
}
}
/* Count audit modules before they are loaded so GLRO(dl_naudit)
is not yet usable. */
static size_t
audit_list_count (struct audit_list *list)
{
/* Restore the audit_list iterator state at the end. */
const char *saved_tail = list->current_tail;
size_t naudit = 0;
assert (list->current_index == 0);
while (audit_list_next (list) != NULL)
naudit++;
list->current_tail = saved_tail;
list->current_index = 0;
return naudit;
}
static void
dl_main_state_init (struct dl_main_state *state)
{
audit_list_init (&state->audit_list);
state->library_path = NULL;
state->library_path_source = NULL;
state->preloadlist = NULL;
state->preloadarg = NULL;
state->glibc_hwcaps_prepend = NULL;
state->glibc_hwcaps_mask = NULL;
state->mode = rtld_mode_normal;
state->any_debug = false;
state->version_info = false;
}
#ifndef HAVE_INLINED_SYSCALLS
/* Set nonzero during loading and initialization of executable and
libraries, cleared before the executable's entry point runs. This
must not be initialized to nonzero, because the unused dynamic
linker loaded in for libc.so's "ld.so.1" dep will provide the
definition seen by libc.so's initializer; that value must be zero,
and will be since that dynamic linker's _dl_start and dl_main will
never be called. */
int _dl_starting_up = 0;
rtld_hidden_def (_dl_starting_up)
#endif
/* This is the structure which defines all variables global to ld.so
(except those which cannot be added for some reason). */
struct rtld_global _rtld_global =
{
/* Get architecture specific initializer. */
#include <dl-procruntime.c>
/* Generally the default presumption without further information is an
* executable stack but this is not true for all platforms. */
._dl_stack_flags = DEFAULT_STACK_PERMS,
#ifdef _LIBC_REENTRANT
._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
._dl_load_write_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
._dl_load_tls_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
#endif
._dl_nns = 1,
._dl_ns =
{
#ifdef _LIBC_REENTRANT
[LM_ID_BASE] = { ._ns_unique_sym_table
= { .lock = _RTLD_LOCK_RECURSIVE_INITIALIZER } }
#endif
}
};
/* If we would use strong_alias here the compiler would see a
non-hidden definition. This would undo the effect of the previous
declaration. So spell out what strong_alias does plus add the
visibility attribute. */
extern struct rtld_global _rtld_local
__attribute__ ((alias ("_rtld_global"), visibility ("hidden")));
/* This variable is similar to _rtld_local, but all values are
read-only after relocation. */
struct rtld_global_ro _rtld_global_ro attribute_relro =
{
/* Get architecture specific initializer. */
#include <dl-procinfo.c>
#ifdef NEED_DL_SYSINFO
._dl_sysinfo = DL_SYSINFO_DEFAULT,
#endif
._dl_debug_fd = STDERR_FILENO,
._dl_use_load_bias = -2,
._dl_correct_cache_id = _DL_CACHE_DEFAULT_ID,
#if !HAVE_TUNABLES
._dl_hwcap_mask = HWCAP_IMPORTANT,
#endif
._dl_lazy = 1,
._dl_fpu_control = _FPU_DEFAULT,
._dl_pagesize = EXEC_PAGESIZE,
._dl_inhibit_cache = 0,
/* Function pointers. */
._dl_debug_printf = _dl_debug_printf,
._dl_mcount = _dl_mcount,
._dl_lookup_symbol_x = _dl_lookup_symbol_x,
._dl_open = _dl_open,
._dl_close = _dl_close,
._dl_catch_error = _rtld_catch_error,
._dl_error_free = _dl_error_free,
._dl_tls_get_addr_soft = _dl_tls_get_addr_soft,
._dl_libc_freeres = __rtld_libc_freeres,
#ifdef HAVE_DL_DISCOVER_OSVERSION
._dl_discover_osversion = _dl_discover_osversion
#endif
};
/* If we would use strong_alias here the compiler would see a
non-hidden definition. This would undo the effect of the previous
declaration. So spell out was strong_alias does plus add the
visibility attribute. */
extern struct rtld_global_ro _rtld_local_ro
__attribute__ ((alias ("_rtld_global_ro"), visibility ("hidden")));
static void dl_main (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv);
/* These two variables cannot be moved into .data.rel.ro. */
static struct libname_list _dl_rtld_libname;
static struct libname_list _dl_rtld_libname2;
/* Variable for statistics. */
RLTD_TIMING_DECLARE (relocate_time, static);
RLTD_TIMING_DECLARE (load_time, static, attribute_relro);
RLTD_TIMING_DECLARE (start_time, static, attribute_relro);
/* Additional definitions needed by TLS initialization. */
#ifdef TLS_INIT_HELPER
TLS_INIT_HELPER
#endif
/* Helper function for syscall implementation. */
#ifdef DL_SYSINFO_IMPLEMENTATION
DL_SYSINFO_IMPLEMENTATION
#endif
/* Before ld.so is relocated we must not access variables which need
relocations. This means variables which are exported. Variables
declared as static are fine. If we can mark a variable hidden this
is fine, too. The latter is important here. We can avoid setting
up a temporary link map for ld.so if we can mark _rtld_global as
hidden. */
#ifdef PI_STATIC_AND_HIDDEN
# define DONT_USE_BOOTSTRAP_MAP 1
#endif
#ifdef DONT_USE_BOOTSTRAP_MAP
static ElfW(Addr) _dl_start_final (void *arg);
#else
struct dl_start_final_info
{
struct link_map l;
RTLD_TIMING_VAR (start_time);
};
static ElfW(Addr) _dl_start_final (void *arg,
struct dl_start_final_info *info);
#endif
/* These defined magically in the linker script. */
extern char _begin[] attribute_hidden;
extern char _etext[] attribute_hidden;
extern char _end[] attribute_hidden;
#ifdef RTLD_START
RTLD_START
#else
# error "sysdeps/MACHINE/dl-machine.h fails to define RTLD_START"
#endif
/* This is the second half of _dl_start (below). It can be inlined safely
under DONT_USE_BOOTSTRAP_MAP, where it is careful not to make any GOT
references. When the tools don't permit us to avoid using a GOT entry
for _dl_rtld_global (no attribute_hidden support), we must make sure
this function is not inlined (see below). */
#ifdef DONT_USE_BOOTSTRAP_MAP
static inline ElfW(Addr) __attribute__ ((always_inline))
_dl_start_final (void *arg)
#else
static ElfW(Addr) __attribute__ ((noinline))
_dl_start_final (void *arg, struct dl_start_final_info *info)
#endif
{
ElfW(Addr) start_addr;
/* If it hasn't happen yet record the startup time. */
rtld_timer_start (&start_time);
#if !defined DONT_USE_BOOTSTRAP_MAP
RTLD_TIMING_SET (start_time, info->start_time);
#endif
/* Transfer data about ourselves to the permanent link_map structure. */
#ifndef DONT_USE_BOOTSTRAP_MAP
GL(dl_rtld_map).l_addr = info->l.l_addr;
GL(dl_rtld_map).l_ld = info->l.l_ld;
GL(dl_rtld_map).l_ld_readonly = info->l.l_ld_readonly;
memcpy (GL(dl_rtld_map).l_info, info->l.l_info,
sizeof GL(dl_rtld_map).l_info);
GL(dl_rtld_map).l_mach = info->l.l_mach;
GL(dl_rtld_map).l_relocated = 1;
#endif
_dl_setup_hash (&GL(dl_rtld_map));
GL(dl_rtld_map).l_real = &GL(dl_rtld_map);
GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin;
GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;
GL(dl_rtld_map).l_text_end = (ElfW(Addr)) _etext;
/* Copy the TLS related data if necessary. */
#ifndef DONT_USE_BOOTSTRAP_MAP
# if NO_TLS_OFFSET != 0
GL(dl_rtld_map).l_tls_offset = NO_TLS_OFFSET;
# endif
#endif
/* Initialize the stack end variable. */
__libc_stack_end = __builtin_frame_address (0);
/* Call the OS-dependent function to set up life so we can do things like
file access. It will call `dl_main' (below) to do all the real work
of the dynamic linker, and then unwind our frame and run the user
entry point on the same stack we entered on. */
start_addr = _dl_sysdep_start (arg, &dl_main);
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_STATISTICS))
{
RTLD_TIMING_VAR (rtld_total_time);
rtld_timer_stop (&rtld_total_time, start_time);
print_statistics (RTLD_TIMING_REF(rtld_total_time));
}
return start_addr;
}
#ifdef DONT_USE_BOOTSTRAP_MAP
# define bootstrap_map GL(dl_rtld_map)
#else
# define bootstrap_map info.l
#endif
static ElfW(Addr) __attribute_used__
_dl_start (void *arg)
{
#ifdef DONT_USE_BOOTSTRAP_MAP
rtld_timer_start (&start_time);
#else
struct dl_start_final_info info;
rtld_timer_start (&info.start_time);
#endif
/* Partly clean the `bootstrap_map' structure up. Don't use
`memset' since it might not be built in or inlined and we cannot
make function calls at this point. Use '__builtin_memset' if we
know it is available. We do not have to clear the memory if we
do not have to use the temporary bootstrap_map. Global variables
are initialized to zero by default. */
#ifndef DONT_USE_BOOTSTRAP_MAP
# ifdef HAVE_BUILTIN_MEMSET
__builtin_memset (bootstrap_map.l_info, '\0', sizeof (bootstrap_map.l_info));
# else
for (size_t cnt = 0;
cnt < sizeof (bootstrap_map.l_info) / sizeof (bootstrap_map.l_info[0]);
++cnt)
bootstrap_map.l_info[cnt] = 0;
# endif
#endif
/* Figure out the run-time load address of the dynamic linker itself. */
bootstrap_map.l_addr = elf_machine_load_address ();
/* Read our own dynamic section and fill in the info array. */
bootstrap_map.l_ld = (void *) bootstrap_map.l_addr + elf_machine_dynamic ();
bootstrap_map.l_ld_readonly = DL_RO_DYN_SECTION;
elf_get_dynamic_info (&bootstrap_map, true, false);
#if NO_TLS_OFFSET != 0
bootstrap_map.l_tls_offset = NO_TLS_OFFSET;
#endif
#ifdef ELF_MACHINE_BEFORE_RTLD_RELOC
ELF_MACHINE_BEFORE_RTLD_RELOC (&bootstrap_map, bootstrap_map.l_info);
#endif
if (bootstrap_map.l_addr || ! bootstrap_map.l_info[VALIDX(DT_GNU_PRELINKED)])
{
/* Relocate ourselves so we can do normal function calls and
data access using the global offset table. */
ELF_DYNAMIC_RELOCATE (&bootstrap_map, NULL, 0, 0, 0);
}
bootstrap_map.l_relocated = 1;
/* Please note that we don't allow profiling of this object and
therefore need not test whether we have to allocate the array
for the relocation results (as done in dl-reloc.c). */
/* Now life is sane; we can call functions and access global data.
Set up to use the operating system facilities, and find out from
the operating system's program loader where to find the program
header table in core. Put the rest of _dl_start into a separate
function, that way the compiler cannot put accesses to the GOT
before ELF_DYNAMIC_RELOCATE. */
__rtld_malloc_init_stubs ();
/* Do not use an initializer for these members because it would
intefere with __rtld_static_init. */
GLRO (dl_find_object) = &_dl_find_object;
{
#ifdef DONT_USE_BOOTSTRAP_MAP
ElfW(Addr) entry = _dl_start_final (arg);
#else
ElfW(Addr) entry = _dl_start_final (arg, &info);
#endif
#ifndef ELF_MACHINE_START_ADDRESS
# define ELF_MACHINE_START_ADDRESS(map, start) (start)
#endif
return ELF_MACHINE_START_ADDRESS (GL(dl_ns)[LM_ID_BASE]._ns_loaded, entry);
}
}
/* Now life is peachy; we can do all normal operations.
On to the real work. */
/* Some helper functions. */
/* Arguments to relocate_doit. */
struct relocate_args
{
struct link_map *l;
int reloc_mode;
};
struct map_args
{
/* Argument to map_doit. */
const char *str;
struct link_map *loader;
int mode;
/* Return value of map_doit. */
struct link_map *map;
};
struct dlmopen_args
{
const char *fname;
struct link_map *map;
};
struct lookup_args
{
const char *name;
struct link_map *map;
void *result;
};
/* Arguments to version_check_doit. */
struct version_check_args
{
int doexit;
int dotrace;
};
static void
relocate_doit (void *a)
{
struct relocate_args *args = (struct relocate_args *) a;
_dl_relocate_object (args->l, args->l->l_scope, args->reloc_mode, 0);
}
static void
map_doit (void *a)
{
struct map_args *args = (struct map_args *) a;
int type = (args->mode == __RTLD_OPENEXEC) ? lt_executable : lt_library;
args->map = _dl_map_object (args->loader, args->str, type, 0,
args->mode, LM_ID_BASE);
}
static void
dlmopen_doit (void *a)
{
struct dlmopen_args *args = (struct dlmopen_args *) a;
args->map = _dl_open (args->fname,
(RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT
| __RTLD_SECURE),
dl_main, LM_ID_NEWLM, _dl_argc, _dl_argv,
__environ);
}
static void
lookup_doit (void *a)
{
struct lookup_args *args = (struct lookup_args *) a;
const ElfW(Sym) *ref = NULL;
args->result = NULL;
lookup_t l = _dl_lookup_symbol_x (args->name, args->map, &ref,
args->map->l_local_scope, NULL, 0,
DL_LOOKUP_RETURN_NEWEST, NULL);
if (ref != NULL)
args->result = DL_SYMBOL_ADDRESS (l, ref);
}
static void
version_check_doit (void *a)
{
struct version_check_args *args = (struct version_check_args *) a;
if (_dl_check_all_versions (GL(dl_ns)[LM_ID_BASE]._ns_loaded, 1,
args->dotrace) && args->doexit)
/* We cannot start the application. Abort now. */
_exit (1);
}
static inline struct link_map *
find_needed (const char *name)
{
struct r_scope_elem *scope = &GL(dl_ns)[LM_ID_BASE]._ns_loaded->l_searchlist;
unsigned int n = scope->r_nlist;
while (n-- > 0)
if (_dl_name_match_p (name, scope->r_list[n]))
return scope->r_list[n];
/* Should never happen. */
return NULL;
}
static int
match_version (const char *string, struct link_map *map)
{
const char *strtab = (const void *) D_PTR (map, l_info[DT_STRTAB]);
ElfW(Verdef) *def;
#define VERDEFTAG (DT_NUM + DT_THISPROCNUM + DT_VERSIONTAGIDX (DT_VERDEF))
if (map->l_info[VERDEFTAG] == NULL)
/* The file has no symbol versioning. */
return 0;
def = (ElfW(Verdef) *) ((char *) map->l_addr
+ map->l_info[VERDEFTAG]->d_un.d_ptr);
while (1)
{
ElfW(Verdaux) *aux = (ElfW(Verdaux) *) ((char *) def + def->vd_aux);
/* Compare the version strings. */
if (strcmp (string, strtab + aux->vda_name) == 0)
/* Bingo! */
return 1;
/* If no more definitions we failed to find what we want. */
if (def->vd_next == 0)
break;
/* Next definition. */
def = (ElfW(Verdef) *) ((char *) def + def->vd_next);
}
return 0;
}
static bool tls_init_tp_called;
static void *
init_tls (size_t naudit)
{
/* Number of elements in the static TLS block. */
GL(dl_tls_static_nelem) = GL(dl_tls_max_dtv_idx);
/* Do not do this twice. The audit interface might have required
the DTV interfaces to be set up early. */
if (GL(dl_initial_dtv) != NULL)
return NULL;
/* Allocate the array which contains the information about the
dtv slots. We allocate a few entries more than needed to
avoid the need for reallocation. */
size_t nelem = GL(dl_tls_max_dtv_idx) + 1 + TLS_SLOTINFO_SURPLUS;
/* Allocate. */
GL(dl_tls_dtv_slotinfo_list) = (struct dtv_slotinfo_list *)
calloc (sizeof (struct dtv_slotinfo_list)
+ nelem * sizeof (struct dtv_slotinfo), 1);
/* No need to check the return value. If memory allocation failed
the program would have been terminated. */
struct dtv_slotinfo *slotinfo = GL(dl_tls_dtv_slotinfo_list)->slotinfo;
GL(dl_tls_dtv_slotinfo_list)->len = nelem;
GL(dl_tls_dtv_slotinfo_list)->next = NULL;
/* Fill in the information from the loaded modules. No namespace
but the base one can be filled at this time. */
assert (GL(dl_ns)[LM_ID_BASE + 1]._ns_loaded == NULL);
int i = 0;
for (struct link_map *l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL;
l = l->l_next)
if (l->l_tls_blocksize != 0)
{
/* This is a module with TLS data. Store the map reference.
The generation counter is zero. */
slotinfo[i].map = l;
/* slotinfo[i].gen = 0; */
++i;
}
assert (i == GL(dl_tls_max_dtv_idx));
/* Calculate the size of the static TLS surplus. */
_dl_tls_static_surplus_init (naudit);
/* Compute the TLS offsets for the various blocks. */
_dl_determine_tlsoffset ();
/* Construct the static TLS block and the dtv for the initial
thread. For some platforms this will include allocating memory
for the thread descriptor. The memory for the TLS block will
never be freed. It should be allocated accordingly. The dtv
array can be changed if dynamic loading requires it. */
void *tcbp = _dl_allocate_tls_storage ();
if (tcbp == NULL)
_dl_fatal_printf ("\
cannot allocate TLS data structures for initial thread\n");
/* Store for detection of the special case by __tls_get_addr
so it knows not to pass this dtv to the normal realloc. */
GL(dl_initial_dtv) = GET_DTV (tcbp);
/* And finally install it for the main thread. */
const char *lossage = TLS_INIT_TP (tcbp);
if (__glibc_unlikely (lossage != NULL))
_dl_fatal_printf ("cannot set up thread-local storage: %s\n", lossage);
__tls_init_tp ();
tls_init_tp_called = true;
return tcbp;
}
static unsigned int
do_preload (const char *fname, struct link_map *main_map, const char *where)
{
const char *objname;
const char *err_str = NULL;
struct map_args args;
bool malloced;
args.str = fname;
args.loader = main_map;
args.mode = __RTLD_SECURE;
unsigned int old_nloaded = GL(dl_ns)[LM_ID_BASE]._ns_nloaded;
(void) _dl_catch_error (&objname, &err_str, &malloced, map_doit, &args);
if (__glibc_unlikely (err_str != NULL))
{
_dl_error_printf ("\
ERROR: ld.so: object '%s' from %s cannot be preloaded (%s): ignored.\n",
fname, where, err_str);
/* No need to call free, this is still before
the libc's malloc is used. */
}
else if (GL(dl_ns)[LM_ID_BASE]._ns_nloaded != old_nloaded)
/* It is no duplicate. */
return 1;
/* Nothing loaded. */
return 0;
}
static void
security_init (void)
{
/* Set up the stack checker's canary. */
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
#ifdef THREAD_SET_STACK_GUARD
THREAD_SET_STACK_GUARD (stack_chk_guard);
#else
__stack_chk_guard = stack_chk_guard;
#endif
/* Set up the pointer guard as well, if necessary. */
uintptr_t pointer_chk_guard
= _dl_setup_pointer_guard (_dl_random, stack_chk_guard);
#ifdef THREAD_SET_POINTER_GUARD
THREAD_SET_POINTER_GUARD (pointer_chk_guard);
#endif
__pointer_chk_guard_local = pointer_chk_guard;
/* We do not need the _dl_random value anymore. The less
information we leave behind, the better, so clear the
variable. */
_dl_random = NULL;
}
#include <setup-vdso.h>
/* The LD_PRELOAD environment variable gives list of libraries
separated by white space or colons that are loaded before the
executable's dependencies and prepended to the global scope list.
(If the binary is running setuid all elements containing a '/' are
ignored since it is insecure.) Return the number of preloads
performed. Ditto for --preload command argument. */
unsigned int
handle_preload_list (const char *preloadlist, struct link_map *main_map,
const char *where)
{
unsigned int npreloads = 0;
const char *p = preloadlist;
char fname[SECURE_PATH_LIMIT];
while (*p != '\0')
{
/* Split preload list at space/colon. */
size_t len = strcspn (p, " :");
if (len > 0 && len < sizeof (fname))
{
memcpy (fname, p, len);
fname[len] = '\0';
}
else
fname[0] = '\0';
/* Skip over the substring and the following delimiter. */
p += len;
if (*p != '\0')
++p;
if (dso_name_valid_for_suid (fname))
npreloads += do_preload (fname, main_map, where);
}
return npreloads;
}
/* Called if the audit DSO cannot be used: if it does not have the
appropriate interfaces, or it expects a more recent version library
version than what the dynamic linker provides. */
static void
unload_audit_module (struct link_map *map, int original_tls_idx)
{
#ifndef NDEBUG
Lmid_t ns = map->l_ns;
#endif
_dl_close (map);
/* Make sure the namespace has been cleared entirely. */
assert (GL(dl_ns)[ns]._ns_loaded == NULL);
assert (GL(dl_ns)[ns]._ns_nloaded == 0);
GL(dl_tls_max_dtv_idx) = original_tls_idx;
}
/* Called to print an error message if loading of an audit module
failed. */
static void
report_audit_module_load_error (const char *name, const char *err_str,
bool malloced)
{
_dl_error_printf ("\
ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
name, err_str);
if (malloced)
free ((char *) err_str);
}
/* Load one audit module. */
static void
load_audit_module (const char *name, struct audit_ifaces **last_audit)
{
int original_tls_idx = GL(dl_tls_max_dtv_idx);
struct dlmopen_args dlmargs;
dlmargs.fname = name;
dlmargs.map = NULL;
const char *objname;
const char *err_str = NULL;
bool malloced;
_dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit, &dlmargs);
if (__glibc_unlikely (err_str != NULL))
{
report_audit_module_load_error (name, err_str, malloced);
return;
}
struct lookup_args largs;
largs.name = "la_version";
largs.map = dlmargs.map;
_dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
if (__glibc_likely (err_str != NULL))
{
unload_audit_module (dlmargs.map, original_tls_idx);
report_audit_module_load_error (name, err_str, malloced);
return;
}
unsigned int (*laversion) (unsigned int) = largs.result;
/* A null symbol indicates that something is very wrong with the
loaded object because defined symbols are supposed to have a
valid, non-null address. */
assert (laversion != NULL);
unsigned int lav = laversion (LAV_CURRENT);
if (lav == 0)
{
/* Only print an error message if debugging because this can
happen deliberately. */
if (GLRO(dl_debug_mask) & DL_DEBUG_FILES)
_dl_debug_printf ("\
file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
dlmargs.map->l_name, dlmargs.map->l_ns);
unload_audit_module (dlmargs.map, original_tls_idx);