diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/internal/vdso.c | 91 | ||||
| -rw-r--r-- | src/time/clock_gettime.c | 18 | 
2 files changed, 104 insertions, 5 deletions
| diff --git a/src/internal/vdso.c b/src/internal/vdso.c new file mode 100644 index 00000000..6ae0212e --- /dev/null +++ b/src/internal/vdso.c @@ -0,0 +1,91 @@ +#include <elf.h> +#include <limits.h> +#include <stdint.h> +#include <string.h> +#include "libc.h" +#include "syscall.h" + +#ifdef VDSO_USEFUL + +#if ULONG_MAX == 0xffffffff +typedef Elf32_Ehdr Ehdr; +typedef Elf32_Phdr Phdr; +typedef Elf32_Sym Sym; +typedef Elf32_Verdef Verdef; +typedef Elf32_Verdaux Verdaux; +#else +typedef Elf64_Ehdr Ehdr; +typedef Elf64_Phdr Phdr; +typedef Elf64_Sym Sym; +typedef Elf64_Verdef Verdef; +typedef Elf64_Verdaux Verdaux; +#endif + +static int checkver(Verdef *def, int vsym, const char *vername, char *strings) +{ +	vsym &= 0x7fff; +	for (;;) { +		if (!(def->vd_flags & VER_FLG_BASE) +		  && (def->vd_ndx & 0x7fff) == vsym) +			break; +		if (def->vd_next == 0) +			return 0; +		def = (Verdef *)((char *)def + def->vd_next); +	} +	Verdaux *aux = (Verdaux *)((char *)def + def->vd_aux); +	return !strcmp(vername, strings + aux->vda_name); +} + +#define OK_TYPES (1<<STT_NOTYPE | 1<<STT_OBJECT | 1<<STT_FUNC | 1<<STT_COMMON) +#define OK_BINDS (1<<STB_GLOBAL | 1<<STB_WEAK | 1<<STB_GNU_UNIQUE) + +void *__vdsosym(const char *vername, const char *name) +{ +	size_t i; +	for (i=0; libc.auxv[i] != AT_SYSINFO_EHDR; i+=2) +		if (!libc.auxv[i]) return 0; +	Ehdr *eh = (void *)libc.auxv[i+1]; +	Phdr *ph = (void *)((char *)eh + eh->e_phoff); +	size_t *dynv=0, base=-1; +	for (i=0; i<eh->e_phnum; i++, ph=(void *)((char *)ph+eh->e_phentsize)) { +		if (ph->p_type == PT_LOAD) +			base = (size_t)eh + ph->p_offset - ph->p_vaddr; +		else if (ph->p_type == PT_DYNAMIC) +			dynv = (void *)((char *)eh + ph->p_offset); +	} +	if (!dynv || base==(size_t)-1) return 0; + +	char *strings = 0; +	Sym *syms = 0; +	uint32_t *hashtab = 0; +	uint16_t *versym = 0; +	Verdef *verdef = 0; +	 +	for (i=0; dynv[i]; i+=2) { +		void *p = (void *)(base + dynv[i+1]); +		switch(dynv[i]) { +		case DT_STRTAB: strings = p; break; +		case DT_SYMTAB: syms = p; break; +		case DT_HASH: hashtab = p; break; +		case DT_VERSYM: versym = p; break; +		case DT_VERDEF: verdef = p; break; +		} +	}	 + +	if (!strings || !syms || !hashtab) return 0; +	if (!verdef) versym = 0; + +	for (i=0; i<hashtab[1]; i++) { +		if (!(1<<(syms[i].st_info&0xf) & OK_TYPES)) continue; +		if (!(1<<(syms[i].st_info>>4) & OK_BINDS)) continue; +		if (!syms[i].st_shndx) continue; +		if (strcmp(name, strings+syms[i].st_name)) continue; +		if (versym && !checkver(verdef, versym[i], vername, strings)) +			continue; +		return (void *)(base + syms[i].st_value); +	} + +	return 0; +} + +#endif diff --git a/src/time/clock_gettime.c b/src/time/clock_gettime.c index ce9f2209..799251d8 100644 --- a/src/time/clock_gettime.c +++ b/src/time/clock_gettime.c @@ -3,6 +3,7 @@  #include <stdint.h>  #include "syscall.h"  #include "libc.h" +#include "atomic.h"  static int sc_clock_gettime(clockid_t clk, struct timespec *ts)  { @@ -20,14 +21,21 @@ static int sc_clock_gettime(clockid_t clk, struct timespec *ts)  	return -1;  } -weak_alias(sc_clock_gettime, __vdso_clock_gettime); - -int (*__cgt)(clockid_t, struct timespec *) = __vdso_clock_gettime; +void *__vdsosym(const char *, const char *);  int __clock_gettime(clockid_t clk, struct timespec *ts)  { -	/* Conditional is to make this work prior to dynamic linking */ -	return __cgt ? __cgt(clk, ts) : sc_clock_gettime(clk, ts); +#ifdef VDSO_CGT_SYM +	static int (*cgt)(clockid_t, struct timespec *); +	if (!cgt) { +		void *f = __vdsosym(VDSO_CGT_VER, VDSO_CGT_SYM); +		if (!f) f = (void *)sc_clock_gettime; +		a_cas_p(&cgt, 0, f); +	} +	return cgt(clk, ts); +#else +	return sc_clock_gettime(clk, ts); +#endif  }  weak_alias(__clock_gettime, clock_gettime); | 
