/src/WasmEdge/lib/loader/aot_section.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | #include "loader/aot_section.h" |
5 | | #include "common/spdlog.h" |
6 | | #include "system/allocator.h" |
7 | | |
8 | | #if WASMEDGE_OS_LINUX || WASMEDGE_OS_MACOS |
9 | | extern "C" { |
10 | | extern void __register_frame(void *); |
11 | | extern void __deregister_frame(void *); |
12 | | } |
13 | | #endif |
14 | | |
15 | | using namespace std::literals; |
16 | | |
17 | | namespace { |
18 | 0 | inline constexpr uint64_t roundDownPageBoundary(const uint64_t Value) { |
19 | | // ARM64 Mac has a special page size |
20 | | #if WASMEDGE_OS_MACOS && defined(__aarch64__) |
21 | | return Value & ~UINT64_C(16383); |
22 | | #else |
23 | 0 | return Value & ~UINT64_C(4095); |
24 | 0 | #endif |
25 | 0 | } |
26 | 0 | inline constexpr uint64_t roundUpPageBoundary(const uint64_t Value) { |
27 | | // ARM64 Mac has a special page size |
28 | | #if WASMEDGE_OS_MACOS && defined(__aarch64__) |
29 | | return roundDownPageBoundary(Value + UINT64_C(16383)); |
30 | | #else |
31 | 0 | return roundDownPageBoundary(Value + UINT64_C(4095)); |
32 | 0 | #endif |
33 | 0 | } |
34 | | } // namespace |
35 | | |
36 | | namespace WasmEdge::Loader { |
37 | | |
38 | 0 | Expect<void> AOTSection::load(const AST::AOTSection &AOTSec) noexcept { |
39 | 0 | BinarySize = 0; |
40 | 0 | for (const auto &Section : AOTSec.getSections()) { |
41 | 0 | const auto Offset = std::get<1>(Section); |
42 | 0 | const auto Size = std::get<2>(Section); |
43 | 0 | BinarySize = std::max(BinarySize, Offset + Size); |
44 | 0 | } |
45 | 0 | BinarySize = roundUpPageBoundary(BinarySize); |
46 | | |
47 | | // The symbol addresses are offsets into the binary about to be mapped, and |
48 | | // are dereferenced without any further check, so reject the out-of-range |
49 | | // ones here. |
50 | 0 | if (!checkAccessBound(AOTSec.getIntrinsicsAddress(), |
51 | 0 | sizeof(const IntrinsicsTable *))) { |
52 | 0 | spdlog::error(ErrCode::Value::IntegerTooLarge); |
53 | 0 | spdlog::error(" AOT intrinsics address out of range."sv); |
54 | 0 | return Unexpect(ErrCode::Value::IntegerTooLarge); |
55 | 0 | } |
56 | 0 | for (const auto Address : AOTSec.getTypesAddress()) { |
57 | 0 | if (!checkAccessBound(Address, 1)) { |
58 | 0 | spdlog::error(ErrCode::Value::IntegerTooLarge); |
59 | 0 | spdlog::error(" AOT type address out of range."sv); |
60 | 0 | return Unexpect(ErrCode::Value::IntegerTooLarge); |
61 | 0 | } |
62 | 0 | } |
63 | 0 | for (const auto Address : AOTSec.getCodesAddress()) { |
64 | 0 | if (!checkAccessBound(Address, 1)) { |
65 | 0 | spdlog::error(ErrCode::Value::IntegerTooLarge); |
66 | 0 | spdlog::error(" AOT code address out of range."sv); |
67 | 0 | return Unexpect(ErrCode::Value::IntegerTooLarge); |
68 | 0 | } |
69 | 0 | } |
70 | | |
71 | 0 | Binary = Allocator::allocate_chunk(BinarySize); |
72 | 0 | if (unlikely(!Binary)) { |
73 | 0 | spdlog::error(ErrCode::Value::MemoryOutOfBounds); |
74 | 0 | return Unexpect(ErrCode::Value::MemoryOutOfBounds); |
75 | 0 | } |
76 | | |
77 | 0 | std::vector<std::pair<uint8_t *, uint64_t>> ExecutableRanges; |
78 | 0 | for (const auto &Section : AOTSec.getSections()) { |
79 | 0 | const auto Offset = std::get<1>(Section); |
80 | 0 | const auto Size = std::get<2>(Section); |
81 | 0 | const auto &Content = std::get<3>(Section); |
82 | 0 | if (Size > BinarySize || Offset > BinarySize || |
83 | 0 | Offset + Size > BinarySize || Content.size() > Size) { |
84 | 0 | return Unexpect(ErrCode::Value::IntegerTooLarge); |
85 | 0 | } |
86 | 0 | std::copy(Content.begin(), Content.end(), Binary + Offset); |
87 | 0 | switch (std::get<0>(Section)) { |
88 | 0 | case 1: { // Text |
89 | 0 | const auto O = roundDownPageBoundary(Offset); |
90 | 0 | const auto S = roundUpPageBoundary(Size + (Offset - O)); |
91 | 0 | ExecutableRanges.emplace_back(Binary + O, S); |
92 | 0 | break; |
93 | 0 | } |
94 | 0 | case 2: // Data |
95 | 0 | break; |
96 | 0 | case 3: // BSS |
97 | 0 | break; |
98 | 0 | #if WASMEDGE_OS_LINUX |
99 | 0 | case 4: // EHFrame |
100 | 0 | EHFrameAddress = reinterpret_cast<void *>(Binary + Offset); |
101 | 0 | break; |
102 | | #elif WASMEDGE_OS_MACOS |
103 | | case 4: // EHFrame |
104 | | EHFrameAddress = reinterpret_cast<uint8_t *>(Binary + Offset); |
105 | | EHFrameSize = Size; |
106 | | break; |
107 | | #elif WASMEDGE_OS_WINDOWS |
108 | | case 4: // PData |
109 | | PDataAddress = reinterpret_cast<void *>(Binary + Offset); |
110 | | PDataSize = |
111 | | static_cast<uint32_t>(Size / sizeof(winapi::RUNTIME_FUNCTION_)); |
112 | | break; |
113 | | #endif |
114 | 0 | default: |
115 | 0 | return Unexpect(ErrCode::Value::IntegerTooLarge); |
116 | 0 | } |
117 | 0 | } |
118 | | |
119 | 0 | for (const auto &[Pointer, Size] : ExecutableRanges) { |
120 | 0 | if (!Allocator::set_chunk_executable(Pointer, Size)) { |
121 | 0 | spdlog::error(ErrCode::Value::MemoryOutOfBounds); |
122 | 0 | spdlog::error(" set_chunk_executable failed:{}"sv, |
123 | 0 | std::strerror(errno)); |
124 | 0 | return Unexpect(ErrCode::Value::MemoryOutOfBounds); |
125 | 0 | } |
126 | 0 | } |
127 | | |
128 | 0 | IntrinsicsAddress = AOTSec.getIntrinsicsAddress(); |
129 | 0 | TypesAddress = AOTSec.getTypesAddress(); |
130 | 0 | CodesAddress = AOTSec.getCodesAddress(); |
131 | |
|
132 | 0 | #if WASMEDGE_OS_LINUX |
133 | 0 | if (EHFrameAddress) { |
134 | 0 | __register_frame(EHFrameAddress); |
135 | 0 | } |
136 | | #elif WASMEDGE_OS_MACOS |
137 | | if (EHFrameAddress) { |
138 | | auto Iter = EHFrameAddress; |
139 | | const auto End = EHFrameAddress + EHFrameSize - 4; |
140 | | |
141 | | while (Iter < End) { |
142 | | if (Iter != EHFrameAddress) { |
143 | | __register_frame(Iter); |
144 | | } |
145 | | const uint32_t Length = *reinterpret_cast<const uint32_t *>(Iter); |
146 | | Iter += Length + 4; |
147 | | } |
148 | | } |
149 | | #elif WASMEDGE_OS_WINDOWS |
150 | | if (PDataSize != 0) { |
151 | | winapi::RtlAddFunctionTable( |
152 | | static_cast<winapi::PRUNTIME_FUNCTION_>(PDataAddress), PDataSize, |
153 | | reinterpret_cast<winapi::ULONG_PTR_>(Binary)); |
154 | | } |
155 | | #endif |
156 | |
|
157 | 0 | return {}; |
158 | 0 | } |
159 | | |
160 | 0 | void AOTSection::unload() noexcept { |
161 | 0 | if (Binary) { |
162 | 0 | #if WASMEDGE_OS_LINUX |
163 | 0 | if (EHFrameAddress) { |
164 | 0 | __deregister_frame(EHFrameAddress); |
165 | 0 | } |
166 | | #elif WASMEDGE_OS_MACOS |
167 | | if (EHFrameAddress) { |
168 | | auto Iter = EHFrameAddress; |
169 | | const auto End = EHFrameAddress + EHFrameSize - 4; |
170 | | |
171 | | while (Iter < End) { |
172 | | if (Iter != EHFrameAddress) { |
173 | | __deregister_frame(Iter); |
174 | | } |
175 | | const uint32_t Length = *reinterpret_cast<const uint32_t *>(Iter); |
176 | | Iter += Length + 4; |
177 | | } |
178 | | } |
179 | | #elif WASMEDGE_OS_WINDOWS |
180 | | if (PDataSize != 0) { |
181 | | winapi::RtlDeleteFunctionTable( |
182 | | static_cast<winapi::PRUNTIME_FUNCTION_>(PDataAddress)); |
183 | | } |
184 | | #endif |
185 | 0 | Allocator::set_chunk_readable_writable(Binary, BinarySize); |
186 | 0 | Allocator::release_chunk(Binary, BinarySize); |
187 | 0 | Binary = nullptr; |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | } // namespace WasmEdge::Loader |