1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <link.h>
17 #include <dlfcn.h>
18 #include <cstdlib>
19
20 #include "mem_hooks.h"
21 #include "libpandabase/utils/span.h"
22
23 namespace panda::os::unix::mem_hooks {
24
25 size_t PandaHooks::allocViaStandard_ = 0;
26 bool PandaHooks::isActive_ = false;
27 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
28 PandaHooks::AddrRange PandaHooks::ignoreCodeRange_;
29 void *(*PandaHooks::realMalloc_)(size_t) = nullptr;
30 void *(*PandaHooks::realMemalign_)(size_t, size_t) = nullptr;
31 void (*PandaHooks::realFree_)(void *) = nullptr;
32
FindLibDwarfCodeRegion(dl_phdr_info * info,size_t size,void * data)33 int FindLibDwarfCodeRegion(dl_phdr_info *info, [[maybe_unused]] size_t size, void *data)
34 {
35 auto arange = reinterpret_cast<PandaHooks::AddrRange *>(data);
36 if (std::string_view(info->dlpi_name).find("libdwarf.so") != std::string_view::npos) {
37 Span<const ElfW(Phdr)> phdrList(info->dlpi_phdr, info->dlpi_phnum);
38 for (ElfW(Phdr) phdr : phdrList) {
39 // NOLINTNEXTLINE(hicpp-signed-bitwise)
40 if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X) != 0) {
41 *arange = PandaHooks::AddrRange(info->dlpi_addr + phdr.p_vaddr, phdr.p_memsz);
42 return 1;
43 }
44 }
45 }
46 return 0;
47 }
48
49 /* static */
Initialize()50 void PandaHooks::Initialize()
51 {
52 // libdwarf allocates a lot of memory using malloc internally.
53 // Since this library is used only for debug purpose don't consider
54 // malloc calls from this library.
55 dl_iterate_phdr(FindLibDwarfCodeRegion, &PandaHooks::ignoreCodeRange_);
56 }
57
58 /* static */
SaveRealFunctions()59 void PandaHooks::SaveRealFunctions()
60 {
61 realMalloc_ = reinterpret_cast<decltype(realMalloc_)>(
62 dlsym(RTLD_NEXT, "malloc")); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
63 ASSERT(realMalloc_ != nullptr);
64 realMemalign_ = reinterpret_cast<decltype(realMemalign_)>(
65 dlsym(RTLD_NEXT, "memalign")); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
66 ASSERT(realMemalign_ != nullptr);
67 realFree_ = reinterpret_cast<decltype(realFree_)>(
68 dlsym(RTLD_NEXT, "free")); // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
69 ASSERT(realFree_ != nullptr);
70 }
71
72 /* static */
ShouldCountAllocation(const void * caller)73 bool PandaHooks::ShouldCountAllocation(const void *caller)
74 {
75 return !ignoreCodeRange_.Contains(ToUintPtr(caller));
76 }
77
78 /* static */
MallocHook(size_t size,const void * caller)79 void *PandaHooks::MallocHook(size_t size, const void *caller)
80 {
81 if (ShouldCountAllocation(caller)) {
82 allocViaStandard_ += size;
83 }
84 // tracking internal allocator is implemented by malloc, we would fail here with this option
85 #ifndef TRACK_INTERNAL_ALLOCATIONS
86 if (allocViaStandard_ > MAX_ALLOC_VIA_STANDARD) {
87 std::cerr << "Too many usage of standard allocations" << std::endl;
88 abort();
89 }
90 #endif // TRACK_INTERNAL_ALLOCATIONS
91 void *result = realMalloc_(size); // NOLINT(cppcoreguidelines-no-malloc)
92 if (UNLIKELY(result == nullptr)) {
93 std::cerr << "Malloc error" << std::endl;
94 abort();
95 }
96 return result;
97 }
98
99 /* static */
MemalignHook(size_t alignment,size_t size,const void * caller)100 void *PandaHooks::MemalignHook(size_t alignment, size_t size, const void *caller)
101 {
102 if (ShouldCountAllocation(caller)) {
103 allocViaStandard_ += size;
104 }
105 // tracking internal allocator is implemented by malloc, we would fail here with this option
106 #ifndef TRACK_INTERNAL_ALLOCATIONS
107 if (allocViaStandard_ > MAX_ALLOC_VIA_STANDARD) {
108 std::cerr << "Too many usage of standard allocations" << std::endl;
109 abort();
110 }
111 #endif // TRACK_INTERNAL_ALLOCATIONS
112 void *result = realMemalign_(alignment, size);
113 if (UNLIKELY(result == nullptr)) {
114 std::cerr << "Align error" << std::endl;
115 abort();
116 }
117 return result;
118 }
119
120 /* static */
FreeHook(void * ptr,const void * caller)121 void PandaHooks::FreeHook(void *ptr, [[maybe_unused]] const void *caller)
122 {
123 realFree_(ptr); // NOLINT(cppcoreguidelines-no-malloc)
124 }
125
126 /* static */
Enable()127 void PandaHooks::Enable()
128 {
129 #ifdef PANDA_USE_MEMORY_HOOKS
130 isActive_ = true;
131 #endif // PANDA_USE_MEMORY_HOOKS
132 }
133
134 /* static */
Disable()135 void PandaHooks::Disable()
136 {
137 #ifdef PANDA_USE_MEMORY_HOOKS
138 isActive_ = false;
139 #endif // PANDA_USE_MEMORY_HOOKS
140 }
141
142 } // namespace panda::os::unix::mem_hooks
143
144 #ifdef PANDA_USE_MEMORY_HOOKS
145
146 // NOLINTNEXTLINE(readability-redundant-declaration,readability-identifier-naming)
malloc(size_t size)147 void *malloc(size_t size) noexcept
148 {
149 if (panda::os::mem_hooks::PandaHooks::realMalloc_ == nullptr) {
150 panda::os::unix::mem_hooks::PandaHooks::SaveRealFunctions();
151 }
152 if (panda::os::mem_hooks::PandaHooks::IsActive()) {
153 void *caller = __builtin_return_address(0);
154 return panda::os::mem_hooks::PandaHooks::MallocHook(size, caller);
155 }
156 return panda::os::mem_hooks::PandaHooks::realMalloc_(size);
157 }
158
159 // NOLINTNEXTLINE(readability-redundant-declaration,readability-identifier-naming)
memalign(size_t alignment,size_t size)160 void *memalign(size_t alignment, size_t size) noexcept
161 {
162 if (panda::os::mem_hooks::PandaHooks::realMemalign_ == nullptr) {
163 panda::os::unix::mem_hooks::PandaHooks::SaveRealFunctions();
164 }
165 if (panda::os::mem_hooks::PandaHooks::IsActive()) {
166 void *caller = __builtin_return_address(0);
167 return panda::os::mem_hooks::PandaHooks::MemalignHook(alignment, size, caller);
168 }
169 return panda::os::mem_hooks::PandaHooks::realMemalign_(alignment, size);
170 }
171
172 // NOLINTNEXTLINE(readability-redundant-declaration,readability-identifier-naming)
free(void * ptr)173 void free(void *ptr) noexcept
174 {
175 if (panda::os::mem_hooks::PandaHooks::realFree_ == nullptr) {
176 panda::os::unix::mem_hooks::PandaHooks::SaveRealFunctions();
177 }
178 if (panda::os::mem_hooks::PandaHooks::IsActive()) {
179 void *caller = __builtin_return_address(0);
180 return panda::os::mem_hooks::PandaHooks::FreeHook(ptr, caller);
181 }
182 return panda::os::mem_hooks::PandaHooks::realFree_(ptr);
183 }
184
185 #endif // PANDA_USE_MEMORY_HOOKS
186