Coverage Report

Created: 2026-11-09 06:49

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
0
    if (Conf.getRuntimeConfigure().getRunMode() != RunMode::AOT) {
92
0
      spdlog::error("Might an invalid wasm file"sv);
93
0
      spdlog::error(ErrCode::Value::MalformedMagic);
94
0
      spdlog::error(
95
0
          "    The AOT compiled WASM shared library is only loadable in the "
96
0
          "AOT run mode. Please use a normal WASM file, or set the run mode "
97
0
          "to AOT."sv);
98
0
      spdlog::error(ErrInfo::InfoFile(FilePath));
99
0
      return Unexpect(ErrCode::Value::MalformedMagic);
100
0
    }
101
102
0
    WASMType = InputType::SharedLibrary;
103
0
    FMgr.reset();
104
0
    std::shared_ptr<SharedLibrary> Library = std::make_shared<SharedLibrary>();
105
0
    EXPECTED_TRY(Library->load(FilePath).map_error(ReportError));
106
0
    EXPECTED_TRY(auto Version, Library->getVersion().map_error(ReportError));
107
0
    if (Version != AOT::kBinaryVersion) {
108
0
      spdlog::error(ErrInfo::InfoMismatch(AOT::kBinaryVersion, Version));
109
0
      spdlog::error(ErrInfo::InfoFile(FilePath));
110
0
      return Unexpect(ErrCode::Value::MalformedVersion);
111
0
    }
112
113
0
    EXPECTED_TRY(auto Code, Library->getWasm().map_error(ReportError));
114
115
    // 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
31.9k
Loader::parseWasmUnit(Span<const uint8_t> Code) {
142
31.9k
  std::lock_guard Lock(Mutex);
143
31.9k
  EXPECTED_TRY(FMgr.setCode(Code));
144
31.9k
  switch (FMgr.getHeaderType()) {
145
  // Filter out the Windows .dll, macOS .dylib, or Linux .so AOT compiled
146
  // shared-library-WASM.
147
3
  case FileMgr::FileHeader::ELF:
148
8
  case FileMgr::FileHeader::DLL:
149
10
  case FileMgr::FileHeader::MachO_32:
150
12
  case FileMgr::FileHeader::MachO_64:
151
12
    spdlog::error("Might an invalid wasm file"sv);
152
12
    spdlog::error(ErrCode::Value::MalformedMagic);
153
12
    spdlog::error(
154
12
        "    The AOT compiled WASM shared library is not supported for loading "
155
12
        "from memory. Please use a normal WASM file, or load the AOT compiled "
156
12
        "WASM shared library from file in the AOT run mode."sv);
157
12
    return Unexpect(ErrCode::Value::MalformedMagic);
158
31.9k
  default:
159
31.9k
    break;
160
31.9k
  }
161
  // For malformed header checking, handle in the module loading.
162
31.9k
  WASMType = InputType::WASM;
163
31.9k
  return loadUnit();
164
31.9k
}
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.22k
Loader::parseModule(Span<const uint8_t> Code) {
179
9.22k
  EXPECTED_TRY(auto ComponentOrModule, parseWasmUnit(Code));
180
4.20k
  if (auto M = std::get_if<std::unique_ptr<AST::Module>>(&ComponentOrModule)) {
181
4.20k
    return std::move(*M);
182
4.20k
  }
183
0
  return Unexpect(ErrCode::Value::MalformedVersion);
184
4.20k
}
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
31.9k
Loader::loadUnit() {
190
31.9k
  EXPECTED_TRY(auto Preamble, loadPreamble());
191
31.7k
  auto &[WasmMagic, Ver] = Preamble;
192
31.7k
  if (Ver == ModuleVersion) {
193
20.9k
    auto Mod = std::make_unique<AST::Module>();
194
20.9k
    Mod->getMagic() = WasmMagic;
195
20.9k
    Mod->getVersion() = Ver;
196
20.9k
    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
20.9k
    FMgr.seek(8);
201
20.9k
    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
8.22k
    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
8.22k
    return Mod;
215
10.8k
  } else if (Ver == ComponentVersion) {
216
10.7k
    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
10.7k
    spdlog::warn("component model is an experimental proposal"sv);
222
10.7k
    auto Comp = std::make_unique<AST::Component::Component>();
223
10.7k
    Comp->getMagic() = WasmMagic;
224
10.7k
    Comp->getVersion() = {Ver[0], Ver[1]};
225
10.7k
    Comp->getLayer() = {Ver[2], Ver[3]};
226
10.7k
    EXPECTED_TRY(loadComponent(*Comp));
227
6.53k
    return Comp;
228
10.7k
  } else {
229
9
    return logLoadError(ErrCode::Value::MalformedVersion, FMgr.getLastOffset(),
230
9
                        ASTNodeAttr::Component);
231
9
  }
232
31.7k
}
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
20.2k
                                AST::TypeSection &TypeSec) {
243
20.2k
  auto &TypeVec = TypeSec.getContent();
244
20.2k
  for (auto &TgType : TagSec.getContent()) {
245
4.18k
    auto TypeIdx = TgType.getTypeIdx();
246
    // Invalid type index would be checked during validation.
247
4.18k
    if (TypeIdx < TypeVec.size()) {
248
3.94k
      TgType.setDefType(&TypeVec[TypeIdx]);
249
3.94k
    }
250
4.18k
  }
251
20.2k
  for (auto &Desc : ImportSec.getContent()) {
252
5.22k
    if (Desc.getExternalType() == ExternalType::Tag) {
253
764
      auto &TgType = Desc.getExternalTagType();
254
764
      auto TypeIdx = TgType.getTypeIdx();
255
      // Invalid type index would be checked during validation.
256
764
      if (TypeIdx < TypeVec.size()) {
257
715
        TgType.setDefType(&TypeVec[TypeIdx]);
258
715
      }
259
764
    }
260
5.22k
  }
261
20.2k
}
262
263
} // namespace Loader
264
} // namespace WasmEdge