xref: /llvm-project/libc/src/__support/OSUtil/linux/vdso.cpp (revision b659abef48efba6e380c0cc1aeae13e656368011)
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