1//===-- interception_linux.cpp ----------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file is a part of AddressSanitizer, an address sanity checker.
10//
11// Linux-specific interception methods.
12//
13// POSIX dynamic-library helpers (dlopen / dlsym) live here for every
14// non-Windows interception target (Linux, *BSD, Darwin, AIX, Fuchsia, ...).
15// macOS/AIX/Fuchsia compile this TU for RTInterception but do not use the
16// Linux-specific InterceptFunction helpers below.
17//===----------------------------------------------------------------------===//
18
19#include "interception.h"
20
21#if !SANITIZER_WINDOWS
22
23# include <dlfcn.h>
24
25# pragma weak dlopen
26# pragma weak dlsym
27#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
28# pragma weak dlvsym
29#endif
30
31namespace __interception {
32
33bool DynamicLoaderAvailable() { return dlopen != nullptr && dlsym != nullptr; }
34
35void* OpenLibrary(const char* name) {
36 if (!DynamicLoaderAvailable())
37 return nullptr;
38 return dlopen(file: name, RTLD_LAZY | RTLD_LOCAL);
39}
40
41void* LookupSymbol(void* handle, const char* symbol) {
42 if (!DynamicLoaderAvailable())
43 return nullptr;
44 return dlsym(handle: handle, name: symbol);
45}
46
47void* LookupSymbolDefault(const char* symbol) {
48 if (!DynamicLoaderAvailable())
49 return nullptr;
50 return dlsym(RTLD_DEFAULT, name: symbol);
51}
52
53void* LookupSymbolNext(const char* symbol) {
54 if (!DynamicLoaderAvailable())
55 return nullptr;
56 return dlsym(RTLD_NEXT, name: symbol);
57}
58
59#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
60void* LookupSymbolNextVersioned(const char* symbol, const char* version) {
61 if (!DynamicLoaderAvailable() || dlvsym == nullptr)
62 return nullptr;
63 return dlvsym(RTLD_NEXT, name: symbol, version: version);
64}
65#endif // SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
66
67} // namespace __interception
68
69#endif // !SANITIZER_WINDOWS
70
71#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \
72 SANITIZER_SOLARIS || SANITIZER_HAIKU
73
74namespace __interception {
75
76#if SANITIZER_NETBSD
77static int StrCmp(const char *s1, const char *s2) {
78 while (true) {
79 if (*s1 != *s2)
80 return false;
81 if (*s1 == 0)
82 return true;
83 s1++;
84 s2++;
85 }
86}
87#endif
88
89static void *GetFuncAddr(const char *name, uptr trampoline) {
90#if SANITIZER_NETBSD
91 // FIXME: Find a better way to handle renames
92 if (StrCmp(name, "sigaction"))
93 name = "__sigaction14";
94#endif
95 void* addr = LookupSymbolNext(symbol: name);
96 if (!addr) {
97 // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is
98 // later in the library search order than the DSO that we are trying to
99 // intercept, which means that we cannot intercept this function. We still
100 // want the address of the real definition, though, so look it up using
101 // RTLD_DEFAULT.
102 addr = LookupSymbolDefault(symbol: name);
103
104 // In case `name' is not loaded, dlsym ends up finding the actual wrapper.
105 // We don't want to intercept the wrapper and have it point to itself.
106 if ((uptr)addr == trampoline)
107 addr = nullptr;
108 }
109 return addr;
110}
111
112bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func,
113 uptr trampoline) {
114 void *addr = GetFuncAddr(name, trampoline);
115 *ptr_to_real = (uptr)addr;
116 return addr && (func == trampoline);
117}
118
119// dlvsym is a GNU extension supported by some other platforms.
120#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
121static void *GetFuncAddr(const char *name, const char *ver) {
122 return LookupSymbolNextVersioned(symbol: name, version: ver);
123}
124
125bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
126 uptr func, uptr trampoline) {
127 void *addr = GetFuncAddr(name, ver);
128 *ptr_to_real = (uptr)addr;
129 return addr && (func == trampoline);
130}
131# endif // SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD
132
133} // namespace __interception
134
135#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD ||
136 // SANITIZER_SOLARIS || SANITIZER_HAIKU
137