/src/WasmEdge/lib/loader/loader.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/loader.h" |
5 | | |
6 | | #include "aot/version.h" |
7 | | |
8 | | #include <algorithm> |
9 | | #include <cstddef> |
10 | | #include <fstream> |
11 | | #include <limits> |
12 | | #include <memory> |
13 | | #include <system_error> |
14 | | #include <utility> |
15 | | #include <variant> |
16 | | |
17 | | using namespace std::literals; |
18 | | |
19 | | namespace WasmEdge { |
20 | | namespace Loader { |
21 | | |
22 | | // Load data from file path. See "include/loader/loader.h". |
23 | | Expect<std::vector<Byte>> |
24 | 0 | Loader::loadFile(const std::filesystem::path &FilePath) { |
25 | 0 | std::error_code EC; |
26 | 0 | size_t FileSize = std::filesystem::file_size(FilePath, EC); |
27 | 0 | if (EC) { |
28 | 0 | spdlog::error(ErrCode::Value::IllegalPath); |
29 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
30 | 0 | return Unexpect(ErrCode::Value::IllegalPath); |
31 | 0 | } |
32 | | |
33 | 0 | std::ifstream Fin(FilePath, std::ios::in | std::ios::binary); |
34 | 0 | if (!Fin) { |
35 | 0 | spdlog::error(ErrCode::Value::IllegalPath); |
36 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
37 | 0 | return Unexpect(ErrCode::Value::IllegalPath); |
38 | 0 | } |
39 | | |
40 | 0 | std::vector<Byte> Buf(FileSize); |
41 | 0 | size_t Index = 0; |
42 | 0 | while (FileSize > 0) { |
43 | 0 | const uint32_t BlockSize = static_cast<uint32_t>( |
44 | 0 | std::min<size_t>(FileSize, std::numeric_limits<uint32_t>::max())); |
45 | 0 | Fin.read(reinterpret_cast<char *>(Buf.data()) + Index, BlockSize); |
46 | 0 | const uint32_t ReadCount = static_cast<uint32_t>(Fin.gcount()); |
47 | 0 | if (ReadCount != BlockSize) { |
48 | 0 | if (Fin.eof()) { |
49 | 0 | spdlog::error(ErrCode::Value::UnexpectedEnd); |
50 | 0 | spdlog::error(ErrInfo::InfoLoading(ReadCount)); |
51 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
52 | 0 | return Unexpect(ErrCode::Value::UnexpectedEnd); |
53 | 0 | } else { |
54 | 0 | spdlog::error(ErrCode::Value::ReadError); |
55 | 0 | spdlog::error(ErrInfo::InfoLoading(ReadCount)); |
56 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
57 | 0 | return Unexpect(ErrCode::Value::ReadError); |
58 | 0 | } |
59 | 0 | } |
60 | 0 | Index += static_cast<size_t>(BlockSize); |
61 | 0 | FileSize -= static_cast<size_t>(BlockSize); |
62 | 0 | } |
63 | 0 | return Buf; |
64 | 0 | } |
65 | | |
66 | | // Parse module or component from file path. See "include/loader/loader.h". |
67 | | Expect<std::variant<std::unique_ptr<AST::Component::Component>, |
68 | | std::unique_ptr<AST::Module>>> |
69 | 0 | Loader::parseWasmUnit(const std::filesystem::path &FilePath) { |
70 | 0 | std::lock_guard Lock(Mutex); |
71 | | |
72 | | // Set path and check the header. |
73 | 0 | EXPECTED_TRY(FMgr.setPath(FilePath).map_error([&FilePath](auto E) { |
74 | 0 | spdlog::error(E); |
75 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
76 | 0 | return E; |
77 | 0 | })); |
78 | | |
79 | 0 | auto ReportError = [&FilePath](auto E) { |
80 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
81 | 0 | return E; |
82 | 0 | }; |
83 | |
|
84 | 0 | switch (FMgr.getHeaderType()) { |
85 | | // Filter out the Windows .dll, MacOS .dylib, or Linux .so AOT compiled |
86 | | // shared-library-WASM. |
87 | 0 | case FileMgr::FileHeader::ELF: |
88 | 0 | case FileMgr::FileHeader::DLL: |
89 | 0 | case FileMgr::FileHeader::MachO_32: |
90 | 0 | case FileMgr::FileHeader::MachO_64: { |
91 | | // AOT compiled shared-library-WASM cases. Use ldmgr to load the module. |
92 | 0 | WASMType = InputType::SharedLibrary; |
93 | 0 | FMgr.reset(); |
94 | 0 | std::shared_ptr<SharedLibrary> Library = std::make_shared<SharedLibrary>(); |
95 | 0 | EXPECTED_TRY(Library->load(FilePath).map_error(ReportError)); |
96 | 0 | EXPECTED_TRY(auto Version, Library->getVersion().map_error(ReportError)); |
97 | 0 | if (Version != AOT::kBinaryVersion) { |
98 | 0 | spdlog::error(ErrInfo::InfoMismatch(AOT::kBinaryVersion, Version)); |
99 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
100 | 0 | return Unexpect(ErrCode::Value::MalformedVersion); |
101 | 0 | } |
102 | | |
103 | 0 | EXPECTED_TRY(auto Code, Library->getWasm().map_error(ReportError)); |
104 | | // Set the binary and load module. |
105 | | // Not to use parseModule() here to keep the `WASMType` value. |
106 | 0 | EXPECTED_TRY(FMgr.setCode(Code).map_error(ReportError)); |
107 | 0 | EXPECTED_TRY(auto Unit, loadUnit().map_error(ReportError)); |
108 | 0 | if (auto Ptr = std::get_if<std::unique_ptr<AST::Module>>(&Unit); |
109 | 0 | likely(!!Ptr)) { |
110 | 0 | if (!Conf.getRuntimeConfigure().isForceInterpreter()) { |
111 | | // If the configure is set to force interpreter mode, not to load the |
112 | | // AOT related data. |
113 | 0 | EXPECTED_TRY(loadExecutable(**Ptr, Library).map_error(ReportError)); |
114 | 0 | } |
115 | 0 | } else { |
116 | 0 | spdlog::error("Component Module is not supported in AOT."sv); |
117 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
118 | 0 | return Unexpect(ErrCode::Value::IllegalGrammar); |
119 | 0 | } |
120 | 0 | return Unit; |
121 | 0 | } |
122 | 0 | default: { |
123 | | // Universal WASM, WASM, or other cases. Load and parse the module directly. |
124 | 0 | WASMType = InputType::WASM; |
125 | 0 | EXPECTED_TRY(auto Unit, loadUnit().map_error(ReportError)); |
126 | 0 | if (auto Ptr = std::get_if<std::unique_ptr<AST::Module>>(&Unit); |
127 | 0 | likely(!!Ptr)) { |
128 | 0 | if (!Conf.getRuntimeConfigure().isForceInterpreter()) { |
129 | | // If the configure is set to force interpreter mode, not to set the |
130 | | // symbol. |
131 | 0 | if (auto &Symbol = (*Ptr)->getSymbol()) { |
132 | 0 | *Symbol = IntrinsicsTable; |
133 | 0 | } |
134 | 0 | } |
135 | 0 | } |
136 | 0 | return Unit; |
137 | 0 | } |
138 | 0 | } |
139 | 0 | } |
140 | | |
141 | | // Parse module or component from byte code. See "include/loader/loader.h". |
142 | | Expect<std::variant<std::unique_ptr<AST::Component::Component>, |
143 | | std::unique_ptr<AST::Module>>> |
144 | 9.30k | Loader::parseWasmUnit(Span<const uint8_t> Code) { |
145 | 9.30k | std::lock_guard Lock(Mutex); |
146 | 9.30k | EXPECTED_TRY(FMgr.setCode(Code)); |
147 | 9.30k | switch (FMgr.getHeaderType()) { |
148 | | // Filter out the Windows .dll, MacOS .dylib, or Linux .so AOT compiled |
149 | | // shared-library-WASM. |
150 | 1 | case FileMgr::FileHeader::ELF: |
151 | 2 | case FileMgr::FileHeader::DLL: |
152 | 3 | case FileMgr::FileHeader::MachO_32: |
153 | 4 | case FileMgr::FileHeader::MachO_64: |
154 | 4 | spdlog::error("Might an invalid wasm file"sv); |
155 | 4 | spdlog::error(ErrCode::Value::MalformedMagic); |
156 | 4 | spdlog::error( |
157 | 4 | " The AOT compiled WASM shared library is not supported for loading " |
158 | 4 | "from memory. Please use the universal WASM binary or pure WASM, or " |
159 | 4 | "load the AOT compiled WASM shared library from file."sv); |
160 | 4 | return Unexpect(ErrCode::Value::MalformedMagic); |
161 | 9.30k | default: |
162 | 9.30k | break; |
163 | 9.30k | } |
164 | | // For malformed header checking, handle in the module loading. |
165 | 9.30k | WASMType = InputType::WASM; |
166 | 9.30k | return loadUnit(); |
167 | 9.30k | } |
168 | | |
169 | | // Parse module from file path. See "include/loader/loader.h". |
170 | | Expect<std::unique_ptr<AST::Module>> |
171 | 0 | Loader::parseModule(const std::filesystem::path &FilePath) { |
172 | 0 | EXPECTED_TRY(auto ComponentOrModule, parseWasmUnit(FilePath)); |
173 | 0 | if (auto M = std::get_if<std::unique_ptr<AST::Module>>(&ComponentOrModule)) { |
174 | 0 | return std::move(*M); |
175 | 0 | } |
176 | 0 | return Unexpect(ErrCode::Value::MalformedVersion); |
177 | 0 | } |
178 | | |
179 | | // Parse module from byte code. See "include/loader/loader.h". |
180 | | Expect<std::unique_ptr<AST::Module>> |
181 | 9.30k | Loader::parseModule(Span<const uint8_t> Code) { |
182 | 9.30k | EXPECTED_TRY(auto ComponentOrModule, parseWasmUnit(Code)); |
183 | 3.80k | if (auto M = std::get_if<std::unique_ptr<AST::Module>>(&ComponentOrModule)) { |
184 | 3.80k | return std::move(*M); |
185 | 3.80k | } |
186 | 0 | return Unexpect(ErrCode::Value::MalformedVersion); |
187 | 3.80k | } |
188 | | |
189 | | // Load module or component unit. See "include/loader/loader.h". |
190 | | Expect<std::variant<std::unique_ptr<AST::Component::Component>, |
191 | | std::unique_ptr<AST::Module>>> |
192 | 9.30k | Loader::loadUnit() { |
193 | 9.30k | EXPECTED_TRY(auto Preamble, loadPreamble()); |
194 | 9.19k | auto &[WasmMagic, Ver] = Preamble; |
195 | 9.19k | if (Ver == ModuleVersion) { |
196 | 9.18k | auto Mod = std::make_unique<AST::Module>(); |
197 | 9.18k | Mod->getMagic() = WasmMagic; |
198 | 9.18k | Mod->getVersion() = Ver; |
199 | 9.18k | if (!Conf.getRuntimeConfigure().isForceInterpreter()) { |
200 | 0 | EXPECTED_TRY(loadModuleAOT(Mod->getAOTSection())); |
201 | 0 | } |
202 | | // Seek to the position after the binary header. |
203 | 9.18k | FMgr.seek(8); |
204 | 9.18k | EXPECTED_TRY(loadModule(*Mod)); |
205 | | |
206 | | // Load library from AOT Section for the universal WASM case. |
207 | | // For the force interpreter mode, skip this. |
208 | 3.80k | if (!Conf.getRuntimeConfigure().isForceInterpreter() && |
209 | 3.80k | WASMType == InputType::UniversalWASM) { |
210 | 0 | EXPECTED_TRY(loadUniversalWASM(*Mod)); |
211 | 0 | } |
212 | 3.80k | return Mod; |
213 | 3.80k | } else if (Ver == ComponentVersion) { |
214 | 1 | if (!Conf.hasProposal(Proposal::Component)) { |
215 | 1 | return logNeedProposal(ErrCode::Value::MalformedVersion, |
216 | 1 | Proposal::Component, FMgr.getLastOffset(), |
217 | 1 | ASTNodeAttr::Component); |
218 | 1 | } |
219 | 0 | spdlog::warn("component model is an experimental proposal"sv); |
220 | 0 | auto Comp = std::make_unique<AST::Component::Component>(); |
221 | 0 | Comp->getMagic() = WasmMagic; |
222 | 0 | Comp->getVersion() = {Ver[0], Ver[1]}; |
223 | 0 | Comp->getLayer() = {Ver[2], Ver[3]}; |
224 | 0 | EXPECTED_TRY(loadComponent(*Comp)); |
225 | 0 | return Comp; |
226 | 4 | } else { |
227 | 4 | return logLoadError(ErrCode::Value::MalformedVersion, FMgr.getLastOffset(), |
228 | 4 | ASTNodeAttr::Component); |
229 | 4 | } |
230 | 9.19k | } |
231 | | |
232 | | // Serialize module into byte code. See "include/loader/loader.h". |
233 | 0 | Expect<std::vector<Byte>> Loader::serializeModule(const AST::Module &Mod) { |
234 | 0 | return Ser.serializeModule(Mod); |
235 | 0 | } |
236 | | |
237 | | // Helper function to set the function type for tag. |
238 | | void Loader::setTagFunctionType(AST::TagSection &TagSec, |
239 | | AST::ImportSection &ImportSec, |
240 | 3.82k | AST::TypeSection &TypeSec) { |
241 | 3.82k | auto &TypeVec = TypeSec.getContent(); |
242 | 3.82k | for (auto &TgType : TagSec.getContent()) { |
243 | 0 | auto TypeIdx = TgType.getTypeIdx(); |
244 | | // Invalid type index would be checked during validation. |
245 | 0 | if (TypeIdx < TypeVec.size()) { |
246 | 0 | TgType.setDefType(&TypeVec[TypeIdx]); |
247 | 0 | } |
248 | 0 | } |
249 | 3.82k | for (auto &Desc : ImportSec.getContent()) { |
250 | 391 | if (Desc.getExternalType() == ExternalType::Tag) { |
251 | 0 | auto &TgType = Desc.getExternalTagType(); |
252 | 0 | auto TypeIdx = TgType.getTypeIdx(); |
253 | | // Invalid type index would be checked during validation. |
254 | 0 | if (TypeIdx < TypeVec.size()) { |
255 | 0 | TgType.setDefType(&TypeVec[TypeIdx]); |
256 | 0 | } |
257 | 0 | } |
258 | 391 | } |
259 | 3.82k | } |
260 | | |
261 | | } // namespace Loader |
262 | | } // namespace WasmEdge |