Skip to content

Commit aee585e

Browse files
committed
up
1 parent 8b623e7 commit aee585e

2 files changed

Lines changed: 268 additions & 22 deletions

File tree

crates/dust_codegen/src/host_runtime_shim.rs

Lines changed: 267 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
#![allow(clippy::missing_safety_doc)]
2-
#![allow(clippy::too_many_arguments)]
3-
41
use std::collections::{HashMap, HashSet};
52
use std::ffi::{CStr, CString};
63
use std::fs::{self, OpenOptions};
@@ -40,6 +37,7 @@ const SHT_PROGBITS: u32 = 1;
4037
const SHT_DYNAMIC: u32 = 6;
4138
const SHT_DYNSYM: u32 = 11;
4239
const STT_TLS: u8 = 6;
40+
const STB_GLOBAL: u8 = 1;
4341
const PT_LOAD: u32 = 1;
4442
const PT_DYNAMIC: u32 = 2;
4543
const PT_INTERP: u32 = 3;
@@ -223,6 +221,7 @@ struct Aarch64TlsSyntheticSlot {
223221
canonical_object_index: u32,
224222
canonical_symbol_index: u32,
225223
slot_index: u32,
224+
reloc_addend: u64,
226225
}
227226

228227
#[derive(Clone, Default)]
@@ -1174,6 +1173,7 @@ fn aarch64_tls_synth_reserve_slot(
11741173
canonical_object_index,
11751174
canonical_symbol_index,
11761175
slot_index,
1176+
reloc_addend: 0,
11771177
});
11781178
Ok(slot_index)
11791179
}
@@ -1186,6 +1186,23 @@ fn aarch64_tls_synth_slot_address_inner(state: &mut LinkerState, slot_index: u32
11861186
Ok(aarch64_tls_synth_slot_address(base, slot_index))
11871187
}
11881188

1189+
fn aarch64_tls_synth_record_addend(state: &mut LinkerState, slot_index: u32, addend: u64) -> Result<(), u32> {
1190+
let slot = state
1191+
.aarch64_tls_synth_slots
1192+
.get_mut(slot_index as usize)
1193+
.ok_or(ERR_INVALID_RELOCATION)?;
1194+
if slot.reloc_addend == 0 {
1195+
slot.reloc_addend = addend;
1196+
return Ok(());
1197+
}
1198+
if slot.reloc_addend != addend {
1199+
// Current staged synthetic slot model coalesces by symbol/model and does not allocate
1200+
// distinct slots for per-site addend variants yet.
1201+
return Err(ERR_NOT_IMPLEMENTED_YET);
1202+
}
1203+
Ok(())
1204+
}
1205+
11891206
fn aarch64_page_delta_bytes_host(target_addr: u64, place_addr: u64) -> u64 {
11901207
let page_mask = !0xfffu64;
11911208
let target_page = target_addr & page_mask;
@@ -1214,6 +1231,7 @@ fn aarch64_tls_synth_reloc_value(
12141231
place_addr: u64,
12151232
) -> Result<u64, u32> {
12161233
let slot_index = aarch64_tls_synth_reserve_slot(state, object_index, symbol_index, reloc_type)?;
1234+
aarch64_tls_synth_record_addend(state, slot_index, addend)?;
12171235
let slot_addr = aarch64_tls_synth_slot_address_inner(state, slot_index)?;
12181236
let target = slot_addr.wrapping_add(addend);
12191237
match reloc_type {
@@ -1458,27 +1476,103 @@ fn elf64_r_info(symbol_index: u32, reloc_type: u32) -> u64 {
14581476
((symbol_index as u64) << 32) | (reloc_type as u64)
14591477
}
14601478

1461-
fn build_aarch64_tls_synth_rela_dyn(state: &LinkerState) -> Vec<Elf64RelaRecord> {
1479+
fn append_elf64_sym(
1480+
dynsym: &mut Vec<u8>,
1481+
st_name: u32,
1482+
st_info: u8,
1483+
st_other: u8,
1484+
st_shndx: u16,
1485+
st_value: u64,
1486+
st_size: u64,
1487+
) {
1488+
let base = dynsym.len();
1489+
dynsym.resize(base.saturating_add(24), 0);
1490+
write_u32_le(dynsym, base, st_name);
1491+
if base + 4 < dynsym.len() {
1492+
dynsym[base + 4] = st_info;
1493+
}
1494+
if base + 5 < dynsym.len() {
1495+
dynsym[base + 5] = st_other;
1496+
}
1497+
write_u16_le(dynsym, base + 6, st_shndx);
1498+
write_u64_le(dynsym, base + 8, st_value);
1499+
write_u64_le(dynsym, base + 16, st_size);
1500+
}
1501+
1502+
fn aarch64_tls_synth_symbol_name(slot: &Aarch64TlsSyntheticSlot) -> String {
1503+
let model_tag = match slot.model {
1504+
AARCH64_TLS_SYNTH_MODEL_TLSGD => "gd",
1505+
AARCH64_TLS_SYNTH_MODEL_TLSLD => "ld",
1506+
AARCH64_TLS_SYNTH_MODEL_TLSDESC => "desc",
1507+
_ => "tls",
1508+
};
1509+
format!(
1510+
"__dust_tls_{}_h{:016x}_o{}_s{}",
1511+
model_tag,
1512+
slot.canonical_name_hash,
1513+
slot.canonical_object_index,
1514+
slot.canonical_symbol_index
1515+
)
1516+
}
1517+
1518+
fn build_aarch64_tls_synth_dynsym_and_rela_dyn(
1519+
state: &LinkerState,
1520+
dynstr: &mut Vec<u8>,
1521+
) -> (Vec<u8>, Vec<Elf64RelaRecord>) {
14621522
let base = match aarch64_tls_synth_region_base_from_state(state) {
14631523
Some(v) => v,
1464-
None => return Vec::new(),
1524+
None => return (Vec::new(), Vec::new()),
14651525
};
1526+
let mut dynsym = vec![0u8; 24]; // null dynsym entry
14661527
let mut out = Vec::new();
1528+
let mut sym_index_by_key: HashMap<(u64, u32, u32), u32> = HashMap::new();
1529+
let mut slot_sym_index: HashMap<u32, u32> = HashMap::new();
1530+
1531+
for slot in &state.aarch64_tls_synth_slots {
1532+
if slot.model == AARCH64_TLS_SYNTH_MODEL_TLSLD {
1533+
continue;
1534+
}
1535+
let key = (
1536+
slot.canonical_name_hash,
1537+
slot.canonical_object_index,
1538+
slot.canonical_symbol_index,
1539+
);
1540+
if let Some(existing) = sym_index_by_key.get(&key) {
1541+
slot_sym_index.insert(slot.slot_index, *existing);
1542+
continue;
1543+
}
1544+
let name = aarch64_tls_synth_symbol_name(slot);
1545+
let st_name = dynstr.len() as u32;
1546+
dynstr.extend_from_slice(name.as_bytes());
1547+
dynstr.push(0);
1548+
let sym_index = (dynsym.len() / 24) as u32;
1549+
append_elf64_sym(
1550+
&mut dynsym,
1551+
st_name,
1552+
((STB_GLOBAL & 0x0f) << 4) | (STT_TLS & 0x0f),
1553+
0,
1554+
SHN_UNDEF,
1555+
0,
1556+
0,
1557+
);
1558+
sym_index_by_key.insert(key, sym_index);
1559+
slot_sym_index.insert(slot.slot_index, sym_index);
1560+
}
1561+
14671562
for slot in &state.aarch64_tls_synth_slots {
14681563
let slot_addr = aarch64_tls_synth_slot_address(base, slot.slot_index);
14691564
match slot.model {
14701565
AARCH64_TLS_SYNTH_MODEL_TLSGD => {
1471-
// Provisional TLSGD GOT pair: module id + dtprel entry. Symbol index remains 0
1472-
// until dynsym symbol emission is implemented for input .o TLS symbols.
1566+
let sym_index = *slot_sym_index.get(&slot.slot_index).unwrap_or(&0);
14731567
out.push(Elf64RelaRecord {
14741568
offset: slot_addr,
1475-
info: elf64_r_info(0, R_AARCH64_TLS_DTPMOD),
1569+
info: elf64_r_info(sym_index, R_AARCH64_TLS_DTPMOD),
14761570
addend: 0,
14771571
});
14781572
out.push(Elf64RelaRecord {
14791573
offset: slot_addr.saturating_add(8),
1480-
info: elf64_r_info(0, R_AARCH64_TLS_DTPREL),
1481-
addend: 0,
1574+
info: elf64_r_info(sym_index, R_AARCH64_TLS_DTPREL),
1575+
addend: slot.reloc_addend,
14821576
});
14831577
}
14841578
AARCH64_TLS_SYNTH_MODEL_TLSLD => {
@@ -1490,16 +1584,17 @@ fn build_aarch64_tls_synth_rela_dyn(state: &LinkerState) -> Vec<Elf64RelaRecord>
14901584
});
14911585
}
14921586
AARCH64_TLS_SYNTH_MODEL_TLSDESC => {
1587+
let sym_index = *slot_sym_index.get(&slot.slot_index).unwrap_or(&0);
14931588
out.push(Elf64RelaRecord {
14941589
offset: slot_addr,
1495-
info: elf64_r_info(0, R_AARCH64_TLSDESC),
1496-
addend: 0,
1590+
info: elf64_r_info(sym_index, R_AARCH64_TLSDESC),
1591+
addend: slot.reloc_addend,
14971592
});
14981593
}
14991594
_ => {}
15001595
}
15011596
}
1502-
out
1597+
(dynsym, out)
15031598
}
15041599

15051600
#[derive(Clone)]
@@ -1658,16 +1753,13 @@ fn write_minimal_elf_exec(
16581753
|| runpath_offset.is_some()
16591754
|| z_now;
16601755

1661-
let dynsym = if dynamic_enabled {
1662-
// Minimal dynsym (null symbol). This keeps DT_SYMTAB/DT_SYMENT structurally valid while
1663-
// synthetic TLS descriptor/GOT relocs are materialized. Full TLS dynsym binding/name
1664-
// parity is tracked separately.
1665-
vec![0u8; 24]
1666-
} else {
1667-
Vec::new()
1668-
};
1756+
let mut dynsym = if dynamic_enabled { vec![0u8; 24] } else { Vec::new() };
16691757
let tls_synth_rela_records = if dynamic_enabled && machine == EM_AARCH64 {
1670-
build_aarch64_tls_synth_rela_dyn(state)
1758+
let (dynsym_tls, rela_tls) = build_aarch64_tls_synth_dynsym_and_rela_dyn(state, &mut dynstr);
1759+
if !dynsym_tls.is_empty() {
1760+
dynsym = dynsym_tls;
1761+
}
1762+
rela_tls
16711763
} else {
16721764
Vec::new()
16731765
};
@@ -7677,3 +7769,156 @@ pub extern "C" fn host_linker_strip_debug() -> u32 {
76777769
state.strip_debug = true;
76787770
set_last_error(&mut state, ERR_OK)
76797771
}
7772+
7773+
#[cfg(test)]
7774+
mod tests {
7775+
use super::*;
7776+
7777+
fn dynamic_tag_map(raw: &[u8]) -> HashMap<u64, u64> {
7778+
let phoff = read_u64_le_at(raw, 32).unwrap_or(0) as usize;
7779+
let phentsize = read_u16_le_at(raw, 54).unwrap_or(0) as usize;
7780+
let phnum = read_u16_le_at(raw, 56).unwrap_or(0) as usize;
7781+
let mut dynamic_off = 0usize;
7782+
let mut dynamic_size = 0usize;
7783+
let mut load_off = 0usize;
7784+
let mut load_vaddr = 0u64;
7785+
for i in 0..phnum {
7786+
let off = phoff + i.saturating_mul(phentsize);
7787+
let p_type = read_u32_le_at(raw, off).unwrap_or(0);
7788+
if p_type == PT_DYNAMIC {
7789+
dynamic_off = read_u64_le_at(raw, off + 8).unwrap_or(0) as usize;
7790+
dynamic_size = read_u64_le_at(raw, off + 32).unwrap_or(0) as usize;
7791+
}
7792+
if p_type == PT_LOAD {
7793+
load_off = read_u64_le_at(raw, off + 8).unwrap_or(0) as usize;
7794+
load_vaddr = read_u64_le_at(raw, off + 16).unwrap_or(0);
7795+
}
7796+
}
7797+
let mut tags = HashMap::new();
7798+
let mut at = dynamic_off;
7799+
let end = dynamic_off.saturating_add(dynamic_size);
7800+
while at + 16 <= end && at + 16 <= raw.len() {
7801+
let tag = read_u64_le_at(raw, at).unwrap_or(DT_NULL);
7802+
let value = read_u64_le_at(raw, at + 8).unwrap_or(0);
7803+
tags.insert(tag, value);
7804+
at += 16;
7805+
if tag == DT_NULL {
7806+
break;
7807+
}
7808+
}
7809+
// Stash PT_LOAD mapping in high sentinel tags for helper-free callers.
7810+
tags.insert(0xffff_ffff_ffff_ff01, load_off as u64);
7811+
tags.insert(0xffff_ffff_ffff_ff02, load_vaddr);
7812+
tags
7813+
}
7814+
7815+
fn vaddr_to_file_off(tags: &HashMap<u64, u64>, vaddr: u64) -> usize {
7816+
let load_off = *tags.get(&0xffff_ffff_ffff_ff01).unwrap_or(&0);
7817+
let load_vaddr = *tags.get(&0xffff_ffff_ffff_ff02).unwrap_or(&0);
7818+
if vaddr < load_vaddr {
7819+
return 0;
7820+
}
7821+
load_off.saturating_add(vaddr - load_vaddr) as usize
7822+
}
7823+
7824+
fn cstr_from(raw: &[u8], off: usize) -> String {
7825+
if off >= raw.len() {
7826+
return String::new();
7827+
}
7828+
let rest = &raw[off..];
7829+
let end = rest.iter().position(|b| *b == 0).unwrap_or(rest.len());
7830+
String::from_utf8_lossy(&rest[..end]).to_string()
7831+
}
7832+
7833+
#[test]
7834+
fn aarch64_tls_synth_elf_emits_symbol_aware_dynsym_and_rela() {
7835+
let mut state = LinkerState::default();
7836+
reset_linker_state_defaults(&mut state);
7837+
state.target = TARGET_AARCH64_LINUX;
7838+
state.output_format = FORMAT_ELF64;
7839+
state.image_base = 0x0040_0000;
7840+
state.entry = 0x0040_1000;
7841+
state.aarch64_tls_synth_base = align_up(
7842+
state.image_base + AARCH64_TLS_SYNTH_REGION_OFFSET,
7843+
AARCH64_TLS_SYNTH_SLOT_SIZE,
7844+
);
7845+
state.aarch64_tls_synth_slots.push(Aarch64TlsSyntheticSlot {
7846+
model: AARCH64_TLS_SYNTH_MODEL_TLSDESC,
7847+
canonical_name_hash: 0x1111_2222_3333_4444,
7848+
canonical_object_index: 1,
7849+
canonical_symbol_index: 7,
7850+
slot_index: 0,
7851+
reloc_addend: 0x20,
7852+
});
7853+
state.aarch64_tls_synth_slots.push(Aarch64TlsSyntheticSlot {
7854+
model: AARCH64_TLS_SYNTH_MODEL_TLSGD,
7855+
canonical_name_hash: 0x5555_6666_7777_8888,
7856+
canonical_object_index: 2,
7857+
canonical_symbol_index: 9,
7858+
slot_index: 1,
7859+
reloc_addend: 0x30,
7860+
});
7861+
7862+
let temp = std::env::temp_dir().join("dustlink_tls_synth_fixture_aarch64.elf");
7863+
let _ = fs::remove_file(&temp);
7864+
let rc = write_elf_output_for_state(&temp, &state, state.entry, state.image_base);
7865+
assert_eq!(rc, ERR_OK);
7866+
let raw = fs::read(&temp).expect("read synthetic elf fixture");
7867+
let _ = fs::remove_file(&temp);
7868+
7869+
let tags = dynamic_tag_map(&raw);
7870+
assert!(tags.contains_key(&DT_SYMTAB));
7871+
assert_eq!(tags.get(&DT_SYMENT).copied().unwrap_or(0), 24);
7872+
assert!(tags.contains_key(&DT_RELA));
7873+
assert_eq!(tags.get(&DT_RELAENT).copied().unwrap_or(0), 24);
7874+
assert!(tags.contains_key(&DT_RELASZ));
7875+
assert_eq!(
7876+
tags.get(&DT_PLTGOT).copied().unwrap_or(0),
7877+
state.aarch64_tls_synth_base
7878+
);
7879+
7880+
let dynstr_vaddr = tags.get(&DT_STRTAB).copied().unwrap_or(0);
7881+
let dynsym_vaddr = tags.get(&DT_SYMTAB).copied().unwrap_or(0);
7882+
let rela_vaddr = tags.get(&DT_RELA).copied().unwrap_or(0);
7883+
let rela_size = tags.get(&DT_RELASZ).copied().unwrap_or(0) as usize;
7884+
let dynstr_off = vaddr_to_file_off(&tags, dynstr_vaddr);
7885+
let dynsym_off = vaddr_to_file_off(&tags, dynsym_vaddr);
7886+
let rela_off = vaddr_to_file_off(&tags, rela_vaddr);
7887+
assert!(rela_off > 0 && rela_size >= 24);
7888+
assert!(rela_off + rela_size <= raw.len());
7889+
7890+
let mut saw_tlsdesc = false;
7891+
let mut saw_tlsgd_dtprel = false;
7892+
let mut saw_named_symbol = false;
7893+
let mut off = rela_off;
7894+
let end = rela_off + rela_size;
7895+
while off + 24 <= end {
7896+
let r_info = read_u64_le_at(&raw, off + 8).unwrap_or(0);
7897+
let r_addend = read_u64_le_at(&raw, off + 16).unwrap_or(0);
7898+
let sym_index = (r_info >> 32) as u32;
7899+
let r_type = (r_info & 0xffff_ffff) as u32;
7900+
if r_type == R_AARCH64_TLSDESC {
7901+
saw_tlsdesc = true;
7902+
assert_ne!(sym_index, 0);
7903+
assert_eq!(r_addend, 0x20);
7904+
}
7905+
if r_type == R_AARCH64_TLS_DTPREL {
7906+
saw_tlsgd_dtprel = true;
7907+
assert_ne!(sym_index, 0);
7908+
assert_eq!(r_addend, 0x30);
7909+
}
7910+
if sym_index != 0 {
7911+
let sym_off = dynsym_off + (sym_index as usize).saturating_mul(24);
7912+
let st_name = read_u32_le_at(&raw, sym_off).unwrap_or(0) as usize;
7913+
let name = cstr_from(&raw, dynstr_off + st_name);
7914+
if name.starts_with("__dust_tls_") {
7915+
saw_named_symbol = true;
7916+
}
7917+
}
7918+
off += 24;
7919+
}
7920+
assert!(saw_tlsdesc);
7921+
assert!(saw_tlsgd_dtprel);
7922+
assert!(saw_named_symbol);
7923+
}
7924+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include!("../src/host_runtime_shim.rs");

0 commit comments

Comments
 (0)