00001 // 00002 // Copyright 2017 The Abseil Authors. 00003 // 00004 // Licensed under the Apache License, Version 2.0 (the "License"); 00005 // you may not use this file except in compliance with the License. 00006 // You may obtain a copy of the License at 00007 // 00008 // https://www.apache.org/licenses/LICENSE-2.0 00009 // 00010 // Unless required by applicable law or agreed to in writing, software 00011 // distributed under the License is distributed on an "AS IS" BASIS, 00012 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 // See the License for the specific language governing permissions and 00014 // limitations under the License. 00015 // 00016 00017 // Allow dynamic symbol lookup in the kernel VDSO page. 00018 // 00019 // VDSO stands for "Virtual Dynamic Shared Object" -- a page of 00020 // executable code, which looks like a shared library, but doesn't 00021 // necessarily exist anywhere on disk, and which gets mmap()ed into 00022 // every process by kernels which support VDSO, such as 2.6.x for 32-bit 00023 // executables, and 2.6.24 and above for 64-bit executables. 00024 // 00025 // More details could be found here: 00026 // http://www.trilithium.com/johan/2005/08/linux-gate/ 00027 // 00028 // VDSOSupport -- a class representing kernel VDSO (if present). 00029 // 00030 // Example usage: 00031 // VDSOSupport vdso; 00032 // VDSOSupport::SymbolInfo info; 00033 // typedef (*FN)(unsigned *, void *, void *); 00034 // FN fn = nullptr; 00035 // if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) { 00036 // fn = reinterpret_cast<FN>(info.address); 00037 // } 00038 00039 #ifndef ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ 00040 #define ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_ 00041 00042 #include <atomic> 00043 00044 #include "absl/base/attributes.h" 00045 #include "absl/debugging/internal/elf_mem_image.h" 00046 00047 #ifdef ABSL_HAVE_ELF_MEM_IMAGE 00048 00049 #ifdef ABSL_HAVE_VDSO_SUPPORT 00050 #error ABSL_HAVE_VDSO_SUPPORT cannot be directly set 00051 #else 00052 #define ABSL_HAVE_VDSO_SUPPORT 1 00053 #endif 00054 00055 namespace absl { 00056 namespace debugging_internal { 00057 00058 // NOTE: this class may be used from within tcmalloc, and can not 00059 // use any memory allocation routines. 00060 class VDSOSupport { 00061 public: 00062 VDSOSupport(); 00063 00064 typedef ElfMemImage::SymbolInfo SymbolInfo; 00065 typedef ElfMemImage::SymbolIterator SymbolIterator; 00066 00067 // On PowerPC64 VDSO symbols can either be of type STT_FUNC or STT_NOTYPE 00068 // depending on how the kernel is built. The kernel is normally built with 00069 // STT_NOTYPE type VDSO symbols. Let's make things simpler first by using a 00070 // compile-time constant. 00071 #ifdef __powerpc64__ 00072 enum { kVDSOSymbolType = STT_NOTYPE }; 00073 #else 00074 enum { kVDSOSymbolType = STT_FUNC }; 00075 #endif 00076 00077 // Answers whether we have a vdso at all. 00078 bool IsPresent() const { return image_.IsPresent(); } 00079 00080 // Allow to iterate over all VDSO symbols. 00081 SymbolIterator begin() const { return image_.begin(); } 00082 SymbolIterator end() const { return image_.end(); } 00083 00084 // Look up versioned dynamic symbol in the kernel VDSO. 00085 // Returns false if VDSO is not present, or doesn't contain given 00086 // symbol/version/type combination. 00087 // If info_out != nullptr, additional details are filled in. 00088 bool LookupSymbol(const char *name, const char *version, 00089 int symbol_type, SymbolInfo *info_out) const; 00090 00091 // Find info about symbol (if any) which overlaps given address. 00092 // Returns true if symbol was found; false if VDSO isn't present 00093 // or doesn't have a symbol overlapping given address. 00094 // If info_out != nullptr, additional details are filled in. 00095 bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const; 00096 00097 // Used only for testing. Replace real VDSO base with a mock. 00098 // Returns previous value of vdso_base_. After you are done testing, 00099 // you are expected to call SetBase() with previous value, in order to 00100 // reset state to the way it was. 00101 const void *SetBase(const void *s); 00102 00103 // Computes vdso_base_ and returns it. Should be called as early as 00104 // possible; before any thread creation, chroot or setuid. 00105 static const void *Init(); 00106 00107 private: 00108 // image_ represents VDSO ELF image in memory. 00109 // image_.ehdr_ == nullptr implies there is no VDSO. 00110 ElfMemImage image_; 00111 00112 // Cached value of auxv AT_SYSINFO_EHDR, computed once. 00113 // This is a tri-state: 00114 // kInvalidBase => value hasn't been determined yet. 00115 // 0 => there is no VDSO. 00116 // else => vma of VDSO Elf{32,64}_Ehdr. 00117 // 00118 // When testing with mock VDSO, low bit is set. 00119 // The low bit is always available because vdso_base_ is 00120 // page-aligned. 00121 static std::atomic<const void *> vdso_base_; 00122 00123 // NOLINT on 'long' because these routines mimic kernel api. 00124 // The 'cache' parameter may be used by some versions of the kernel, 00125 // and should be nullptr or point to a static buffer containing at 00126 // least two 'long's. 00127 static long InitAndGetCPU(unsigned *cpu, void *cache, // NOLINT 'long'. 00128 void *unused); 00129 static long GetCPUViaSyscall(unsigned *cpu, void *cache, // NOLINT 'long'. 00130 void *unused); 00131 typedef long (*GetCpuFn)(unsigned *cpu, void *cache, // NOLINT 'long'. 00132 void *unused); 00133 00134 // This function pointer may point to InitAndGetCPU, 00135 // GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization. 00136 ABSL_CONST_INIT static std::atomic<GetCpuFn> getcpu_fn_; 00137 00138 friend int GetCPU(void); // Needs access to getcpu_fn_. 00139 00140 VDSOSupport(const VDSOSupport&) = delete; 00141 VDSOSupport& operator=(const VDSOSupport&) = delete; 00142 }; 00143 00144 // Same as sched_getcpu() on later glibc versions. 00145 // Return current CPU, using (fast) __vdso_getcpu@LINUX_2.6 if present, 00146 // otherwise use syscall(SYS_getcpu,...). 00147 // May return -1 with errno == ENOSYS if the kernel doesn't 00148 // support SYS_getcpu. 00149 int GetCPU(); 00150 00151 } // namespace debugging_internal 00152 } // namespace absl 00153 00154 #endif // ABSL_HAVE_ELF_MEM_IMAGE 00155 00156 #endif // ABSL_DEBUGGING_INTERNAL_VDSO_SUPPORT_H_