vdso_support.cc
Go to the documentation of this file.
00001 // Copyright 2017 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 // Allow dynamic symbol lookup in the kernel VDSO page.
00016 //
00017 // VDSOSupport -- a class representing kernel VDSO (if present).
00018 
00019 #include "absl/debugging/internal/vdso_support.h"
00020 
00021 #ifdef ABSL_HAVE_VDSO_SUPPORT     // defined in vdso_support.h
00022 
00023 #include <errno.h>
00024 #include <fcntl.h>
00025 #include <sys/syscall.h>
00026 #include <unistd.h>
00027 
00028 #if __GLIBC_PREREQ(2, 16)  // GLIBC-2.16 implements getauxval.
00029 #include <sys/auxv.h>
00030 #endif
00031 
00032 #include "absl/base/dynamic_annotations.h"
00033 #include "absl/base/internal/raw_logging.h"
00034 #include "absl/base/port.h"
00035 
00036 #ifndef AT_SYSINFO_EHDR
00037 #define AT_SYSINFO_EHDR 33  // for crosstoolv10
00038 #endif
00039 
00040 namespace absl {
00041 namespace debugging_internal {
00042 
00043 ABSL_CONST_INIT
00044 std::atomic<const void *> VDSOSupport::vdso_base_(
00045     debugging_internal::ElfMemImage::kInvalidBase);
00046 
00047 std::atomic<VDSOSupport::GetCpuFn> VDSOSupport::getcpu_fn_(&InitAndGetCPU);
00048 VDSOSupport::VDSOSupport()
00049     // If vdso_base_ is still set to kInvalidBase, we got here
00050     // before VDSOSupport::Init has been called. Call it now.
00051     : image_(vdso_base_.load(std::memory_order_relaxed) ==
00052                      debugging_internal::ElfMemImage::kInvalidBase
00053                  ? Init()
00054                  : vdso_base_.load(std::memory_order_relaxed)) {}
00055 
00056 // NOTE: we can't use GoogleOnceInit() below, because we can be
00057 // called by tcmalloc, and none of the *once* stuff may be functional yet.
00058 //
00059 // In addition, we hope that the VDSOSupportHelper constructor
00060 // causes this code to run before there are any threads, and before
00061 // InitGoogle() has executed any chroot or setuid calls.
00062 //
00063 // Finally, even if there is a race here, it is harmless, because
00064 // the operation should be idempotent.
00065 const void *VDSOSupport::Init() {
00066   const auto kInvalidBase = debugging_internal::ElfMemImage::kInvalidBase;
00067 #if __GLIBC_PREREQ(2, 16)
00068   if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
00069     errno = 0;
00070     const void *const sysinfo_ehdr =
00071         reinterpret_cast<const void *>(getauxval(AT_SYSINFO_EHDR));
00072     if (errno == 0) {
00073       vdso_base_.store(sysinfo_ehdr, std::memory_order_relaxed);
00074     }
00075   }
00076 #endif  // __GLIBC_PREREQ(2, 16)
00077   if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
00078     // Valgrind zaps AT_SYSINFO_EHDR and friends from the auxv[]
00079     // on stack, and so glibc works as if VDSO was not present.
00080     // But going directly to kernel via /proc/self/auxv below bypasses
00081     // Valgrind zapping. So we check for Valgrind separately.
00082     if (RunningOnValgrind()) {
00083       vdso_base_.store(nullptr, std::memory_order_relaxed);
00084       getcpu_fn_.store(&GetCPUViaSyscall, std::memory_order_relaxed);
00085       return nullptr;
00086     }
00087     int fd = open("/proc/self/auxv", O_RDONLY);
00088     if (fd == -1) {
00089       // Kernel too old to have a VDSO.
00090       vdso_base_.store(nullptr, std::memory_order_relaxed);
00091       getcpu_fn_.store(&GetCPUViaSyscall, std::memory_order_relaxed);
00092       return nullptr;
00093     }
00094     ElfW(auxv_t) aux;
00095     while (read(fd, &aux, sizeof(aux)) == sizeof(aux)) {
00096       if (aux.a_type == AT_SYSINFO_EHDR) {
00097         vdso_base_.store(reinterpret_cast<void *>(aux.a_un.a_val),
00098                          std::memory_order_relaxed);
00099         break;
00100       }
00101     }
00102     close(fd);
00103     if (vdso_base_.load(std::memory_order_relaxed) == kInvalidBase) {
00104       // Didn't find AT_SYSINFO_EHDR in auxv[].
00105       vdso_base_.store(nullptr, std::memory_order_relaxed);
00106     }
00107   }
00108   GetCpuFn fn = &GetCPUViaSyscall;  // default if VDSO not present.
00109   if (vdso_base_.load(std::memory_order_relaxed)) {
00110     VDSOSupport vdso;
00111     SymbolInfo info;
00112     if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) {
00113       fn = reinterpret_cast<GetCpuFn>(const_cast<void *>(info.address));
00114     }
00115   }
00116   // Subtle: this code runs outside of any locks; prevent compiler
00117   // from assigning to getcpu_fn_ more than once.
00118   getcpu_fn_.store(fn, std::memory_order_relaxed);
00119   return vdso_base_.load(std::memory_order_relaxed);
00120 }
00121 
00122 const void *VDSOSupport::SetBase(const void *base) {
00123   ABSL_RAW_CHECK(base != debugging_internal::ElfMemImage::kInvalidBase,
00124                  "internal error");
00125   const void *old_base = vdso_base_.load(std::memory_order_relaxed);
00126   vdso_base_.store(base, std::memory_order_relaxed);
00127   image_.Init(base);
00128   // Also reset getcpu_fn_, so GetCPU could be tested with simulated VDSO.
00129   getcpu_fn_.store(&InitAndGetCPU, std::memory_order_relaxed);
00130   return old_base;
00131 }
00132 
00133 bool VDSOSupport::LookupSymbol(const char *name,
00134                                const char *version,
00135                                int type,
00136                                SymbolInfo *info) const {
00137   return image_.LookupSymbol(name, version, type, info);
00138 }
00139 
00140 bool VDSOSupport::LookupSymbolByAddress(const void *address,
00141                                         SymbolInfo *info_out) const {
00142   return image_.LookupSymbolByAddress(address, info_out);
00143 }
00144 
00145 // NOLINT on 'long' because this routine mimics kernel api.
00146 long VDSOSupport::GetCPUViaSyscall(unsigned *cpu,  // NOLINT(runtime/int)
00147                                    void *, void *) {
00148 #ifdef SYS_getcpu
00149   return syscall(SYS_getcpu, cpu, nullptr, nullptr);
00150 #else
00151   // x86_64 never implemented sys_getcpu(), except as a VDSO call.
00152   static_cast<void>(cpu);  // Avoid an unused argument compiler warning.
00153   errno = ENOSYS;
00154   return -1;
00155 #endif
00156 }
00157 
00158 // Use fast __vdso_getcpu if available.
00159 long VDSOSupport::InitAndGetCPU(unsigned *cpu,  // NOLINT(runtime/int)
00160                                 void *x, void *y) {
00161   Init();
00162   GetCpuFn fn = getcpu_fn_.load(std::memory_order_relaxed);
00163   ABSL_RAW_CHECK(fn != &InitAndGetCPU, "Init() did not set getcpu_fn_");
00164   return (*fn)(cpu, x, y);
00165 }
00166 
00167 // This function must be very fast, and may be called from very
00168 // low level (e.g. tcmalloc). Hence I avoid things like
00169 // GoogleOnceInit() and ::operator new.
00170 ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY
00171 int GetCPU() {
00172   unsigned cpu;
00173   int ret_code = (*VDSOSupport::getcpu_fn_)(&cpu, nullptr, nullptr);
00174   return ret_code == 0 ? cpu : ret_code;
00175 }
00176 
00177 // We need to make sure VDSOSupport::Init() is called before
00178 // InitGoogle() does any setuid or chroot calls.  If VDSOSupport
00179 // is used in any global constructor, this will happen, since
00180 // VDSOSupport's constructor calls Init.  But if not, we need to
00181 // ensure it here, with a global constructor of our own.  This
00182 // is an allowed exception to the normal rule against non-trivial
00183 // global constructors.
00184 static class VDSOInitHelper {
00185  public:
00186   VDSOInitHelper() { VDSOSupport::Init(); }
00187 } vdso_init_helper;
00188 
00189 }  // namespace debugging_internal
00190 }  // namespace absl
00191 
00192 #endif  // ABSL_HAVE_VDSO_SUPPORT


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:16