/src/WasmEdge/lib/executor/instantiate/import.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | #include "executor/executor.h" |
5 | | |
6 | | #include "common/errinfo.h" |
7 | | #include "common/spdlog.h" |
8 | | |
9 | | #include <cstdint> |
10 | | #include <string_view> |
11 | | #include <utility> |
12 | | |
13 | | using namespace std::literals; |
14 | | |
15 | | namespace WasmEdge { |
16 | | namespace Executor { |
17 | | |
18 | | namespace { |
19 | | template <typename... Args> |
20 | | auto logMatchError(std::string_view ModName, std::string_view ExtName, |
21 | 0 | ExternalType ExtType, Args &&...Values) { |
22 | 0 | spdlog::error(ErrCode::Value::IncompatibleImportType); |
23 | 0 | spdlog::error(ErrInfo::InfoMismatch(std::forward<Args>(Values)...)); |
24 | 0 | spdlog::error(ErrInfo::InfoLinking(ModName, ExtName, ExtType)); |
25 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Import)); |
26 | 0 | return Unexpect(ErrCode::Value::IncompatibleImportType); |
27 | 0 | } Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<WasmEdge::ExternalType const&, WasmEdge::ExternalType>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, WasmEdge::ExternalType const&, WasmEdge::ExternalType&&) Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&, std::__1::vector<WasmEdge::ValType, std::__1::allocator<WasmEdge::ValType> > const&) Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<WasmEdge::ValType const&, bool, unsigned long, unsigned long, WasmEdge::ValType const&, bool, unsigned long, unsigned long>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, WasmEdge::ValType const&, bool&&, unsigned long&&, unsigned long&&, WasmEdge::ValType const&, bool&&, unsigned long&&, unsigned long&&) Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<bool, unsigned long, unsigned long, bool, unsigned long, unsigned long>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, bool&&, unsigned long&&, unsigned long&&, bool&&, unsigned long&&, unsigned long&&) Unexecuted instantiation: import.cpp:auto WasmEdge::Executor::(anonymous namespace)::logMatchError<WasmEdge::ValType const&, WasmEdge::ValMut, WasmEdge::ValType const&, WasmEdge::ValMut>(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, WasmEdge::ExternalType, WasmEdge::ValType const&, WasmEdge::ValMut&&, WasmEdge::ValType const&, WasmEdge::ValMut&&) |
28 | | |
29 | | auto logUnknownError(std::string_view ModName, std::string_view ExtName, |
30 | 0 | ExternalType ExtType) { |
31 | 0 | spdlog::error(ErrCode::Value::UnknownImport); |
32 | 0 | spdlog::error(ErrInfo::InfoLinking(ModName, ExtName, ExtType)); |
33 | 0 | spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Desc_Import)); |
34 | 0 | return Unexpect(ErrCode::Value::UnknownImport); |
35 | 0 | } |
36 | | |
37 | 0 | bool matchLimit(const AST::Limit &Exp, const AST::Limit &Got) { |
38 | 0 | if (Exp.getAddrType() != Got.getAddrType()) { |
39 | 0 | return false; |
40 | 0 | } |
41 | 0 | if (Exp.isShared() != Got.isShared()) { |
42 | 0 | return false; |
43 | 0 | } |
44 | 0 | if ((Got.getMin() < Exp.getMin()) || (Exp.hasMax() && !Got.hasMax())) { |
45 | 0 | return false; |
46 | 0 | } |
47 | 0 | if (Exp.hasMax() && Got.hasMax() && Got.getMax() > Exp.getMax()) { |
48 | 0 | return false; |
49 | 0 | } |
50 | 0 | return true; |
51 | 0 | } |
52 | | |
53 | | Expect<void> |
54 | | checkImportMatched(std::string_view ModName, std::string_view ExtName, |
55 | | const ExternalType ExtType, |
56 | 0 | const Runtime::Instance::ModuleInstance &ModInst) { |
57 | 0 | switch (ExtType) { |
58 | 0 | case ExternalType::Function: |
59 | 0 | if (auto Res = ModInst.findFuncExports(ExtName); likely(Res != nullptr)) { |
60 | 0 | return {}; |
61 | 0 | } |
62 | 0 | break; |
63 | 0 | case ExternalType::Table: |
64 | 0 | if (auto Res = ModInst.findTableExports(ExtName); likely(Res != nullptr)) { |
65 | 0 | return {}; |
66 | 0 | } |
67 | 0 | break; |
68 | 0 | case ExternalType::Memory: |
69 | 0 | if (auto Res = ModInst.findMemoryExports(ExtName); likely(Res != nullptr)) { |
70 | 0 | return {}; |
71 | 0 | } |
72 | 0 | break; |
73 | 0 | case ExternalType::Global: |
74 | 0 | if (auto Res = ModInst.findGlobalExports(ExtName); likely(Res != nullptr)) { |
75 | 0 | return {}; |
76 | 0 | } |
77 | 0 | break; |
78 | 0 | case ExternalType::Tag: |
79 | 0 | if (auto Res = ModInst.findTagExports(ExtName); likely(Res != nullptr)) { |
80 | 0 | return {}; |
81 | 0 | } |
82 | 0 | break; |
83 | 0 | default: |
84 | 0 | assumingUnreachable(); |
85 | 0 | } |
86 | | |
87 | | // Check for error external types or unknown imports. |
88 | 0 | if (ModInst.findFuncExports(ExtName)) { |
89 | 0 | return logMatchError(ModName, ExtName, ExtType, ExtType, |
90 | 0 | ExternalType::Function); |
91 | 0 | } |
92 | 0 | if (ModInst.findTableExports(ExtName)) { |
93 | 0 | return logMatchError(ModName, ExtName, ExtType, ExtType, |
94 | 0 | ExternalType::Table); |
95 | 0 | } |
96 | 0 | if (ModInst.findMemoryExports(ExtName)) { |
97 | 0 | return logMatchError(ModName, ExtName, ExtType, ExtType, |
98 | 0 | ExternalType::Memory); |
99 | 0 | } |
100 | 0 | if (ModInst.findTagExports(ExtName)) { |
101 | 0 | return logMatchError(ModName, ExtName, ExtType, ExtType, ExternalType::Tag); |
102 | 0 | } |
103 | 0 | if (ModInst.findGlobalExports(ExtName)) { |
104 | 0 | return logMatchError(ModName, ExtName, ExtType, ExtType, |
105 | 0 | ExternalType::Global); |
106 | 0 | } |
107 | | |
108 | 0 | return logUnknownError(ModName, ExtName, ExtType); |
109 | 0 | } |
110 | | } // namespace |
111 | | |
112 | | // Instantiate imports. See "include/executor/executor.h". |
113 | | Expect<void> Executor::instantiate( |
114 | | std::function<const Runtime::Instance::ModuleInstance *(std::string_view)> |
115 | | ModuleFinder, |
116 | | Runtime::Instance::ModuleInstance &ModInst, |
117 | 0 | const AST::ImportSection &ImportSec) { |
118 | | // Iterate and instantiate import descriptions. |
119 | 0 | for (const auto &ImpDesc : ImportSec.getContent()) { |
120 | | // Get data from import description and find import module. |
121 | 0 | auto ExtType = ImpDesc.getExternalType(); |
122 | 0 | auto ModName = ImpDesc.getModuleName(); |
123 | 0 | auto ExtName = ImpDesc.getExternalName(); |
124 | 0 | const auto *ImpModInst = ModuleFinder(ModName); |
125 | 0 | if (unlikely(ImpModInst == nullptr)) { |
126 | 0 | auto Res = logUnknownError(ModName, ExtName, ExtType); |
127 | 0 | if (ModName == "wasi_snapshot_preview1"sv) { |
128 | 0 | spdlog::error(" This is a WASI related import. Please ensure that " |
129 | 0 | "you've turned on the WASI configuration."sv); |
130 | 0 | } else if (ModName == "wasi_nn"sv) { |
131 | 0 | spdlog::error(" This is a WASI-NN related import. Please ensure " |
132 | 0 | "that you've turned on the WASI-NN configuration and " |
133 | 0 | "installed the WASI-NN plug-in."sv); |
134 | 0 | } else if (ModName == "wasi_crypto_common"sv || |
135 | 0 | ModName == "wasi_crypto_asymmetric_common"sv || |
136 | 0 | ModName == "wasi_crypto_kx"sv || |
137 | 0 | ModName == "wasi_crypto_signatures"sv || |
138 | 0 | ModName == "wasi_crypto_symmetric"sv) { |
139 | 0 | spdlog::error(" This is a WASI-Crypto related import. Please ensure " |
140 | 0 | "that you've turned on the WASI-Crypto configuration and " |
141 | 0 | "installed the WASI-Crypto plug-in."); |
142 | 0 | } else if (ModName == "env"sv) { |
143 | 0 | spdlog::error( |
144 | 0 | " This may be the import of host environment like JavaScript or " |
145 | 0 | "Golang. Please check that you've registered the necessary host " |
146 | 0 | "modules from the host programming language."sv); |
147 | 0 | } |
148 | 0 | return Res; |
149 | 0 | } |
150 | 0 | EXPECTED_TRY(checkImportMatched(ModName, ExtName, ExtType, *ImpModInst)); |
151 | | |
152 | | // Add the imports to the module instance. |
153 | 0 | switch (ExtType) { |
154 | 0 | case ExternalType::Function: { |
155 | | // Get the function type index. The external type is checked in |
156 | | // validation. |
157 | 0 | uint32_t TypeIdx = ImpDesc.getExternalFuncTypeIdx(); |
158 | | // Import matching. |
159 | 0 | auto *ImpInst = ImpModInst->findFuncExports(ExtName); |
160 | | // External function type should match the import function type in |
161 | | // description. |
162 | |
|
163 | 0 | if (!AST::TypeMatcher::matchType(ModInst.getTypeList(), TypeIdx, |
164 | 0 | ImpModInst->getTypeList(), |
165 | 0 | ImpInst->getTypeIndex())) { |
166 | 0 | const auto &ExpDefType = **ModInst.getType(TypeIdx); |
167 | 0 | bool IsMatchV2 = false; |
168 | 0 | const auto &ExpFuncType = ExpDefType.getCompositeType().getFuncType(); |
169 | 0 | const auto &ImpFuncType = ImpInst->getFuncType(); |
170 | 0 | if (ModName == "wasi_snapshot_preview1"sv) { |
171 | | /* |
172 | | * The following functions should provide V1 and V2. |
173 | | "sock_open_v2", |
174 | | "sock_bind_v2", |
175 | | "sock_connect_v2", |
176 | | "sock_listen_v2", |
177 | | "sock_accept_v2", |
178 | | "sock_recv_v2", |
179 | | "sock_recv_from_v2", |
180 | | "sock_send_v2", |
181 | | "sock_send_to_v2", |
182 | | "sock_getlocaladdr_v2", |
183 | | "sock_getpeeraddr_v2" |
184 | | */ |
185 | 0 | std::vector<std::string_view> CompatibleWASISocketAPI = { |
186 | 0 | "sock_open"sv, "sock_bind"sv, "sock_connect"sv, |
187 | 0 | "sock_listen"sv, "sock_accept"sv, "sock_recv"sv, |
188 | 0 | "sock_recv_from"sv, "sock_send"sv, "sock_send_to"sv, |
189 | 0 | "sock_getlocaladdr"sv, "sock_getpeeraddr"sv}; |
190 | 0 | for (auto Iter = CompatibleWASISocketAPI.begin(); |
191 | 0 | Iter != CompatibleWASISocketAPI.end(); Iter++) { |
192 | 0 | if (ExtName == *Iter) { |
193 | 0 | auto *ImpInstV2 = |
194 | 0 | ImpModInst->findFuncExports(std::string(*Iter) + "_v2"); |
195 | 0 | if (ImpInstV2 != nullptr && |
196 | 0 | AST::TypeMatcher::matchType(ModInst.getTypeList(), TypeIdx, |
197 | 0 | ImpModInst->getTypeList(), |
198 | 0 | ImpInstV2->getTypeIndex())) { |
199 | | // Try to match the new version |
200 | 0 | ImpInst = ImpInstV2; |
201 | 0 | IsMatchV2 = true; |
202 | 0 | break; |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | 0 | } |
207 | 0 | if (!IsMatchV2) { |
208 | 0 | return logMatchError( |
209 | 0 | ModName, ExtName, ExtType, ExpFuncType.getParamTypes(), |
210 | 0 | ExpFuncType.getReturnTypes(), ImpFuncType.getParamTypes(), |
211 | 0 | ImpFuncType.getReturnTypes()); |
212 | 0 | } |
213 | 0 | } |
214 | | // Set the matched function address in the module instance. |
215 | 0 | ModInst.importFunction(ImpInst); |
216 | | |
217 | | // If the imported function is a WASI function, mark it in the module. |
218 | 0 | if (!ModInst.getWASIModule() && ModName == "wasi_snapshot_preview1"sv) { |
219 | 0 | ModInst.setWASIModule(ImpModInst); |
220 | 0 | } |
221 | 0 | break; |
222 | 0 | } |
223 | 0 | case ExternalType::Table: { |
224 | | // Get table type. External type checked in validation. |
225 | 0 | const auto &TabType = ImpDesc.getExternalTableType(); |
226 | 0 | const auto &TabLim = TabType.getLimit(); |
227 | | // Import matching. External table type should match the one in import |
228 | | // description. |
229 | 0 | auto *ImpInst = ImpModInst->findTableExports(ExtName); |
230 | 0 | const auto &ImpType = ImpInst->getTableType(); |
231 | 0 | const auto &ImpLim = ImpType.getLimit(); |
232 | | // External table reference type should match the import table reference |
233 | | // type in description, and vice versa. |
234 | 0 | if (!AST::TypeMatcher::matchType( |
235 | 0 | ModInst.getTypeList(), TabType.getRefType(), |
236 | 0 | ImpModInst->getTypeList(), ImpType.getRefType()) || |
237 | 0 | !AST::TypeMatcher::matchType( |
238 | 0 | ImpModInst->getTypeList(), ImpType.getRefType(), |
239 | 0 | ModInst.getTypeList(), TabType.getRefType()) || |
240 | 0 | !matchLimit(TabLim, ImpLim)) { |
241 | 0 | return logMatchError(ModName, ExtName, ExtType, TabType.getRefType(), |
242 | 0 | TabLim.hasMax(), TabLim.getMin(), TabLim.getMax(), |
243 | 0 | ImpType.getRefType(), ImpLim.hasMax(), |
244 | 0 | ImpLim.getMin(), ImpLim.getMax()); |
245 | 0 | } |
246 | | // Set the matched table address in the module instance. |
247 | 0 | ModInst.importTable(ImpInst); |
248 | 0 | break; |
249 | 0 | } |
250 | 0 | case ExternalType::Memory: { |
251 | | // Get memory type. External type checked in validation. |
252 | 0 | const auto &MemType = ImpDesc.getExternalMemoryType(); |
253 | 0 | const auto &MemLim = MemType.getLimit(); |
254 | | // Import matching. External memory type should match the one in import |
255 | | // description. |
256 | 0 | auto *ImpInst = ImpModInst->findMemoryExports(ExtName); |
257 | 0 | const auto &ImpLim = ImpInst->getMemoryType().getLimit(); |
258 | 0 | if (!matchLimit(MemLim, ImpLim)) { |
259 | 0 | return logMatchError(ModName, ExtName, ExtType, MemLim.hasMax(), |
260 | 0 | MemLim.getMin(), MemLim.getMax(), ImpLim.hasMax(), |
261 | 0 | ImpLim.getMin(), ImpLim.getMax()); |
262 | 0 | } |
263 | | // Set the matched memory address in the module instance. |
264 | 0 | ModInst.importMemory(ImpInst); |
265 | 0 | break; |
266 | 0 | } |
267 | 0 | case ExternalType::Tag: { |
268 | | // Get tag type. External type checked in validation. |
269 | 0 | const auto &TagType = ImpDesc.getExternalTagType(); |
270 | | // Import matching. |
271 | 0 | auto *ImpInst = ImpModInst->findTagExports(ExtName); |
272 | 0 | if (!AST::TypeMatcher::matchType( |
273 | 0 | ModInst.getTypeList(), TagType.getTypeIdx(), |
274 | 0 | ImpModInst->getTypeList(), ImpInst->getTagType().getTypeIdx())) { |
275 | 0 | const auto &ExpFuncType = |
276 | 0 | TagType.getDefType().getCompositeType().getFuncType(); |
277 | 0 | const auto &ImpFuncType = |
278 | 0 | ImpInst->getTagType().getDefType().getCompositeType().getFuncType(); |
279 | 0 | return logMatchError( |
280 | 0 | ModName, ExtName, ExtType, ExpFuncType.getParamTypes(), |
281 | 0 | ExpFuncType.getReturnTypes(), ImpFuncType.getParamTypes(), |
282 | 0 | ImpFuncType.getReturnTypes()); |
283 | 0 | } |
284 | 0 | ModInst.importTag(ImpInst); |
285 | 0 | break; |
286 | 0 | } |
287 | 0 | case ExternalType::Global: { |
288 | | // Get global type. External type checked in validation. |
289 | 0 | const auto &GlobType = ImpDesc.getExternalGlobalType(); |
290 | | // Import matching. External global type should match the one in |
291 | | // import description. |
292 | 0 | auto *ImpInst = ImpModInst->findGlobalExports(ExtName); |
293 | 0 | const auto &ImpType = ImpInst->getGlobalType(); |
294 | 0 | bool IsMatch = false; |
295 | 0 | if (ImpType.getValMut() == GlobType.getValMut()) { |
296 | | // For both const or both var: external global value type should match |
297 | | // the import global value type in description. |
298 | 0 | IsMatch = AST::TypeMatcher::matchType( |
299 | 0 | ModInst.getTypeList(), GlobType.getValType(), |
300 | 0 | ImpModInst->getTypeList(), ImpType.getValType()); |
301 | 0 | if (ImpType.getValMut() == ValMut::Var) { |
302 | | // If both var: import global value type in description should also |
303 | | // match the external global value type. |
304 | 0 | IsMatch &= AST::TypeMatcher::matchType( |
305 | 0 | ImpModInst->getTypeList(), ImpType.getValType(), |
306 | 0 | ModInst.getTypeList(), GlobType.getValType()); |
307 | 0 | } |
308 | 0 | } |
309 | 0 | if (!IsMatch) { |
310 | 0 | return logMatchError(ModName, ExtName, ExtType, GlobType.getValType(), |
311 | 0 | GlobType.getValMut(), ImpType.getValType(), |
312 | 0 | ImpType.getValMut()); |
313 | 0 | } |
314 | | // Set the matched global address in the module instance. |
315 | 0 | ModInst.importGlobal(ImpInst); |
316 | 0 | break; |
317 | 0 | } |
318 | 0 | default: |
319 | 0 | assumingUnreachable(); |
320 | 0 | } |
321 | 0 | } |
322 | 0 | return {}; |
323 | 0 | } |
324 | | |
325 | | } // namespace Executor |
326 | | } // namespace WasmEdge |