brintos

brintos / llvm-project-archived public Read only

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