///|
/// Host and module contract used to accept or reject a decoded artifact.
pub(all) struct ArtifactCompatibility {
target : @artifact.TargetSpec
available_cpu_features : Array[@artifact.CpuFeature]
jit_abi_version : Int
codegen_revision : String
module_identity : @artifact.ModuleIdentity
policy : @artifact.CompilationPolicy
runtime_symbols : Array[@semantic_machv.ExternalSymbol]
data_symbols : Array[@semantic_machv.DataSymbol]
} derive(Eq, Debug)
///|
pub suberror ArtifactIncompatible {
TargetMismatch(
expected~ : @artifact.TargetSpec,
actual~ : @artifact.TargetSpec
)
MissingCpuFeature(name~ : String)
DuplicateCpuFeature(name~ : String)
JitAbiVersionMismatch(expected~ : Int, actual~ : Int)
CodegenRevisionMismatch(expected~ : String, actual~ : String)
ModuleIdentityMismatch(
expected~ : @artifact.ModuleIdentity,
actual~ : @artifact.ModuleIdentity
)
CompilationPolicyMismatch(
expected~ : @artifact.CompilationPolicy,
actual~ : @artifact.CompilationPolicy
)
DuplicateFunctionIndex(index~ : Int)
DuplicateSymbol(name~ : String)
UnknownSymbol(kind~ : String, name~ : String)
} derive(Eq, Debug)
///|
pub suberror ArtifactLoadError {
DecodeFailed(cause~ : @artifact.ArtifactDecodeError)
Incompatible(cause~ : ArtifactIncompatible)
CodeObjectInvalid(cause~ : @code_object.CodeObjectVerifyError)
} derive(Eq, Debug)
///|
/// A decoded artifact whose manifest and every code object have been checked.
pub struct LoadedArtifact {
artifact : @artifact.Artifact
code_objects : Array[@code_object.UnlinkedCodeObject]
}
///|
pub fn LoadedArtifact::artifact(self : LoadedArtifact) -> @artifact.Artifact {
self.artifact
}
///|
pub fn LoadedArtifact::code_objects(
self : LoadedArtifact,
) -> Array[@code_object.UnlinkedCodeObject] {
self.code_objects.copy()
}
///|
fn contains_cpu_feature(
features : Array[@artifact.CpuFeature],
name : String,
) -> Bool {
features.any(fn(feature) { feature.name == name })
}
///|
fn contains_external_symbol(
symbols : Array[@semantic_machv.ExternalSymbol],
name : String,
) -> Bool {
symbols.any(fn(symbol) { symbol.name == name })
}
///|
fn contains_data_symbol(
symbols : Array[@semantic_machv.DataSymbol],
name : String,
) -> Bool {
symbols.any(fn(symbol) { symbol.name == name })
}
///|
fn contains_function_symbol(
functions : Array[@artifact.FunctionCode],
name : String,
) -> Bool {
functions.any(fn(function) { function.identity.symbol.name == name })
}
///|
fn contains_import_symbol(
imports : Array[@artifact.ImportIdentity],
name : String,
) -> Bool {
imports.any(fn(import_) { import_.symbol.name == name })
}
///|
fn verify_artifact_compatibility(
decoded : @artifact.Artifact,
expected : ArtifactCompatibility,
) -> Unit raise ArtifactIncompatible {
let manifest = decoded.manifest
if manifest.target != expected.target {
raise TargetMismatch(expected=expected.target, actual=manifest.target)
}
let required_feature_names : Array[String] = []
for feature in manifest.required_cpu_features {
if required_feature_names.contains(feature.name) {
raise DuplicateCpuFeature(name=feature.name)
}
required_feature_names.push(feature.name)
if !contains_cpu_feature(expected.available_cpu_features, feature.name) {
raise MissingCpuFeature(name=feature.name)
}
}
if manifest.jit_abi_version != expected.jit_abi_version {
raise JitAbiVersionMismatch(
expected=expected.jit_abi_version,
actual=manifest.jit_abi_version,
)
}
if manifest.codegen_revision != expected.codegen_revision {
raise CodegenRevisionMismatch(
expected=expected.codegen_revision,
actual=manifest.codegen_revision,
)
}
if manifest.module_identity != expected.module_identity {
raise ModuleIdentityMismatch(
expected=expected.module_identity,
actual=manifest.module_identity,
)
}
if manifest.policy != expected.policy {
raise CompilationPolicyMismatch(
expected=expected.policy,
actual=manifest.policy,
)
}
}
///|
fn verify_artifact_identities(
decoded : @artifact.Artifact,
expected : ArtifactCompatibility,
) -> Unit raise ArtifactIncompatible {
let function_indices : Array[Int] = []
let symbols : Array[String] = []
for import_ in decoded.imports {
if function_indices.contains(import_.function_index) {
raise DuplicateFunctionIndex(index=import_.function_index)
}
function_indices.push(import_.function_index)
if import_.symbol.name.is_empty() || symbols.contains(import_.symbol.name) {
raise DuplicateSymbol(name=import_.symbol.name)
}
symbols.push(import_.symbol.name)
}
for function in decoded.functions {
if function_indices.contains(function.identity.function_index) {
raise DuplicateFunctionIndex(index=function.identity.function_index)
}
function_indices.push(function.identity.function_index)
if function.identity.symbol.name.is_empty() ||
symbols.contains(function.identity.symbol.name) {
raise DuplicateSymbol(name=function.identity.symbol.name)
}
symbols.push(function.identity.symbol.name)
for relocation in function.relocations {
match relocation.target {
Code(symbol) =>
if !contains_function_symbol(decoded.functions, symbol.name) &&
!contains_import_symbol(decoded.imports, symbol.name) {
raise UnknownSymbol(kind="code", name=symbol.name)
}
External(symbol) =>
if !contains_import_symbol(decoded.imports, symbol.name) &&
!contains_external_symbol(expected.runtime_symbols, symbol.name) {
raise UnknownSymbol(kind="external", name=symbol.name)
}
Data(symbol) =>
if !contains_data_symbol(expected.data_symbols, symbol.name) {
raise UnknownSymbol(kind="data", name=symbol.name)
}
}
}
}
}
///|
fn build_verified_code_objects(
decoded : @artifact.Artifact,
) -> Array[@code_object.UnlinkedCodeObject] raise @code_object.CodeObjectVerifyError {
let code_objects : Array[@code_object.UnlinkedCodeObject] = []
for function in decoded.functions {
code_objects.push(
@code_object.build(
decoded.manifest.target.architecture,
function.code,
relocations=function.relocations,
sources=function.sources,
traps=function.traps,
safepoints=function.safepoints,
unwind=function.unwind,
),
)
}
code_objects
}
///|
/// Check compatibility and reconstruct verified unlinked code objects from an
/// in-memory artifact produced by the live compiler.
///
/// No symbol is resolved and no executable memory is allocated or published.
pub fn verify_artifact(
decoded : @artifact.Artifact,
compatibility : ArtifactCompatibility,
) -> LoadedArtifact raise ArtifactLoadError {
verify_artifact_compatibility(decoded, compatibility) catch {
error => raise Incompatible(cause=error)
}
verify_artifact_identities(decoded, compatibility) catch {
error => raise Incompatible(cause=error)
}
let code_objects = build_verified_code_objects(decoded) catch {
error => raise CodeObjectInvalid(cause=error)
}
{ artifact: decoded, code_objects }
}
///|
/// Decode, check compatibility, and reconstruct verified unlinked code objects.
///
/// Decoded and live artifacts converge on `verify_artifact`, before the shared
/// installer resolves any symbol or allocates executable memory.
pub fn load_artifact(
bytes : Bytes,
compatibility : ArtifactCompatibility,
limits? : @artifact.DecodeLimits = @artifact.DecodeLimits::default(),
) -> LoadedArtifact raise ArtifactLoadError {
let decoded = @artifact.decode(bytes, limits~) catch {
error => raise DecodeFailed(cause=error)
}
verify_artifact(decoded, compatibility)
}