add dl_iterate_phdr interface

patches by Alex Caudill (npx). the dynamic-linked version is almost
identical to the final submitted patch; I just added a couple missing
lines for saving the phdr address when the dynamic linker is invoked
directly to run a program, and removed a couple to avoid introducing
another unnecessary type. the static-linked version is based on npx's
draft. it could use some improvements which are contingent on the
startup code saving some additional information for later use.
This commit is contained in:
Rich Felker 2012-10-31 21:27:48 -04:00
parent 76f28cfce5
commit 18c0e02e2b
3 changed files with 114 additions and 7 deletions

27
include/link.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef _LINK_H
#define _LINK_H
#include <elf.h>
#define __NEED_size_t
#include <bits/alltypes.h>
#if UINTPTR_MAX > 0xffffffff
#define ElfW(type) Elf64_ ## type
#else
#define ElfW(type) Elf32_ ## type
#endif
struct dl_phdr_info {
ElfW(Addr) dlpi_addr;
const char *dlpi_name;
const ElfW(Phdr) *dlpi_phdr;
ElfW(Half) dlpi_phnum;
unsigned long long int dlpi_adds;
unsigned long long int dlpi_subs;
size_t dlpi_tls_modid;
void *dlpi_tls_data;
};
int dl_iterate_phdr(int (*)(struct dl_phdr_info *, size_t, void *), void *);
#endif

View File

@ -0,0 +1,43 @@
#ifndef SHARED
#include <elf.h>
#include <link.h>
#include "libc.h"
#define AUX_CNT 38
int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void *data), void *data)
{
unsigned char *p;
ElfW(Phdr) *phdr, *tls_phdr=0;
size_t base = 0;
size_t n;
struct dl_phdr_info info;
size_t i, aux[AUX_CNT];
for (i=0; libc.auxv[i]; i+=2)
if (libc.auxv[i]<AUX_CNT) aux[libc.auxv[i]] = libc.auxv[i+1];
for (p=(void *)aux[AT_PHDR],n=aux[AT_PHNUM]; n; n--,p+=aux[AT_PHENT]) {
phdr = (void *)p;
if (phdr->p_type == PT_PHDR)
base = aux[AT_PHDR] - phdr->p_vaddr;
if (phdr->p_type == PT_TLS)
tls_phdr = phdr;
}
info.dlpi_addr = base;
info.dlpi_name = "/proc/self/exe";
info.dlpi_phdr = (void *)aux[AT_PHDR];
info.dlpi_phnum = aux[AT_PHNUM];
info.dlpi_adds = 0;
info.dlpi_subs = 0;
if (tls_phdr) {
info.dlpi_tls_modid = 1;
info.dlpi_tls_data = (void *)(base + tls_phdr->p_vaddr);
} else {
info.dlpi_tls_modid = 0;
info.dlpi_tls_data = 0;
}
return (callback)(&info, sizeof (info), data);
}
#endif

View File

@ -13,6 +13,7 @@
#include <errno.h>
#include <limits.h>
#include <elf.h>
#include <link.h>
#include <setjmp.h>
#include <pthread.h>
#include <ctype.h>
@ -56,6 +57,8 @@ struct dso {
size_t *dynv;
struct dso *next, *prev;
Phdr *phdr;
int phnum;
int refcnt;
Sym *syms;
uint32_t *hashtab;
@ -91,6 +94,7 @@ void *__install_initial_tls(void *);
static struct dso *head, *tail, *ldso, *fini_head;
static char *env_path, *sys_path, *r_path;
static unsigned long long gencnt;
static int ssp_used;
static int runtime;
static int ldd_mode;
@ -323,6 +327,8 @@ static void *map_library(int fd, struct dso *dso)
eh->e_phoff = sizeof *eh;
}
ph = (void *)((char *)buf + eh->e_phoff);
dso->phdr = ph;
dso->phnum = eh->e_phnum;
for (i=eh->e_phnum; i; i--, ph=(void *)((char *)ph+eh->e_phentsize)) {
if (ph->p_type == PT_DYNAMIC)
dyn = ph->p_vaddr;
@ -825,18 +831,19 @@ void *__dynlink(int argc, char **argv)
lib->name = lib->shortname = "libc.so";
lib->global = 1;
ehdr = (void *)lib->base;
find_map_range((void *)(aux[AT_BASE]+ehdr->e_phoff),
ehdr->e_phnum, ehdr->e_phentsize, lib);
lib->dynv = (void *)(lib->base + find_dyn(
(void *)(aux[AT_BASE]+ehdr->e_phoff),
ehdr->e_phnum, ehdr->e_phentsize));
lib->phnum = ehdr->e_phnum;
lib->phdr = (void *)(aux[AT_BASE]+ehdr->e_phoff);
find_map_range(lib->phdr, ehdr->e_phnum, ehdr->e_phentsize, lib);
lib->dynv = (void *)(lib->base + find_dyn(lib->phdr,
ehdr->e_phnum, ehdr->e_phentsize));
decode_dyn(lib);
if (aux[AT_PHDR]) {
size_t interp_off = 0;
size_t tls_image = 0;
/* Find load address of the main program, via AT_PHDR vs PT_PHDR. */
phdr = (void *)aux[AT_PHDR];
app->phdr = phdr = (void *)aux[AT_PHDR];
app->phnum = aux[AT_PHNUM];
for (i=aux[AT_PHNUM]; i; i--, phdr=(void *)((char *)phdr + aux[AT_PHENT])) {
if (phdr->p_type == PT_PHDR)
app->base = (void *)(aux[AT_PHDR] - phdr->p_vaddr);
@ -884,6 +891,8 @@ void *__dynlink(int argc, char **argv)
close(fd);
lib->name = ldname;
app->name = argv[0];
app->phnum = ehdr->e_phnum;
app->phdr = (void *)(app->base + ehdr->e_phoff);
aux[AT_ENTRY] = ehdr->e_entry;
}
if (app->tls_size) {
@ -907,7 +916,8 @@ void *__dynlink(int argc, char **argv)
/* Attach to vdso, if provided by the kernel */
if (search_vec(auxv, &vdso_base, AT_SYSINFO_EHDR)) {
ehdr = (void *)vdso_base;
phdr = (void *)(vdso_base + ehdr->e_phoff);
vdso->phdr = phdr = (void *)(vdso_base + ehdr->e_phoff);
vdso->phnum = ehdr->e_phnum;
for (i=ehdr->e_phnum; i; i--, phdr=(void *)((char *)phdr + ehdr->e_phentsize)) {
if (phdr->p_type == PT_DYNAMIC)
vdso->dynv = (void *)(vdso_base + phdr->p_offset);
@ -1068,6 +1078,7 @@ void *dlopen(const char *file, int mode)
orig_tail = tail;
end:
__release_ptc();
if (p) gencnt++;
pthread_rwlock_unlock(&lock);
if (p) do_init_fini(orig_tail);
pthread_setcancelstate(cs, 0);
@ -1192,6 +1203,32 @@ void *__dlsym(void *restrict p, const char *restrict s, void *restrict ra)
pthread_rwlock_unlock(&lock);
return res;
}
int dl_iterate_phdr(int(*callback)(struct dl_phdr_info *info, size_t size, void *data), void *data)
{
struct dso *current;
struct dl_phdr_info info;
int ret = 0;
for(current = head; current;) {
info.dlpi_addr = (uintptr_t)current->base;
info.dlpi_name = current->name;
info.dlpi_phdr = current->phdr;
info.dlpi_phnum = current->phnum;
info.dlpi_adds = gencnt;
info.dlpi_subs = 0;
info.dlpi_tls_modid = current->tls_id;
info.dlpi_tls_data = current->tls_image;
ret = (callback)(&info, sizeof (info), data);
if (ret != 0) break;
pthread_rwlock_rdlock(&lock);
current = current->next;
pthread_rwlock_unlock(&lock);
}
return ret;
}
#else
void *dlopen(const char *file, int mode)
{