diff options
-rw-r--r-- | ChangeLog | 45 | ||||
-rw-r--r-- | elf/Makefile | 2 | ||||
-rw-r--r-- | elf/dl-deps.c | 93 | ||||
-rw-r--r-- | elf/dl-lookup.c | 105 | ||||
-rw-r--r-- | elf/dl-minimal.c | 13 | ||||
-rw-r--r-- | elf/dl-reloc.c | 20 | ||||
-rw-r--r-- | elf/dl-runtime.c | 5 | ||||
-rw-r--r-- | elf/dlfcn.h | 1 | ||||
-rw-r--r-- | elf/dlopen.c | 31 | ||||
-rw-r--r-- | elf/dlsym.c | 3 | ||||
-rw-r--r-- | elf/link.h | 35 | ||||
-rw-r--r-- | elf/linux-compat.c | 7 | ||||
-rw-r--r-- | elf/rtld.c | 55 |
13 files changed, 253 insertions, 162 deletions
@@ -1,9 +1,40 @@ +Tue Jun 4 02:25:44 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu> + + * elf/linux-compat.c (_init): Pass reloc_addr arg to _dl_lookup_symbol. + + * elf/dl-lookup.c (_dl_lookup_symbol): Use l_searchlist. + + * elf/dl-deps.c: New file. + * elf/Makefile (rtld-routines): Add dl-deps. + * elf/link.h: Declare _dl_map_object_deps, _dl_open. + * elf/rtld.c (dl_main): Use _dl_map_object_deps. + * elf/dlopen.c: Use _dl_open. + + * elf/rtld.c (dl_main): Remove BEFORE_RTLD variable and list + frobnication based on its value. Detach RTLD_MAP from the chain if + its l_opencount is zero after loading deps. + + * elf/dlfcn.h (RTLD_BINDING_MASK): New macro. + + * elf/link.h (struct link_map): Replace l_deps_loaded flag member with + `struct link_map **l_searchlist' and `unsigned int l_nsearchlist'. + * elf/dl-lookup.c (_dl_lookup_symbol): Make SYMBOL_SCOPE arg an array + of two link_map ptrs. Search the maps in the l_searchlist of each + elt that is not null. + * elf/dl-reloc.c (_dl_relocate_object): Pass proper SCOPE array. + * elf/dl-runtime.c: Likewise. + * elf/dlsym.c: Likewise. + * elf/rtld.c (dl_main): Likewise. + + * elf/dl-minimal.c (realloc): Support realloc of the block most + recently returned by the minimal malloc. + Tue Jun 4 00:16:03 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu> * sysdeps/gnu/errlist.awk: Produce N_("..."), not _N("..."). * intl: Updated from gettext-0.10.13. - * intl/localealias.c intl/dcgettext.c [_LIBC]: Define HAVE_ALLOCA. + * intl/localealias.c, intl/dcgettext.c [_LIBC]: Define HAVE_ALLOCA. * signal/siggetmask.c: New file. Wrapper around sigblock with link_warning about obsolescence. @@ -38,18 +69,6 @@ Mon Jun 3 15:58:22 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu> for a HANDLE signal to IGNORE because the signal is blocked, clean up any pending suspension left over from a call to resume. -Mon Jun 3 01:29:53 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu> - - * elf/link.h (struct link_map): Replace l_deps_loaded flag member with - `struct link_map **l_searchlist'. - * elf/dl-lookup.c (_dl_lookup_symbol): Make SYMBOL_SCOPE arg an array - of two link_map ptrs. Search the maps in the l_searchlist of each - of the two elts that is non-null. - * elf/dl-reloc.c (_dl_relocate_object): Pass proper SCOPE array. - * elf/dl-runtime.c: Likewise. - * elf/dlsym.c: Likewise. - * elf/rtld.c (dl_main): Likewise. - Mon Jun 3 00:30:35 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu> * elf/dl-lookup.c (_dl_lookup_symbol): Take new arg RELOC_ADDR and diff --git a/elf/Makefile b/elf/Makefile index 6124fa9..8f34a9c 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -29,7 +29,7 @@ libdl-routines := dlopen dlclose dlsym dlerror libdl-inhibit-o = $(filter-out .so,$(object-suffixes)) # Build only shared. rtld-routines := rtld $(addprefix dl-,minimal load lookup object reloc \ - runtime sysdep error init fini) + deps runtime sysdep error init fini) distribute = $(rtld-routines:=.c) dynamic-link.h do-rel.h \ soinit.c sofini.c ldd.sh.in linux-compat.c diff --git a/elf/dl-deps.c b/elf/dl-deps.c new file mode 100644 index 0000000..f87475a --- /dev/null +++ b/elf/dl-deps.c @@ -0,0 +1,93 @@ +/* Load the dependencies of a mapped object. +Copyright (C) 1996 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 Library General Public License as +published by the Free Software Foundation; either version 2 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include <link.h> +#include <errno.h> +#include <dlfcn.h> +#include <stdlib.h> + +void +_dl_map_object_deps (struct link_map *map) +{ + unsigned int nlist = 1; + struct link_map **list = malloc (sizeof *list); + unsigned int done; + + /* Start the search list with one element: MAP itself. */ + list[0] = map; + + /* Process each element of the search list, loading each of its immediate + dependencies and appending them to the list as we step through it. + This produces a flat, ordered list that represents a breadth-first + search of the dependency tree. */ + for (done = 0; done < nlist; ++done) + { + struct link_map *l = list[done]; + if (l->l_info[DT_NEEDED]) + { + const char *strtab + = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr); + const Elf32_Dyn *d; + for (d = l->l_ld; d->d_tag != DT_NULL; ++d) + if (d->d_tag == DT_NEEDED) + { + /* Extend the list and put this object on the end. */ + struct link_map **n + = realloc (list, (nlist + 1) * sizeof *list); + if (n) + list = n; + else + { + free (list); + _dl_signal_error (ENOMEM, map->l_name, + "finding dependencies"); + } + list[nlist++] = _dl_map_object (l, strtab + d->d_un.d_val); + } + } + } + + map->l_searchlist = list; + map->l_nsearchlist = nlist; +} + + +struct link_map * +_dl_open (struct link_map *parent, const char *file, int mode) +{ + struct link_map *new, *l; + Elf32_Addr init; + + /* Load the named object. */ + new = _dl_map_object (parent, file); + + /* Load that object's dependencies. */ + _dl_map_object_deps (new); + + /* Relocate the objects loaded. */ + for (l = new; l; l = l->l_next) + if (! l->l_relocated) + _dl_relocate_object (l, (mode & RTLD_BINDING_MASK) == RTLD_LAZY); + + /* Run the initializer functions of new objects. */ + while (init = _dl_init_next ()) + (*(void (*) (void)) init) (); + + return new; +} diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index b72a6a2..26357c2 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -29,77 +29,84 @@ Cambridge, MA 02139, USA. */ Elf32_Addr _dl_lookup_symbol (const char *undef_name, const Elf32_Sym **ref, - struct link_map *symbol_scope, + struct link_map *symbol_scope[2], const char *reference_name, Elf32_Addr reloc_addr, int noplt) { unsigned long int hash = elf_hash (undef_name); - struct link_map *map; struct { Elf32_Addr a; const Elf32_Sym *s; } weak_value = { 0, NULL }; + size_t i; + struct link_map **scope, *map; /* Search the relevant loaded objects for a definition. */ - for (map = symbol_scope; map; map = map->l_next) - { - const Elf32_Sym *symtab; - const char *strtab; - Elf32_Word symidx; - - symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr); - strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr); - - /* Search the appropriate hash bucket in this object's symbol table - for a definition for the same symbol name. */ - for (symidx = map->l_buckets[hash % map->l_nbuckets]; - symidx != STN_UNDEF; - symidx = map->l_chain[symidx]) + for (scope = symbol_scope; scope < &symbol_scope[2]; ++scope) + if (*scope) + for (i = 0; i < (*scope)->l_nsearchlist; ++i) { - const Elf32_Sym *sym = &symtab[symidx]; + const Elf32_Sym *symtab; + const char *strtab; + Elf32_Word symidx; + + map = (*scope)->l_searchlist[i]; - if (sym->st_value == 0 || /* No value. */ - reloc_addr == map->l_addr + sym->st_value || /* Self ref. */ - (noplt && sym->st_shndx == SHN_UNDEF)) /* Unwanted PLT entry. */ - continue; + symtab = ((void *) map->l_addr + map->l_info[DT_SYMTAB]->d_un.d_ptr); + strtab = ((void *) map->l_addr + map->l_info[DT_STRTAB]->d_un.d_ptr); - switch (ELF32_ST_TYPE (sym->st_info)) + /* Search the appropriate hash bucket in this object's symbol table + for a definition for the same symbol name. */ + for (symidx = map->l_buckets[hash % map->l_nbuckets]; + symidx != STN_UNDEF; + symidx = map->l_chain[symidx]) { - case STT_NOTYPE: - case STT_FUNC: - case STT_OBJECT: - break; - default: - /* Not a code/data definition. */ - continue; - } + const Elf32_Sym *sym = &symtab[symidx]; - if (sym != *ref && strcmp (strtab + sym->st_name, undef_name)) - /* Not the symbol we are looking for. */ - continue; + if (sym->st_value == 0 || /* No value. */ + /* Cannot resolve to the location being filled in. */ + reloc_addr == map->l_addr + sym->st_value || + (noplt && sym->st_shndx == SHN_UNDEF)) /* Reject PLT. */ + continue; - switch (ELF32_ST_BIND (sym->st_info)) - { - case STB_GLOBAL: - /* Global definition. Just what we need. */ - *ref = sym; - return map->l_addr; - case STB_WEAK: - /* Weak definition. Use this value if we don't find another. */ - if (! weak_value.s) + switch (ELF32_ST_TYPE (sym->st_info)) { - weak_value.s = sym; - weak_value.a = map->l_addr; + case STT_NOTYPE: + case STT_FUNC: + case STT_OBJECT: + break; + default: + /* Not a code/data definition. */ + continue; + } + + if (sym != *ref && strcmp (strtab + sym->st_name, undef_name)) + /* Not the symbol we are looking for. */ + continue; + + switch (ELF32_ST_BIND (sym->st_info)) + { + case STB_GLOBAL: + /* Global definition. Just what we need. */ + *ref = sym; + return map->l_addr; + case STB_WEAK: + /* Weak definition. Use this value if we don't find + another. */ + if (! weak_value.s) + { + weak_value.s = sym; + weak_value.a = map->l_addr; + } + break; + default: + /* Local symbols are ignored. */ + break; } - break; - default: - /* Local symbols are ignored. */ - break; } } - } if (weak_value.s == NULL && ELF32_ST_BIND ((*ref)->st_info) != STB_WEAK) { diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c index 9e0a770..c61b515 100644 --- a/elf/dl-minimal.c +++ b/elf/dl-minimal.c @@ -1,5 +1,5 @@ /* Minimal replacements for basic facilities used in the dynamic linker. -Copyright (C) 1995 Free Software Foundation, Inc. +Copyright (C) 1995, 1996 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 @@ -84,10 +84,17 @@ free (void *ptr) } weak_symbol (free) -/* This is never called. */ +/* This is only called with the most recent block returned by malloc. */ void * realloc (void *ptr, size_t n) -{ ptr += n; abort (); } +{ + void *new; + assert (ptr == alloc_last_block); + alloc_ptr = alloc_last_block; + new = malloc (n); + assert (new == ptr); + return new; +} weak_symbol (realloc) /* Avoid signal frobnication in setjmp/longjmp. Keeps things smaller. */ diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c index 424d303..bfa0174 100644 --- a/elf/dl-reloc.c +++ b/elf/dl-reloc.c @@ -54,12 +54,11 @@ _dl_relocate_object (struct link_map *l, int lazy) } { - struct link_map *real_next, *scope; + struct link_map *scope[2]; const char *strtab = ((void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr); - Elf32_Addr resolve (const Elf32_Sym **ref, Elf32_Addr reloc_addr, int noplt) { @@ -67,16 +66,16 @@ _dl_relocate_object (struct link_map *l, int lazy) l->l_name, reloc_addr, noplt); } - real_next = l->l_next; if (l->l_info[DT_SYMBOLIC]) { - if (l->l_prev) - l->l_prev->l_next = real_next; - l->l_next = _dl_loaded; - scope = l; + scope[0] = l; + scope[1] = _dl_loaded; } else - scope = _dl_loaded; + { + scope[0] = _dl_loaded; + scope[1] = l; + } if (l->l_type == lt_interpreter) /* We cannot be lazy when relocating the dynamic linker itself. It @@ -87,11 +86,6 @@ _dl_relocate_object (struct link_map *l, int lazy) lazy = 0; ELF_DYNAMIC_RELOCATE (l, lazy, resolve); - - /* Restore list frobnication done above for DT_SYMBOLIC. */ - l->l_next = real_next; - if (l->l_prev) - l->l_prev->l_next = l; } /* Set up the PLT so its unrelocated entries will diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c index 0beba76..dce34f8 100644 --- a/elf/dl-runtime.c +++ b/elf/dl-runtime.c @@ -70,8 +70,9 @@ fixup ( Elf32_Addr resolve (const Elf32_Sym **ref, Elf32_Addr reloc_addr, int noplt) { - return _dl_lookup_symbol (strtab + (*ref)->st_name, ref, _dl_loaded, - l->l_name, reloc_addr, noplt); + struct link_map *scope[2] = { _dl_loaded, NULL }; + return _dl_lookup_symbol (strtab + (*ref)->st_name, ref, + scope, l->l_name, reloc_addr, noplt); } /* Perform the specified relocation. */ diff --git a/elf/dlfcn.h b/elf/dlfcn.h index a405baa..8d19b8b 100644 --- a/elf/dlfcn.h +++ b/elf/dlfcn.h @@ -24,6 +24,7 @@ Cambridge, MA 02139, USA. */ /* The MODE argument to `dlopen' contains one of the following: */ #define RTLD_LAZY 0x001 /* Lazy function call binding. */ #define RTLD_NOW 0x002 /* Immediate function call binding. */ +#define RTLD_BINDING_MASK 0x3 /* Mask of binding time value. */ /* If the following bit is set in the MODE argument to `dlopen', the symbols of the loaded object and its dependencies are made diff --git a/elf/dlopen.c b/elf/dlopen.c index 26ed35a..74ab8bb 100644 --- a/elf/dlopen.c +++ b/elf/dlopen.c @@ -24,38 +24,11 @@ Cambridge, MA 02139, USA. */ void * dlopen (const char *file, int mode) { - struct link_map *new, *l; + struct link_map *new; void doit (void) { - Elf32_Addr init; - - new = _dl_map_object (_dl_loaded, file); - - /* Map in any dependencies. */ - for (l = new; l; l = l->l_next) - if (! l->l_deps_loaded) - { - if (l->l_info[DT_NEEDED]) - { - const char *strtab - = (void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr; - const Elf32_Dyn *d; - for (d = l->l_ld; d->d_tag != DT_NULL; ++d) - if (d->d_tag == DT_NEEDED) - _dl_map_object (l, strtab + d->d_un.d_val); - } - l->l_deps_loaded = 1; - } - - /* Relocate the objects loaded. */ - for (l = new; l; l = l->l_next) - if (! l->l_relocated) - _dl_relocate_object (l, mode == RTLD_LAZY); - - /* Run the initializer functions of new objects. */ - while (init = _dl_init_next ()) - (*(void (*) (void)) init) (); + new = _dl_open (_dl_loaded, file, mode); } return _dlerror_run (doit) ? NULL : new; diff --git a/elf/dlsym.c b/elf/dlsym.c index cc50650..804d404 100644 --- a/elf/dlsym.c +++ b/elf/dlsym.c @@ -33,7 +33,8 @@ dlsym (void *handle, const char *name) int lose; void doit (void) { - loadbase = _dl_lookup_symbol (name, &ref, map, map->l_name, 0); + struct link_map *scope[2] = { map, NULL }; + loadbase = _dl_lookup_symbol (name, &ref, scope, map->l_name, 0, 0); } /* Confine the symbol scope to just this map. */ @@ -90,6 +90,12 @@ struct link_map Elf32_Word l_phnum; /* Number of program header entries. */ Elf32_Addr l_entry; /* Entry point location. */ + /* Array of DT_NEEDED dependencies and their dependencies, in + dependency order for symbol lookup. This is null before the + dependencies have been loaded. */ + struct link_map **l_searchlist; + unsigned int l_nsearchlist; + /* Symbol hash table. */ Elf32_Word l_nbuckets; const Elf32_Word *l_buckets, *l_chain; @@ -102,7 +108,6 @@ struct link_map lt_library, /* Library needed by main executable. */ lt_loaded, /* Extra run-time loaded shared object. */ } l_type:2; - unsigned int l_deps_loaded:1; /* Nonzero if DT_NEEDED items loaded. */ unsigned int l_relocated:1; /* Nonzero if object's relocations done. */ unsigned int l_init_called:1; /* Nonzero if DT_INIT function called. */ unsigned int l_init_running:1; /* Nonzero while DT_INIT function runs. */ @@ -176,22 +181,36 @@ extern struct link_map *_dl_map_object (struct link_map *loader, extern struct link_map *_dl_map_object_from_fd (const char *name, int fd, char *realname); +/* Call _dl_map_object on the dependencies of MAP, and + set up MAP->l_searchlist. */ +extern void _dl_map_object_deps (struct link_map *map); + /* Cache the locations of MAP's hash table. */ extern void _dl_setup_hash (struct link_map *map); +/* Open the shared object NAME, relocate it, and run its initializer if it + hasn't already been run. LOADER's DT_RPATH is used in searching for + NAME. MODE is as for `dlopen' (see <dlfcn.h>). If the object is + already opened, returns its existing map. */ +extern struct link_map *_dl_open (struct link_map *loader, + const char *name, int mode); + + + /* Search loaded objects' symbol tables for a definition of the symbol referred to by UNDEF. *SYM is the symbol table entry containing the reference; it is replaced with the defining symbol, and the base load - address of the defining object is returned. SYMBOL_SCOPE is the head of - the chain used for searching. REFERENCE_NAME should name the object - containing the reference; it is used in error messages. RELOC_ADDR is - the address being fixed up and the chosen symbol cannot be one with this - value. If NOPLT is nonzero, then the reference must not be resolved to - a PLT entry. */ + address of the defining object is returned. Each of SYMBOL_SCOPE[0] and + SYMBOL_SCOPE[1] that is not null and their dependencies are searched to + resolve the name. REFERENCE_NAME should name the object containing the + reference; it is used in error messages. RELOC_ADDR is the address + being fixed up and the chosen symbol cannot be one with this value. If + NOPLT is nonzero, then the reference must not be resolved to a PLT + entry. */ extern Elf32_Addr _dl_lookup_symbol (const char *undef, const Elf32_Sym **sym, - struct link_map *symbol_scope, + struct link_map *symbol_scope[2], const char *reference_name, Elf32_Addr reloc_addr, int noplt); diff --git a/elf/linux-compat.c b/elf/linux-compat.c index ed1595e..8821a14 100644 --- a/elf/linux-compat.c +++ b/elf/linux-compat.c @@ -1,5 +1,5 @@ /* Initializer for Linux-compatible dynamic linker `/lib/ld-linux.so.1'. -Copyright (C) 1995 Free Software Foundation, Inc. +Copyright (C) 1995, 1996 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 @@ -33,8 +33,9 @@ void _init (void) { const Elf32_Sym *ref = NULL; - Elf32_Addr loadbase = _dl_lookup_symbol ("atexit", &ref, _dl_loaded, + struct link_map *scope[2] = { _dl_loaded, NULL }; + Elf32_Addr loadbase = _dl_lookup_symbol ("atexit", &ref, scope, "<ld-linux.so.1 initialization>", - 1); + 0, 1); (*(__typeof (atexit) *) (loadbase + ref->st_value)) (&_dl_fini); } @@ -124,7 +124,7 @@ dl_main (const Elf32_Phdr *phdr, Elf32_Addr *user_entry) { const Elf32_Phdr *ph; - struct link_map *l, *last, *before_rtld; + struct link_map *l; const char *interpreter_name; int lazy; int list_only = 0; @@ -250,44 +250,19 @@ of this helper program; chances are you did not intend to run this program.\n", l->l_next = &rtld_map; rtld_map.l_prev = l; - /* Now process all the DT_NEEDED entries and map in the objects. - Each new link_map will go on the end of the chain, so we will - come across it later in the loop to map in its dependencies. */ - before_rtld = NULL; - for (l = _dl_loaded; l; l = l->l_next) - { - if (l->l_info[DT_NEEDED]) - { - const char *strtab - = (void *) l->l_addr + l->l_info[DT_STRTAB]->d_un.d_ptr; - const Elf32_Dyn *d; - last = l; - for (d = l->l_ld; d->d_tag != DT_NULL; ++d) - if (d->d_tag == DT_NEEDED) - { - struct link_map *new; - new = _dl_map_object (l, strtab + d->d_un.d_val); - if (!before_rtld && new == &rtld_map) - before_rtld = last; - last = new; - } - } - l->l_deps_loaded = 1; - } + /* Load all the libraries specified by DT_NEEDED entries. */ + _dl_map_object_deps (l); - /* If any DT_NEEDED entry referred to the interpreter object itself, - reorder the list so it appears after its dependent. If not, - remove it from the maps we will use for symbol resolution. */ - rtld_map.l_prev->l_next = rtld_map.l_next; - if (rtld_map.l_next) - rtld_map.l_next->l_prev = rtld_map.l_prev; - if (before_rtld) + /* XXX if kept, move it so l_next list is in dep order because + it will determine gdb's search order. + Perhaps do this always, so later dlopen by name finds it? + XXX But then gdb always considers it present. */ + if (rtld_map.l_opencount == 0) { - rtld_map.l_prev = before_rtld; - rtld_map.l_next = before_rtld->l_next; - before_rtld->l_next = &rtld_map; - if (rtld_map.l_next) - rtld_map.l_next->l_prev = &rtld_map; + /* No DT_NEEDED entry referred to the interpreter object itself, + so remove it from the list of visible objects. */ + rtld_map.l_prev->l_next = rtld_map.l_next; + rtld_map.l_next->l_prev = rtld_map.l_prev; } if (list_only) @@ -316,9 +291,9 @@ of this helper program; chances are you did not intend to run this program.\n", for (i = 1; i < _dl_argc; ++i) { const Elf32_Sym *ref = NULL; - Elf32_Addr loadbase = _dl_lookup_symbol (_dl_argv[i], &ref, - _dl_loaded, "argument", - 0, 0); + struct link_map *scope[2] ={ _dl_loaded, NULL }; + Elf32_Addr loadbase + = _dl_lookup_symbol (_dl_argv[i], &ref, scope, "argument", 0, 0); char buf[20], *bp; buf[sizeof buf - 1] = '\0'; bp = _itoa (ref->st_value, &buf[sizeof buf - 1], 16, 0); |