1// Copyright (c) 2008, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30// ---
31// Author: Paul Pluzhnikov
32//
33// Allow dynamic symbol lookup in the kernel VDSO page.
34//
35// VDSO stands for "Virtual Dynamic Shared Object" -- a page of
36// executable code, which looks like a shared library, but doesn't
37// necessarily exist anywhere on disk, and which gets mmap()ed into
38// every process by kernels which support VDSO, such as 2.6.x for 32-bit
39// executables, and 2.6.24 and above for 64-bit executables.
40//
41// More details could be found here:
42// http://www.trilithium.com/johan/2005/08/linux-gate/
43//
44// VDSOSupport -- a class representing kernel VDSO (if present).
45//
46// Example usage:
47//  VDSOSupport vdso;
48//  VDSOSupport::SymbolInfo info;
49//  typedef (*FN)(unsigned *, void *, void *);
50//  FN fn = NULL;
51//  if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) {
52//     fn = reinterpret_cast<FN>(info.address);
53//  }
54
55#ifndef BASE_VDSO_SUPPORT_H_
56#define BASE_VDSO_SUPPORT_H_
57
58#include <config.h>
59#include "base/basictypes.h"
60#include "base/elf_mem_image.h"
61
62#ifdef HAVE_ELF_MEM_IMAGE
63
64#define HAVE_VDSO_SUPPORT 1
65
66#include <stdlib.h>     // for NULL
67
68namespace base {
69
70// NOTE: this class may be used from within tcmalloc, and can not
71// use any memory allocation routines.
72class VDSOSupport {
73 public:
74  VDSOSupport();
75
76  typedef ElfMemImage::SymbolInfo SymbolInfo;
77  typedef ElfMemImage::SymbolIterator SymbolIterator;
78
79  // Answers whether we have a vdso at all.
80  bool IsPresent() const { return image_.IsPresent(); }
81
82  // Allow to iterate over all VDSO symbols.
83  SymbolIterator begin() const { return image_.begin(); }
84  SymbolIterator end() const { return image_.end(); }
85
86  // Look up versioned dynamic symbol in the kernel VDSO.
87  // Returns false if VDSO is not present, or doesn't contain given
88  // symbol/version/type combination.
89  // If info_out != NULL, additional details are filled in.
90  bool LookupSymbol(const char *name, const char *version,
91                    int symbol_type, SymbolInfo *info_out) const;
92
93  // Find info about symbol (if any) which overlaps given address.
94  // Returns true if symbol was found; false if VDSO isn't present
95  // or doesn't have a symbol overlapping given address.
96  // If info_out != NULL, additional details are filled in.
97  bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const;
98
99  // Used only for testing. Replace real VDSO base with a mock.
100  // Returns previous value of vdso_base_. After you are done testing,
101  // you are expected to call SetBase() with previous value, in order to
102  // reset state to the way it was.
103  const void *SetBase(const void *s);
104
105  // Computes vdso_base_ and returns it. Should be called as early as
106  // possible; before any thread creation, chroot or setuid.
107  static const void *Init();
108
109 private:
110  // image_ represents VDSO ELF image in memory.
111  // image_.ehdr_ == NULL implies there is no VDSO.
112  ElfMemImage image_;
113
114  // Cached value of auxv AT_SYSINFO_EHDR, computed once.
115  // This is a tri-state:
116  //   kInvalidBase   => value hasn't been determined yet.
117  //              0   => there is no VDSO.
118  //           else   => vma of VDSO Elf{32,64}_Ehdr.
119  //
120  // When testing with mock VDSO, low bit is set.
121  // The low bit is always available because vdso_base_ is
122  // page-aligned.
123  static const void *vdso_base_;
124
125  // NOLINT on 'long' because these routines mimic kernel api.
126  // The 'cache' parameter may be used by some versions of the kernel,
127  // and should be NULL or point to a static buffer containing at
128  // least two 'long's.
129  static long InitAndGetCPU(unsigned *cpu, void *cache,     // NOLINT 'long'.
130                            void *unused);
131  static long GetCPUViaSyscall(unsigned *cpu, void *cache,  // NOLINT 'long'.
132                               void *unused);
133  typedef long (*GetCpuFn)(unsigned *cpu, void *cache,      // NOLINT 'long'.
134                           void *unused);
135
136  // This function pointer may point to InitAndGetCPU,
137  // GetCPUViaSyscall, or __vdso_getcpu at different stages of initialization.
138  static GetCpuFn getcpu_fn_;
139
140  friend int GetCPU(void);  // Needs access to getcpu_fn_.
141
142  DISALLOW_COPY_AND_ASSIGN(VDSOSupport);
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.
150int GetCPU();
151}  // namespace base
152
153#endif  // HAVE_ELF_MEM_IMAGE
154
155#endif  // BASE_VDSO_SUPPORT_H_
156