#![warn(missing_docs)]
use std::panic;
use std::slice;
use luna_core::runtime::Value;
use luna_core::version::LuaVersion;
use luna_core::vm::Vm;
#[cfg(all(target_os = "windows", feature = "jit-helpers"))]
mod windows_section;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn luna_aot_run(bytecode: *const u8, len: usize) -> i32 {
if bytecode.is_null() || len == 0 {
eprintln!(
"luna-runtime-helpers: embedded bytecode section is empty \
(ptr={bytecode:p}, len={len}) — was the bytecode .o linked in?"
);
return 1;
}
#[cfg(feature = "jit-helpers")]
{
let n = jit_helpers_pin::force_link_jit_helpers();
std::hint::black_box(n);
}
let bytecode_slice: &'static [u8] = unsafe { slice::from_raw_parts(bytecode, len) };
let result = panic::catch_unwind(panic::AssertUnwindSafe(|| run_inner(bytecode_slice)));
match result {
Ok(code) => code,
Err(payload) => {
let msg = panic_payload_text(&payload);
eprintln!("luna-runtime-helpers: vm panicked: {msg}");
1
}
}
}
fn run_inner(bytecode: &[u8]) -> i32 {
let mut vm = Vm::new(LuaVersion::Lua55);
vm.set_bytecode_loading(true);
#[cfg(feature = "jit-helpers")]
{
vm.install_jit_backend(
luna_jit::jit_backend::CraneliftBackend,
luna_jit::jit_backend::CraneliftBackend,
);
}
#[cfg(feature = "jit-helpers")]
{
let resolved = aot_strkey_resolver::resolve_all(&mut vm);
if std::env::var_os("LUNA_AOT_PROBE").is_some() {
eprintln!("luna-runtime-helpers: aot_strkey_resolved = {resolved}");
}
let chains_resolved = aot_inline_chain_resolver::resolve_all();
if std::env::var_os("LUNA_AOT_PROBE").is_some() {
eprintln!("luna-runtime-helpers: aot_inline_chains_resolved = {chains_resolved}");
}
}
let closure = match vm.load(bytecode, b"=embedded") {
Ok(c) => c,
Err(e) => {
eprintln!(
"luna-runtime-helpers: load failed at line {}: {}",
e.line,
String::from_utf8_lossy(&e.msg)
);
return 1;
}
};
#[cfg(feature = "jit-helpers")]
{
let root_proto = unsafe { (*closure.as_ptr()).proto };
let installed = aot_trace_registry::install_all(&mut vm, root_proto);
if std::env::var_os("LUNA_AOT_PROBE").is_some() {
eprintln!("luna-runtime-helpers: aot_trace_install_count = {installed}");
}
}
let rc = match vm.call_value(Value::Closure(closure), &[]) {
Ok(_results) => 0,
Err(err) => {
let msg = vm.error_text(&err);
eprintln!("luna-runtime-helpers: runtime error: {msg}");
if let Some(tb) = vm.take_error_traceback() {
eprintln!("{tb}");
}
1
}
};
#[cfg(feature = "jit-helpers")]
if std::env::var_os("LUNA_AOT_PROBE").is_some() {
let fires = luna_jit::jit_backend::trace_materialize_frames_fires();
eprintln!("luna-runtime-helpers: trace_materialize_frames_fires = {fires}");
}
rc
}
fn panic_payload_text(payload: &(dyn std::any::Any + Send)) -> String {
if let Some(s) = payload.downcast_ref::<&'static str>() {
(*s).to_string()
} else if let Some(s) = payload.downcast_ref::<String>() {
s.clone()
} else {
"(non-string panic payload)".to_string()
}
}
pub fn run_bytecode(bytecode: &[u8]) -> i32 {
run_inner(bytecode)
}
#[cfg(feature = "jit-helpers")]
pub use luna_jit::jit_backend::{
luna_jit_materialize_sunk_table, luna_jit_new_table, luna_jit_new_table_sized,
luna_jit_op_close, luna_jit_op_closure, luna_jit_op_concat, luna_jit_op_get_tab_up,
luna_jit_op_tforcall, luna_jit_spill_to_stack, luna_jit_stack_load, luna_jit_stack_tag,
luna_jit_stack_update_raw, luna_jit_str_buf_acquire, luna_jit_str_buf_extend,
luna_jit_str_buf_intern, luna_jit_str_buf_release, luna_jit_table_get_field,
luna_jit_table_get_float, luna_jit_table_get_int, luna_jit_table_len, luna_jit_table_set_field,
luna_jit_table_set_float_float, luna_jit_table_set_int, luna_jit_table_set_nil,
luna_jit_table_set_raw, luna_jit_trace_materialize_frames, luna_jit_upval_get,
};
#[cfg(feature = "jit-helpers")]
mod jit_helpers_pin {
use luna_jit::jit_backend as jb;
type AnyFn = *const u8;
#[repr(transparent)]
struct PinnedFn(AnyFn);
unsafe impl Sync for PinnedFn {}
#[used]
#[unsafe(no_mangle)]
static LUNA_AOT_HELPER_PIN: [PinnedFn; 27] = [
PinnedFn(jb::luna_jit_new_table as AnyFn),
PinnedFn(jb::luna_jit_new_table_sized as AnyFn),
PinnedFn(jb::luna_jit_materialize_sunk_table as AnyFn),
PinnedFn(jb::luna_jit_table_set_int as AnyFn),
PinnedFn(jb::luna_jit_table_set_raw as AnyFn),
PinnedFn(jb::luna_jit_table_set_field as AnyFn),
PinnedFn(jb::luna_jit_table_get_field as AnyFn),
PinnedFn(jb::luna_jit_op_get_tab_up as AnyFn),
PinnedFn(jb::luna_jit_table_set_nil as AnyFn),
PinnedFn(jb::luna_jit_table_set_float_float as AnyFn),
PinnedFn(jb::luna_jit_table_get_int as AnyFn),
PinnedFn(jb::luna_jit_table_get_float as AnyFn),
PinnedFn(jb::luna_jit_upval_get as AnyFn),
PinnedFn(jb::luna_jit_op_close as AnyFn),
PinnedFn(jb::luna_jit_stack_update_raw as AnyFn),
PinnedFn(jb::luna_jit_op_concat as AnyFn),
PinnedFn(jb::luna_jit_str_buf_acquire as AnyFn),
PinnedFn(jb::luna_jit_str_buf_release as AnyFn),
PinnedFn(jb::luna_jit_str_buf_extend as AnyFn),
PinnedFn(jb::luna_jit_str_buf_intern as AnyFn),
PinnedFn(jb::luna_jit_op_tforcall as AnyFn),
PinnedFn(jb::luna_jit_stack_load as AnyFn),
PinnedFn(jb::luna_jit_stack_tag as AnyFn),
PinnedFn(jb::luna_jit_spill_to_stack as AnyFn),
PinnedFn(jb::luna_jit_op_closure as AnyFn),
PinnedFn(jb::luna_jit_trace_materialize_frames as AnyFn),
PinnedFn(jb::luna_jit_table_len as AnyFn),
];
static NEVER_TRIP: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
#[allow(unreachable_code)]
pub fn force_link_jit_helpers() -> usize {
let mut sum: usize = 0;
for slot in LUNA_AOT_HELPER_PIN.iter() {
sum = sum.wrapping_add(std::hint::black_box(slot.0 as usize));
}
if NEVER_TRIP.load(std::sync::atomic::Ordering::Relaxed) {
unsafe {
let _ = jb::luna_jit_new_table();
let _ = jb::luna_jit_new_table_sized(0);
let _ = jb::luna_jit_materialize_sunk_table(
0,
std::ptr::null(),
std::ptr::null(),
0,
std::ptr::null(),
std::ptr::null(),
std::ptr::null(),
);
jb::luna_jit_table_set_int(0, 0, 0);
jb::luna_jit_table_set_raw(0, 0, 0, 0);
jb::luna_jit_table_set_field(0, 0, 0, 0);
let _ = jb::luna_jit_table_get_field(0, 0);
let _ = jb::luna_jit_op_get_tab_up(0, 0);
jb::luna_jit_table_set_nil(0, 0);
jb::luna_jit_table_set_float_float(0, 0, 0);
let _ = jb::luna_jit_table_get_int(0, 0);
let _ = jb::luna_jit_table_get_float(0, 0);
let _ = jb::luna_jit_upval_get(0);
let _ = jb::luna_jit_op_close(0);
jb::luna_jit_stack_update_raw(0, 0);
let _ = jb::luna_jit_op_concat(0, 0);
let _ = jb::luna_jit_str_buf_acquire();
jb::luna_jit_str_buf_release(0);
let _ = jb::luna_jit_str_buf_extend(0, 0);
let _ = jb::luna_jit_str_buf_intern(0);
let _ = jb::luna_jit_op_tforcall(
0,
0,
std::ptr::null_mut(),
std::ptr::null_mut(),
std::ptr::null_mut(),
);
let _ = jb::luna_jit_stack_load(0);
let _ = jb::luna_jit_stack_tag(0);
jb::luna_jit_spill_to_stack(0, 0, 0);
let _ = jb::luna_jit_op_closure(0);
let _ = jb::luna_jit_trace_materialize_frames(0, std::ptr::null());
let _ = jb::luna_jit_table_len(0);
}
}
std::hint::black_box(sum);
LUNA_AOT_HELPER_PIN.len()
}
}
#[cfg(feature = "jit-helpers")]
pub fn force_link_jit_helpers() -> usize {
jit_helpers_pin::force_link_jit_helpers()
}
pub const fn force_link_aot_entry() -> unsafe extern "C" fn(*const u8, usize) -> i32 {
luna_aot_run
}
#[cfg(feature = "jit-helpers")]
pub mod aot_strkey_resolver {
use luna_core::vm::Vm;
#[repr(C)]
struct IndexEntry {
bytes_ptr: *const u8,
slot_ptr: *mut *const u8,
}
#[cfg(all(unix, not(target_vendor = "apple")))]
unsafe extern "C" {
#[link_name = "__start_luna_strkey_idx"]
static mut LUNA_STRKEY_IDX_START: u8;
#[link_name = "__stop_luna_strkey_idx"]
static mut LUNA_STRKEY_IDX_END: u8;
}
#[cfg(target_vendor = "apple")]
unsafe extern "C" {
#[link_name = "\u{1}section$start$__DATA$luna_strkey_idx"]
static mut LUNA_STRKEY_IDX_START: u8;
#[link_name = "\u{1}section$end$__DATA$luna_strkey_idx"]
static mut LUNA_STRKEY_IDX_END: u8;
}
pub fn resolve_all(vm: &mut Vm) -> usize {
let (base, len_bytes): (*const u8, usize) = {
#[cfg(target_os = "windows")]
{
match crate::windows_section::find_section(b".lt_skix") {
Some((b, l)) => (b, l),
None => return 0,
}
}
#[cfg(all(not(target_os = "windows"), unix, not(target_vendor = "apple")))]
{
let start = &raw mut LUNA_STRKEY_IDX_START as *mut IndexEntry;
let end = &raw mut LUNA_STRKEY_IDX_END as *mut IndexEntry;
let len = (end as isize) - (start as isize);
if len <= 0 {
return 0;
}
(start as *const u8, len as usize)
}
#[cfg(all(not(target_os = "windows"), target_vendor = "apple"))]
{
let start = &raw mut LUNA_STRKEY_IDX_START as *mut IndexEntry;
let end = &raw mut LUNA_STRKEY_IDX_END as *mut IndexEntry;
let len = (end as isize) - (start as isize);
if len <= 0 {
return 0;
}
(start as *const u8, len as usize)
}
#[cfg(not(any(
target_os = "windows",
all(unix, not(target_vendor = "apple")),
target_vendor = "apple"
)))]
{
let _ = vm;
return 0;
}
};
walk_index_bytes(vm, base, len_bytes)
}
fn walk_index_bytes(vm: &mut Vm, base: *const u8, len_bytes: usize) -> usize {
if base.is_null() || len_bytes == 0 {
return 0;
}
let n_entries = len_bytes / core::mem::size_of::<IndexEntry>();
let start = base as *const IndexEntry;
let mut populated = 0usize;
unsafe {
for i in 0..n_entries {
let entry = &*start.add(i);
if entry.bytes_ptr.is_null() || entry.slot_ptr.is_null() {
continue;
}
let len = core::ptr::read_unaligned(entry.bytes_ptr as *const u64) as usize;
let payload = entry.bytes_ptr.add(8);
let bytes = core::slice::from_raw_parts(payload, len);
let interned = vm.heap.intern(bytes);
core::ptr::write(entry.slot_ptr, interned.as_ptr() as *const u8);
populated += 1;
}
}
populated
}
}
#[cfg(feature = "jit-helpers")]
pub mod aot_inline_chain_resolver {
use luna_core::jit::trace_types::FrameMaterializeInfo;
const FRAME_MATERIALIZE_INFO_SIZE: usize = 12;
#[repr(C)]
struct IndexEntry {
bytes_ptr: *const u8,
slot_ptr: *mut *const FrameMaterializeInfo,
}
#[cfg(all(unix, not(target_vendor = "apple")))]
unsafe extern "C" {
#[link_name = "__start_luna_inline_chnx"]
static mut LUNA_INLINE_CHNX_START: u8;
#[link_name = "__stop_luna_inline_chnx"]
static mut LUNA_INLINE_CHNX_END: u8;
}
#[cfg(target_vendor = "apple")]
unsafe extern "C" {
#[link_name = "\u{1}section$start$__DATA$luna_inline_chnx"]
static mut LUNA_INLINE_CHNX_START: u8;
#[link_name = "\u{1}section$end$__DATA$luna_inline_chnx"]
static mut LUNA_INLINE_CHNX_END: u8;
}
pub fn resolve_all() -> usize {
let (base, len_bytes): (*const u8, usize) = {
#[cfg(target_os = "windows")]
{
match crate::windows_section::find_section(b".lt_chai") {
Some((b, l)) => (b, l),
None => return 0,
}
}
#[cfg(all(not(target_os = "windows"), unix, not(target_vendor = "apple")))]
{
let start = &raw mut LUNA_INLINE_CHNX_START as *mut IndexEntry;
let end = &raw mut LUNA_INLINE_CHNX_END as *mut IndexEntry;
let len = (end as isize) - (start as isize);
if len <= 0 {
return 0;
}
(start as *const u8, len as usize)
}
#[cfg(all(not(target_os = "windows"), target_vendor = "apple"))]
{
let start = &raw mut LUNA_INLINE_CHNX_START as *mut IndexEntry;
let end = &raw mut LUNA_INLINE_CHNX_END as *mut IndexEntry;
let len = (end as isize) - (start as isize);
if len <= 0 {
return 0;
}
(start as *const u8, len as usize)
}
#[cfg(not(any(
target_os = "windows",
all(unix, not(target_vendor = "apple")),
target_vendor = "apple"
)))]
{
return 0;
}
};
walk_index_bytes(base, len_bytes)
}
fn walk_index_bytes(base: *const u8, len_bytes: usize) -> usize {
if base.is_null() || len_bytes == 0 {
return 0;
}
let probe_on = std::env::var_os("LUNA_AOT_PROBE").is_some();
let n_entries = len_bytes / core::mem::size_of::<IndexEntry>();
let start = base as *const IndexEntry;
let mut populated = 0usize;
unsafe {
for i in 0..n_entries {
let entry = &*start.add(i);
if entry.bytes_ptr.is_null() || entry.slot_ptr.is_null() {
continue;
}
let count = core::ptr::read_unaligned(entry.bytes_ptr as *const u64) as usize;
let Some(bytes_len) = count.checked_mul(FRAME_MATERIALIZE_INFO_SIZE) else {
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_inline_chain skip entry {i} reason=count_overflow count={count}"
);
}
continue;
};
let payload = entry.bytes_ptr.add(8);
let raw = core::slice::from_raw_parts(payload, bytes_len);
let mut vec: Vec<FrameMaterializeInfo> = Vec::with_capacity(count);
for j in 0..count {
let off = j * FRAME_MATERIALIZE_INFO_SIZE;
let base_offset = u32::from_le_bytes(raw[off..off + 4].try_into().unwrap());
let pc = u32::from_le_bytes(raw[off + 4..off + 8].try_into().unwrap());
let nresults = i32::from_le_bytes(raw[off + 8..off + 12].try_into().unwrap());
vec.push(FrameMaterializeInfo {
base_offset,
pc,
nresults,
});
}
let rc: luna_core::jit::send_compat::TArc<[FrameMaterializeInfo]> = vec.into();
let chain_ptr: *const FrameMaterializeInfo = if count == 0 {
core::ptr::null()
} else {
&rc[0] as *const FrameMaterializeInfo
};
core::mem::forget(rc);
core::ptr::write(entry.slot_ptr, chain_ptr);
populated += 1;
}
}
populated
}
}
#[cfg(feature = "jit-helpers")]
pub mod aot_trace_registry {
use luna_core::jit::aot_meta::{
AotTraceIndexEntry, decode_meta_blob, unpack_exit_tag, unpack_tag_res_kind,
};
use luna_core::jit::trace_types::{CompiledTrace, ExitTag, TraceFn};
use luna_core::vm::Vm;
#[cfg(all(unix, not(target_vendor = "apple")))]
unsafe extern "C" {
#[link_name = "__start_luna_trace_meta"]
static mut LUNA_TRACE_META_START: u8;
#[link_name = "__stop_luna_trace_meta"]
static mut LUNA_TRACE_META_END: u8;
}
#[cfg(target_vendor = "apple")]
unsafe extern "C" {
#[link_name = "\u{1}section$start$__DATA$luna_trace_meta"]
static mut LUNA_TRACE_META_START: u8;
#[link_name = "\u{1}section$end$__DATA$luna_trace_meta"]
static mut LUNA_TRACE_META_END: u8;
}
pub fn install_all(
vm: &mut Vm,
root: luna_core::runtime::Gc<luna_core::runtime::function::Proto>,
) -> usize {
let (base, len_bytes): (*const u8, usize) = {
#[cfg(target_os = "windows")]
{
match crate::windows_section::find_section(b".lt_meta") {
Some((b, l)) => (b, l),
None => return 0,
}
}
#[cfg(all(not(target_os = "windows"), unix, not(target_vendor = "apple")))]
{
let start = &raw mut LUNA_TRACE_META_START as *mut AotTraceIndexEntry;
let end = &raw mut LUNA_TRACE_META_END as *mut AotTraceIndexEntry;
let len = (end as isize) - (start as isize);
if len <= 0 {
return 0;
}
(start as *const u8, len as usize)
}
#[cfg(all(not(target_os = "windows"), target_vendor = "apple"))]
{
let start = &raw mut LUNA_TRACE_META_START as *mut AotTraceIndexEntry;
let end = &raw mut LUNA_TRACE_META_END as *mut AotTraceIndexEntry;
let len = (end as isize) - (start as isize);
if len <= 0 {
return 0;
}
(start as *const u8, len as usize)
}
#[cfg(not(any(
target_os = "windows",
all(unix, not(target_vendor = "apple")),
target_vendor = "apple"
)))]
{
let _ = (vm, root);
return 0;
}
};
unsafe { walk_meta_section(vm, root, base as *const AotTraceIndexEntry, len_bytes) }
}
unsafe fn walk_meta_section(
vm: &mut Vm,
root: luna_core::runtime::Gc<luna_core::runtime::function::Proto>,
start: *const AotTraceIndexEntry,
len_bytes: usize,
) -> usize {
if start.is_null() || len_bytes < core::mem::size_of::<AotTraceIndexEntry>() {
return 0;
}
let n_entries = len_bytes / core::mem::size_of::<AotTraceIndexEntry>();
let proto_hashes = vm.collect_proto_hashes(root);
let probe_on = std::env::var_os("LUNA_AOT_PROBE").is_some();
let mut installed = 0usize;
unsafe {
for i in 0..n_entries {
let entry = &*start.add(i);
if entry.fn_ptr == 0 || entry.meta_ptr == 0 {
continue;
}
let meta_bytes = core::slice::from_raw_parts(
entry.meta_ptr as *const u8,
entry.meta_len as usize,
);
let decoded = match decode_meta_blob(meta_bytes) {
Ok(d) => d,
Err(reason) => {
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_trace skip head_pc={} reason={reason}",
entry.head_pc
);
}
continue;
}
};
let matched = proto_hashes
.iter()
.find(|(_p, h)| *h == entry.proto_hash)
.map(|(p, _h)| *p);
let Some(proto) = matched else {
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_trace skip head_pc={} reason=proto_hash_unmatched",
entry.head_pc
);
}
continue;
};
let mut exit_tags_vec: Vec<ExitTag> = Vec::with_capacity(decoded.exit_tags.len());
let mut tag_decode_ok = true;
for raw in decoded.exit_tags.iter().copied() {
if let Some(t) = unpack_exit_tag(raw) {
exit_tags_vec.push(t);
} else {
tag_decode_ok = false;
break;
}
}
let Some(tag_res_kind) = unpack_tag_res_kind(decoded.header.tag_res_kind) else {
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_trace skip head_pc={} reason=tag_res_kind_invalid",
entry.head_pc
);
}
continue;
};
if !tag_decode_ok {
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_trace skip head_pc={} reason=exit_tag_invalid",
entry.head_pc
);
}
continue;
}
let entry_tags_rc: luna_core::jit::send_compat::TArc<[u8]> =
decoded.entry_tags.into();
let exit_tags_rc: luna_core::jit::send_compat::TArc<[ExitTag]> =
exit_tags_vec.into();
let mut per_exit_tags_decoded: Vec<(
u32,
luna_core::jit::send_compat::TArc<[ExitTag]>,
)> = Vec::with_capacity(decoded.per_exit_tags.len());
let mut per_exit_tags_ok = true;
for ent in &decoded.per_exit_tags {
let mut tags: Vec<ExitTag> = Vec::with_capacity(ent.tags_packed.len());
for raw in ent.tags_packed.iter().copied() {
if let Some(t) = unpack_exit_tag(raw) {
tags.push(t);
} else {
per_exit_tags_ok = false;
break;
}
}
if !per_exit_tags_ok {
break;
}
per_exit_tags_decoded.push((ent.cont_pc, tags.into()));
}
if !per_exit_tags_ok {
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_trace skip head_pc={} reason=per_exit_tag_invalid",
entry.head_pc
);
}
continue;
}
let mut per_exit_inline_decoded: Vec<luna_core::jit::trace_types::InlineSideExit> =
Vec::with_capacity(decoded.per_exit_inline.len());
let mut inline_ok = true;
for ent in &decoded.per_exit_inline {
let Some(chain_vec) = ent.rebuild_chain() else {
inline_ok = false;
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_trace skip head_pc={} reason=per_exit_inline_chain_invalid (cont_pc={})",
entry.head_pc, ent.cont_pc
);
}
break;
};
let mut tags: Vec<ExitTag> = Vec::with_capacity(ent.tags_packed.len());
for raw in ent.tags_packed.iter().copied() {
if let Some(t) = unpack_exit_tag(raw) {
tags.push(t);
} else {
inline_ok = false;
break;
}
}
if !inline_ok {
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_trace skip head_pc={} reason=per_exit_inline_tag_invalid (cont_pc={})",
entry.head_pc, ent.cont_pc
);
}
break;
}
per_exit_inline_decoded.push(luna_core::jit::trace_types::InlineSideExit {
cont_pc: ent.cont_pc,
head_resume_pc: ent.head_resume_pc,
exit_tags: tags.into(),
chain: chain_vec.into(),
side_trace_ptr: Box::new(luna_core::jit::send_compat::TCellPtr::null()),
});
}
if !inline_ok {
continue;
}
let fn_ptr_raw = entry.fn_ptr as *const u8;
let trace_entry: TraceFn = core::mem::transmute::<*const u8, TraceFn>(fn_ptr_raw);
let ct = CompiledTrace::from_aot_meta(
trace_entry,
decoded.header.head_pc,
decoded.header.n_ops,
decoded.header.dispatchable != 0,
decoded.header.window_size,
entry_tags_rc,
exit_tags_rc,
tag_res_kind,
per_exit_tags_decoded,
per_exit_inline_decoded,
);
vm.install_aot_trace(proto, ct);
installed += 1;
if probe_on {
eprintln!(
"luna-runtime-helpers: aot_trace_installed head_pc={}",
decoded.header.head_pc
);
}
}
}
installed
}
}