/src/WasmEdge/lib/loader/loader.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
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 | | // Open the shared library long enough to validate its WasmEdge AOT |
92 | | // version and extract the embedded WASM bytes. Whether the native |
93 | | // handle stays alive depends on the configured RunMode. |
94 | 0 | WASMType = InputType::SharedLibrary; |
95 | 0 | FMgr.reset(); |
96 | 0 | std::shared_ptr<SharedLibrary> Library = std::make_shared<SharedLibrary>(); |
97 | 0 | EXPECTED_TRY(Library->load(FilePath).map_error(ReportError)); |
98 | 0 | EXPECTED_TRY(auto Version, Library->getVersion().map_error(ReportError)); |
99 | 0 | if (Version != AOT::kBinaryVersion) { |
100 | 0 | spdlog::error(ErrInfo::InfoMismatch(AOT::kBinaryVersion, Version)); |
101 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
102 | 0 | return Unexpect(ErrCode::Value::MalformedVersion); |
103 | 0 | } |
104 | | |
105 | 0 | EXPECTED_TRY(auto Code, Library->getWasm().map_error(ReportError)); |
106 | |
|
107 | 0 | if (Conf.getRuntimeConfigure().getRunMode() != RunMode::AOT) { |
108 | | // Non-AOT mode: drop the native handle now and re-parse the embedded |
109 | | // WASM bytes as plain WASM. No AOT function symbol from the shared |
110 | | // library is resolved or called. |
111 | 0 | Library.reset(); |
112 | 0 | return parseWasmUnit(Code); |
113 | 0 | } |
114 | | |
115 | | // AOT mode: keep the library alive and load executable symbols. |
116 | 0 | EXPECTED_TRY(FMgr.setCode(Code).map_error(ReportError)); |
117 | 0 | EXPECTED_TRY(auto Unit, loadUnit().map_error(ReportError)); |
118 | 0 | if (auto Ptr = std::get_if<std::unique_ptr<AST::Module>>(&Unit); |
119 | 0 | likely(!!Ptr)) { |
120 | 0 | EXPECTED_TRY(loadExecutable(**Ptr, Library).map_error(ReportError)); |
121 | 0 | } else { |
122 | 0 | spdlog::error("Component Module is not supported in AOT."sv); |
123 | 0 | spdlog::error(ErrInfo::InfoFile(FilePath)); |
124 | 0 | return Unexpect(ErrCode::Value::IllegalGrammar); |
125 | 0 | } |
126 | 0 | return Unit; |
127 | 0 | } |
128 | 0 | default: { |
129 | | // Universal WASM, WASM, or other cases. Load and parse the module directly. |
130 | | // The intrinsics-table pointer (if any executable is loaded) is patched |
131 | | // inside loadExecutable, so no symbol manipulation is needed here. |
132 | 0 | WASMType = InputType::WASM; |
133 | 0 | return loadUnit().map_error(ReportError); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | } |
137 | | |
138 | | // Parse module or component from byte code. See "include/loader/loader.h". |
139 | | Expect<std::variant<std::unique_ptr<AST::Component::Component>, |
140 | | std::unique_ptr<AST::Module>>> |
141 | 9.46k | Loader::parseWasmUnit(Span<const uint8_t> Code) { |
142 | 9.46k | std::lock_guard Lock(Mutex); |
143 | 9.46k | EXPECTED_TRY(FMgr.setCode(Code)); |
144 | 9.46k | switch (FMgr.getHeaderType()) { |
145 | | // Filter out the Windows .dll, macOS .dylib, or Linux .so AOT compiled |
146 | | // shared-library-WASM. |
147 | 1 | case FileMgr::FileHeader::ELF: |
148 | 2 | case FileMgr::FileHeader::DLL: |
149 | 3 | case FileMgr::FileHeader::MachO_32: |
150 | 4 | case FileMgr::FileHeader::MachO_64: |
151 | 4 | spdlog::error("Might an invalid wasm file"sv); |
152 | 4 | spdlog::error(ErrCode::Value::MalformedMagic); |
153 | 4 | spdlog::error( |
154 | 4 | " The AOT compiled WASM shared library is not supported for loading " |
155 | 4 | "from memory. Please use the universal WASM binary or pure WASM, or " |
156 | 4 | "load the AOT compiled WASM shared library from file."sv); |
157 | 4 | return Unexpect(ErrCode::Value::MalformedMagic); |
158 | 9.45k | default: |
159 | 9.45k | break; |
160 | 9.46k | } |
161 | | // For malformed header checking, handle in the module loading. |
162 | 9.45k | WASMType = InputType::WASM; |
163 | 9.45k | return loadUnit(); |
164 | 9.46k | } |
165 | | |
166 | | // Parse module from file path. See "include/loader/loader.h". |
167 | | Expect<std::unique_ptr<AST::Module>> |
168 | 0 | Loader::parseModule(const std::filesystem::path &FilePath) { |
169 | 0 | EXPECTED_TRY(auto ComponentOrModule, parseWasmUnit(FilePath)); |
170 | 0 | if (auto M = std::get_if<std::unique_ptr<AST::Module>>(&ComponentOrModule)) { |
171 | 0 | return std::move(*M); |
172 | 0 | } |
173 | 0 | return Unexpect(ErrCode::Value::MalformedVersion); |
174 | 0 | } |
175 | | |
176 | | // Parse module from byte code. See "include/loader/loader.h". |
177 | | Expect<std::unique_ptr<AST::Module>> |
178 | 9.46k | Loader::parseModule(Span<const uint8_t> Code) { |
179 | 9.46k | EXPECTED_TRY(auto ComponentOrModule, parseWasmUnit(Code)); |
180 | 4.29k | if (auto M = std::get_if<std::unique_ptr<AST::Module>>(&ComponentOrModule)) { |
181 | 4.29k | return std::move(*M); |
182 | 4.29k | } |
183 | 0 | return Unexpect(ErrCode::Value::MalformedVersion); |
184 | 4.29k | } |
185 | | |
186 | | // Load module or component unit. See "include/loader/loader.h". |
187 | | Expect<std::variant<std::unique_ptr<AST::Component::Component>, |
188 | | std::unique_ptr<AST::Module>>> |
189 | 9.45k | Loader::loadUnit() { |
190 | 9.45k | EXPECTED_TRY(auto Preamble, loadPreamble()); |
191 | 9.38k | auto &[WasmMagic, Ver] = Preamble; |
192 | 9.38k | if (Ver == ModuleVersion) { |
193 | 9.37k | auto Mod = std::make_unique<AST::Module>(); |
194 | 9.37k | Mod->getMagic() = WasmMagic; |
195 | 9.37k | Mod->getVersion() = Ver; |
196 | 9.37k | if (Conf.getRuntimeConfigure().getRunMode() == RunMode::AOT) { |
197 | 0 | EXPECTED_TRY(loadModuleAOT(Mod->getAOTSection())); |
198 | 0 | } |
199 | | // Seek to the position after the binary header. |
200 | 9.37k | FMgr.seek(8); |
201 | 9.37k | EXPECTED_TRY(loadModule(*Mod)); |
202 | | |
203 | | // Load library from AOT Section for the universal WASM case only when the |
204 | | // configured run mode is AOT. |
205 | 4.29k | if (Conf.getRuntimeConfigure().getRunMode() == RunMode::AOT) { |
206 | 0 | if (WASMType == InputType::UniversalWASM) { |
207 | 0 | EXPECTED_TRY(loadUniversalWASM(*Mod)); |
208 | 0 | } else if (WASMType == InputType::WASM) { |
209 | | // AOT requested on a plain .wasm with no AOT artifact. |
210 | 0 | spdlog::warn("AOT was requested but the input has no AOT artifact, " |
211 | 0 | "falling back to interpreter."sv); |
212 | 0 | } |
213 | 0 | } |
214 | 4.29k | return Mod; |
215 | 4.29k | } else if (Ver == ComponentVersion) { |
216 | 1 | if (!Conf.hasProposal(Proposal::Component)) { |
217 | 1 | return logNeedProposal(ErrCode::Value::MalformedVersion, |
218 | 1 | Proposal::Component, FMgr.getLastOffset(), |
219 | 1 | ASTNodeAttr::Component); |
220 | 1 | } |
221 | 0 | spdlog::warn("component model is an experimental proposal"sv); |
222 | 0 | auto Comp = std::make_unique<AST::Component::Component>(); |
223 | 0 | Comp->getMagic() = WasmMagic; |
224 | 0 | Comp->getVersion() = {Ver[0], Ver[1]}; |
225 | 0 | Comp->getLayer() = {Ver[2], Ver[3]}; |
226 | 0 | EXPECTED_TRY(loadComponent(*Comp)); |
227 | 0 | return Comp; |
228 | 4 | } else { |
229 | 4 | return logLoadError(ErrCode::Value::MalformedVersion, FMgr.getLastOffset(), |
230 | 4 | ASTNodeAttr::Component); |
231 | 4 | } |
232 | 9.38k | } |
233 | | |
234 | | // Serialize module into byte code. See "include/loader/loader.h". |
235 | 0 | Expect<std::vector<Byte>> Loader::serializeModule(const AST::Module &Mod) { |
236 | 0 | return Ser.serializeModule(Mod); |
237 | 0 | } |
238 | | |
239 | | // Helper function to set the function type for tag. |
240 | | void Loader::setTagFunctionType(AST::TagSection &TagSec, |
241 | | AST::ImportSection &ImportSec, |
242 | 4.31k | AST::TypeSection &TypeSec) { |
243 | 4.31k | auto &TypeVec = TypeSec.getContent(); |
244 | 4.31k | for (auto &TgType : TagSec.getContent()) { |
245 | 158 | auto TypeIdx = TgType.getTypeIdx(); |
246 | | // Invalid type index would be checked during validation. |
247 | 158 | if (TypeIdx < TypeVec.size()) { |
248 | 111 | TgType.setDefType(&TypeVec[TypeIdx]); |
249 | 111 | } |
250 | 158 | } |
251 | 4.31k | for (auto &Desc : ImportSec.getContent()) { |
252 | 662 | if (Desc.getExternalType() == ExternalType::Tag) { |
253 | 26 | auto &TgType = Desc.getExternalTagType(); |
254 | 26 | auto TypeIdx = TgType.getTypeIdx(); |
255 | | // Invalid type index would be checked during validation. |
256 | 26 | if (TypeIdx < TypeVec.size()) { |
257 | 15 | TgType.setDefType(&TypeVec[TypeIdx]); |
258 | 15 | } |
259 | 26 | } |
260 | 662 | } |
261 | 4.31k | } |
262 | | |
263 | | } // namespace Loader |
264 | | } // namespace WasmEdge |