vdso.cpp revision 10726d52ac3a7b34a6e2d9c40532037ca1108485
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <link.h>
18#include <string.h>
19#include <sys/auxv.h>
20#include <unistd.h>
21
22#if defined(__aarch64__) || defined(__x86_64__) || defined (__i386__)
23
24#if defined(__aarch64__)
25#define VDSO_CLOCK_GETTIME_SYMBOL "__kernel_clock_gettime"
26#define VDSO_GETTIMEOFDAY_SYMBOL  "__kernel_gettimeofday"
27#elif defined(__x86_64__) || defined(__i386__)
28#define VDSO_CLOCK_GETTIME_SYMBOL "__vdso_clock_gettime"
29#define VDSO_GETTIMEOFDAY_SYMBOL  "__vdso_gettimeofday"
30#endif
31
32#include <errno.h>
33#include <limits.h>
34#include <sys/mman.h>
35#include <time.h>
36
37#include "private/bionic_prctl.h"
38#include "private/libc_logging.h"
39
40extern "C" int __clock_gettime(int, timespec*);
41extern "C" int __gettimeofday(timeval*, struct timezone*);
42
43struct vdso_entry {
44  const char* name;
45  void* fn;
46};
47
48enum {
49  VDSO_CLOCK_GETTIME = 0,
50  VDSO_GETTIMEOFDAY,
51  VDSO_END
52};
53
54static union {
55  vdso_entry entries[VDSO_END];
56  char padding[PAGE_SIZE];
57} vdso __attribute__((aligned(PAGE_SIZE))) = {{
58  [VDSO_CLOCK_GETTIME] = { VDSO_CLOCK_GETTIME_SYMBOL, reinterpret_cast<void*>(__clock_gettime) },
59  [VDSO_GETTIMEOFDAY] = { VDSO_GETTIMEOFDAY_SYMBOL, reinterpret_cast<void*>(__gettimeofday) },
60}};
61
62int clock_gettime(int clock_id, timespec* tp) {
63  int (*vdso_clock_gettime)(int, timespec*) =
64      reinterpret_cast<int (*)(int, timespec*)>(vdso.entries[VDSO_CLOCK_GETTIME].fn);
65  return vdso_clock_gettime(clock_id, tp);
66}
67
68int gettimeofday(timeval* tv, struct timezone* tz) {
69  int (*vdso_gettimeofday)(timeval*, struct timezone*) =
70      reinterpret_cast<int (*)(timeval*, struct timezone*)>(vdso.entries[VDSO_GETTIMEOFDAY].fn);
71  return vdso_gettimeofday(tv, tz);
72}
73
74static void __libc_init_vdso_entries() {
75  // Do we have a vdso?
76  uintptr_t vdso_ehdr_addr = getauxval(AT_SYSINFO_EHDR);
77  ElfW(Ehdr)* vdso_ehdr = reinterpret_cast<ElfW(Ehdr)*>(vdso_ehdr_addr);
78  if (vdso_ehdr == nullptr) {
79    return;
80  }
81
82  // How many symbols does it have?
83  size_t symbol_count = 0;
84  ElfW(Shdr)* vdso_shdr = reinterpret_cast<ElfW(Shdr)*>(vdso_ehdr_addr + vdso_ehdr->e_shoff);
85  for (size_t i = 0; i < vdso_ehdr->e_shnum; ++i) {
86    if (vdso_shdr[i].sh_type == SHT_DYNSYM) {
87      symbol_count = vdso_shdr[i].sh_size / sizeof(ElfW(Sym));
88    }
89  }
90  if (symbol_count == 0) {
91    return;
92  }
93
94  // Where's the dynamic table?
95  ElfW(Addr) vdso_addr = 0;
96  ElfW(Dyn)* vdso_dyn = nullptr;
97  ElfW(Phdr)* vdso_phdr = reinterpret_cast<ElfW(Phdr)*>(vdso_ehdr_addr + vdso_ehdr->e_phoff);
98  for (size_t i = 0; i < vdso_ehdr->e_phnum; ++i) {
99    if (vdso_phdr[i].p_type == PT_DYNAMIC) {
100      vdso_dyn = reinterpret_cast<ElfW(Dyn)*>(vdso_ehdr_addr + vdso_phdr[i].p_offset);
101    } else if (vdso_phdr[i].p_type == PT_LOAD) {
102      vdso_addr = vdso_ehdr_addr + vdso_phdr[i].p_offset - vdso_phdr[i].p_vaddr;
103    }
104  }
105  if (vdso_addr == 0 || vdso_dyn == nullptr) {
106    return;
107  }
108
109  // Where are the string and symbol tables?
110  const char* strtab = nullptr;
111  ElfW(Sym)* symtab = nullptr;
112  for (ElfW(Dyn)* d = vdso_dyn; d->d_tag != DT_NULL; ++d) {
113    if (d->d_tag == DT_STRTAB) {
114      strtab = reinterpret_cast<const char*>(vdso_addr + d->d_un.d_ptr);
115    } else if (d->d_tag == DT_SYMTAB) {
116      symtab = reinterpret_cast<ElfW(Sym)*>(vdso_addr + d->d_un.d_ptr);
117    }
118  }
119  if (strtab == nullptr || symtab == nullptr) {
120    return;
121  }
122
123  // Are there any symbols we want?
124  for (size_t i = 0; i < symbol_count; ++i) {
125    for (size_t j = 0; j < VDSO_END; ++j) {
126      if (strcmp(vdso.entries[j].name, strtab + symtab[i].st_name) == 0) {
127        vdso.entries[j].fn = reinterpret_cast<void*>(vdso_addr + symtab[i].st_value);
128      }
129    }
130  }
131}
132
133void __libc_init_vdso() {
134  __libc_init_vdso_entries();
135
136  // We can't use PR_SET_VMA because this isn't an anonymous region.
137  // Long-term we should be able to replace all of this with ifuncs.
138  static_assert(PAGE_SIZE == sizeof(vdso), "sizeof(vdso) too large");
139  if (mprotect(vdso.entries, sizeof(vdso), PROT_READ) == -1) {
140    __libc_fatal("failed to mprotect PROT_READ vdso function pointer table: %s", strerror(errno));
141  }
142}
143
144#else
145
146void __libc_init_vdso() {
147}
148
149#endif
150