/src/WasmEdge/lib/loader/ast/module.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: 2019-2024 Second State INC |
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.18k | std::optional<uint64_t> Bound) { |
24 | 9.18k | uint64_t StartOffset = FMgr.getOffset(); |
25 | 9.18k | auto ReportError = [](auto E) { |
26 | 5.30k | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
27 | 5.30k | return E; |
28 | 5.30k | }; |
29 | | |
30 | | // Variables to record the loaded section types. |
31 | 9.18k | HasDataSection = false; |
32 | 9.18k | std::vector<uint8_t> Secs = {0x0BU, 0x0AU, 0x0CU, 0x09U, 0x08U, 0x07U, 0x06U, |
33 | 9.18k | 0x0DU, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U}; |
34 | 9.18k | uint64_t Offset = FMgr.getOffset(); |
35 | | |
36 | | // Read Section index and create Section nodes. |
37 | 69.8k | while (!Bound.has_value() || Bound.value() > Offset - StartOffset) { |
38 | 69.8k | uint8_t NewSectionId = 0x00; |
39 | | // If not read section ID, seems the end of file and break. |
40 | 69.8k | if (auto Res = FMgr.readByte()) { |
41 | 65.9k | NewSectionId = *Res; |
42 | 65.9k | } else { |
43 | 3.82k | if (Res.error() == ErrCode::Value::UnexpectedEnd) { |
44 | 3.82k | break; |
45 | 3.82k | } else { |
46 | 0 | return logLoadError(Res.error(), FMgr.getLastOffset(), |
47 | 0 | ASTNodeAttr::Module); |
48 | 0 | } |
49 | 3.82k | } |
50 | | |
51 | | // Sections except the custom section should be unique and in order. |
52 | 65.9k | if (NewSectionId > 0x00U && NewSectionId < 0x0EU) { |
53 | 95.3k | while (Secs.size() > 0 && Secs.back() != NewSectionId) { |
54 | 74.4k | Secs.pop_back(); |
55 | 74.4k | } |
56 | 20.8k | if (Secs.empty()) { |
57 | 14 | return logLoadError(ErrCode::Value::JunkSection, FMgr.getLastOffset(), |
58 | 14 | ASTNodeAttr::Module); |
59 | 14 | } |
60 | 20.8k | Secs.pop_back(); |
61 | 20.8k | } |
62 | | |
63 | 65.9k | switch (NewSectionId) { |
64 | 45.0k | case 0x00: |
65 | 45.0k | Mod.getCustomSections().emplace_back(); |
66 | 45.0k | EXPECTED_TRY( |
67 | 44.8k | loadSection(Mod.getCustomSections().back()).map_error(ReportError)); |
68 | 44.8k | break; |
69 | 44.8k | case 0x01: |
70 | 4.55k | EXPECTED_TRY(loadSection(Mod.getTypeSection()).map_error(ReportError)); |
71 | 4.40k | break; |
72 | 4.40k | case 0x02: |
73 | 279 | EXPECTED_TRY(loadSection(Mod.getImportSection()).map_error(ReportError)); |
74 | 132 | break; |
75 | 4.36k | case 0x03: |
76 | 4.36k | EXPECTED_TRY( |
77 | 4.28k | loadSection(Mod.getFunctionSection()).map_error(ReportError)); |
78 | 4.28k | break; |
79 | 4.28k | case 0x04: |
80 | 443 | EXPECTED_TRY(loadSection(Mod.getTableSection()).map_error(ReportError)); |
81 | 337 | break; |
82 | 1.53k | case 0x05: |
83 | 1.53k | EXPECTED_TRY(loadSection(Mod.getMemorySection()).map_error(ReportError)); |
84 | 1.44k | break; |
85 | 1.44k | case 0x06: |
86 | 683 | EXPECTED_TRY(loadSection(Mod.getGlobalSection()).map_error(ReportError)); |
87 | 186 | break; |
88 | 1.25k | case 0x07: |
89 | 1.25k | EXPECTED_TRY(loadSection(Mod.getExportSection()).map_error(ReportError)); |
90 | 1.13k | break; |
91 | 1.13k | case 0x08: |
92 | 46 | EXPECTED_TRY(loadSection(Mod.getStartSection()).map_error(ReportError)); |
93 | 20 | break; |
94 | 1.43k | case 0x09: |
95 | 1.43k | EXPECTED_TRY(loadSection(Mod.getElementSection()).map_error(ReportError)); |
96 | 195 | break; |
97 | 4.58k | case 0x0A: |
98 | 4.58k | EXPECTED_TRY(loadSection(Mod.getCodeSection()).map_error(ReportError)); |
99 | 3.42k | break; |
100 | 3.42k | case 0x0B: |
101 | 1.59k | EXPECTED_TRY(loadSection(Mod.getDataSection()).map_error(ReportError)); |
102 | 150 | break; |
103 | 150 | case 0x0C: |
104 | | // This section is for BulkMemoryOperations or ReferenceTypes proposal. |
105 | 99 | if (!Conf.hasProposal(Proposal::BulkMemoryOperations) && |
106 | 99 | !Conf.hasProposal(Proposal::ReferenceTypes)) { |
107 | 0 | return logNeedProposal(ErrCode::Value::MalformedSection, |
108 | 0 | Proposal::BulkMemoryOperations, |
109 | 0 | FMgr.getLastOffset(), ASTNodeAttr::Module); |
110 | 0 | } |
111 | 99 | EXPECTED_TRY( |
112 | 79 | loadSection(Mod.getDataCountSection()).map_error(ReportError)); |
113 | 79 | HasDataSection = true; |
114 | 79 | break; |
115 | 2 | case 0x0D: |
116 | | // This section is for ExceptionHandling proposal. |
117 | 2 | if (!Conf.hasProposal(Proposal::ExceptionHandling)) { |
118 | 2 | return logNeedProposal(ErrCode::Value::MalformedSection, |
119 | 2 | Proposal::ExceptionHandling, |
120 | 2 | FMgr.getLastOffset(), ASTNodeAttr::Module); |
121 | 2 | } |
122 | 0 | EXPECTED_TRY(loadSection(Mod.getTagSection()).map_error(ReportError)); |
123 | 0 | break; |
124 | 38 | default: |
125 | 38 | return logLoadError(ErrCode::Value::MalformedSection, |
126 | 38 | FMgr.getLastOffset(), ASTNodeAttr::Module); |
127 | 65.9k | } |
128 | | |
129 | 60.6k | Offset = FMgr.getOffset(); |
130 | 60.6k | } |
131 | | |
132 | 3.82k | setTagFunctionType(Mod.getTagSection(), Mod.getImportSection(), |
133 | 3.82k | Mod.getTypeSection()); |
134 | | |
135 | | // Verify the function section and code section are matched. |
136 | 3.82k | if (Mod.getFunctionSection().getContent().size() != |
137 | 3.82k | Mod.getCodeSection().getContent().size()) { |
138 | 11 | spdlog::error(ErrCode::Value::IncompatibleFuncCode); |
139 | 11 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
140 | 11 | return Unexpect(ErrCode::Value::IncompatibleFuncCode); |
141 | 11 | } |
142 | | |
143 | | // Verify the data count section and data segments are matched. |
144 | 3.81k | if (Mod.getDataCountSection().getContent()) { |
145 | 45 | if (Mod.getDataSection().getContent().size() != |
146 | 45 | *(Mod.getDataCountSection().getContent())) { |
147 | 11 | spdlog::error(ErrCode::Value::IncompatibleDataCount); |
148 | 11 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
149 | 11 | return Unexpect(ErrCode::Value::IncompatibleDataCount); |
150 | 11 | } |
151 | 45 | } |
152 | | |
153 | 3.80k | return {}; |
154 | 3.81k | } |
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 | size_t Offset = 0; |
161 | 0 | for (const auto &ImpDesc : Mod.getImportSection().getContent()) { |
162 | 0 | if (ImpDesc.getExternalType() == ExternalType::Function) { |
163 | 0 | ++Offset; |
164 | 0 | } |
165 | 0 | } |
166 | 0 | auto &CodeSegs = Mod.getCodeSection().getContent(); |
167 | | |
168 | | // Check the symbols. |
169 | 0 | auto FuncTypeSymbols = Exec->getTypes(SubTypes.size()); |
170 | 0 | auto CodeSymbols = Exec->getCodes(Offset, CodeSegs.size()); |
171 | 0 | auto IntrinsicsSymbol = Exec->getIntrinsics(); |
172 | 0 | if (unlikely(FuncTypeSymbols.size() != SubTypes.size())) { |
173 | 0 | spdlog::error(" AOT section -- number of types not matching:{} {}, " |
174 | 0 | "use interpreter mode instead.", |
175 | 0 | FuncTypeSymbols.size(), SubTypes.size()); |
176 | 0 | return Unexpect(ErrCode::Value::IllegalGrammar); |
177 | 0 | } |
178 | 0 | if (unlikely(CodeSymbols.size() != CodeSegs.size())) { |
179 | 0 | spdlog::error(" AOT section -- number of codes not matching:{} {}, " |
180 | 0 | "use interpreter mode instead.", |
181 | 0 | CodeSymbols.size(), CodeSegs.size()); |
182 | 0 | return Unexpect(ErrCode::Value::IllegalGrammar); |
183 | 0 | } |
184 | 0 | if (unlikely(!IntrinsicsSymbol)) { |
185 | 0 | spdlog::error(" AOT section -- intrinsics table symbol not found, use " |
186 | 0 | "interpreter mode instead."); |
187 | 0 | return Unexpect(ErrCode::Value::IllegalGrammar); |
188 | 0 | } |
189 | | |
190 | | // Set the symbols into the module. |
191 | 0 | uint32_t FuncTypeIdx = 0; |
192 | 0 | for (auto &SubType : SubTypes) { |
193 | 0 | if (SubType.getCompositeType().isFunc()) { |
194 | 0 | SubType.getCompositeType().getFuncType().setSymbol( |
195 | 0 | std::move(FuncTypeSymbols[FuncTypeIdx])); |
196 | 0 | } |
197 | 0 | FuncTypeIdx++; |
198 | 0 | } |
199 | 0 | for (size_t I = 0; I < CodeSegs.size(); ++I) { |
200 | 0 | CodeSegs[I].setSymbol(std::move(CodeSymbols[I])); |
201 | 0 | } |
202 | 0 | Mod.setSymbol(std::move(IntrinsicsSymbol)); |
203 | 0 | if (!Conf.getRuntimeConfigure().isForceInterpreter()) { |
204 | | // If the configure is set to force interpreter mode, not to set the |
205 | | // symbol. |
206 | 0 | if (auto &Symbol = Mod.getSymbol()) { |
207 | 0 | *Symbol = IntrinsicsTable; |
208 | 0 | } |
209 | 0 | } |
210 | |
|
211 | 0 | return {}; |
212 | 0 | } |
213 | | |
214 | 0 | Expect<void> Loader::loadUniversalWASM(AST::Module &Mod) { |
215 | 0 | if (!Conf.getRuntimeConfigure().isForceInterpreter()) { |
216 | 0 | auto Exec = std::make_shared<AOTSection>(); |
217 | 0 | if (auto Res = Exec->load(Mod.getAOTSection()); unlikely(!Res)) { |
218 | 0 | spdlog::error(" AOT section -- library load failed:{} , use " |
219 | 0 | "interpreter mode instead.", |
220 | 0 | Res.error()); |
221 | 0 | } else { |
222 | 0 | if (loadExecutable(Mod, Exec)) { |
223 | 0 | return {}; |
224 | 0 | } |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | // Fallback to the interpreter mode case: Re-read the code section. |
229 | 0 | WASMType = InputType::WASM; |
230 | 0 | FMgr.seek(Mod.getCodeSection().getStartOffset()); |
231 | 0 | return loadSection(Mod.getCodeSection()).map_error([](auto E) { |
232 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module)); |
233 | 0 | return E; |
234 | 0 | }); |
235 | 0 | } |
236 | | |
237 | 0 | Expect<void> Loader::loadModuleAOT(AST::AOTSection &AOTSection) { |
238 | | // Find and Read the AOT custom section first. Jump the others. |
239 | | // This loop is for checking the input is an universal WASM or not. |
240 | | // Therefore, if the configure is set as force interpreter mode, skip this. |
241 | 0 | while (WASMType != InputType::SharedLibrary) { |
242 | | // This loop only overview the custom sections and read the AOT section. |
243 | | // For the other general errors, break and handle in the sequentially |
244 | | // parsing below. |
245 | 0 | uint8_t NewSectionId = 0x00; |
246 | 0 | if (auto Res = FMgr.readByte()) { |
247 | 0 | NewSectionId = *Res; |
248 | 0 | } else { |
249 | 0 | break; |
250 | 0 | } |
251 | | |
252 | 0 | if (NewSectionId == 0x00U) { |
253 | | // Load the section size. |
254 | 0 | uint32_t ContentSize = 0; |
255 | 0 | if (auto Res = FMgr.readU32()) { |
256 | 0 | ContentSize = *Res; |
257 | 0 | } else { |
258 | 0 | break; |
259 | 0 | } |
260 | 0 | if (ContentSize > FMgr.getRemainSize()) { |
261 | 0 | break; |
262 | 0 | } |
263 | | |
264 | | // Load the section name. |
265 | 0 | auto StartOffset = FMgr.getOffset(); |
266 | 0 | std::string Name; |
267 | 0 | if (auto Res = FMgr.readName()) { |
268 | | // The UTF-8 failed case will be ignored here. |
269 | 0 | Name = std::move(*Res); |
270 | 0 | } |
271 | |
|
272 | 0 | auto ReadSize = FMgr.getOffset() - StartOffset; |
273 | 0 | if (ContentSize < ReadSize) { |
274 | | // Syntax error of overread. Jump to the next section. |
275 | 0 | FMgr.seek(StartOffset + ContentSize); |
276 | 0 | continue; |
277 | 0 | } |
278 | | |
279 | 0 | if (Name == "wasmedge") { |
280 | | // Found the AOT section in universal WASM. Load the AOT code. |
281 | | // Read the content. |
282 | 0 | std::vector<uint8_t> Content; |
283 | 0 | if (auto Res = FMgr.readBytes(ContentSize - ReadSize)) { |
284 | 0 | Content = std::move(*Res); |
285 | 0 | } else { |
286 | 0 | break; |
287 | 0 | } |
288 | | |
289 | | // Load the AOT section. |
290 | 0 | FileMgr VecMgr; |
291 | 0 | AST::AOTSection NewAOTSection; |
292 | 0 | VecMgr.setCode(Content); |
293 | 0 | if (auto Res = loadSection(VecMgr, NewAOTSection)) { |
294 | | // Also handle the duplicated AOT sections case. |
295 | | // If the new AOT section discovered, use the new one. |
296 | 0 | WASMType = InputType::UniversalWASM; |
297 | 0 | AOTSection = std::move(NewAOTSection); |
298 | 0 | } else { |
299 | | // If the new AOT section load failed, use the old one or the |
300 | | // interpreter mode. |
301 | 0 | if (WASMType == InputType::UniversalWASM) { |
302 | 0 | spdlog::info( |
303 | 0 | " Load AOT section failed. Use the previous succeeded one."); |
304 | 0 | } else { |
305 | 0 | spdlog::info( |
306 | 0 | " Load AOT section failed. Use interpreter mode instead."); |
307 | 0 | } |
308 | 0 | } |
309 | 0 | } else { |
310 | | // Found other custom sections. Jump to the next section. |
311 | 0 | FMgr.seek(StartOffset + ContentSize); |
312 | 0 | continue; |
313 | 0 | } |
314 | 0 | } else { |
315 | 0 | if (auto Res = FMgr.jumpContent(); unlikely(!Res)) { |
316 | 0 | break; |
317 | 0 | } |
318 | 0 | } |
319 | 0 | } |
320 | 0 | return {}; |
321 | 0 | } |
322 | | |
323 | | } // namespace Loader |
324 | | } // namespace WasmEdge |