1 // 2 // Copyright 2017 The Abseil Authors. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // https://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 // Allow dynamic symbol lookup in the kernel VDSO page. 18 // 19 // VDSO stands for "Virtual Dynamic Shared Object" -- a page of 20 // executable code, which looks like a shared library, but doesn't 21 // necessarily exist anywhere on disk, and which gets mmap()ed into 22 // every process by kernels which support VDSO, such as 2.6.x for 32-bit 23 // executables, and 2.6.24 and above for 64-bit executables. 24 // 25 // More details could be found here: 26 // http://www.trilithium.com/johan/2005/08/linux-gate/ 27 // 28 // VDSOSupport -- a class representing kernel VDSO (if present). 29 // 30 // Example usage: 31 // VDSOSupport vdso; 32 // VDSOSupport::SymbolInfo info; 33 // typedef (*FN)(unsigned *, void *, void *); 34 // FN fn = nullptr; 35 // if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) { 36 // fn = reinterpret_cast<FN>(info.address); 37 // } 38 39 #ifndef ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ 40 #define ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ 41 42 #include <atomic> 43 44 #include "absl/base/attributes.h" 45 #include "absl/debugging/internal/elf_mem_image.h" 46 47 #ifdef ABSL_HAVE_ELF_MEM_IMAGE 48 49 #ifdef ABSL_HAVE_VDSO_SUPPORT 50 #error ABSL_HAVE_VDSO_SUPPORT cannot be directly set 51 #else 52 #define ABSL_HAVE_VDSO_SUPPORT 1 53 #endif 54 55 namespace absl { 56 ABSL_NAMESPACE_BEGIN 57 namespace debugging_internal { 58 59 // NOTE: this class may be used from within tcmalloc, and can not 60 // use any memory allocation routines. 61 class VDSOSupport { 62 public: 63 VDSOSupport(); 64 65 typedef ElfMemImage::SymbolInfo SymbolInfo; 66 typedef ElfMemImage::SymbolIterator SymbolIterator; 67 68 // On PowerPC64 VDSO symbols can either be of type STT_FUNC or STT_NOTYPE 69 // depending on how the kernel is built. The kernel is normally built with 70 // STT_NOTYPE type VDSO symbols. Let's make things simpler first by using a 71 // compile-time constant. 72 #ifdef __powerpc64__ 73 enum { kVDSOSymbolType = STT_NOTYPE }; 74 #else 75 enum { kVDSOSymbolType = STT_FUNC }; 76 #endif 77 78 // Answers whether we have a vdso at all. IsPresent()79 bool IsPresent() const { return image_.IsPresent(); } 80 81 // Allow to iterate over all VDSO symbols. begin()82 SymbolIterator begin() const { return image_.begin(); } end()83 SymbolIterator end() const { return image_.end(); } 84 85 // Look up versioned dynamic symbol in the kernel VDSO. 86 // Returns false if VDSO is not present, or doesn't contain given 87 // symbol/version/type combination. 88 // If info_out != nullptr, additional details are filled in. 89 bool LookupSymbol(const char *name, const char *version, 90 int symbol_type, SymbolInfo *info_out) const; 91 92 // Find info about symbol (if any) which overlaps given address. 93 // Returns true if symbol was found; false if VDSO isn't present 94 // or doesn't have a symbol overlapping given address. 95 // If info_out != nullptr, additional details are filled in. 96 bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const; 97 98 // Used only for testing. Replace real VDSO base with a mock. 99 // Returns previous value of vdso_base_. After you are done testing, 100 // you are expected to call SetBase() with previous value, in order to 101 // reset state to the way it was. 102 const void *SetBase(const void *s); 103 104 // Computes vdso_base_ and returns it. Should be called as early as 105 // possible; before any thread creation, chroot or setuid. 106 static const void *Init(); 107 108 private: 109 // image_ represents VDSO ELF image in memory. 110 // image_.ehdr_ == nullptr implies there is no VDSO. 111 ElfMemImage image_; 112 113 // Cached value of auxv AT_SYSINFO_EHDR, computed once. 114 // This is a tri-state: 115 // kInvalidBase => value hasn't been determined yet. 116 // 0 => there is no VDSO. 117 // else => vma of VDSO Elf{32,64}_Ehdr. 118 // 119 // When testing with mock VDSO, low bit is set. 120 // The low bit is always available because vdso_base_ is 121 // page-aligned. 122 static std::atomic<const void *> vdso_base_; 123 124 // NOLINT on 'long' because these routines mimic kernel api. 125 // The 'cache' parameter may be used by some versions of the kernel, 126 // and should be nullptr or point to a static buffer containing at 127 // least two 'long's. 128 static long InitAndGetCPU(unsigned *cpu, void *cache, // NOLINT 'long'. 129 void *unused); 130 static long GetCPUViaSyscall(unsigned *cpu, void *cache, // NOLINT 'long'. 131 void *unused); 132 typedef long (*GetCpuFn)(unsigned *cpu, void *cache, // NOLINT 'long'. 133 void *unused); 134 135 // This function pointer may point to InitAndGetCPU, 136 // GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization. 137 ABSL_CONST_INIT static std::atomic<GetCpuFn> getcpu_fn_; 138 139 friend int GetCPU(void); // Needs access to getcpu_fn_. 140 141 VDSOSupport(const VDSOSupport&) = delete; 142 VDSOSupport& operator=(const VDSOSupport&) = delete; 143 }; 144 145 // Same as sched_getcpu() on later glibc versions. 146 // Return current CPU, using (fast) __vdso_getcpu@LINUX_2.6 if present, 147 // otherwise use syscall(SYS_getcpu,...). 148 // May return -1 with errno == ENOSYS if the kernel doesn't 149 // support SYS_getcpu. 150 int GetCPU(); 151 152 } // namespace debugging_internal 153 ABSL_NAMESPACE_END 154 } // namespace absl 155 156 #endif // ABSL_HAVE_ELF_MEM_IMAGE 157 158 #endif // ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ 159