mirror of
git://git.musl-libc.org/musl
synced 2024-12-14 18:55:23 +00:00
add working vdso clock_gettime support, including static linking
the vdso symbol lookup code is based on the original 2011 patch by Nicholas J. Kain, with some streamlining, pointer arithmetic fixes, and one symbol version matching fix. on the consumer side (clock_gettime), per-arch macros for the particular symbol name and version to lookup are added in syscall_arch.h, and no vdso code is pulled in on archs which do not define these macros. at this time, vdso is enabled only on x86_64. the vdso support at the dynamic linker level is no longer useful to libc, but is left in place for the sake of debuggers (which may need the vdso in the link map to find its functions) and possibly use with dlsym.
This commit is contained in:
parent
0d0c2f4034
commit
58e75db471
@ -60,3 +60,7 @@ static __inline long __syscall6(long n, long a1, long a2, long a3, long a4, long
|
||||
"d"(a3), "r"(r10), "r"(r8), "r"(r9) : "rcx", "r11", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define VDSO_USEFUL
|
||||
#define VDSO_CGT_SYM "__vdso_clock_gettime"
|
||||
#define VDSO_CGT_VER "LINUX_2.6"
|
||||
|
91
src/internal/vdso.c
Normal file
91
src/internal/vdso.c
Normal file
@ -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
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user