7
7
// ===----------------------------------------------------------------------===//
8
8
#include " src/__support/OSUtil/linux/vdso.h"
9
9
#include " src/__support/CPP/array.h"
10
+ #include " src/__support/CPP/optional.h"
10
11
#include " src/__support/CPP/string_view.h"
11
12
#include " src/__support/threads/callonce.h"
12
13
#include " src/__support/threads/linux/futex_word.h"
@@ -70,7 +71,9 @@ cpp::string_view find_version(Verdef *verdef, ElfW(Half) * versym,
70
71
// skip if this is a file-level version
71
72
if (def->vd_flags & VER_FLG_BASE)
72
73
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.
74
77
if ((def->vd_ndx & 0x7FFF ) == identifier) {
75
78
Verdaux *aux = def->aux ();
76
79
return strtab + aux->vda_name ;
@@ -82,61 +85,58 @@ cpp::string_view find_version(Verdef *verdef, ElfW(Half) * versym,
82
85
using VDSOArray =
83
86
cpp::array<void *, static_cast <size_t >(VDSOSym::VDSOSymCount)>;
84
87
85
- static VDSOArray symbol_table;
86
- } // namespace
88
+ VDSOArray symbol_table;
87
89
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
+ }
93
101
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;
110
107
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
+ }
124
125
}
125
126
}
127
+ }
128
+ };
126
129
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 ;
134
137
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 );
138
138
// 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) {
140
140
if (vdso_phdr[i].p_type == PT_DYNAMIC)
141
141
vdso_dyn = reinterpret_cast <ElfW (Dyn) *>(vdso_ehdr_addr +
142
142
vdso_phdr[i].p_offset );
@@ -146,14 +146,13 @@ void *get_symbol(VDSOSym sym) {
146
146
vdso_ehdr_addr + vdso_phdr[i].p_offset - vdso_phdr[i].p_vaddr ;
147
147
148
148
if (vdso_addr && vdso_dyn)
149
- break ;
149
+ return PhdrInfo{vdso_addr, vdso_dyn} ;
150
150
}
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 ;
155
151
156
- // now, locate several more tables inside the dynmaic linking section
152
+ return cpp::nullopt;
153
+ }
154
+
155
+ cpp::optional<VDSOSymbolTable> populate_symbol_table () {
157
156
const char *strtab = nullptr ;
158
157
ElfW (Sym) *symtab = nullptr ;
159
158
ElfW (Half) *versym = nullptr ;
@@ -178,26 +177,73 @@ void *get_symbol(VDSOSym sym) {
178
177
}
179
178
}
180
179
if (strtab == nullptr || symtab == nullptr )
181
- return ;
180
+ return cpp::nullopt ;
182
181
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
+ };
192
185
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
+ }
200
202
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);
201
247
return symbol_table[index];
202
248
}
203
249
} // namespace vdso
0 commit comments