/src/WasmEdge/lib/loader/ast/module.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 "loader/loader.h" |
6 | | #include "loader/shared_library.h" |
7 | | |
8 | | #include <cstddef> |
9 | | #include <cstdint> |
10 | | #include <memory> |
11 | | #include <optional> |
12 | | #include <string> |
13 | | #include <utility> |
14 | | #include <vector> |
15 | | |
16 | | using namespace std::literals; |
17 | | |
18 | | namespace WasmEdge { |
19 | | namespace Loader { |
20 | | |
21 | | // Load binary to construct Module node. See "include/loader/loader.h". |
22 | | Expect<void> Loader::loadModule(AST::Module &Mod, |
23 | 9.37k | std::optional<uint64_t> Bound) { |
24 | 9.37k | uint64_t StartOffset = FMgr.getOffset(); |
25 | 9.37k | auto ReportError = [](auto E) { |
26 | 5.01k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
27 | 5.01k | return E; |
28 | 5.01k | }; |
29 | | |
30 | | // Variables to record the loaded section types. |
31 | 9.37k | HasDataSection = false; |
32 | 9.37k | std::vector<uint8_t> Secs = {0x0BU, 0x0AU, 0x0CU, 0x09U, 0x08U, 0x07U, 0x06U, |
33 | 9.37k | 0x0DU, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U}; |
34 | 9.37k | uint64_t Offset = FMgr.getOffset(); |
35 | | |
36 | | // Read Section index and create Section nodes. |
37 | 77.1k | while (!Bound.has_value() || Bound.value() > Offset - StartOffset) { |
38 | 77.1k | uint8_t NewSectionId = 0x00; |
39 | | // If not read section ID, seems the end of file and break. |
40 | 77.1k | if (auto Res = FMgr.readByte()) { |
41 | 72.8k | NewSectionId = *Res; |
42 | 72.8k | } else { |
43 | 4.31k | if (Res.error() == ErrCode::Value::UnexpectedEnd) { |
44 | 4.31k | break; |
45 | 4.31k | } else { |
46 | 0 | return logLoadError(Res.error(), FMgr.getLastOffset(), |
47 | 0 | ASTNodeAttr::Module); |
48 | 0 | } |
49 | 4.31k | } |
50 | | |
51 | | // Sections except the custom section should be unique and in order. |
52 | 72.8k | if (NewSectionId > 0x00U && NewSectionId < 0x0EU) { |
53 | 95.8k | while (Secs.size() > 0 && Secs.back() != NewSectionId) { |
54 | 74.3k | Secs.pop_back(); |
55 | 74.3k | } |
56 | 21.4k | if (Secs.empty()) { |
57 | 19 | return logLoadError(ErrCode::Value::JunkSection, FMgr.getLastOffset(), |
58 | 19 | ASTNodeAttr::Module); |
59 | 19 | } |
60 | 21.4k | Secs.pop_back(); |
61 | 21.4k | } |
62 | | |
63 | 72.8k | switch (NewSectionId) { |
64 | 51.3k | case 0x00: |
65 | 51.3k | Mod.getCustomSections().emplace_back(); |
66 | 51.3k | EXPECTED_TRY( |
67 | 51.1k | loadSection(Mod.getCustomSections().back()).map_error(ReportError)); |
68 | 51.1k | break; |
69 | 51.1k | case 0x01: |
70 | 4.99k | EXPECTED_TRY(loadSection(Mod.getTypeSection()).map_error(ReportError)); |
71 | 4.73k | break; |
72 | 4.73k | case 0x02: |
73 | 338 | EXPECTED_TRY(loadSection(Mod.getImportSection()).map_error(ReportError)); |
74 | 166 | break; |
75 | 4.57k | case 0x03: |
76 | 4.57k | EXPECTED_TRY( |
77 | 4.50k | loadSection(Mod.getFunctionSection()).map_error(ReportError)); |
78 | 4.50k | break; |
79 | 4.50k | case 0x04: |
80 | 539 | EXPECTED_TRY(loadSection(Mod.getTableSection()).map_error(ReportError)); |
81 | 422 | break; |
82 | 1.49k | case 0x05: |
83 | 1.49k | EXPECTED_TRY(loadSection(Mod.getMemorySection()).map_error(ReportError)); |
84 | 1.42k | break; |
85 | 1.42k | case 0x06: |
86 | 614 | EXPECTED_TRY(loadSection(Mod.getGlobalSection()).map_error(ReportError)); |
87 | 234 | break; |
88 | 1.04k | case 0x07: |
89 | 1.04k | EXPECTED_TRY(loadSection(Mod.getExportSection()).map_error(ReportError)); |
90 | 927 | break; |
91 | 927 | case 0x08: |
92 | 49 | EXPECTED_TRY(loadSection(Mod.getStartSection()).map_error(ReportError)); |
93 | 18 | break; |
94 | 1.32k | case 0x09: |
95 | 1.32k | EXPECTED_TRY(loadSection(Mod.getElementSection()).map_error(ReportError)); |
96 | 197 | break; |
97 | 4.74k | case 0x0A: |
98 | 4.74k | EXPECTED_TRY(loadSection(Mod.getCodeSection()).map_error(ReportError)); |
99 | 3.75k | break; |
100 | 3.75k | case 0x0B: |
101 | 1.53k | EXPECTED_TRY(loadSection(Mod.getDataSection()).map_error(ReportError)); |
102 | 144 | break; |
103 | 144 | case 0x0C: |
104 | | // This section is for BulkMemoryOperations or ReferenceTypes proposal. |
105 | 112 | if (!Conf.hasProposal(Proposal::BulkMemoryOperations) && |
106 | 0 | !Conf.hasProposal(Proposal::ReferenceTypes)) { |
107 | 0 | return logNeedProposal(ErrCode::Value::MalformedSection, |
108 | 0 | Proposal::BulkMemoryOperations, |
109 | 0 | FMgr.getLastOffset(), ASTNodeAttr::Module); |
110 | 0 | } |
111 | 112 | EXPECTED_TRY( |
112 | 82 | loadSection(Mod.getDataCountSection()).map_error(ReportError)); |
113 | 82 | HasDataSection = true; |
114 | 82 | break; |
115 | 75 | case 0x0D: |
116 | | // This section is for ExceptionHandling proposal. |
117 | 75 | if (!Conf.hasProposal(Proposal::ExceptionHandling)) { |
118 | 0 | return logNeedProposal(ErrCode::Value::MalformedSection, |
119 | 0 | Proposal::ExceptionHandling, |
120 | 0 | FMgr.getLastOffset(), ASTNodeAttr::Module); |
121 | 0 | } |
122 | 75 | EXPECTED_TRY(loadSection(Mod.getTagSection()).map_error(ReportError)); |
123 | 28 | break; |
124 | 29 | default: |
125 | 29 | return logLoadError(ErrCode::Value::MalformedSection, |
126 | 29 | FMgr.getLastOffset(), ASTNodeAttr::Module); |
127 | 72.8k | } |
128 | | |
129 | 67.7k | Offset = FMgr.getOffset(); |
130 | 67.7k | } |
131 | | |
132 | 4.31k | setTagFunctionType(Mod.getTagSection(), Mod.getImportSection(), |
133 | 4.31k | Mod.getTypeSection()); |
134 | | |
135 | | // Verify that the function section and code section match. |
136 | 4.31k | if (Mod.getFunctionSection().getContent().size() != |
137 | 4.31k | Mod.getCodeSection().getContent().size()) { |
138 | 7 | spdlog::error(ErrCode::Value::IncompatibleFuncCode); |
139 | 7 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
140 | 7 | return Unexpect(ErrCode::Value::IncompatibleFuncCode); |
141 | 7 | } |
142 | | |
143 | | // Verify that the data count section and data segments match. |
144 | 4.31k | if (Mod.getDataCountSection().getContent()) { |
145 | 44 | if (Mod.getDataSection().getContent().size() != |
146 | 44 | *(Mod.getDataCountSection().getContent())) { |
147 | 12 | spdlog::error(ErrCode::Value::IncompatibleDataCount); |
148 | 12 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
149 | 12 | return Unexpect(ErrCode::Value::IncompatibleDataCount); |
150 | 12 | } |
151 | 44 | } |
152 | | |
153 | 4.29k | return {}; |
154 | 4.31k | } |
155 | | |
156 | | // Setup symbols from loaded binary. See "include/loader/loader.h". |
157 | | Expect<void> Loader::loadExecutable(AST::Module &Mod, |
158 | 0 | std::shared_ptr<Executable> Exec) { |
159 | 0 | auto &SubTypes = Mod.getTypeSection().getContent(); |
160 | 0 | const size_t Offset = Mod.getImportFuncCount(); |
161 | 0 | auto &CodeSegs = Mod.getCodeSection().getContent(); |
162 | | |
163 | | // Check the symbols. |
164 | 0 | auto FuncTypeSymbols = Exec->getTypes(SubTypes.size()); |
165 | 0 | auto CodeSymbols = Exec->getCodes(Offset, CodeSegs.size()); |
166 | 0 | auto IntrinsicsSymbol = Exec->getIntrinsics(); |
167 | 0 | if (unlikely(FuncTypeSymbols.size() != SubTypes.size())) { |
168 | 0 | spdlog::error(" AOT section -- number of types not matching:{} {}, " |
169 | 0 | "use interpreter mode instead.", |
170 | 0 | FuncTypeSymbols.size(), SubTypes.size()); |
171 | 0 | return Unexpect(ErrCode::Value::IllegalGrammar); |
172 | 0 | } |
173 | 0 | if (unlikely(CodeSymbols.size() != CodeSegs.size())) { |
174 | 0 | spdlog::error(" AOT section -- number of codes not matching:{} {}, " |
175 | 0 | "use interpreter mode instead.", |
176 | 0 | CodeSymbols.size(), CodeSegs.size()); |
177 | 0 | return Unexpect(ErrCode::Value::IllegalGrammar); |
178 | 0 | } |
179 | 0 | if (unlikely(!IntrinsicsSymbol)) { |
180 | 0 | spdlog::error(" AOT section -- intrinsics table symbol not found, use " |
181 | 0 | "interpreter mode instead."); |
182 | 0 | return Unexpect(ErrCode::Value::IllegalGrammar); |
183 | 0 | } |
184 | | |
185 | | // Set the symbols in the module. |
186 | 0 | uint32_t FuncTypeIdx = 0; |
187 | 0 | for (auto &SubType : SubTypes) { |
188 | 0 | if (SubType.getCompositeType().isFunc()) { |
189 | 0 | SubType.getCompositeType().getFuncType().setSymbol( |
190 | 0 | std::move(FuncTypeSymbols[FuncTypeIdx])); |
191 | 0 | } |
192 | 0 | FuncTypeIdx++; |
193 | 0 | } |
194 | 0 | for (size_t I = 0; I < CodeSegs.size(); ++I) { |
195 | 0 | CodeSegs[I].setSymbol(std::move(CodeSymbols[I])); |
196 | 0 | } |
197 | 0 | Mod.setSymbol(std::move(IntrinsicsSymbol)); |
198 | | // loadExecutable is reached only when native code is being prepared (AOT |
199 | | // load or JIT compile). Patch the intrinsics-table pointer embedded in |
200 | | // the produced executable so the native code can call back into the |
201 | | // runtime. |
202 | 0 | if (auto &Symbol = Mod.getSymbol()) { |
203 | 0 | *Symbol = IntrinsicsTable; |
204 | 0 | } |
205 | |
|
206 | 0 | return {}; |
207 | 0 | } |
208 | | |
209 | 0 | Expect<void> Loader::loadUniversalWASM(AST::Module &Mod) { |
210 | 0 | if (Conf.getRuntimeConfigure().getRunMode() == RunMode::AOT) { |
211 | 0 | auto Exec = std::make_shared<AOTSection>(); |
212 | 0 | if (auto Res = Exec->load(Mod.getAOTSection()); unlikely(!Res)) { |
213 | 0 | spdlog::warn("AOT was requested but loading the AOT section failed: " |
214 | 0 | "{}, falling back to interpreter."sv, |
215 | 0 | Res.error()); |
216 | 0 | } else { |
217 | 0 | if (loadExecutable(Mod, Exec)) { |
218 | 0 | return {}; |
219 | 0 | } |
220 | 0 | spdlog::warn("AOT was requested but linking the AOT executable failed, " |
221 | 0 | "falling back to interpreter."sv); |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | | // Fallback to the interpreter mode case: Re-read the code section. |
226 | 0 | WASMType = InputType::WASM; |
227 | 0 | FMgr.seek(Mod.getCodeSection().getStartOffset()); |
228 | 0 | return loadSection(Mod.getCodeSection()).map_error([](auto E) { |
229 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
230 | 0 | return E; |
231 | 0 | }); |
232 | 0 | } |
233 | | |
234 | 0 | Expect<void> Loader::loadModuleAOT(AST::AOTSection &AOTSection) { |
235 | | // Find and read the AOT custom section first. Jump over the others. |
236 | | // This loop checks whether the input is a universal WASM. |
237 | | // Therefore, if the configuration is set to force interpreter mode, skip |
238 | | // this. |
239 | 0 | while (WASMType != InputType::SharedLibrary) { |
240 | | // This loop only scans custom sections and reads the AOT section. |
241 | | // For other general errors, break and handle them in the sequential |
242 | | // parsing below. |
243 | 0 | uint8_t NewSectionId = 0x00; |
244 | 0 | if (auto Res = FMgr.readByte()) { |
245 | 0 | NewSectionId = *Res; |
246 | 0 | } else { |
247 | 0 | break; |
248 | 0 | } |
249 | | |
250 | 0 | if (NewSectionId == 0x00U) { |
251 | | // Load the section size. |
252 | 0 | uint32_t ContentSize = 0; |
253 | 0 | if (auto Res = FMgr.readU32()) { |
254 | 0 | ContentSize = *Res; |
255 | 0 | } else { |
256 | 0 | break; |
257 | 0 | } |
258 | 0 | if (ContentSize > FMgr.getRemainSize()) { |
259 | 0 | break; |
260 | 0 | } |
261 | | |
262 | | // Load the section name. |
263 | 0 | auto StartOffset = FMgr.getOffset(); |
264 | 0 | std::string Name; |
265 | 0 | if (auto Res = FMgr.readName()) { |
266 | | // The UTF-8 failure case will be ignored here. |
267 | 0 | Name = std::move(*Res); |
268 | 0 | } |
269 | |
|
270 | 0 | auto ReadSize = FMgr.getOffset() - StartOffset; |
271 | 0 | if (ContentSize < ReadSize) { |
272 | | // Syntax error caused by overread. Jump to the next section. |
273 | 0 | FMgr.seek(StartOffset + ContentSize); |
274 | 0 | continue; |
275 | 0 | } |
276 | | |
277 | 0 | if (Name == "wasmedge") { |
278 | | // Found the AOT section in universal WASM. Load the AOT code. |
279 | | // Read the content. |
280 | 0 | std::vector<uint8_t> Content; |
281 | 0 | if (auto Res = FMgr.readBytes(ContentSize - ReadSize)) { |
282 | 0 | Content = std::move(*Res); |
283 | 0 | } else { |
284 | 0 | break; |
285 | 0 | } |
286 | | |
287 | | // Load the AOT section. |
288 | 0 | FileMgr VecMgr; |
289 | 0 | AST::AOTSection NewAOTSection; |
290 | 0 | VecMgr.setCode(Content); |
291 | 0 | if (auto Res = loadSection(VecMgr, NewAOTSection)) { |
292 | | // Also handle the duplicated AOT sections case. |
293 | | // If the new AOT section discovered, use the new one. |
294 | 0 | WASMType = InputType::UniversalWASM; |
295 | 0 | AOTSection = std::move(NewAOTSection); |
296 | 0 | } else { |
297 | | // If the new AOT section load failed, use the old one or the |
298 | | // interpreter mode. |
299 | 0 | if (WASMType == InputType::UniversalWASM) { |
300 | 0 | spdlog::info( |
301 | 0 | " Load AOT section failed. Use the previous succeeded one."); |
302 | 0 | } else { |
303 | 0 | spdlog::info( |
304 | 0 | " Load AOT section failed. Use interpreter mode instead."); |
305 | 0 | } |
306 | 0 | } |
307 | 0 | } else { |
308 | | // Found other custom sections. Jump to the next section. |
309 | 0 | FMgr.seek(StartOffset + ContentSize); |
310 | 0 | continue; |
311 | 0 | } |
312 | 0 | } else { |
313 | 0 | if (auto Res = FMgr.jumpContent(); unlikely(!Res)) { |
314 | 0 | break; |
315 | 0 | } |
316 | 0 | } |
317 | 0 | } |
318 | 0 | return {}; |
319 | 0 | } |
320 | | |
321 | | } // namespace Loader |
322 | | } // namespace WasmEdge |