1d8e124dfSSchrodinger ZHU Yifan //===------------- Linux VDSO Implementation --------------------*- C++ -*-===// 2d8e124dfSSchrodinger ZHU Yifan // 3d8e124dfSSchrodinger ZHU Yifan // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4d8e124dfSSchrodinger ZHU Yifan // See https://llvm.org/LICENSE.txt for license information. 5d8e124dfSSchrodinger ZHU Yifan // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6d8e124dfSSchrodinger ZHU Yifan // 7d8e124dfSSchrodinger ZHU Yifan //===----------------------------------------------------------------------===// 8d8e124dfSSchrodinger ZHU Yifan #include "src/__support/OSUtil/linux/vdso.h" 9d8e124dfSSchrodinger ZHU Yifan #include "hdr/link_macros.h" 10d8e124dfSSchrodinger ZHU Yifan #include "hdr/sys_auxv_macros.h" 11d8e124dfSSchrodinger ZHU Yifan #include "src/__support/CPP/array.h" 12d8e124dfSSchrodinger ZHU Yifan #include "src/__support/CPP/optional.h" 13d8e124dfSSchrodinger ZHU Yifan #include "src/__support/CPP/string_view.h" 14d8e124dfSSchrodinger ZHU Yifan #include "src/__support/threads/callonce.h" 15d8e124dfSSchrodinger ZHU Yifan #include "src/__support/threads/linux/futex_word.h" 16d8e124dfSSchrodinger ZHU Yifan #include "src/errno/libc_errno.h" 17d8e124dfSSchrodinger ZHU Yifan #include "src/sys/auxv/getauxval.h" 18d8e124dfSSchrodinger ZHU Yifan #include <linux/auxvec.h> 19d8e124dfSSchrodinger ZHU Yifan 20d8e124dfSSchrodinger ZHU Yifan // TODO: This is a temporary workaround to avoid including elf.h 21d8e124dfSSchrodinger ZHU Yifan // Include our own headers for ElfW and friends once we have them. 22d8e124dfSSchrodinger ZHU Yifan namespace LIBC_NAMESPACE_DECL { 23d8e124dfSSchrodinger ZHU Yifan 24d8e124dfSSchrodinger ZHU Yifan namespace vdso { 25d8e124dfSSchrodinger ZHU Yifan 26d8e124dfSSchrodinger ZHU Yifan Symbol::VDSOArray Symbol::global_cache{}; 27d8e124dfSSchrodinger ZHU Yifan CallOnceFlag Symbol::once_flag = callonce_impl::NOT_CALLED; 28d8e124dfSSchrodinger ZHU Yifan 29d8e124dfSSchrodinger ZHU Yifan namespace { 30d8e124dfSSchrodinger ZHU Yifan // See https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/symverdefs.html 31d8e124dfSSchrodinger ZHU Yifan struct Verdaux { 32d8e124dfSSchrodinger ZHU Yifan ElfW(Word) vda_name; /* Version or dependency names */ 33d8e124dfSSchrodinger ZHU Yifan ElfW(Word) vda_next; /* Offset in bytes to next verdaux 34d8e124dfSSchrodinger ZHU Yifan entry */ 35d8e124dfSSchrodinger ZHU Yifan }; 36d8e124dfSSchrodinger ZHU Yifan struct Verdef { 37d8e124dfSSchrodinger ZHU Yifan ElfW(Half) vd_version; /* Version revision */ 38d8e124dfSSchrodinger ZHU Yifan ElfW(Half) vd_flags; /* Version information */ 39d8e124dfSSchrodinger ZHU Yifan ElfW(Half) vd_ndx; /* Version Index */ 40d8e124dfSSchrodinger ZHU Yifan ElfW(Half) vd_cnt; /* Number of associated aux entries */ 41d8e124dfSSchrodinger ZHU Yifan ElfW(Word) vd_hash; /* Version name hash value */ 42d8e124dfSSchrodinger ZHU Yifan ElfW(Word) vd_aux; /* Offset in bytes to verdaux array */ 43d8e124dfSSchrodinger ZHU Yifan ElfW(Word) vd_next; /* Offset in bytes to next verdef entry */ 44d8e124dfSSchrodinger ZHU Yifan Verdef *next() const { 45d8e124dfSSchrodinger ZHU Yifan if (vd_next == 0) 46d8e124dfSSchrodinger ZHU Yifan return nullptr; 47d8e124dfSSchrodinger ZHU Yifan return reinterpret_cast<Verdef *>(reinterpret_cast<uintptr_t>(this) + 48d8e124dfSSchrodinger ZHU Yifan vd_next); 49d8e124dfSSchrodinger ZHU Yifan } 50d8e124dfSSchrodinger ZHU Yifan Verdaux *aux() const { 51d8e124dfSSchrodinger ZHU Yifan return reinterpret_cast<Verdaux *>(reinterpret_cast<uintptr_t>(this) + 52d8e124dfSSchrodinger ZHU Yifan vd_aux); 53d8e124dfSSchrodinger ZHU Yifan } 54d8e124dfSSchrodinger ZHU Yifan }; 55d8e124dfSSchrodinger ZHU Yifan 56d8e124dfSSchrodinger ZHU Yifan // version search procedure specified by 57d8e124dfSSchrodinger ZHU Yifan // https://refspecs.linuxfoundation.org/LSB_1.3.0/gLSB/gLSB/symversion.html#SYMVERTBL 58d8e124dfSSchrodinger ZHU Yifan cpp::string_view find_version(Verdef *verdef, ElfW(Half) * versym, 59d8e124dfSSchrodinger ZHU Yifan const char *strtab, size_t idx) { 60*b659abefSlntue #ifndef VER_FLG_BASE 61d8e124dfSSchrodinger ZHU Yifan constexpr ElfW(Half) VER_FLG_BASE = 0x1; 62*b659abefSlntue #endif 63d8e124dfSSchrodinger ZHU Yifan if (!versym) 64d8e124dfSSchrodinger ZHU Yifan return ""; 65d8e124dfSSchrodinger ZHU Yifan ElfW(Half) identifier = versym[idx] & 0x7FFF; 66d8e124dfSSchrodinger ZHU Yifan // iterate through all version definitions 67d8e124dfSSchrodinger ZHU Yifan for (Verdef *def = verdef; def != nullptr; def = def->next()) { 68d8e124dfSSchrodinger ZHU Yifan // skip if this is a file-level version 69d8e124dfSSchrodinger ZHU Yifan if (def->vd_flags & VER_FLG_BASE) 70d8e124dfSSchrodinger ZHU Yifan continue; 71d8e124dfSSchrodinger ZHU Yifan // check if the version identifier matches. Highest bit is used to determine 72d8e124dfSSchrodinger ZHU Yifan // whether the symbol is local. Only lower 15 bits are used for version 73d8e124dfSSchrodinger ZHU Yifan // identifier. 74d8e124dfSSchrodinger ZHU Yifan if ((def->vd_ndx & 0x7FFF) == identifier) { 75d8e124dfSSchrodinger ZHU Yifan Verdaux *aux = def->aux(); 76d8e124dfSSchrodinger ZHU Yifan return strtab + aux->vda_name; 77d8e124dfSSchrodinger ZHU Yifan } 78d8e124dfSSchrodinger ZHU Yifan } 79d8e124dfSSchrodinger ZHU Yifan return ""; 80d8e124dfSSchrodinger ZHU Yifan } 81d8e124dfSSchrodinger ZHU Yifan 82d8e124dfSSchrodinger ZHU Yifan size_t shdr_get_symbol_count(ElfW(Shdr) * vdso_shdr, size_t e_shnum) { 83d8e124dfSSchrodinger ZHU Yifan if (!vdso_shdr) 84d8e124dfSSchrodinger ZHU Yifan return 0; 85d8e124dfSSchrodinger ZHU Yifan // iterate all sections until we locate the dynamic symbol section 86d8e124dfSSchrodinger ZHU Yifan for (size_t i = 0; i < e_shnum; ++i) { 87d8e124dfSSchrodinger ZHU Yifan // dynamic symbol section is a table section 88d8e124dfSSchrodinger ZHU Yifan // therefore, the number of entries can be computed as the ratio 89d8e124dfSSchrodinger ZHU Yifan // of the section size to the size of a single entry 90d8e124dfSSchrodinger ZHU Yifan if (vdso_shdr[i].sh_type == SHT_DYNSYM) 91d8e124dfSSchrodinger ZHU Yifan return vdso_shdr[i].sh_size / vdso_shdr[i].sh_entsize; 92d8e124dfSSchrodinger ZHU Yifan } 93d8e124dfSSchrodinger ZHU Yifan return 0; 94d8e124dfSSchrodinger ZHU Yifan } 95d8e124dfSSchrodinger ZHU Yifan 96d8e124dfSSchrodinger ZHU Yifan struct VDSOSymbolTable { 97d8e124dfSSchrodinger ZHU Yifan const char *strtab; 98d8e124dfSSchrodinger ZHU Yifan ElfW(Sym) * symtab; 99d8e124dfSSchrodinger ZHU Yifan // The following can be nullptr if the vDSO does not have versioning 100d8e124dfSSchrodinger ZHU Yifan ElfW(Half) * versym; 101d8e124dfSSchrodinger ZHU Yifan Verdef *verdef; 102d8e124dfSSchrodinger ZHU Yifan 103d8e124dfSSchrodinger ZHU Yifan void populate_symbol_cache(Symbol::VDSOArray &symbol_table, 104d8e124dfSSchrodinger ZHU Yifan size_t symbol_count, ElfW(Addr) vdso_addr) { 105d8e124dfSSchrodinger ZHU Yifan for (size_t i = 0, e = symbol_table.size(); i < e; ++i) { 106d8e124dfSSchrodinger ZHU Yifan Symbol sym = i; 107d8e124dfSSchrodinger ZHU Yifan cpp::string_view name = sym.name(); 108d8e124dfSSchrodinger ZHU Yifan cpp::string_view version = sym.version(); 109d8e124dfSSchrodinger ZHU Yifan if (name.empty()) 110d8e124dfSSchrodinger ZHU Yifan continue; 111d8e124dfSSchrodinger ZHU Yifan 112d8e124dfSSchrodinger ZHU Yifan for (size_t j = 0; j < symbol_count; ++j) { 113d8e124dfSSchrodinger ZHU Yifan if (name == strtab + symtab[j].st_name) { 114d8e124dfSSchrodinger ZHU Yifan // we find a symbol with desired name 115d8e124dfSSchrodinger ZHU Yifan // now we need to check if it has the right version 116d8e124dfSSchrodinger ZHU Yifan if (versym && verdef && 117d8e124dfSSchrodinger ZHU Yifan version != find_version(verdef, versym, strtab, j)) 118d8e124dfSSchrodinger ZHU Yifan continue; 119d8e124dfSSchrodinger ZHU Yifan 120d8e124dfSSchrodinger ZHU Yifan // put the symbol address into the symbol table 121d8e124dfSSchrodinger ZHU Yifan symbol_table[i] = 122d8e124dfSSchrodinger ZHU Yifan reinterpret_cast<void *>(vdso_addr + symtab[j].st_value); 123d8e124dfSSchrodinger ZHU Yifan } 124d8e124dfSSchrodinger ZHU Yifan } 125d8e124dfSSchrodinger ZHU Yifan } 126d8e124dfSSchrodinger ZHU Yifan } 127d8e124dfSSchrodinger ZHU Yifan }; 128d8e124dfSSchrodinger ZHU Yifan 129d8e124dfSSchrodinger ZHU Yifan struct PhdrInfo { 130d8e124dfSSchrodinger ZHU Yifan ElfW(Addr) vdso_addr; 131d8e124dfSSchrodinger ZHU Yifan ElfW(Dyn) * vdso_dyn; 132d8e124dfSSchrodinger ZHU Yifan static cpp::optional<PhdrInfo> from(ElfW(Phdr) * vdso_phdr, size_t e_phnum, 133d8e124dfSSchrodinger ZHU Yifan uintptr_t vdso_ehdr_addr) { 134d8e124dfSSchrodinger ZHU Yifan constexpr ElfW(Addr) INVALID_ADDR = static_cast<ElfW(Addr)>(-1); 135d8e124dfSSchrodinger ZHU Yifan ElfW(Addr) vdso_addr = INVALID_ADDR; 136d8e124dfSSchrodinger ZHU Yifan ElfW(Dyn) *vdso_dyn = nullptr; 137d8e124dfSSchrodinger ZHU Yifan if (!vdso_phdr) 138d8e124dfSSchrodinger ZHU Yifan return cpp::nullopt; 139d8e124dfSSchrodinger ZHU Yifan // iterate through all the program headers until we get the desired pieces 140d8e124dfSSchrodinger ZHU Yifan for (size_t i = 0; i < e_phnum; ++i) { 141d8e124dfSSchrodinger ZHU Yifan if (vdso_phdr[i].p_type == PT_DYNAMIC) 142d8e124dfSSchrodinger ZHU Yifan vdso_dyn = reinterpret_cast<ElfW(Dyn) *>(vdso_ehdr_addr + 143d8e124dfSSchrodinger ZHU Yifan vdso_phdr[i].p_offset); 144d8e124dfSSchrodinger ZHU Yifan 145d8e124dfSSchrodinger ZHU Yifan if (vdso_phdr[i].p_type == PT_LOAD) 146d8e124dfSSchrodinger ZHU Yifan vdso_addr = 147d8e124dfSSchrodinger ZHU Yifan vdso_ehdr_addr + vdso_phdr[i].p_offset - vdso_phdr[i].p_vaddr; 148d8e124dfSSchrodinger ZHU Yifan 149d8e124dfSSchrodinger ZHU Yifan if (vdso_addr && vdso_dyn) 150d8e124dfSSchrodinger ZHU Yifan return PhdrInfo{vdso_addr, vdso_dyn}; 151d8e124dfSSchrodinger ZHU Yifan } 152d8e124dfSSchrodinger ZHU Yifan 153d8e124dfSSchrodinger ZHU Yifan return cpp::nullopt; 154d8e124dfSSchrodinger ZHU Yifan } 155d8e124dfSSchrodinger ZHU Yifan 156d8e124dfSSchrodinger ZHU Yifan cpp::optional<VDSOSymbolTable> populate_symbol_table() { 157d8e124dfSSchrodinger ZHU Yifan const char *strtab = nullptr; 158d8e124dfSSchrodinger ZHU Yifan ElfW(Sym) *symtab = nullptr; 159d8e124dfSSchrodinger ZHU Yifan ElfW(Half) *versym = nullptr; 160d8e124dfSSchrodinger ZHU Yifan Verdef *verdef = nullptr; 161d8e124dfSSchrodinger ZHU Yifan for (ElfW(Dyn) *d = vdso_dyn; d->d_tag != DT_NULL; ++d) { 162d8e124dfSSchrodinger ZHU Yifan switch (d->d_tag) { 163d8e124dfSSchrodinger ZHU Yifan case DT_STRTAB: 164d8e124dfSSchrodinger ZHU Yifan strtab = reinterpret_cast<const char *>(vdso_addr + d->d_un.d_ptr); 165d8e124dfSSchrodinger ZHU Yifan break; 166d8e124dfSSchrodinger ZHU Yifan case DT_SYMTAB: 167d8e124dfSSchrodinger ZHU Yifan symtab = reinterpret_cast<ElfW(Sym) *>(vdso_addr + d->d_un.d_ptr); 168d8e124dfSSchrodinger ZHU Yifan break; 169d8e124dfSSchrodinger ZHU Yifan case DT_VERSYM: 170d8e124dfSSchrodinger ZHU Yifan versym = reinterpret_cast<uint16_t *>(vdso_addr + d->d_un.d_ptr); 171d8e124dfSSchrodinger ZHU Yifan break; 172d8e124dfSSchrodinger ZHU Yifan case DT_VERDEF: 173d8e124dfSSchrodinger ZHU Yifan verdef = reinterpret_cast<Verdef *>(vdso_addr + d->d_un.d_ptr); 174d8e124dfSSchrodinger ZHU Yifan break; 175d8e124dfSSchrodinger ZHU Yifan } 176d8e124dfSSchrodinger ZHU Yifan if (strtab && symtab && versym && verdef) 177d8e124dfSSchrodinger ZHU Yifan break; 178d8e124dfSSchrodinger ZHU Yifan } 179d8e124dfSSchrodinger ZHU Yifan if (strtab == nullptr || symtab == nullptr) 180d8e124dfSSchrodinger ZHU Yifan return cpp::nullopt; 181d8e124dfSSchrodinger ZHU Yifan 182d8e124dfSSchrodinger ZHU Yifan return VDSOSymbolTable{strtab, symtab, versym, verdef}; 183d8e124dfSSchrodinger ZHU Yifan } 184d8e124dfSSchrodinger ZHU Yifan }; 185d8e124dfSSchrodinger ZHU Yifan } // namespace 186d8e124dfSSchrodinger ZHU Yifan 187d8e124dfSSchrodinger ZHU Yifan void Symbol::initialize_vdso_global_cache() { 188d8e124dfSSchrodinger ZHU Yifan // first clear the symbol table 189d8e124dfSSchrodinger ZHU Yifan for (auto &i : global_cache) 190d8e124dfSSchrodinger ZHU Yifan i = nullptr; 191d8e124dfSSchrodinger ZHU Yifan 192d8e124dfSSchrodinger ZHU Yifan // get the address of the VDSO, protect errno since getauxval may change 193d8e124dfSSchrodinger ZHU Yifan // it 194d8e124dfSSchrodinger ZHU Yifan int errno_backup = libc_errno; 195d8e124dfSSchrodinger ZHU Yifan uintptr_t vdso_ehdr_addr = getauxval(AT_SYSINFO_EHDR); 196d8e124dfSSchrodinger ZHU Yifan // Get the memory address of the vDSO ELF header. 197d8e124dfSSchrodinger ZHU Yifan auto vdso_ehdr = reinterpret_cast<ElfW(Ehdr) *>(vdso_ehdr_addr); 198d8e124dfSSchrodinger ZHU Yifan // leave the table unpopulated if we don't have vDSO 199d8e124dfSSchrodinger ZHU Yifan if (vdso_ehdr == nullptr) { 200d8e124dfSSchrodinger ZHU Yifan libc_errno = errno_backup; 201d8e124dfSSchrodinger ZHU Yifan return; 202d8e124dfSSchrodinger ZHU Yifan } 203d8e124dfSSchrodinger ZHU Yifan 204d8e124dfSSchrodinger ZHU Yifan // locate the section header inside the elf using the section header 205d8e124dfSSchrodinger ZHU Yifan // offset 206d8e124dfSSchrodinger ZHU Yifan auto vdso_shdr = 207d8e124dfSSchrodinger ZHU Yifan reinterpret_cast<ElfW(Shdr) *>(vdso_ehdr_addr + vdso_ehdr->e_shoff); 208d8e124dfSSchrodinger ZHU Yifan size_t symbol_count = shdr_get_symbol_count(vdso_shdr, vdso_ehdr->e_shnum); 209d8e124dfSSchrodinger ZHU Yifan 210d8e124dfSSchrodinger ZHU Yifan // early return if no symbol is found 211d8e124dfSSchrodinger ZHU Yifan if (symbol_count == 0) 212d8e124dfSSchrodinger ZHU Yifan return; 213d8e124dfSSchrodinger ZHU Yifan 214d8e124dfSSchrodinger ZHU Yifan // We need to find both the loadable segment and the dynamic linking of 215d8e124dfSSchrodinger ZHU Yifan // the vDSO. compute vdso_phdr as the program header using the program 216d8e124dfSSchrodinger ZHU Yifan // header offset 217d8e124dfSSchrodinger ZHU Yifan ElfW(Phdr) *vdso_phdr = 218d8e124dfSSchrodinger ZHU Yifan reinterpret_cast<ElfW(Phdr) *>(vdso_ehdr_addr + vdso_ehdr->e_phoff); 219d8e124dfSSchrodinger ZHU Yifan cpp::optional<PhdrInfo> phdr_info = 220d8e124dfSSchrodinger ZHU Yifan PhdrInfo::from(vdso_phdr, vdso_ehdr->e_phnum, vdso_ehdr_addr); 221d8e124dfSSchrodinger ZHU Yifan // early return if either the dynamic linking or the loadable segment is 222d8e124dfSSchrodinger ZHU Yifan // not found 223d8e124dfSSchrodinger ZHU Yifan if (!phdr_info.has_value()) 224d8e124dfSSchrodinger ZHU Yifan return; 225d8e124dfSSchrodinger ZHU Yifan 226d8e124dfSSchrodinger ZHU Yifan // now, locate several more tables inside the dynmaic linking section 227d8e124dfSSchrodinger ZHU Yifan cpp::optional<VDSOSymbolTable> vdso_symbol_table = 228d8e124dfSSchrodinger ZHU Yifan phdr_info->populate_symbol_table(); 229d8e124dfSSchrodinger ZHU Yifan 230d8e124dfSSchrodinger ZHU Yifan // early return if we can't find any required fields of the symbol table 231d8e124dfSSchrodinger ZHU Yifan if (!vdso_symbol_table.has_value()) 232d8e124dfSSchrodinger ZHU Yifan return; 233d8e124dfSSchrodinger ZHU Yifan 234d8e124dfSSchrodinger ZHU Yifan // finally, populate the global symbol table cache 235d8e124dfSSchrodinger ZHU Yifan vdso_symbol_table->populate_symbol_cache(global_cache, symbol_count, 236d8e124dfSSchrodinger ZHU Yifan phdr_info->vdso_addr); 237d8e124dfSSchrodinger ZHU Yifan } 238d8e124dfSSchrodinger ZHU Yifan } // namespace vdso 239d8e124dfSSchrodinger ZHU Yifan } // namespace LIBC_NAMESPACE_DECL 240