Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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