blob: 9ce996f4637abe888749036694d8e15358b11f31 [file]
/*
* Copyright 2026 Rive
*/
#ifdef WITH_RIVE_SCRIPTING_WASM
#include "rive/wasm/prelinked_aot.hpp"
#if !defined(__EMSCRIPTEN__) && WASM_ENABLE_PRELINKED_AOT != 0
// AOT_MAGIC_NUMBER / AOT_CURRENT_VERSION for container validation.
#include "aot_runtime.h"
#include "wasm_export.h"
#include <cstdio>
#include <cstring>
#endif
#include <vector>
namespace rive
{
// Function-local so registrations from static constructors are safe.
static std::vector<PrelinkedAotModule>& prelinkedRegistry()
{
static std::vector<PrelinkedAotModule> registry;
return registry;
}
void registerPrelinkedAotModule(const PrelinkedAotModule& module)
{
prelinkedRegistry().push_back(module);
}
const PrelinkedAotModule* findPrelinkedAotModule(
uint64_t moduleKey,
size_t wasmSize,
bool hardwareBounds)
{
for (const PrelinkedAotModule& module : prelinkedRegistry())
{
if (module.moduleKey == moduleKey && module.wasmSize == wasmSize &&
module.hardwareBounds == hardwareBounds)
{
return &module;
}
}
return nullptr;
}
#if !defined(__EMSCRIPTEN__) && WASM_ENABLE_PRELINKED_AOT != 0
// The relocation section is dropped: the native linker applied every fixup,
// which is what makes the text usable without runtime executable memory.
void* loadPrelinkedAotModule(
const PrelinkedAotModule& prelinked,
char* error,
uint32_t errorSize)
{
constexpr uint32_t kSectionText = 2;
constexpr uint32_t kSectionRelocation = 5;
// The from sections loader never sees the container's magic and version,
// so an artifact from an incompatible wamrc is rejected here.
if (prelinked.aotSize < 8)
{
snprintf(error, errorSize, "prelinked aot container too small");
return nullptr;
}
uint32_t magic;
uint32_t version;
memcpy(&magic, prelinked.aot, 4);
memcpy(&version, prelinked.aot + 4, 4);
if (magic != AOT_MAGIC_NUMBER || version != AOT_CURRENT_VERSION)
{
snprintf(
error,
errorSize,
"prelinked aot container magic/version mismatch "
"(%08x v%u, runtime expects v%u)",
magic,
version,
(uint32_t)AOT_CURRENT_VERSION);
return nullptr;
}
const uint8_t* p = prelinked.aot + 8;
const uint8_t* end = prelinked.aot + prelinked.aotSize;
std::vector<wasm_section_t> sections;
while (true)
{
// Section headers are 4 byte aligned; compared as integers, since the
// aligned address may lie past end, where pointers are undefined.
uintptr_t aligned = ((uintptr_t)p + 3) & ~(uintptr_t)3;
if (aligned > (uintptr_t)end || (uintptr_t)end - aligned < 8)
{
break;
}
p = (const uint8_t*)aligned;
uint32_t type;
uint32_t size;
memcpy(&type, p, 4);
memcpy(&size, p + 4, 4);
p += 8;
if (size > (size_t)(end - p))
{
snprintf(error, errorSize, "prelinked aot container truncated");
return nullptr;
}
if (type != kSectionRelocation)
{
wasm_section_t section = {};
section.section_type = (int)type;
if (type == kSectionText)
{
section.section_body = const_cast<uint8_t*>(prelinked.text);
section.section_body_size = (uint32_t)prelinked.textSize;
}
else
{
section.section_body = const_cast<uint8_t*>(p);
section.section_body_size = size;
}
sections.push_back(section);
}
p += size;
}
if (sections.empty())
{
snprintf(error, errorSize, "prelinked aot container empty");
return nullptr;
}
for (size_t i = 0; i + 1 < sections.size(); i++)
{
sections[i].next = &sections[i + 1];
}
sections.back().next = nullptr;
return wasm_runtime_load_from_sections(
sections.data(),
true,
error,
errorSize);
}
#endif
} // namespace rive
#endif