diff --git a/lib/libc/gen/aux.c b/lib/libc/gen/aux.c index b5c079b..4bf8643 100644 --- a/lib/libc/gen/aux.c +++ b/lib/libc/gen/aux.c @@ -1,5 +1,5 @@ /*- - * Copyright 2010 Konstantin Belousov . + * Copyright 2010, 2012 Konstantin Belousov . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,10 +36,34 @@ __FBSDID("$FreeBSD$"); #include "un-namespace.h" #include "libc_private.h" -Elf_Auxinfo *__elf_aux_vector; +extern char **environ; +extern int _DYNAMIC; +#pragma weak _DYNAMIC -static pthread_once_t aux_once = PTHREAD_ONCE_INIT; +void *__elf_aux_vector; +static pthread_once_t aux_vector_once = PTHREAD_ONCE_INIT; + +static void +init_aux_vector_once(void) +{ + Elf_Addr *sp; + + sp = (Elf_Addr *)environ; + while (*sp++ != 0) + ; + __elf_aux_vector = (Elf_Auxinfo *)sp; +} +void +__init_elf_aux_vector(void) +{ + + if (&_DYNAMIC != NULL) + return; + _once(&aux_vector_once, init_aux_vector_once); +} + +static pthread_once_t aux_once = PTHREAD_ONCE_INIT; static int pagesize, osreldate, canary_len, ncpus, pagesizes_len; static char *canary, *pagesizes; @@ -86,6 +110,7 @@ _elf_aux_info(int aux, void *buf, int buflen) { int res; + __init_elf_aux_vector(); if (__elf_aux_vector == NULL) return (ENOSYS); _once(&aux_once, init_aux); diff --git a/lib/libc/gen/dl_iterate_phdr.3 b/lib/libc/gen/dl_iterate_phdr.3 new file mode 100644 index 0000000..2d81d68 --- /dev/null +++ b/lib/libc/gen/dl_iterate_phdr.3 @@ -0,0 +1,115 @@ +.\" Copyright (c) 2005 Mark Kettenis +.\" Copyright (c) 2012 Konstantin Belousov +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $OpenBSD: dl_iterate_phdr.3,v 1.3 2007/05/31 19:19:48 jmc Exp $ +.\" $FreeBSD$ +.Dd February 15, 2012 +.Dt DL_ITERATE_PHDR 3 +.Os +.Sh NAME +.Nm dl_iterate_phdr +.Nd iterate over program headers +.Sh LIBRARY +For the dynamically linked binaries, the service is provided by +.Xr ld-elf.so.1 5 +dynamic linker. +Statically linked programs use an implementation of +.Fn dl_iterate_phdr +from libc. +.Sh SYNOPSIS +.Fd #include +.Ft int +.Fn dl_iterate_phdr "int (*callback)(struct dl_phdr_info *, size_t, void *)" "void *data" +.Sh DESCRIPTION +The +.Fn dl_iterate_phdr +function iterates over all ELF objects loaded into a process's +address space, calling +.Fa callback +for each object, passing it information about the object's +program headers and the +.Fa data +argument. +The iteration is aborted when all objects are passed, or when the next +.Fa callback +call returns non-zero value. +The information about the program headers is passed in a structure +that is defined as: +.Bd -literal +struct dl_phdr_info { + Elf_Addr dlpi_addr; + const char *dlpi_name; + const Elf_Phdr *dlpi_phdr; + Elf_Half dlpi_phnum; + unsigned long long int dlpi_adds; + unsigned long long int dlpi_subs; + size_t dlpi_tls_modid; + void *dlpi_tls_data; +}; +.Ed +.Pp +The members of +.Li struct dl_phdr_info +have the following meaning: +.Bl -tag -width dlpi_tls_modid +.It Fa dlpi_addr +The base address at which the object is mapped into the address +space of the calling process. +.It Fa dlpi_name +The name of the ELF object. +.It Fa dlpi_phdr +A pointer to the object's program headers. +.It Fa dlpi_phnum +The number of program headers in the object. +.It Fa dlpi_adds +The counter of the object loads performed by the dynamic linker. +.It Fa dlpi_subs +The counter of the object unloads performed by the dynamic linker. +.It Fa dlpi_tls_modid +The TLS index of the object. +.It Fa dlpi_tls_data +A pointer to the initialization data for the object TLS segment. +.El +.Pp +Future versions of +.Fx +might add more members to this structure. +To make it possible for programs to check whether any new members have +been added, the size of the structure is passed as an second argument to +.Fa callback . +.Pp +The third argument to callback is the +.Fa data +value passed to the call to +.Fn dl_iterate_phdr , +allowing the +.Fa callback +to have a context. +.Sh RETURN VALUES +The +.Fn dl_iterate_phdr +returns the value returned by the last +.Fa callback +call executed. +.Sh SEE ALSO +.Xr ld 1 , +.Xr ld-elf.so 1 , +.Xr dlopen 3 , +.Xr elf 5 +.Sh HISTORY +The +.Nm +function first appeared in +.Fx 7.0 . diff --git a/lib/libc/gen/dlfcn.c b/lib/libc/gen/dlfcn.c index 7be9f87..ad24bb4 100644 --- a/lib/libc/gen/dlfcn.c +++ b/lib/libc/gen/dlfcn.c @@ -34,6 +34,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include "namespace.h" +#include +#include "un-namespace.h" +#include "libc_private.h" static char sorry[] = "Service unavailable"; @@ -138,13 +142,58 @@ _rtld_thread_init(void * li) _rtld_error(sorry); } +static pthread_once_t dl_phdr_info_once = PTHREAD_ONCE_INIT; +static struct dl_phdr_info phdr_info; + +static void +dl_init_phdr_info(void) +{ + Elf_Auxinfo *auxp; + size_t phent; + unsigned int i; + + phent = 0; + for (auxp = __elf_aux_vector; auxp->a_type != AT_NULL; auxp++) { + switch (auxp->a_type) { + case AT_BASE: + phdr_info.dlpi_addr = (Elf_Addr)auxp->a_un.a_ptr; + break; + case AT_EXECPATH: + phdr_info.dlpi_name = (const char *)auxp->a_un.a_ptr; + break; + case AT_PHDR: + phdr_info.dlpi_phdr = + (const Elf_Phdr *)auxp->a_un.a_ptr; + break; + case AT_PHENT: + phent = auxp->a_un.a_val; + break; + case AT_PHNUM: + phdr_info.dlpi_phnum = (Elf_Half)auxp->a_un.a_val; + break; + } + } + for (i = 0; i < phdr_info.dlpi_phnum; i++) { + if (phdr_info.dlpi_phdr[i].p_type == PT_TLS) { + phdr_info.dlpi_tls_modid = 1; + phdr_info.dlpi_tls_data = + (void*)phdr_info.dlpi_phdr[i].p_vaddr; + } + } + phdr_info.dlpi_adds = 1; +} + #pragma weak dl_iterate_phdr int dl_iterate_phdr(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data) { - _rtld_error(sorry); - return 0; + + __init_elf_aux_vector(); + if (__elf_aux_vector == NULL) + return (1); + _once(&dl_phdr_info_once, dl_init_phdr_info); + return (callback(&phdr_info, sizeof(phdr_info), data)); } #pragma weak fdlopen diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index c7284cc..d4b24a7 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -44,6 +44,15 @@ extern int __isthreaded; /* + * Elf_Auxinfo *__elf_aux_vector, the pointer to the ELF aux vector + * provided by kernel. Either set for us by rtld, or found at runtime + * on stack for static binaries. + * + * Type is void to avoid polluting whole libc with ELF types. + */ +extern void *__elf_aux_vector; + +/* * libc should use libc_dlopen internally, which respects a global * flag where loading of new shared objects can be restricted. */ @@ -229,6 +238,7 @@ int _execvpe(const char *, char * const *, char * const *); int _elf_aux_info(int aux, void *buf, int buflen); struct dl_phdr_info; int __elf_phdr_match_addr(struct dl_phdr_info *, void *); +void __init_elf_aux_vector(void); void _pthread_cancel_enter(int); void _pthread_cancel_leave(int);