Coverage Report

Created: 2025-07-01 06:18

/src/WasmEdge/lib/loader/ast/module.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/aot_section.h"
5
#include "loader/loader.h"
6
#include "loader/shared_library.h"
7
8
#include <cstddef>
9
#include <cstdint>
10
#include <memory>
11
#include <optional>
12
#include <string>
13
#include <utility>
14
#include <vector>
15
16
using namespace std::literals;
17
18
namespace WasmEdge {
19
namespace Loader {
20
21
// Load binary to construct Module node. See "include/loader/loader.h".
22
Expect<void> Loader::loadModule(AST::Module &Mod,
23
9.18k
                                std::optional<uint64_t> Bound) {
24
9.18k
  uint64_t StartOffset = FMgr.getOffset();
25
9.18k
  auto ReportError = [](auto E) {
26
5.30k
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module));
27
5.30k
    return E;
28
5.30k
  };
29
30
  // Variables to record the loaded section types.
31
9.18k
  HasDataSection = false;
32
9.18k
  std::vector<uint8_t> Secs = {0x0BU, 0x0AU, 0x0CU, 0x09U, 0x08U, 0x07U, 0x06U,
33
9.18k
                               0x0DU, 0x05U, 0x04U, 0x03U, 0x02U, 0x01U};
34
9.18k
  uint64_t Offset = FMgr.getOffset();
35
36
  // Read Section index and create Section nodes.
37
69.8k
  while (!Bound.has_value() || Bound.value() > Offset - StartOffset) {
38
69.8k
    uint8_t NewSectionId = 0x00;
39
    // If not read section ID, seems the end of file and break.
40
69.8k
    if (auto Res = FMgr.readByte()) {
41
65.9k
      NewSectionId = *Res;
42
65.9k
    } else {
43
3.82k
      if (Res.error() == ErrCode::Value::UnexpectedEnd) {
44
3.82k
        break;
45
3.82k
      } else {
46
0
        return logLoadError(Res.error(), FMgr.getLastOffset(),
47
0
                            ASTNodeAttr::Module);
48
0
      }
49
3.82k
    }
50
51
    // Sections except the custom section should be unique and in order.
52
65.9k
    if (NewSectionId > 0x00U && NewSectionId < 0x0EU) {
53
95.3k
      while (Secs.size() > 0 && Secs.back() != NewSectionId) {
54
74.4k
        Secs.pop_back();
55
74.4k
      }
56
20.8k
      if (Secs.empty()) {
57
14
        return logLoadError(ErrCode::Value::JunkSection, FMgr.getLastOffset(),
58
14
                            ASTNodeAttr::Module);
59
14
      }
60
20.8k
      Secs.pop_back();
61
20.8k
    }
62
63
65.9k
    switch (NewSectionId) {
64
45.0k
    case 0x00:
65
45.0k
      Mod.getCustomSections().emplace_back();
66
45.0k
      EXPECTED_TRY(
67
44.8k
          loadSection(Mod.getCustomSections().back()).map_error(ReportError));
68
44.8k
      break;
69
44.8k
    case 0x01:
70
4.55k
      EXPECTED_TRY(loadSection(Mod.getTypeSection()).map_error(ReportError));
71
4.40k
      break;
72
4.40k
    case 0x02:
73
279
      EXPECTED_TRY(loadSection(Mod.getImportSection()).map_error(ReportError));
74
132
      break;
75
4.36k
    case 0x03:
76
4.36k
      EXPECTED_TRY(
77
4.28k
          loadSection(Mod.getFunctionSection()).map_error(ReportError));
78
4.28k
      break;
79
4.28k
    case 0x04:
80
443
      EXPECTED_TRY(loadSection(Mod.getTableSection()).map_error(ReportError));
81
337
      break;
82
1.53k
    case 0x05:
83
1.53k
      EXPECTED_TRY(loadSection(Mod.getMemorySection()).map_error(ReportError));
84
1.44k
      break;
85
1.44k
    case 0x06:
86
683
      EXPECTED_TRY(loadSection(Mod.getGlobalSection()).map_error(ReportError));
87
186
      break;
88
1.25k
    case 0x07:
89
1.25k
      EXPECTED_TRY(loadSection(Mod.getExportSection()).map_error(ReportError));
90
1.13k
      break;
91
1.13k
    case 0x08:
92
46
      EXPECTED_TRY(loadSection(Mod.getStartSection()).map_error(ReportError));
93
20
      break;
94
1.43k
    case 0x09:
95
1.43k
      EXPECTED_TRY(loadSection(Mod.getElementSection()).map_error(ReportError));
96
195
      break;
97
4.58k
    case 0x0A:
98
4.58k
      EXPECTED_TRY(loadSection(Mod.getCodeSection()).map_error(ReportError));
99
3.42k
      break;
100
3.42k
    case 0x0B:
101
1.59k
      EXPECTED_TRY(loadSection(Mod.getDataSection()).map_error(ReportError));
102
150
      break;
103
150
    case 0x0C:
104
      // This section is for BulkMemoryOperations or ReferenceTypes proposal.
105
99
      if (!Conf.hasProposal(Proposal::BulkMemoryOperations) &&
106
99
          !Conf.hasProposal(Proposal::ReferenceTypes)) {
107
0
        return logNeedProposal(ErrCode::Value::MalformedSection,
108
0
                               Proposal::BulkMemoryOperations,
109
0
                               FMgr.getLastOffset(), ASTNodeAttr::Module);
110
0
      }
111
99
      EXPECTED_TRY(
112
79
          loadSection(Mod.getDataCountSection()).map_error(ReportError));
113
79
      HasDataSection = true;
114
79
      break;
115
2
    case 0x0D:
116
      // This section is for ExceptionHandling proposal.
117
2
      if (!Conf.hasProposal(Proposal::ExceptionHandling)) {
118
2
        return logNeedProposal(ErrCode::Value::MalformedSection,
119
2
                               Proposal::ExceptionHandling,
120
2
                               FMgr.getLastOffset(), ASTNodeAttr::Module);
121
2
      }
122
0
      EXPECTED_TRY(loadSection(Mod.getTagSection()).map_error(ReportError));
123
0
      break;
124
38
    default:
125
38
      return logLoadError(ErrCode::Value::MalformedSection,
126
38
                          FMgr.getLastOffset(), ASTNodeAttr::Module);
127
65.9k
    }
128
129
60.6k
    Offset = FMgr.getOffset();
130
60.6k
  }
131
132
3.82k
  setTagFunctionType(Mod.getTagSection(), Mod.getImportSection(),
133
3.82k
                     Mod.getTypeSection());
134
135
  // Verify the function section and code section are matched.
136
3.82k
  if (Mod.getFunctionSection().getContent().size() !=
137
3.82k
      Mod.getCodeSection().getContent().size()) {
138
11
    spdlog::error(ErrCode::Value::IncompatibleFuncCode);
139
11
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module));
140
11
    return Unexpect(ErrCode::Value::IncompatibleFuncCode);
141
11
  }
142
143
  // Verify the data count section and data segments are matched.
144
3.81k
  if (Mod.getDataCountSection().getContent()) {
145
45
    if (Mod.getDataSection().getContent().size() !=
146
45
        *(Mod.getDataCountSection().getContent())) {
147
11
      spdlog::error(ErrCode::Value::IncompatibleDataCount);
148
11
      spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module));
149
11
      return Unexpect(ErrCode::Value::IncompatibleDataCount);
150
11
    }
151
45
  }
152
153
3.80k
  return {};
154
3.81k
}
155
156
// Setup symbols from loaded binary. See "include/loader/loader.h".
157
Expect<void> Loader::loadExecutable(AST::Module &Mod,
158
0
                                    std::shared_ptr<Executable> Exec) {
159
0
  auto &SubTypes = Mod.getTypeSection().getContent();
160
0
  size_t Offset = 0;
161
0
  for (const auto &ImpDesc : Mod.getImportSection().getContent()) {
162
0
    if (ImpDesc.getExternalType() == ExternalType::Function) {
163
0
      ++Offset;
164
0
    }
165
0
  }
166
0
  auto &CodeSegs = Mod.getCodeSection().getContent();
167
168
  // Check the symbols.
169
0
  auto FuncTypeSymbols = Exec->getTypes(SubTypes.size());
170
0
  auto CodeSymbols = Exec->getCodes(Offset, CodeSegs.size());
171
0
  auto IntrinsicsSymbol = Exec->getIntrinsics();
172
0
  if (unlikely(FuncTypeSymbols.size() != SubTypes.size())) {
173
0
    spdlog::error("    AOT section -- number of types not matching:{} {}, "
174
0
                  "use interpreter mode instead.",
175
0
                  FuncTypeSymbols.size(), SubTypes.size());
176
0
    return Unexpect(ErrCode::Value::IllegalGrammar);
177
0
  }
178
0
  if (unlikely(CodeSymbols.size() != CodeSegs.size())) {
179
0
    spdlog::error("    AOT section -- number of codes not matching:{} {}, "
180
0
                  "use interpreter mode instead.",
181
0
                  CodeSymbols.size(), CodeSegs.size());
182
0
    return Unexpect(ErrCode::Value::IllegalGrammar);
183
0
  }
184
0
  if (unlikely(!IntrinsicsSymbol)) {
185
0
    spdlog::error("    AOT section -- intrinsics table symbol not found, use "
186
0
                  "interpreter mode instead.");
187
0
    return Unexpect(ErrCode::Value::IllegalGrammar);
188
0
  }
189
190
  // Set the symbols into the module.
191
0
  uint32_t FuncTypeIdx = 0;
192
0
  for (auto &SubType : SubTypes) {
193
0
    if (SubType.getCompositeType().isFunc()) {
194
0
      SubType.getCompositeType().getFuncType().setSymbol(
195
0
          std::move(FuncTypeSymbols[FuncTypeIdx]));
196
0
    }
197
0
    FuncTypeIdx++;
198
0
  }
199
0
  for (size_t I = 0; I < CodeSegs.size(); ++I) {
200
0
    CodeSegs[I].setSymbol(std::move(CodeSymbols[I]));
201
0
  }
202
0
  Mod.setSymbol(std::move(IntrinsicsSymbol));
203
0
  if (!Conf.getRuntimeConfigure().isForceInterpreter()) {
204
    // If the configure is set to force interpreter mode, not to set the
205
    // symbol.
206
0
    if (auto &Symbol = Mod.getSymbol()) {
207
0
      *Symbol = IntrinsicsTable;
208
0
    }
209
0
  }
210
211
0
  return {};
212
0
}
213
214
0
Expect<void> Loader::loadUniversalWASM(AST::Module &Mod) {
215
0
  if (!Conf.getRuntimeConfigure().isForceInterpreter()) {
216
0
    auto Exec = std::make_shared<AOTSection>();
217
0
    if (auto Res = Exec->load(Mod.getAOTSection()); unlikely(!Res)) {
218
0
      spdlog::error("    AOT section -- library load failed:{} , use "
219
0
                    "interpreter mode instead.",
220
0
                    Res.error());
221
0
    } else {
222
0
      if (loadExecutable(Mod, Exec)) {
223
0
        return {};
224
0
      }
225
0
    }
226
0
  }
227
228
  // Fallback to the interpreter mode case: Re-read the code section.
229
0
  WASMType = InputType::WASM;
230
0
  FMgr.seek(Mod.getCodeSection().getStartOffset());
231
0
  return loadSection(Mod.getCodeSection()).map_error([](auto E) {
232
0
    spdlog::error(ErrInfo::InfoAST(ASTNodeAttr::Module));
233
0
    return E;
234
0
  });
235
0
}
236
237
0
Expect<void> Loader::loadModuleAOT(AST::AOTSection &AOTSection) {
238
  // Find and Read the AOT custom section first. Jump the others.
239
  // This loop is for checking the input is an universal WASM or not.
240
  // Therefore, if the configure is set as force interpreter mode, skip this.
241
0
  while (WASMType != InputType::SharedLibrary) {
242
    // This loop only overview the custom sections and read the AOT section.
243
    // For the other general errors, break and handle in the sequentially
244
    // parsing below.
245
0
    uint8_t NewSectionId = 0x00;
246
0
    if (auto Res = FMgr.readByte()) {
247
0
      NewSectionId = *Res;
248
0
    } else {
249
0
      break;
250
0
    }
251
252
0
    if (NewSectionId == 0x00U) {
253
      // Load the section size.
254
0
      uint32_t ContentSize = 0;
255
0
      if (auto Res = FMgr.readU32()) {
256
0
        ContentSize = *Res;
257
0
      } else {
258
0
        break;
259
0
      }
260
0
      if (ContentSize > FMgr.getRemainSize()) {
261
0
        break;
262
0
      }
263
264
      // Load the section name.
265
0
      auto StartOffset = FMgr.getOffset();
266
0
      std::string Name;
267
0
      if (auto Res = FMgr.readName()) {
268
        // The UTF-8 failed case will be ignored here.
269
0
        Name = std::move(*Res);
270
0
      }
271
272
0
      auto ReadSize = FMgr.getOffset() - StartOffset;
273
0
      if (ContentSize < ReadSize) {
274
        // Syntax error of overread. Jump to the next section.
275
0
        FMgr.seek(StartOffset + ContentSize);
276
0
        continue;
277
0
      }
278
279
0
      if (Name == "wasmedge") {
280
        // Found the AOT section in universal WASM. Load the AOT code.
281
        // Read the content.
282
0
        std::vector<uint8_t> Content;
283
0
        if (auto Res = FMgr.readBytes(ContentSize - ReadSize)) {
284
0
          Content = std::move(*Res);
285
0
        } else {
286
0
          break;
287
0
        }
288
289
        // Load the AOT section.
290
0
        FileMgr VecMgr;
291
0
        AST::AOTSection NewAOTSection;
292
0
        VecMgr.setCode(Content);
293
0
        if (auto Res = loadSection(VecMgr, NewAOTSection)) {
294
          // Also handle the duplicated AOT sections case.
295
          // If the new AOT section discovered, use the new one.
296
0
          WASMType = InputType::UniversalWASM;
297
0
          AOTSection = std::move(NewAOTSection);
298
0
        } else {
299
          // If the new AOT section load failed, use the old one or the
300
          // interpreter mode.
301
0
          if (WASMType == InputType::UniversalWASM) {
302
0
            spdlog::info(
303
0
                "    Load AOT section failed. Use the previous succeeded one.");
304
0
          } else {
305
0
            spdlog::info(
306
0
                "    Load AOT section failed. Use interpreter mode instead.");
307
0
          }
308
0
        }
309
0
      } else {
310
        // Found other custom sections. Jump to the next section.
311
0
        FMgr.seek(StartOffset + ContentSize);
312
0
        continue;
313
0
      }
314
0
    } else {
315
0
      if (auto Res = FMgr.jumpContent(); unlikely(!Res)) {
316
0
        break;
317
0
      }
318
0
    }
319
0
  }
320
0
  return {};
321
0
}
322
323
} // namespace Loader
324
} // namespace WasmEdge