/src/WasmEdge/lib/api/wasmedge_compat.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | // >>>>>>>> WasmEdge compat ABI shims >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
5 | | // |
6 | | // Several C API functions changed their signatures across releases. Each such |
7 | | // change pins the previous ABI under its historical version node (bound with |
8 | | // `.symver`) while `wasmedge.cpp` keeps the new default version, so binaries |
9 | | // built against the old library keep resolving the old symbol. |
10 | | // |
11 | | // - 0.16 -> 0.17: five functions changed from the by-value `WasmEdge_Limit` |
12 | | // struct to the opaque `WasmEdge_LimitContext` handle. Old ABI pinned at |
13 | | // `@WASMEDGE_0.16`, new default `@@WASMEDGE_0.17`. |
14 | | // - 0.17 -> 0.18: the four `WasmEdge_ModuleInstanceAdd{Function,Table,Memory, |
15 | | // Global}` functions changed their return type from `void` to |
16 | | // `WasmEdge_Result`. Old ABI pinned at `@WASMEDGE_0.17`, new default |
17 | | // `@@WASMEDGE_0.18`. |
18 | | // |
19 | | // The shims are frozen copies of the previous-release implementations so the |
20 | | // old ABIs stay pinned bit-for-bit. |
21 | | // |
22 | | // Compiled only into the shared library on Linux/Android (see CMakeLists.txt). |
23 | | // Kept in a separate TU because under ThinLTO the module-level `.symver` asm |
24 | | // would otherwise collide with the in-IR definition of the same public symbol. |
25 | | |
26 | | #include "wasmedge/wasmedge.h" |
27 | | |
28 | | #include "ast/type.h" |
29 | | #include "runtime/instance/module.h" |
30 | | |
31 | | #include <algorithm> |
32 | | #include <array> |
33 | | #include <cstdint> |
34 | | #include <memory> |
35 | | #include <string_view> |
36 | | |
37 | | #if defined(__ELF__) && (defined(__linux__) || defined(__ANDROID__)) |
38 | | |
39 | | namespace { |
40 | | using namespace WasmEdge; |
41 | | |
42 | | // Local copies of the `wasmedge.cpp` helpers, kept here to stay self-contained. |
43 | 0 | inline ValType genValType(const WasmEdge_ValType &T) noexcept { |
44 | 0 | std::array<uint8_t, 8> R; |
45 | 0 | std::copy_n(T.Data, 8, R.begin()); |
46 | 0 | return ValType(R); |
47 | 0 | } |
48 | 0 | inline auto *toTabTypeCxt(AST::TableType *Cxt) noexcept { |
49 | 0 | return reinterpret_cast<WasmEdge_TableTypeContext *>(Cxt); |
50 | 0 | } |
51 | | inline const auto * |
52 | 0 | fromTabTypeCxt(const WasmEdge_TableTypeContext *Cxt) noexcept { |
53 | 0 | return reinterpret_cast<const AST::TableType *>(Cxt); |
54 | 0 | } |
55 | 0 | inline auto *toMemTypeCxt(AST::MemoryType *Cxt) noexcept { |
56 | 0 | return reinterpret_cast<WasmEdge_MemoryTypeContext *>(Cxt); |
57 | 0 | } |
58 | | inline const auto * |
59 | 0 | fromMemTypeCxt(const WasmEdge_MemoryTypeContext *Cxt) noexcept { |
60 | 0 | return reinterpret_cast<const AST::MemoryType *>(Cxt); |
61 | 0 | } |
62 | 0 | inline std::string_view genStrView(const WasmEdge_String S) noexcept { |
63 | 0 | return std::string_view(S.Buf, S.Length); |
64 | 0 | } |
65 | 0 | inline auto *fromModCxt(WasmEdge_ModuleInstanceContext *Cxt) noexcept { |
66 | 0 | return reinterpret_cast<Runtime::Instance::ModuleInstance *>(Cxt); |
67 | 0 | } |
68 | 0 | inline auto *fromFuncCxt(WasmEdge_FunctionInstanceContext *Cxt) noexcept { |
69 | 0 | return reinterpret_cast<Runtime::Instance::FunctionInstance *>(Cxt); |
70 | 0 | } |
71 | 0 | inline auto *fromTabCxt(WasmEdge_TableInstanceContext *Cxt) noexcept { |
72 | 0 | return reinterpret_cast<Runtime::Instance::TableInstance *>(Cxt); |
73 | 0 | } |
74 | 0 | inline auto *fromMemCxt(WasmEdge_MemoryInstanceContext *Cxt) noexcept { |
75 | 0 | return reinterpret_cast<Runtime::Instance::MemoryInstance *>(Cxt); |
76 | 0 | } |
77 | 0 | inline auto *fromGlobCxt(WasmEdge_GlobalInstanceContext *Cxt) noexcept { |
78 | 0 | return reinterpret_cast<Runtime::Instance::GlobalInstance *>(Cxt); |
79 | 0 | } |
80 | | } // namespace |
81 | | |
82 | | extern "C" { |
83 | | |
84 | | // Layout-compatible revival of the 0.16 `WasmEdge_Limit` struct. `Min`/`Max` |
85 | | // are `uint32_t` (Memory64 widened them to 64-bit only in 0.17), keeping the |
86 | | // by-value ABI bit-for-bit identical to what 0.16 consumers were built against. |
87 | | typedef struct WasmEdge_Limit_0_16 { |
88 | | bool HasMax; |
89 | | bool Shared; |
90 | | uint32_t Min; |
91 | | uint32_t Max; |
92 | | } WasmEdge_Limit_0_16; |
93 | | |
94 | | __attribute__((used, visibility("default"))) bool |
95 | | WasmEdge_LimitIsEqual_Compat_016(const WasmEdge_Limit_0_16 Lim1, |
96 | 0 | const WasmEdge_Limit_0_16 Lim2) { |
97 | 0 | return Lim1.HasMax == Lim2.HasMax && Lim1.Shared == Lim2.Shared && |
98 | 0 | Lim1.Min == Lim2.Min && Lim1.Max == Lim2.Max; |
99 | 0 | } |
100 | | |
101 | | __attribute__((used, visibility("default"))) WasmEdge_TableTypeContext * |
102 | | WasmEdge_TableTypeCreate_Compat_016(const WasmEdge_ValType RefType, |
103 | 0 | const WasmEdge_Limit_0_16 Limit) { |
104 | 0 | WasmEdge::ValType RT = genValType(RefType); |
105 | 0 | if (!RT.isRefType()) { |
106 | 0 | return nullptr; |
107 | 0 | } |
108 | 0 | if (Limit.HasMax) { |
109 | 0 | return toTabTypeCxt(new WasmEdge::AST::TableType(RT, Limit.Min, Limit.Max)); |
110 | 0 | } else { |
111 | 0 | return toTabTypeCxt(new WasmEdge::AST::TableType(RT, Limit.Min)); |
112 | 0 | } |
113 | 0 | } |
114 | | |
115 | | __attribute__((used, visibility("default"))) WasmEdge_Limit_0_16 |
116 | 0 | WasmEdge_TableTypeGetLimit_Compat_016(const WasmEdge_TableTypeContext *Cxt) { |
117 | 0 | if (Cxt) { |
118 | 0 | const auto &Lim = fromTabTypeCxt(Cxt)->getLimit(); |
119 | 0 | return WasmEdge_Limit_0_16{/* HasMax */ Lim.hasMax(), |
120 | 0 | /* Shared */ Lim.isShared(), |
121 | 0 | /* Min */ static_cast<uint32_t>(Lim.getMin()), |
122 | 0 | /* Max */ static_cast<uint32_t>(Lim.getMax())}; |
123 | 0 | } |
124 | 0 | return WasmEdge_Limit_0_16{/* HasMax */ false, /* Shared */ false, |
125 | 0 | /* Min */ 0, /* Max */ 0}; |
126 | 0 | } |
127 | | |
128 | | __attribute__((used, visibility("default"))) WasmEdge_MemoryTypeContext * |
129 | 0 | WasmEdge_MemoryTypeCreate_Compat_016(const WasmEdge_Limit_0_16 Limit) { |
130 | 0 | if (Limit.Shared) { |
131 | 0 | return toMemTypeCxt( |
132 | 0 | new WasmEdge::AST::MemoryType(Limit.Min, Limit.Max, true)); |
133 | 0 | } else if (Limit.HasMax) { |
134 | 0 | return toMemTypeCxt(new WasmEdge::AST::MemoryType(Limit.Min, Limit.Max)); |
135 | 0 | } else { |
136 | 0 | return toMemTypeCxt(new WasmEdge::AST::MemoryType(Limit.Min)); |
137 | 0 | } |
138 | 0 | } |
139 | | |
140 | | __attribute__((used, visibility("default"))) WasmEdge_Limit_0_16 |
141 | 0 | WasmEdge_MemoryTypeGetLimit_Compat_016(const WasmEdge_MemoryTypeContext *Cxt) { |
142 | 0 | if (Cxt) { |
143 | 0 | const auto &Lim = fromMemTypeCxt(Cxt)->getLimit(); |
144 | 0 | return WasmEdge_Limit_0_16{/* HasMax */ Lim.hasMax(), |
145 | 0 | /* Shared */ Lim.isShared(), |
146 | 0 | /* Min */ static_cast<uint32_t>(Lim.getMin()), |
147 | 0 | /* Max */ static_cast<uint32_t>(Lim.getMax())}; |
148 | 0 | } |
149 | 0 | return WasmEdge_Limit_0_16{/* HasMax */ false, /* Shared */ false, |
150 | 0 | /* Min */ 0, /* Max */ 0}; |
151 | 0 | } |
152 | | |
153 | | // The 0.17 -> 0.18 transition changed these four functions from returning |
154 | | // `void` to returning `WasmEdge_Result`. These shims pin the old `void` ABI: |
155 | | // they perform the same best-effort add and discard the result, matching the |
156 | | // 0.17 behavior. |
157 | | __attribute__((used, visibility("default"))) void |
158 | | WasmEdge_ModuleInstanceAddFunction_Compat_017( |
159 | | WasmEdge_ModuleInstanceContext *Cxt, const WasmEdge_String Name, |
160 | 0 | WasmEdge_FunctionInstanceContext *FuncCxt) { |
161 | 0 | if (Cxt && FuncCxt) { |
162 | 0 | static_cast<void>(fromModCxt(Cxt)->addHostFunc( |
163 | 0 | genStrView(Name), std::unique_ptr<Runtime::Instance::FunctionInstance>( |
164 | 0 | fromFuncCxt(FuncCxt)))); |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | | __attribute__((used, visibility("default"))) void |
169 | | WasmEdge_ModuleInstanceAddTable_Compat_017( |
170 | | WasmEdge_ModuleInstanceContext *Cxt, const WasmEdge_String Name, |
171 | 0 | WasmEdge_TableInstanceContext *TableCxt) { |
172 | 0 | if (Cxt && TableCxt) { |
173 | 0 | static_cast<void>(fromModCxt(Cxt)->addHostTable( |
174 | 0 | genStrView(Name), std::unique_ptr<Runtime::Instance::TableInstance>( |
175 | 0 | fromTabCxt(TableCxt)))); |
176 | 0 | } |
177 | 0 | } |
178 | | |
179 | | __attribute__((used, visibility("default"))) void |
180 | | WasmEdge_ModuleInstanceAddMemory_Compat_017( |
181 | | WasmEdge_ModuleInstanceContext *Cxt, const WasmEdge_String Name, |
182 | 0 | WasmEdge_MemoryInstanceContext *MemoryCxt) { |
183 | 0 | if (Cxt && MemoryCxt) { |
184 | 0 | static_cast<void>(fromModCxt(Cxt)->addHostMemory( |
185 | 0 | genStrView(Name), std::unique_ptr<Runtime::Instance::MemoryInstance>( |
186 | 0 | fromMemCxt(MemoryCxt)))); |
187 | 0 | } |
188 | 0 | } |
189 | | |
190 | | __attribute__((used, visibility("default"))) void |
191 | | WasmEdge_ModuleInstanceAddGlobal_Compat_017( |
192 | | WasmEdge_ModuleInstanceContext *Cxt, const WasmEdge_String Name, |
193 | 0 | WasmEdge_GlobalInstanceContext *GlobalCxt) { |
194 | 0 | if (Cxt && GlobalCxt) { |
195 | 0 | static_cast<void>(fromModCxt(Cxt)->addHostGlobal( |
196 | 0 | genStrView(Name), std::unique_ptr<Runtime::Instance::GlobalInstance>( |
197 | 0 | fromGlobCxt(GlobalCxt)))); |
198 | 0 | } |
199 | 0 | } |
200 | | |
201 | | } // extern "C" |
202 | | |
203 | | // Bind each shim to its historical `@WASMEDGE_0.16` symbol name. The shims need |
204 | | // default visibility for `.symver` to export the alias; their own |
205 | | // `WasmEdge_*_Compat_016` names are hidden via the `local:` list in |
206 | | // `lib/api/libwasmedge.lds` so only the `@WASMEDGE_0.16` aliases are exported. |
207 | | __asm__(".symver WasmEdge_LimitIsEqual_Compat_016, " |
208 | | "WasmEdge_LimitIsEqual@WASMEDGE_0.16"); |
209 | | __asm__(".symver WasmEdge_TableTypeCreate_Compat_016, " |
210 | | "WasmEdge_TableTypeCreate@WASMEDGE_0.16"); |
211 | | __asm__(".symver WasmEdge_TableTypeGetLimit_Compat_016, " |
212 | | "WasmEdge_TableTypeGetLimit@WASMEDGE_0.16"); |
213 | | __asm__(".symver WasmEdge_MemoryTypeCreate_Compat_016, " |
214 | | "WasmEdge_MemoryTypeCreate@WASMEDGE_0.16"); |
215 | | __asm__(".symver WasmEdge_MemoryTypeGetLimit_Compat_016, " |
216 | | "WasmEdge_MemoryTypeGetLimit@WASMEDGE_0.16"); |
217 | | __asm__(".symver WasmEdge_ModuleInstanceAddFunction_Compat_017, " |
218 | | "WasmEdge_ModuleInstanceAddFunction@WASMEDGE_0.17"); |
219 | | __asm__(".symver WasmEdge_ModuleInstanceAddTable_Compat_017, " |
220 | | "WasmEdge_ModuleInstanceAddTable@WASMEDGE_0.17"); |
221 | | __asm__(".symver WasmEdge_ModuleInstanceAddMemory_Compat_017, " |
222 | | "WasmEdge_ModuleInstanceAddMemory@WASMEDGE_0.17"); |
223 | | __asm__(".symver WasmEdge_ModuleInstanceAddGlobal_Compat_017, " |
224 | | "WasmEdge_ModuleInstanceAddGlobal@WASMEDGE_0.17"); |
225 | | |
226 | | #endif // __ELF__ && (__linux__ || __ANDROID__) |
227 | | |
228 | | // <<<<<<<< WasmEdge compat ABI shims <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |