1 /* $NetBSD: rtld.c,v 1.87 2003/01/16 08:45:56 itohy Exp $ */
4 * Copyright (c) 1998 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/types.h>
36 #include <sys/errno.h>
53 * This is a slight hack to allow the same loader to be used on
54 * 4k and 8k AOUT_LDPGSZ executables.
56 static int page_size
= 0x2000;
58 #define PAGSIZ page_size
63 #define anon_open() do { \
64 if ((anon_fd = open("/dev/zero", O_RDWR, 0)) == -1) \
65 err("open: %s", "/dev/zero"); \
67 #define anon_close() do { \
68 (void)close(anon_fd); \
77 * Loader private data, hung off <so_map>->som_spd
79 struct somap_private
{
81 struct so_map
*spd_parent
;
84 #define _RTLD_MAIN 1 /* Marks the main program */
85 #define _RTLD_RTLD 2 /* Marks the run-time linker */
86 #define _RTLD_DL 4 /* A dlopen'ed object */
87 #define _RTLD_GLOBAL 8 /* Map is open to global search */
94 long spd_offset
; /* Correction for Sun main programs */
98 #define LM_PRIVATE(smp) ((struct somap_private *)(smp)->som_spd)
101 #define LM_OFFSET(smp) (LM_PRIVATE(smp)->spd_offset)
103 #define LM_OFFSET(smp) (0)
106 /* Base address for section_dispatch_table entries */
107 #define LM_LDBASE(smp) (smp->som_addr + LM_OFFSET(smp))
109 /* Start of text segment */
110 #define LM_TXTADDR(smp) (smp->som_addr == (caddr_t)0 ? PAGSIZ : 0)
112 /* Start of run-time relocation_info */
113 #define LM_REL(smp) ((struct relocation_info *) \
114 (smp->som_addr + LM_OFFSET(smp) + LD_REL((smp)->som_dynamic)))
116 /* Start of symbols */
117 #define LM_SYMBOLBASE(smp) ((long) \
118 (smp->som_addr + LM_OFFSET(smp) + LD_SYMBOL((smp)->som_dynamic)))
119 #define LM_SYMBOL(smp, i) ((struct nzlist *) \
120 (LM_PRIVATE(smp)->spd_symbolbase + \
121 (i) * LM_PRIVATE(smp)->spd_symbolsize))
123 /* Start of hash table */
124 #define LM_HASH(smp) ((struct rrs_hash *) \
125 ((smp)->som_addr + LM_OFFSET(smp) + LD_HASH((smp)->som_dynamic)))
127 /* Start of strings */
128 #define LM_STRINGBASE(smp) ((char *) \
129 ((smp)->som_addr + LM_OFFSET(smp) + LD_STRINGS((smp)->som_dynamic)))
131 /* Start of search paths */
132 #define LM_PATHS(smp) ((char *) \
133 ((smp)->som_addr + LM_OFFSET(smp) + LD_PATHS((smp)->som_dynamic)))
136 #define LM_ETEXT(smp) ((char *) \
137 ((smp)->som_addr + LM_TXTADDR(smp) + LD_TEXTSZ((smp)->som_dynamic)))
139 /* PLT is in data segment, so don't use LM_OFFSET here */
140 #define LM_PLT(smp) ((jmpslot_t *) \
141 ((smp)->som_addr + LD_PLT((smp)->som_dynamic)))
143 /* Parent of link map */
144 #define LM_PARENT(smp) (LM_PRIVATE(smp)->spd_parent)
146 static char __main_progname
[] = "main";
147 static char *main_progname
= __main_progname
;
148 static char us
[] = "/usr/libexec/ld.so";
151 char *__progname
= us
;
154 static uid_t uid
, euid
;
155 static gid_t gid
, egid
;
157 static int anon_fd
= -1;
159 struct so_map
*link_map_head
, *main_map
;
160 struct so_map
**link_map_tail
= &link_map_head
;
161 struct rt_symbol
*rt_symbol_head
;
163 static char *ld_library_path
;
164 static char *ld_preload_path
;
165 static int no_intern_search
;
166 static int ld_suppress_warnings
;
167 static int ld_warn_non_pure_code
;
169 static int ld_tracing
;
171 static void *__dlopen
__P((const char *, int));
172 static int __dlclose
__P((void *));
173 static void *__dlsym
__P((void *, const char *));
174 static int __dlctl
__P((void *, int, void *));
175 static void __dlexit
__P((void));
176 static int __dladdr
__P((const void *, Dl_info
*));
178 static struct ld_entry ld_entry
= {
179 __dlopen
, __dlclose
, __dlsym
, __dlctl
, __dlexit
, __dladdr
182 void xprintf
__P((char *, ...));
183 int rtld
__P((int, struct crt_ldso
*, struct _dynamic
*));
184 void binder_entry
__P((void));
185 long binder
__P((jmpslot_t
*));
186 static int load_subs
__P((struct so_map
*));
187 static struct so_map
*map_object
__P((struct sod
*, struct so_map
*));
188 static void unmap_object
__P((struct so_map
*));
189 static struct so_map
*alloc_link_map
__P(( char *, struct sod
*,
190 struct so_map
*, caddr_t
,
191 size_t, struct _dynamic
*));
192 static void free_link_map
__P((struct so_map
*));
193 static inline void check_text_reloc
__P(( struct relocation_info
*,
196 static void init_maps
__P((struct so_map
*));
197 static void reloc_map
__P((struct so_map
*));
198 static void reloc_copy
__P((struct so_map
*));
199 static void call_map
__P((struct so_map
*, char *));
200 static char *rtfindlib
__P((char *, int, int, int *, char *));
201 static struct nzlist
*lookup
__P(( const char *, struct so_map
*,
202 struct so_map
**, int));
203 static inline struct rt_symbol
*lookup_rts
__P((const char *));
204 static struct rt_symbol
*enter_rts
__P((const char *, long, int, caddr_t
,
205 long, struct so_map
*));
206 static void clear_rts
__P((struct rt_symbol
*));
207 static void maphints
__P((void));
208 static void unmaphints
__P((void));
209 static int hash_string
__P((const char *));
210 static int hinthash
__P((char *, int, int));
211 static char *findhint
__P((char *, int, int, char *));
213 static void preload
__P((char *));
214 static void ld_trace
__P((struct so_map
*));
215 static void build_sod
__P((const char *, struct sod
*));
218 strcmp (register const char *s1
, register const char *s2
)
223 return (*(unsigned char *)s1
- *(unsigned char *)--s2
);
226 /* `md-static-funcs.c' implements these functions: */
227 static void md_relocate_simple
__P((struct relocation_info
*,
230 #include "md-static-funcs.c"
233 * Called from assembler stub that has set up crtp (passed from crt0)
234 * and dp (our __DYNAMIC).
237 rtld(version
, crtp
, dp
)
239 struct crt_ldso
*crtp
;
243 int nreloc
; /* # of ld.so relocations */
244 struct relocation_info
*reloc
;
245 struct so_debug
*ddp
;
249 if ( version
!= CRT_VERSION_BSD_2
&&
250 version
!= CRT_VERSION_BSD_3
&&
251 version
!= CRT_VERSION_BSD_4
&&
252 version
!= CRT_VERSION_SUN
)
255 /* Fixup __DYNAMIC structure */
256 (long)dp
->d_un
.d_sdt
+= crtp
->crt_ba
;
258 /* Divide by hand to avoid possible use of library division routine */
259 for ( nreloc
= 0, n
= LD_RELSZ(dp
);
261 n
-= sizeof(struct relocation_info
) ) nreloc
++;
264 /* Relocate ourselves */
265 for ( reloc
= (struct relocation_info
*)(LD_REL(dp
) + crtp
->crt_ba
);
269 register long addr
= reloc
->r_address
+ crtp
->crt_ba
;
271 md_relocate_simple(reloc
, crtp
->crt_ba
, (char *)addr
);
274 /* Now (and NOT BEFORE this point) we can call externals. */
276 if (version
>= CRT_VERSION_BSD_4
)
277 __progname
= crtp
->crt_ldso
;
279 if (version
>= CRT_VERSION_BSD_3
)
280 main_progname
= crtp
->crt_prog
;
282 /* Setup out (private) environ variable */
283 environ
= crtp
->crt_ep
;
286 * Make sure we do not allow the library search path
287 * to be modified through the environment if we are
288 * running either setuid or setgid.
290 uid
= getuid(); euid
= geteuid();
291 gid
= getgid(); egid
= getegid();
292 careful
= (uid
!= euid
) || (gid
!= egid
);
294 unsetenv("LD_LIBRARY_PATH");
295 unsetenv("LD_PRELOAD");
300 * Locate the a.out header and check the machine ID.
301 * If we see MID_M68K4K, we alter the page size we
302 * use for address calculations. This allows the
303 * same loader to be used for 4k and 8k executables
304 * and libraries. Trust me; the world is a better
305 * place because this.
308 register long textaddr
;
309 register struct exec
*eh
;
311 /* XXX Assume this is near the start of text. */
312 textaddr
= ((long)crtp
->crt_bp
) & ~0xFFF;
313 eh
= (struct exec
*) textaddr
;
315 xprintf("%s: textaddr is 0x%x\n", us
, textaddr
);
316 xprintf("\t magic=0x%x\n", eh
->a_midmag
);
319 * Use this rather than N_PAGSIZ(), to be
322 if (N_GETMID((*eh
)) == MID_M68K4K
)
325 xprintf("\t page_size=%d\n", page_size
);
328 #endif /* __m68k__ */
330 /* Setup directory search */
331 ld_library_path
= getenv("LD_LIBRARY_PATH");
332 add_search_path(ld_library_path
);
333 if (getenv("LD_NOSTD_PATH") == NULL
)
336 ld_suppress_warnings
= getenv("LD_SUPPRESS_WARNINGS") != NULL
;
337 ld_warn_non_pure_code
= getenv("LD_WARN_NON_PURE_CODE") != NULL
;
339 no_intern_search
= getenv("LD_NO_INTERN_SEARCH") != 0;
344 * Init object administration. We start off with a map description
345 * for `main' and `rtld'.
347 smp
= alloc_link_map(main_progname
, (struct sod
*)0, (struct so_map
*)0,
348 (caddr_t
)0, 0, crtp
->crt_dp
);
349 LM_PRIVATE(smp
)->spd_refcount
++;
350 LM_PRIVATE(smp
)->spd_flags
|= _RTLD_MAIN
| _RTLD_GLOBAL
;
353 smp
= alloc_link_map(us
, (struct sod
*)0, (struct so_map
*)0,
354 (caddr_t
)crtp
->crt_ba
, 0, dp
);
355 LM_PRIVATE(smp
)->spd_refcount
++;
356 LM_PRIVATE(smp
)->spd_flags
|= _RTLD_RTLD
;
358 /* Fill in some field in main's __DYNAMIC structure */
359 if (version
>= CRT_VERSION_BSD_4
)
360 crtp
->crt_ldentry
= &ld_entry
;
362 crtp
->crt_dp
->d_entry
= &ld_entry
;
365 /* Handle LD_PRELOAD's here */
366 ld_preload_path
= getenv("LD_PRELOAD");
367 if (ld_preload_path
!= NULL
)
368 preload(ld_preload_path
);
370 /* Load subsidiary objects into the process address space */
371 ld_tracing
= (int)getenv("LD_TRACE_LOADED_OBJECTS");
372 load_subs(link_map_head
);
374 ld_trace(link_map_head
);
378 init_maps(link_map_head
);
380 crtp
->crt_dp
->d_un
.d_sdt
->sdt_loaded
= link_map_head
->som_next
;
382 ddp
= crtp
->crt_dp
->d_debug
;
383 ddp
->dd_cc
= rt_symbol_head
;
384 if (ddp
->dd_in_debugger
) {
385 caddr_t addr
= (caddr_t
)((long)crtp
->crt_bp
& (~(PAGSIZ
- 1)));
387 /* Set breakpoint for the benefit of debuggers */
388 if (mprotect(addr
, PAGSIZ
,
389 PROT_READ
|PROT_WRITE
|PROT_EXEC
) == -1) {
390 err(1, "Cannot set breakpoint (%s)", main_progname
);
392 md_set_breakpoint((long)crtp
->crt_bp
, (long *)&ddp
->dd_bpt_shadow
);
393 if (mprotect(addr
, PAGSIZ
, PROT_READ
|PROT_EXEC
) == -1) {
394 err(1, "Cannot re-protect breakpoint (%s)",
398 ddp
->dd_bpt_addr
= crtp
->crt_bp
;
400 ddp
->dd_sym_loaded
= 1;
403 /* Close the hints file */
406 /* Close our file descriptor */
407 (void)close(crtp
->crt_ldfd
);
419 /* Propagate _RTLD_GLOBAL parent flag */
420 flag
|= (LM_PRIVATE(smp
)->spd_flags
& _RTLD_GLOBAL
);
422 for (; smp
; smp
= smp
->som_next
) {
426 if (LM_PRIVATE(smp
)->spd_flags
& _RTLD_RTLD
)
429 if (smp
->som_dynamic
)
430 next
= LD_NEED(smp
->som_dynamic
);
433 struct so_map
*newmap
;
435 sodp
= (struct sod
*)(LM_LDBASE(smp
) + next
);
437 if ((newmap
= map_object(sodp
, smp
)) == NULL
) {
439 char *fmt
= sodp
->sod_library
?
440 "%s: lib%s.so.%d.%d" :
442 err(1, fmt
, main_progname
,
443 sodp
->sod_name
+LM_LDBASE(smp
),
447 newmap
= alloc_link_map(NULL
, sodp
, smp
, 0, 0, 0);
449 LM_PRIVATE(newmap
)->spd_refcount
++;
450 /* Note: existing maps also acquire the new flag */
451 LM_PRIVATE(newmap
)->spd_flags
|= flag
;
452 next
= sodp
->sod_next
;
462 char *fmt1
, *fmt2
, *fmt
, *main_local
;
465 if ((main_local
= getenv("LD_TRACE_LOADED_OBJECTS_PROGNAME")) == NULL
)
468 if ((fmt1
= getenv("LD_TRACE_LOADED_OBJECTS_FMT1")) == NULL
)
469 fmt1
= "\t-l%o.%m => %p (%x)\n";
471 if ((fmt2
= getenv("LD_TRACE_LOADED_OBJECTS_FMT2")) == NULL
)
472 fmt2
= "\t%o (%x)\n";
474 for (; smp
; smp
= smp
->som_next
) {
478 if ((sodp
= smp
->som_sod
) == NULL
)
481 name
= (char *)sodp
->sod_name
;
483 name
+= (long)LM_LDBASE(LM_PARENT(smp
));
485 if ((path
= smp
->som_path
) == NULL
)
488 fmt
= sodp
->sod_library
? fmt1
: fmt2
;
489 while ((c
= *fmt
++) != '\0') {
515 printf("%s", main_local
);
518 printf("%s", main_progname
);
524 printf("%d", sodp
->sod_major
);
527 printf("%d", sodp
->sod_minor
);
533 printf("%p", smp
->som_addr
);
544 * Allocate a new link map for shared object NAME loaded at ADDR as a
545 * result of the presence of link object LOP in the link map PARENT.
547 static struct so_map
*
548 alloc_link_map(path
, sodp
, parent
, addr
, size
, dp
)
551 struct so_map
*parent
;
557 struct somap_private
*smpp
;
559 smpp
= (struct somap_private
*)xmalloc(sizeof(struct somap_private
));
560 smp
= (struct so_map
*)xmalloc(sizeof(struct so_map
));
561 smp
->som_next
= NULL
;
562 *link_map_tail
= smp
;
563 link_map_tail
= &smp
->som_next
;
565 /*smp->som_sodbase = 0; NOT USED */
567 smp
->som_addr
= addr
;
568 smp
->som_path
= path
?strdup(path
):NULL
;
570 smp
->som_dynamic
= dp
;
571 smp
->som_spd
= (caddr_t
)smpp
;
573 smpp
->spd_refcount
= 0;
575 smpp
->spd_parent
= parent
;
576 smpp
->spd_size
= size
;
582 (addr
==0 && dp
->d_version
==LD_VERSION_SUN
) ? PAGSIZ
: 0;
586 * Pre-compute the location of symbol and string tables;
587 * they do not change while this object is loaded.
589 smpp
->spd_symbolsize
= LD_VERSION_NZLIST_P(dp
->d_version
)
590 ? sizeof(struct nzlist
)
591 : sizeof(struct nlist
);
592 smpp
->spd_symbolbase
= LM_SYMBOLBASE(smp
);
593 smpp
->spd_stringbase
= LM_STRINGBASE(smp
);
599 * Free the link map for an object being unmapped. The link map
600 * has already been removed from the link map list, so it can't be used
601 * after it's been unmapped.
608 if ((LM_PRIVATE(smp
)->spd_flags
& _RTLD_DL
) != 0) {
609 /* free synthetic sod structure allocated in __dlopen() */
610 free((char *)smp
->som_sod
->sod_name
);
614 /* free the link map structure. */
616 if (smp
->som_path
!= NULL
)
622 * Map object identified by link object SODP which was found
625 static struct so_map
*
626 map_object(sodp
, smp
)
639 name
= (char *)sodp
->sod_name
;
641 name
+= (long)LM_LDBASE(smp
);
644 xprintf("map_object: loading %s\n", name
);
647 if (sodp
->sod_library
) {
650 if (smp
== NULL
|| no_intern_search
||
651 LD_PATHS(smp
->som_dynamic
) == 0) {
654 ipath
= LM_PATHS(smp
);
655 add_search_path(ipath
);
658 path
= rtfindlib(name
, sodp
->sod_major
,
659 sodp
->sod_minor
, &usehints
, ipath
);
661 remove_search_path(ipath
);
668 if (careful
&& *name
!= '/') {
675 /* Check if already loaded */
676 for (p
= link_map_head
; p
; p
= p
->som_next
)
677 if (p
->som_path
&& strcmp(p
->som_path
, path
) == 0)
683 if ((fd
= open(path
, O_RDONLY
, 0)) == -1) {
691 if (read(fd
, &hdr
, sizeof(hdr
)) != sizeof(hdr
)) {
703 if ((addr
= mmap(0, hdr
.a_text
+ hdr
.a_data
+ hdr
.a_bss
,
705 MAP_FILE
|MAP_PRIVATE
, fd
, 0)) == (caddr_t
)-1) {
710 xprintf("map1: 0x%x for 0x%x\n", addr
, hdr
.a_text
+ hdr
.a_data
+ hdr
.a_bss
);
713 if (mprotect(addr
+ hdr
.a_text
, hdr
.a_data
,
714 PROT_READ
|PROT_WRITE
|PROT_EXEC
) != 0) {
719 if (mmap(addr
+ hdr
.a_text
+ hdr
.a_data
, hdr
.a_bss
,
720 PROT_READ
|PROT_WRITE
|PROT_EXEC
,
721 MAP_ANON
|MAP_PRIVATE
|MAP_FIXED
,
722 anon_fd
, 0) == (caddr_t
)-1) {
729 /* Assume _DYNAMIC is the first data item */
730 dp
= (struct _dynamic
*)(addr
+hdr
.a_text
);
732 /* Fixup __DYNAMIC structure */
733 (long)dp
->d_un
.d_sdt
+= (long)addr
;
735 return alloc_link_map(path
, sodp
, smp
, addr
,
736 hdr
.a_text
+ hdr
.a_data
+ hdr
.a_bss
, dp
);
740 * Unmap a mapped object.
746 struct so_map
*p
, **pp
;
747 struct rt_symbol
*rtsp
, *rtp
;
749 /* remove from link map list */
751 while ((p
= *pp
) != NULL
) {
757 warnx("warning: link map entry for %s not on link map list!",
762 *pp
= smp
->som_next
; /* make list skip it */
763 if (link_map_tail
== &smp
->som_next
) /* and readjust tail pointer */
766 /* unmap from address space */
767 (void)munmap(smp
->som_addr
, LM_PRIVATE(smp
)->spd_size
);
769 /* remove any globals from the global list that reference this smp */
770 while (rt_symbol_head
->rt_smp
== smp
) {
771 rtp
= rt_symbol_head
;
772 rt_symbol_head
= rtp
->rt_next
;
776 for (rtsp
= rt_symbol_head
; (rtp
= rtsp
->rt_next
) != NULL
;) {
777 rtsp
->rt_next
= rtp
->rt_next
;
778 if (rtp
->rt_smp
== smp
) {
790 /* Relocate all loaded objects according to their RRS segments */
791 for (smp
= head
; smp
; smp
= smp
->som_next
) {
792 if (LM_PRIVATE(smp
)->spd_flags
& _RTLD_RTLD
)
797 /* Copy any relocated initialized data. */
798 for (smp
= head
; smp
; smp
= smp
->som_next
) {
799 if (LM_PRIVATE(smp
)->spd_flags
& _RTLD_RTLD
)
804 /* Call any object initialization routines. */
805 for (smp
= head
; smp
; smp
= smp
->som_next
) {
806 if (LM_PRIVATE(smp
)->spd_flags
& _RTLD_RTLD
)
808 call_map(smp
, ".init");
809 call_map(smp
, "__init");
814 check_text_reloc(r
, smp
, addr
)
815 struct relocation_info
*r
;
821 if (addr
>= LM_ETEXT(smp
))
824 if (RELOC_EXTERN_P(r
))
825 sym
= LM_PRIVATE(smp
)->spd_stringbase
+
826 LM_SYMBOL(smp
, RELOC_SYMBOL(r
))->nz_strx
;
830 if (ld_warn_non_pure_code
&& !ld_suppress_warnings
)
831 warnx("warning: non pure code in %s at %x (%s)",
832 smp
->som_path
, r
->r_address
, sym
);
834 if (smp
->som_write
== 0 &&
835 mprotect(smp
->som_addr
+ LM_TXTADDR(smp
),
836 LD_TEXTSZ(smp
->som_dynamic
),
837 PROT_READ
|PROT_WRITE
|PROT_EXEC
) == -1) {
839 err(1, "Cannot enable writes to %s:%s",
840 main_progname
, smp
->som_path
);
850 struct _dynamic
*dp
= smp
->som_dynamic
;
851 struct relocation_info
*r
= LM_REL(smp
);
852 struct relocation_info
*rend
= r
+ LD_RELSZ(dp
)/sizeof(*r
);
853 long symbolbase
= LM_PRIVATE(smp
)->spd_symbolbase
;
854 char *stringbase
= LM_PRIVATE(smp
)->spd_stringbase
;
855 int symsize
= LM_PRIVATE(smp
)->spd_symbolsize
;
858 md_fix_jmpslot(LM_PLT(smp
),
859 (long)LM_PLT(smp
), (long)binder_entry
, 1);
861 for (; r
< rend
; r
++) {
863 caddr_t addr
= smp
->som_addr
+ r
->r_address
;
865 check_text_reloc(r
, smp
, addr
);
867 if (RELOC_EXTERN_P(r
)) {
868 struct so_map
*src_map
= NULL
;
869 struct nzlist
*p
, *np
;
870 long relocation
= md_get_addend(r
, addr
);
875 p
= (struct nzlist
*)
876 (symbolbase
+ symsize
* RELOC_SYMBOL(r
));
878 if (p
->nz_type
== (N_SETV
+ N_EXT
))
881 sym
= stringbase
+ p
->nz_strx
;
883 #if defined(__arm32__) && 1 /* XXX MAGIC! */
884 if (r
->r_baserel
&& r
->r_length
== 2)
887 np
= lookup(sym
, smp
, &src_map
, 0/*XXX-jumpslots!*/);
889 errx(1, "Undefined symbol \"%s\" in %s:%s",
890 sym
, main_progname
, smp
->som_path
);
893 * Found symbol definition.
894 * If it's in a link map, adjust value
895 * according to the load address of that map.
896 * Otherwise it's a run-time allocated common
897 * whose value is already up-to-date.
899 relocation
+= np
->nz_value
;
901 relocation
+= (long)src_map
->som_addr
;
903 if (RELOC_PCREL_P(r
))
904 relocation
-= (long)smp
->som_addr
;
906 if (RELOC_COPY_P(r
) && src_map
) {
907 if (p
->nz_size
!= np
->nz_size
)
908 warnx("symbol %s at %p in %s changed "
909 "size: expected %ld, actual %ld",
911 src_map
->som_addr
+ np
->nz_value
,
913 p
->nz_size
, np
->nz_size
);
917 src_map
->som_addr
+ np
->nz_value
,
918 p
->nz_size
, src_map
);
921 md_relocate(r
, relocation
, addr
, 0);
926 md_get_rt_segment_addend(r
, addr
)
928 md_get_addend(r
, addr
)
930 + (long)smp
->som_addr
, addr
, 0);
935 if (smp
->som_write
) {
936 if (mprotect(smp
->som_addr
+ LM_TXTADDR(smp
),
937 LD_TEXTSZ(smp
->som_dynamic
),
938 PROT_READ
|PROT_EXEC
) == -1) {
940 err(1, "Cannot disable writes to %s:%s",
941 main_progname
, smp
->som_path
);
951 struct rt_symbol
*rtsp
;
953 for (rtsp
= rt_symbol_head
; rtsp
; rtsp
= rtsp
->rt_next
)
954 if ((rtsp
->rt_smp
== NULL
|| rtsp
->rt_smp
== smp
) &&
955 rtsp
->rt_sp
->nz_type
== N_DATA
+ N_EXT
) {
956 bcopy(rtsp
->rt_srcaddr
, (caddr_t
)rtsp
->rt_sp
->nz_value
,
957 rtsp
->rt_sp
->nz_size
);
966 struct so_map
*src_map
= smp
;
969 np
= lookup(sym
, smp
, &src_map
, 1);
972 xprintf("call_map: %s at %#x+%#x enter\n", sym
, src_map
->som_addr
, np
->nz_value
);
974 #if defined(__arm32__) && 1 /* XXX MAGIC! */
975 (*(void (*) __P((u_int
)))(src_map
->som_addr
+ np
->nz_value
))((u_int
)src_map
->som_addr
);
977 (*(void (*) __P((void)))(src_map
->som_addr
+ np
->nz_value
))();
980 xprintf("call_map: %s at %#x+%#x exit\n", sym
, src_map
->som_addr
, np
->nz_value
);
986 * Run-time common symbol table.
989 #define RTC_TABSIZE 57
990 static struct rt_symbol
*rt_symtab
[RTC_TABSIZE
];
993 * Compute hash value for run-time symbol table
1005 k
= (((k
<< 1) + (k
>> 14)) ^ (*cp
++)) & 0x3fff;
1011 * Lookup KEY in the run-time common symbol table.
1014 static inline struct rt_symbol
*
1018 register int hashval
;
1019 register struct rt_symbol
*rtsp
;
1021 /* Determine which bucket. */
1023 hashval
= hash_string(key
) % RTC_TABSIZE
;
1025 /* Search the bucket. */
1027 for (rtsp
= rt_symtab
[hashval
]; rtsp
; rtsp
= rtsp
->rt_link
)
1028 if (strcmp(key
, rtsp
->rt_sp
->nz_name
) == 0)
1034 static struct rt_symbol
*
1035 enter_rts(name
, value
, type
, srcaddr
, size
, smp
)
1043 register int hashval
;
1044 register struct rt_symbol
*rtsp
, **rpp
;
1046 /* Determine which bucket */
1047 hashval
= hash_string(name
) % RTC_TABSIZE
;
1049 /* Find end of bucket */
1050 for (rpp
= &rt_symtab
[hashval
]; *rpp
; rpp
= &(*rpp
)->rt_link
)
1053 /* Allocate new common symbol */
1054 rtsp
= (struct rt_symbol
*)malloc(sizeof(struct rt_symbol
));
1055 rtsp
->rt_sp
= (struct nzlist
*)malloc(sizeof(struct nzlist
));
1056 rtsp
->rt_sp
->nz_name
= strdup(name
);
1057 rtsp
->rt_sp
->nz_value
= value
;
1058 rtsp
->rt_sp
->nz_type
= type
;
1059 rtsp
->rt_sp
->nz_size
= size
;
1060 rtsp
->rt_srcaddr
= srcaddr
;
1062 rtsp
->rt_link
= NULL
;
1064 /* Link onto linear list as well */
1065 rtsp
->rt_next
= rt_symbol_head
;
1066 rt_symbol_head
= rtsp
;
1075 struct rt_symbol
*rtp
;
1077 struct rt_symbol
*lrt
;
1078 int hashval
= hash_string(rtp
->rt_sp
->nz_name
) % RTC_TABSIZE
;
1080 if (rtp
== rt_symtab
[hashval
]) {
1081 rt_symtab
[hashval
] = rtp
->rt_next
;
1083 for (lrt
= rt_symtab
[hashval
]; lrt
->rt_link
;
1084 lrt
= lrt
->rt_link
) {
1085 if (lrt
->rt_link
== rtp
) {
1086 lrt
->rt_link
= rtp
->rt_link
->rt_link
;
1096 * Lookup NAME in the link maps. The link map producing a definition
1097 * is returned in SRC_MAP. If SRC_MAP is not NULL on entry the search is
1098 * confined to that map. If STRONG is set, the symbol returned must
1099 * have a proper type (used by binder()).
1101 static struct nzlist
*
1102 lookup(name
, ref_map
, src_map
, strong
)
1104 struct so_map
*ref_map
; /* map cont. the reference */
1105 struct so_map
**src_map
; /* map cont. the definition IN/OUT */
1108 long common_size
= 0;
1109 struct so_map
*smp
, *weak_smp
;
1110 struct rt_symbol
*rtsp
;
1111 struct nzlist
*weak_np
= 0;
1113 if ((rtsp
= lookup_rts(name
)) != NULL
) {
1114 /* common symbol is not a member of particular shlib */
1116 return (rtsp
->rt_sp
);
1119 weak_smp
= NULL
; /* XXX - gcc! */
1122 * Search all maps for a definition of NAME
1124 for (smp
= link_map_head
; smp
; smp
= smp
->som_next
) {
1127 struct rrs_hash
*hp
;
1131 /* Some local caching */
1133 struct rrs_hash
*hashbase
;
1137 np
= NULL
; /* XXX - gcc! */
1139 if (*src_map
&& smp
!= *src_map
)
1143 * When doing a global search, consider only maps
1144 * marked for global lookup.
1146 if (*src_map
== NULL
&& smp
!= ref_map
&&
1147 (LM_PRIVATE(smp
)->spd_flags
& _RTLD_GLOBAL
) == 0)
1150 if ((buckets
= LD_BUCKETS(smp
->som_dynamic
)) == 0)
1153 if (LM_PRIVATE(smp
)->spd_flags
& _RTLD_RTLD
)
1158 * Compute bucket in which the symbol might be found.
1160 for (hashval
= 0, cp
= (char *) name
; *cp
; cp
++)
1161 hashval
= (hashval
<< 1) + *cp
;
1163 hashval
= (hashval
& 0x7fffffff) % buckets
;
1165 hashbase
= LM_HASH(smp
);
1166 hp
= hashbase
+ hashval
;
1167 if (hp
->rh_symbolnum
== -1)
1168 /* Nothing in this bucket */
1171 symbolbase
= LM_PRIVATE(smp
)->spd_symbolbase
;
1172 stringbase
= LM_PRIVATE(smp
)->spd_stringbase
;
1173 symsize
= LM_PRIVATE(smp
)->spd_symbolsize
;
1176 np
= (struct nzlist
*)
1177 (symbolbase
+ hp
->rh_symbolnum
* symsize
);
1178 cp
= stringbase
+ np
->nz_strx
;
1179 if (strcmp(cp
, name
) == 0)
1181 if (hp
->rh_next
== 0)
1184 hp
= hashbase
+ hp
->rh_next
;
1187 /* Nothing in this bucket */
1191 * We have a symbol with the name we're looking for.
1193 if (np
->nz_type
== N_INDR
+N_EXT
) {
1195 * Next symbol gives the aliased name. Restart
1196 * search with new name and confine to this map.
1198 name
= stringbase
+ (++np
)->nz_strx
;
1203 if (np
->nz_value
== 0)
1204 /* It's not a definition */
1207 if (np
->nz_type
== N_UNDF
+N_EXT
&& np
->nz_value
!= 0) {
1208 if (N_AUX(&np
->nlist
) == AUX_FUNC
) {
1209 /* It's a `weak' function definition */
1213 /* It's a common, note value and continue search */
1214 if (common_size
< np
->nz_value
)
1215 common_size
= np
->nz_value
;
1219 if (N_BIND(&np
->nlist
) == BIND_WEAK
&& weak_np
== 0) {
1230 *src_map
= weak_smp
;
1234 if (common_size
== 0)
1239 * It's a common, enter into run-time common symbol table.
1241 rtsp
= enter_rts(name
, (long)calloc(1, common_size
),
1242 N_UNDF
+ N_EXT
, 0, common_size
, NULL
);
1244 /* common symbol is not a member of particular shlib */
1248 xprintf("Allocating common: %s size %d at %#x\n", name
, common_size
, rtsp
->rt_sp
->nz_value
);
1255 * Find the symbol in any mapped object that is closest to, but not
1256 * exceeds, the given address.
1264 struct rt_symbol
*rtsp
;
1268 * First look for an exact match in the commons table.
1270 for (i
= 0; i
< RTC_TABSIZE
; i
++) {
1271 for (rtsp
= rt_symtab
[i
]; rtsp
; rtsp
= rtsp
->rt_link
)
1272 if (rtsp
->rt_sp
->nz_value
== (long)addr
) {
1275 dli
->dli_sname
= rtsp
->rt_sp
->nz_name
;
1276 dli
->dli_saddr
= (void *)addr
;
1282 * Search all symbols tables for a ADDR
1284 for (smp
= link_map_head
; smp
; smp
= smp
->som_next
) {
1285 struct so_map
*src_map
= smp
;
1287 struct rrs_hash
*hp
;
1291 /* Some local caching */
1293 struct rrs_hash
*hashbase
;
1298 if ((LM_PRIVATE(smp
)->spd_flags
& _RTLD_GLOBAL
) == 0)
1301 if (LM_PRIVATE(smp
)->spd_flags
& _RTLD_RTLD
)
1304 if ((caddr_t
)addr
< smp
->som_addr
)
1307 np
= lookup("_end", smp
, &src_map
, 1);
1308 if ((caddr_t
)addr
> np
->nz_value
+ smp
->som_addr
)
1311 if ((buckets
= LD_BUCKETS(smp
->som_dynamic
)) == 0)
1315 * Walk the entire symbol table of this object
1316 * in search for the nearest symbol.
1318 hashbase
= LM_HASH(smp
);
1319 symbolbase
= LM_PRIVATE(smp
)->spd_symbolbase
;
1320 stringbase
= LM_PRIVATE(smp
)->spd_stringbase
;
1321 symsize
= LM_PRIVATE(smp
)->spd_symbolsize
;
1323 for (i
= 0; i
< buckets
; i
++) {
1325 if (hp
->rh_symbolnum
== -1)
1326 /* Nothing in this bucket */
1331 np
= (struct nzlist
*)
1332 (symbolbase
+ hp
->rh_symbolnum
* symsize
);
1334 hp
= (hp
->rh_next
== 0)
1336 : hashbase
+ hp
->rh_next
;
1338 /* Skip any undefined symbol */
1339 if (np
->nz_type
== N_UNDF
+N_EXT
)
1342 /* Compute absolute address */
1343 v
= np
->nz_value
+ src_map
->som_addr
;
1346 * Make this symbol the new candidate if it
1347 * is closer to, but not exceeding, ADDR.
1349 if (v
>= cur
&& v
<= (caddr_t
)addr
) {
1350 dli
->dli_fname
= smp
->som_path
;
1351 dli
->dli_fbase
= LM_LDBASE(smp
);
1352 dli
->dli_sname
= stringbase
+
1370 * This routine is called from the jumptable to resolve
1371 * procedure calls to shared objects.
1377 struct so_map
*smp
, *src_map
= NULL
;
1384 * Find the PLT map that contains JSP.
1386 for (smp
= link_map_head
; smp
; smp
= smp
->som_next
) {
1387 if (LM_PLT(smp
) < jsp
&&
1388 jsp
< LM_PLT(smp
) + LD_PLTSZ(smp
->som_dynamic
)/sizeof(*jsp
))
1393 errx(1, "Call to binder from unknown location: %p", jsp
);
1395 index
= jsp
->reloc_index
& JMPSLOT_RELOC_MASK
;
1397 /* Get the local symbol this jmpslot refers to */
1398 sym
= LM_PRIVATE(smp
)->spd_stringbase
+
1399 LM_SYMBOL(smp
,RELOC_SYMBOL(&LM_REL(smp
)[index
]))->nz_strx
;
1401 np
= lookup(sym
, smp
, &src_map
, 1);
1403 errx(1, "Undefined symbol \"%s\" called from %s:%s at %p",
1404 sym
, main_progname
, smp
->som_path
, jsp
);
1406 /* Fixup jmpslot so future calls transfer directly to target */
1407 addr
= np
->nz_value
;
1409 addr
+= (long)src_map
->som_addr
;
1411 md_fix_jmpslot(jsp
, (long)jsp
, addr
, 0);
1414 xprintf(" BINDER: %s located at %d(%p) = %#x in %s\n", sym
, index
, jsp
, addr
, src_map
->som_path
);
1421 static size_t hsize
;
1422 static struct hints_header
*hheader
;
1423 static struct hints_bucket
*hbuckets
;
1424 static char *hstrtab
;
1425 static char *hint_search_path
= "";
1427 #define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
1433 struct stat statbuf
;
1435 if ((hfd
= open(_PATH_LD_HINTS
, O_RDONLY
, 0)) == -1)
1438 if (fstat(hfd
, &statbuf
) != 0 ||
1439 (hsize
= (size_t)statbuf
.st_size
) < sizeof(struct hints_header
))
1442 hsize
= (hsize
+ PAGSIZ
- 1) & -PAGSIZ
;
1444 addr
= mmap(0, hsize
, PROT_READ
, MAP_FILE
|MAP_PRIVATE
, hfd
, 0);
1445 if (addr
== (caddr_t
)-1)
1448 hheader
= (struct hints_header
*)addr
;
1449 if (HH_BADMAG(*hheader
) || hheader
->hh_ehints
> hsize
) {
1450 munmap(addr
, hsize
);
1454 if (hheader
->hh_version
!= LD_HINTS_VERSION_1
&&
1455 hheader
->hh_version
!= LD_HINTS_VERSION_2
) {
1456 munmap(addr
, hsize
);
1460 hbuckets
= (struct hints_bucket
*)(addr
+ hheader
->hh_hashtab
);
1461 hstrtab
= (char *)(addr
+ hheader
->hh_strtab
);
1462 if (hheader
->hh_version
>= LD_HINTS_VERSION_2
)
1463 hint_search_path
= hstrtab
+ hheader
->hh_dirlist
;
1470 hheader
= (struct hints_header
*)-1;
1479 munmap((caddr_t
)hheader
, hsize
);
1486 hinthash(cp
, vmajor
, vminor
)
1493 k
= (((k
<< 1) + (k
>> 14)) ^ (*cp
++)) & 0x3fff;
1495 k
= (((k
<< 1) + (k
>> 14)) ^ (vmajor
*257)) & 0x3fff;
1496 if (hheader
->hh_version
== LD_HINTS_VERSION_1
)
1497 k
= (((k
<< 1) + (k
>> 14)) ^ (vminor
*167)) & 0x3fff;
1506 findhint(name
, major
, minor
, prefered_path
)
1509 char *prefered_path
;
1511 struct hints_bucket
*bp
;
1513 if (hheader
->hh_nbucket
== 0)
1516 bp
= hbuckets
+ (hinthash(name
, major
, minor
) % hheader
->hh_nbucket
);
1520 if (bp
->hi_namex
>= hheader
->hh_strtab_sz
) {
1521 warnx("Bad name index: %#x", bp
->hi_namex
);
1524 if (bp
->hi_pathx
>= hheader
->hh_strtab_sz
) {
1525 warnx("Bad path index: %#x", bp
->hi_pathx
);
1529 if (strcmp(name
, hstrtab
+ bp
->hi_namex
) == 0) {
1530 /* It's `name', check version numbers */
1531 if (bp
->hi_major
== major
&&
1532 (bp
->hi_ndewey
< 2 || bp
->hi_minor
>= minor
)) {
1533 if (prefered_path
== NULL
||
1534 strncmp(prefered_path
,
1535 hstrtab
+ bp
->hi_pathx
,
1536 strlen(prefered_path
)) == 0) {
1537 return hstrtab
+ bp
->hi_pathx
;
1542 if (bp
->hi_next
== -1)
1545 /* Move on to next in bucket */
1546 bp
= &hbuckets
[bp
->hi_next
];
1549 /* No hints available for name */
1554 rtfindlib(name
, major
, minor
, usehints
, ipath
)
1563 if (hheader
== NULL
)
1566 if (!HINTS_VALID
|| !(*usehints
))
1569 /* NOTE: `ipath' may reside in a piece of read-only memory */
1571 if (ld_library_path
|| ipath
) {
1572 /* Prefer paths from some explicit LD_LIBRARY_PATH */
1573 register char *lpath
;
1576 dp
= lpath
= concat(ld_library_path
? ld_library_path
: "",
1577 (ld_library_path
&& ipath
) ? ":" : "",
1578 ipath
? ipath
: "");
1580 while ((cp
= strsep(&dp
, ":")) != NULL
) {
1581 cp
= findhint(name
, major
, minor
, cp
);
1590 * Not found in hints; try directory search now, before
1591 * we get a spurious hint match below (i.e. a match not
1592 * on one of the paths we're supposed to search first.
1595 cp
= (char *)findshlib(name
, &major
, &realminor
, 0);
1596 if (cp
&& realminor
>= minor
)
1600 /* No LD_LIBRARY_PATH or lib not found in there; check default */
1601 cp
= findhint(name
, major
, minor
, NULL
);
1606 /* No hints available for name */
1609 add_search_path(hint_search_path
);
1610 cp
= (char *)findshlib(name
, &major
, &realminor
, 0);
1611 remove_search_path(hint_search_path
);
1613 if (realminor
< minor
&& !ld_suppress_warnings
)
1614 warnx("warning: lib%s.so.%d.%d: "
1615 "minor version >= %d expected, using it anyway",
1616 name
, major
, realminor
, minor
);
1626 struct so_map
*nsmp
;
1630 dp
= paths
= strdup(paths
);
1632 errx(1, "preload: out of memory");
1635 while ((cp
= strsep(&dp
, ":")) != NULL
) {
1636 if ((sodp
= (struct sod
*)malloc(sizeof(struct sod
))) == NULL
) {
1637 errx(1, "preload: %s: out of memory", cp
);
1641 sodp
->sod_name
= (long)strdup(cp
);
1642 sodp
->sod_library
= 0;
1643 sodp
->sod_major
= sodp
->sod_minor
= 0;
1645 if ((nsmp
= map_object(sodp
, 0)) == NULL
) {
1646 errx(1, "preload: %s: cannot map object", cp
);
1648 LM_PRIVATE(nsmp
)->spd_refcount
++;
1649 LM_PRIVATE(nsmp
)->spd_flags
|= _RTLD_GLOBAL
;
1656 static struct somap_private dlmap_private
= {
1665 static struct so_map dlmap
= {
1672 (struct _dynamic
*)0,
1673 (caddr_t
)&dlmap_private
1679 * Populate sod struct for dlopen's call to map_object
1682 build_sod(name
, sodp
)
1686 unsigned int tuplet
;
1688 char *realname
= NULL
, *tok
, *etok
, *cp
;
1690 /* default is an absolute or relative path */
1691 sodp
->sod_name
= (long)strdup(name
); /* strtok is destructive */
1692 sodp
->sod_library
= 0;
1693 sodp
->sod_major
= sodp
->sod_minor
= 0;
1695 /* asking for lookup? */
1696 if (strncmp((char *)sodp
->sod_name
, "lib", 3) != 0)
1699 /* skip over 'lib' */
1700 cp
= (char *)sodp
->sod_name
+ 3;
1703 if ((strchr(cp
, '.') == NULL
) || (*(cp
+strlen(cp
)-1) == '.'))
1709 /* loop through name - parse skipping name */
1710 for (tuplet
= 0; (tok
= strsep(&cp
, ".")) != NULL
; tuplet
++) {
1713 /* removed 'lib' and extensions from name */
1717 /* 'so' extension */
1718 if (strcmp(tok
, "so") != 0)
1722 /* major version extension */
1723 major
= strtol(tok
, &etok
, 10);
1724 if (*tok
== '\0' || *etok
!= '\0')
1728 /* minor version extension */
1729 minor
= strtol(tok
, &etok
, 10);
1730 if (*tok
== '\0' || *etok
!= '\0')
1733 /* if we get here, it must be weird */
1738 if (realname
== NULL
)
1741 cp
= (char *)sodp
->sod_name
;
1742 sodp
->sod_name
= (long)strdup(realname
);
1744 sodp
->sod_library
= 1;
1745 sodp
->sod_major
= major
;
1746 sodp
->sod_minor
= minor
;
1750 free((char *)sodp
->sod_name
);
1751 sodp
->sod_name
= (long)strdup(name
);
1755 __dlopen(name
, mode
)
1763 * A NULL argument returns the current set of mapped objects.
1766 LM_PRIVATE(link_map_head
)->spd_refcount
++;
1767 /* Return magic token */
1771 if ((sodp
= (struct sod
*)malloc(sizeof(struct sod
))) == NULL
) {
1776 build_sod(name
, sodp
);
1778 if ((smp
= map_object(sodp
, main_map
)) == NULL
) {
1780 xprintf("%s: %s\n", name
, strerror(errno
));
1783 free((char *)sodp
->sod_name
);
1787 if ((mode
& RTLD_LOCAL
) == 0)
1788 LM_PRIVATE(smp
)->spd_flags
|= _RTLD_GLOBAL
;
1790 if (LM_PRIVATE(smp
)->spd_refcount
++ > 0) {
1791 free((char *)sodp
->sod_name
);
1796 LM_PRIVATE(smp
)->spd_flags
|= _RTLD_DL
;
1798 if (load_subs(smp
) != 0) {
1799 if (--LM_PRIVATE(smp
)->spd_refcount
== 0) {
1816 if (fd
== (void *)1)
1819 smp
= (struct so_map
*)fd
;
1821 xprintf("dlclose(%s): refcount = %d\n", smp
->som_path
, LM_PRIVATE(smp
)->spd_refcount
);
1823 if (--LM_PRIVATE(smp
)->spd_refcount
!= 0)
1826 if ((LM_PRIVATE(smp
)->spd_flags
& _RTLD_DL
) == 0)
1829 /* Dismantle shared object map and descriptor */
1830 call_map(smp
, "__fini");
1832 unload_subs(smp
); /* XXX should unload implied objects */
1844 struct so_map
*smp
, *src_map
= NULL
;
1849 * Restrict search to passed map if dlopen()ed.
1851 if (fd
== (void *)1)
1852 smp
= link_map_head
;
1854 src_map
= smp
= (struct so_map
*)fd
;
1856 np
= lookup(sym
, smp
, &src_map
, 1);
1860 /* Fixup jmpslot so future calls transfer directly to target */
1861 addr
= np
->nz_value
;
1863 addr
+= (long)src_map
->som_addr
;
1865 return (void *)addr
;
1869 __dlctl(fd
, cmd
, arg
)
1877 *(int *)arg
= dlerrno
;
1881 dlerrno
= EOPNOTSUPP
;
1892 /* Call any object initialization routines. */
1893 for (smp
= link_map_head
; smp
; smp
= smp
->som_next
) {
1894 if (LM_PRIVATE(smp
)->spd_flags
& _RTLD_RTLD
)
1896 call_map(smp
, ".fini");
1901 xprintf(char *fmt
, ...)
1907 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1908 (void)write(1, buf
, strlen(buf
));