Coverage Report

Created: 2025-07-01 06:18

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