| // | |
| // Copyright 2017 The Abseil Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| // Allow dynamic symbol lookup in the kernel VDSO page. | |
| // | |
| // VDSO stands for "Virtual Dynamic Shared Object" -- a page of | |
| // executable code, which looks like a shared library, but doesn't | |
| // necessarily exist anywhere on disk, and which gets mmap()ed into | |
| // every process by kernels which support VDSO, such as 2.6.x for 32-bit | |
| // executables, and 2.6.24 and above for 64-bit executables. | |
| // | |
| // More details could be found here: | |
| // http://www.trilithium.com/johan/2005/08/linux-gate/ | |
| // | |
| // VDSOSupport -- a class representing kernel VDSO (if present). | |
| // | |
| // Example usage: | |
| // VDSOSupport vdso; | |
| // VDSOSupport::SymbolInfo info; | |
| // typedef (*FN)(unsigned *, void *, void *); | |
| // FN fn = nullptr; | |
| // if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) { | |
| // fn = reinterpret_cast<FN>(info.address); | |
| // } | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| namespace debugging_internal { | |
| // NOTE: this class may be used from within tcmalloc, and can not | |
| // use any memory allocation routines. | |
| class VDSOSupport { | |
| public: | |
| VDSOSupport(); | |
| typedef ElfMemImage::SymbolInfo SymbolInfo; | |
| typedef ElfMemImage::SymbolIterator SymbolIterator; | |
| // On PowerPC64 VDSO symbols can either be of type STT_FUNC or STT_NOTYPE | |
| // depending on how the kernel is built. The kernel is normally built with | |
| // STT_NOTYPE type VDSO symbols. Let's make things simpler first by using a | |
| // compile-time constant. | |
| enum { kVDSOSymbolType = STT_NOTYPE }; | |
| enum { kVDSOSymbolType = STT_FUNC }; | |
| // Answers whether we have a vdso at all. | |
| bool IsPresent() const { return image_.IsPresent(); } | |
| // Allow to iterate over all VDSO symbols. | |
| SymbolIterator begin() const { return image_.begin(); } | |
| SymbolIterator end() const { return image_.end(); } | |
| // Look up versioned dynamic symbol in the kernel VDSO. | |
| // Returns false if VDSO is not present, or doesn't contain given | |
| // symbol/version/type combination. | |
| // If info_out != nullptr, additional details are filled in. | |
| bool LookupSymbol(const char *name, const char *version, | |
| int symbol_type, SymbolInfo *info_out) const; | |
| // Find info about symbol (if any) which overlaps given address. | |
| // Returns true if symbol was found; false if VDSO isn't present | |
| // or doesn't have a symbol overlapping given address. | |
| // If info_out != nullptr, additional details are filled in. | |
| bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const; | |
| // Used only for testing. Replace real VDSO base with a mock. | |
| // Returns previous value of vdso_base_. After you are done testing, | |
| // you are expected to call SetBase() with previous value, in order to | |
| // reset state to the way it was. | |
| const void *SetBase(const void *s); | |
| // Computes vdso_base_ and returns it. Should be called as early as | |
| // possible; before any thread creation, chroot or setuid. | |
| static const void *Init(); | |
| private: | |
| // image_ represents VDSO ELF image in memory. | |
| // image_.ehdr_ == nullptr implies there is no VDSO. | |
| ElfMemImage image_; | |
| // Cached value of auxv AT_SYSINFO_EHDR, computed once. | |
| // This is a tri-state: | |
| // kInvalidBase => value hasn't been determined yet. | |
| // 0 => there is no VDSO. | |
| // else => vma of VDSO Elf{32,64}_Ehdr. | |
| // | |
| // When testing with mock VDSO, low bit is set. | |
| // The low bit is always available because vdso_base_ is | |
| // page-aligned. | |
| static std::atomic<const void *> vdso_base_; | |
| // NOLINT on 'long' because these routines mimic kernel api. | |
| // The 'cache' parameter may be used by some versions of the kernel, | |
| // and should be nullptr or point to a static buffer containing at | |
| // least two 'long's. | |
| static long InitAndGetCPU(unsigned *cpu, void *cache, // NOLINT 'long'. | |
| void *unused); | |
| static long GetCPUViaSyscall(unsigned *cpu, void *cache, // NOLINT 'long'. | |
| void *unused); | |
| typedef long (*GetCpuFn)(unsigned *cpu, void *cache, // NOLINT 'long'. | |
| void *unused); | |
| // This function pointer may point to InitAndGetCPU, | |
| // GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization. | |
| ABSL_CONST_INIT static std::atomic<GetCpuFn> getcpu_fn_; | |
| friend int GetCPU(void); // Needs access to getcpu_fn_. | |
| VDSOSupport(const VDSOSupport&) = delete; | |
| VDSOSupport& operator=(const VDSOSupport&) = delete; | |
| }; | |
| // Same as sched_getcpu() on later glibc versions. | |
| // Return current CPU, using (fast) __vdso_getcpu@LINUX_2.6 if present, | |
| // otherwise use syscall(SYS_getcpu,...). | |
| // May return -1 with errno == ENOSYS if the kernel doesn't | |
| // support SYS_getcpu. | |
| int GetCPU(); | |
| } // namespace debugging_internal | |
| ABSL_NAMESPACE_END | |
| } // namespace absl | |