use crate::input::ObjData;
use crate::layout::Layout;
const S_THREAD_LOCAL_REGULAR: u32 = 0x11;
const S_THREAD_LOCAL_ZEROFILL: u32 = 0x12;
const S_THREAD_LOCAL_VARIABLES: u32 = 0x13;
const THUNK_SIZE: usize = 24;
pub fn finalize_tlv(layout: &mut Layout, objects: &[ObjData]) -> Result<bool, String> {
let Some(&vars_idx) = layout.sec_map.get("__DATA:__thread_vars") else {
return Ok(false);
};
let mut template: Vec<(u64, u64)> = layout
.merged
.iter()
.filter(|ms| {
matches!(
ms.flags & 0xFF,
S_THREAD_LOCAL_REGULAR | S_THREAD_LOCAL_ZEROFILL
)
})
.map(|ms| (ms.vm_addr, ms.virtual_size as u64))
.collect();
template.sort();
for w in template.windows(2) {
if w[0].0 + w[0].1 != w[1].0 {
return Err(format!(
"TLV template sections not contiguous: {:#x}+{:#x} then {:#x}",
w[0].0, w[0].1, w[1].0
));
}
}
let Some(&(template_base, _)) = template.first() else {
return Err("TLV descriptors present but no TLV template sections".to_string());
};
let template_end = template.last().map(|&(vm, size)| vm + size).unwrap_or(template_base);
let template_size = template_end - template_base;
let mut found = false;
let chunks = layout.merged[vars_idx].chunks.clone();
let data = &mut layout.merged[vars_idx].data;
for chunk in &chunks {
let chunk_len = objects[chunk.obj_idx].sections[chunk.sec_idx].data.len();
let n_thunks = chunk_len / THUNK_SIZE;
for t in 0..n_thunks {
let off = chunk.offset_in_merged + t * THUNK_SIZE;
let var_addr = u64::from_le_bytes(data[off + 16..off + 24].try_into().unwrap());
if var_addr == 0 {
if data[off..off + THUNK_SIZE].iter().all(|&b| b == 0) {
continue;
}
return Err(format!(
"TLV thunk at {}:{off:#x} has null offset field",
layout.merged[vars_idx].name
));
}
if var_addr < template_base || var_addr >= template_base + template_size {
return Err(format!(
"TLV thunk at {}:{off:#x}: $tlv$init {var_addr:#x} outside template \
[{template_base:#x}, {:#x})",
layout.merged[vars_idx].name,
template_base + template_size
));
}
data[off..off + 8].fill(0);
data[off + 8..off + 16].fill(0);
data[off + 16..off + 24].copy_from_slice(&(var_addr - template_base).to_le_bytes());
found = true;
}
}
Ok(found)
}