Skip to content

Commit f5eae6b

Browse files
author
Yifan Zhu
committed
restructure vdso initialization routine
1 parent b8dd399 commit f5eae6b

File tree

2 files changed

+118
-71
lines changed

2 files changed

+118
-71
lines changed

libc/src/__support/OSUtil/linux/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ add_object_library(
2626
DEPENDS
2727
.${LIBC_TARGET_ARCHITECTURE}.vdso
2828
libc.src.__support.CPP.array
29+
libc.src.__support.CPP.optional
2930
libc.src.__support.CPP.string_view
3031
libc.src.__support.threads.callonce
3132
libc.src.__support.threads.linux.futex_word_type

libc/src/__support/OSUtil/linux/vdso.cpp

Lines changed: 117 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88
#include "src/__support/OSUtil/linux/vdso.h"
99
#include "src/__support/CPP/array.h"
10+
#include "src/__support/CPP/optional.h"
1011
#include "src/__support/CPP/string_view.h"
1112
#include "src/__support/threads/callonce.h"
1213
#include "src/__support/threads/linux/futex_word.h"
@@ -70,7 +71,9 @@ cpp::string_view find_version(Verdef *verdef, ElfW(Half) * versym,
7071
// skip if this is a file-level version
7172
if (def->vd_flags & VER_FLG_BASE)
7273
continue;
73-
// check if the version identifier matches
74+
// check if the version identifier matches. Highest bit is used to determine
75+
// whether the symbol is local. Only lower 15 bits are used for version
76+
// identifier.
7477
if ((def->vd_ndx & 0x7FFF) == identifier) {
7578
Verdaux *aux = def->aux();
7679
return strtab + aux->vda_name;
@@ -82,61 +85,58 @@ cpp::string_view find_version(Verdef *verdef, ElfW(Half) * versym,
8285
using VDSOArray =
8386
cpp::array<void *, static_cast<size_t>(VDSOSym::VDSOSymCount)>;
8487

85-
static VDSOArray symbol_table;
86-
} // namespace
88+
VDSOArray symbol_table;
8789

88-
void *get_symbol(VDSOSym sym) {
89-
// if sym is invalid, return nullptr
90-
const auto index = static_cast<size_t>(sym);
91-
if (index >= symbol_table.size())
92-
return nullptr;
90+
size_t shdr_get_symbol_count(ElfW(Shdr) * vdso_shdr, size_t e_shnum) {
91+
// iterate all sections until we locate the dynamic symbol section
92+
for (size_t i = 0; i < e_shnum; ++i) {
93+
// dynamic symbol section is a table section
94+
// therefore, the number of entries can be computed as the ratio
95+
// of the section size to the size of a single entry
96+
if (vdso_shdr[i].sh_type == SHT_DYNSYM)
97+
return vdso_shdr[i].sh_size / vdso_shdr[i].sh_entsize;
98+
}
99+
return 0;
100+
}
93101

94-
static FutexWordType once_flag = 0;
95-
callonce(reinterpret_cast<CallOnceFlag *>(&once_flag), [] {
96-
// first clear the symbol table
97-
for (auto &i : symbol_table)
98-
i = nullptr;
99-
100-
// get the address of the VDSO, protect errno since getauxval may change it
101-
int errno_backup = libc_errno;
102-
uintptr_t vdso_ehdr_addr = getauxval(AT_SYSINFO_EHDR);
103-
// Get the memory address of the vDSO ELF header.
104-
auto vdso_ehdr = reinterpret_cast<ElfW(Ehdr) *>(vdso_ehdr_addr);
105-
// leave the table unpopulated if we don't have vDSO
106-
if (vdso_ehdr == nullptr) {
107-
libc_errno = errno_backup;
108-
return;
109-
}
102+
struct VDSOSymbolTable {
103+
const char *strtab;
104+
ElfW(Sym) * symtab;
105+
ElfW(Half) * versym;
106+
Verdef *verdef;
110107

111-
// count entries
112-
size_t symbol_count = 0;
113-
// locate the section header inside the elf using the section header offset
114-
auto vdso_shdr =
115-
reinterpret_cast<ElfW(Shdr) *>(vdso_ehdr_addr + vdso_ehdr->e_shoff);
116-
// iterate all sections until we locate the dynamic symbol section
117-
for (size_t i = 0; i < vdso_ehdr->e_shnum; ++i) {
118-
if (vdso_shdr[i].sh_type == SHT_DYNSYM) {
119-
// dynamic symbol section is a table section
120-
// therefore, the number of entries can be computed as the ratio
121-
// of the section size to the size of a single entry
122-
symbol_count = vdso_shdr[i].sh_size / vdso_shdr[i].sh_entsize;
123-
break;
108+
void populate_symbol_cache(size_t symbol_count, ElfW(Addr) vdso_addr) {
109+
for (size_t i = 0; i < symbol_table.size(); ++i) {
110+
auto sym = static_cast<VDSOSym>(i);
111+
cpp::string_view name = symbol_name(sym);
112+
cpp::string_view version = symbol_version(sym);
113+
for (size_t j = 0; j < symbol_count; ++j) {
114+
if (name == strtab + symtab[j].st_name) {
115+
// we find a symbol with desired name
116+
// now we need to check if it has the right version
117+
if (versym && verdef)
118+
if (version != find_version(verdef, versym, strtab, j))
119+
continue;
120+
121+
// put the symbol address into the symbol table
122+
symbol_table[i] =
123+
reinterpret_cast<void *>(vdso_addr + symtab[j].st_value);
124+
}
124125
}
125126
}
127+
}
128+
};
126129

127-
// early return if no symbol is found
128-
if (symbol_count == 0)
129-
return;
130-
131-
// We need to find both the loadable segment and the dynamic linking of the
132-
// vDSO.
133-
auto vdso_addr = static_cast<ElfW(Addr)>(-1);
130+
struct PhdrInfo {
131+
ElfW(Addr) vdso_addr;
132+
ElfW(Dyn) * vdso_dyn;
133+
static cpp::optional<PhdrInfo> from(ElfW(Phdr) * vdso_phdr, size_t e_phnum,
134+
uintptr_t vdso_ehdr_addr) {
135+
static constexpr ElfW(Addr) INVALID_ADDR = static_cast<ElfW(Addr)>(-1);
136+
ElfW(Addr) vdso_addr = INVALID_ADDR;
134137
ElfW(Dyn) *vdso_dyn = nullptr;
135-
// compute vdso_phdr as the program header using the program header offset
136-
ElfW(Phdr) *vdso_phdr =
137-
reinterpret_cast<ElfW(Phdr) *>(vdso_ehdr_addr + vdso_ehdr->e_phoff);
138138
// iterate through all the program headers until we get the desired pieces
139-
for (size_t i = 0; i < vdso_ehdr->e_phnum; ++i) {
139+
for (size_t i = 0; i < e_phnum; ++i) {
140140
if (vdso_phdr[i].p_type == PT_DYNAMIC)
141141
vdso_dyn = reinterpret_cast<ElfW(Dyn) *>(vdso_ehdr_addr +
142142
vdso_phdr[i].p_offset);
@@ -146,14 +146,13 @@ void *get_symbol(VDSOSym sym) {
146146
vdso_ehdr_addr + vdso_phdr[i].p_offset - vdso_phdr[i].p_vaddr;
147147

148148
if (vdso_addr && vdso_dyn)
149-
break;
149+
return PhdrInfo{vdso_addr, vdso_dyn};
150150
}
151-
// early return if either the dynamic linking or the loadable segment is not
152-
// found
153-
if (vdso_dyn == nullptr || vdso_addr == static_cast<ElfW(Addr)>(-1))
154-
return;
155151

156-
// now, locate several more tables inside the dynmaic linking section
152+
return cpp::nullopt;
153+
}
154+
155+
cpp::optional<VDSOSymbolTable> populate_symbol_table() {
157156
const char *strtab = nullptr;
158157
ElfW(Sym) *symtab = nullptr;
159158
ElfW(Half) *versym = nullptr;
@@ -178,26 +177,73 @@ void *get_symbol(VDSOSym sym) {
178177
}
179178
}
180179
if (strtab == nullptr || symtab == nullptr)
181-
return;
180+
return cpp::nullopt;
182181

183-
for (size_t i = 0; i < symbol_table.size(); ++i) {
184-
for (size_t j = 0; j < symbol_count; ++j) {
185-
auto sym = static_cast<VDSOSym>(i);
186-
if (symbol_name(sym) == strtab + symtab[j].st_name) {
187-
// we find a symbol with desired name
188-
// now we need to check if it has the right version
189-
if (versym && verdef)
190-
if (symbol_version(sym) != find_version(verdef, versym, strtab, j))
191-
continue;
182+
return VDSOSymbolTable{strtab, symtab, versym, verdef};
183+
}
184+
};
192185

193-
// put the symbol address into the symbol table
194-
symbol_table[i] =
195-
reinterpret_cast<void *>(vdso_addr + symtab[j].st_value);
196-
}
197-
}
198-
}
199-
});
186+
void initialize_vdso_global_cache() {
187+
// first clear the symbol table
188+
for (auto &i : symbol_table)
189+
i = nullptr;
190+
191+
// get the address of the VDSO, protect errno since getauxval may change
192+
// it
193+
int errno_backup = libc_errno;
194+
uintptr_t vdso_ehdr_addr = getauxval(AT_SYSINFO_EHDR);
195+
// Get the memory address of the vDSO ELF header.
196+
auto vdso_ehdr = reinterpret_cast<ElfW(Ehdr) *>(vdso_ehdr_addr);
197+
// leave the table unpopulated if we don't have vDSO
198+
if (vdso_ehdr == nullptr) {
199+
libc_errno = errno_backup;
200+
return;
201+
}
200202

203+
// locate the section header inside the elf using the section header
204+
// offset
205+
auto vdso_shdr =
206+
reinterpret_cast<ElfW(Shdr) *>(vdso_ehdr_addr + vdso_ehdr->e_shoff);
207+
size_t symbol_count = shdr_get_symbol_count(vdso_shdr, vdso_ehdr->e_shnum);
208+
209+
// early return if no symbol is found
210+
if (symbol_count == 0)
211+
return;
212+
213+
// We need to find both the loadable segment and the dynamic linking of
214+
// the vDSO. compute vdso_phdr as the program header using the program
215+
// header offset
216+
ElfW(Phdr) *vdso_phdr =
217+
reinterpret_cast<ElfW(Phdr) *>(vdso_ehdr_addr + vdso_ehdr->e_phoff);
218+
cpp::optional<PhdrInfo> phdr_info =
219+
PhdrInfo::from(vdso_phdr, vdso_ehdr->e_phnum, vdso_ehdr_addr);
220+
// early return if either the dynamic linking or the loadable segment is
221+
// not found
222+
if (!phdr_info.has_value())
223+
return;
224+
225+
// now, locate several more tables inside the dynmaic linking section
226+
cpp::optional<VDSOSymbolTable> vdso_symbol_table =
227+
phdr_info->populate_symbol_table();
228+
229+
// early return if we can't find any required fields of the symbol table
230+
if (!vdso_symbol_table.has_value())
231+
return;
232+
233+
// finally, populate the global symbol table cache
234+
vdso_symbol_table->populate_symbol_cache(symbol_count, phdr_info->vdso_addr);
235+
}
236+
} // namespace
237+
238+
void *get_symbol(VDSOSym sym) {
239+
// if sym is invalid, return nullptr
240+
const auto index = static_cast<size_t>(sym);
241+
if (index >= symbol_table.size())
242+
return nullptr;
243+
244+
static FutexWordType once_flag = 0;
245+
callonce(reinterpret_cast<CallOnceFlag *>(&once_flag),
246+
initialize_vdso_global_cache);
201247
return symbol_table[index];
202248
}
203249
} // namespace vdso

0 commit comments

Comments
 (0)