pr 06
This commit is contained in:
parent
99f024eaa9
commit
bfcdc5538d
@ -7,7 +7,6 @@ use prometeu_bytecode::{ConstantPoolEntry, DebugInfo};
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use prometeu_abi::virtual_machine::{ProgramImage, Value};
|
use prometeu_abi::virtual_machine::{ProgramImage, Value};
|
||||||
use prometeu_analysis::ids::ProjectId;
|
use prometeu_analysis::ids::ProjectId;
|
||||||
use prometeu_bytecode::readwrite::{read_u32_le, write_u32_le};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum LinkError {
|
pub enum LinkError {
|
||||||
@ -210,7 +209,7 @@ impl Linker {
|
|||||||
let next_pc = instr.next_pc;
|
let next_pc = instr.next_pc;
|
||||||
let imm_start = instr.pc + 2; // start of immediate payload
|
let imm_start = instr.pc + 2; // start of immediate payload
|
||||||
let imm_u32_opt = match opcode {
|
let imm_u32_opt = match opcode {
|
||||||
OpCode::PushConst | OpCode::Call | OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue => {
|
OpCode::PushConst | OpCode::Call => {
|
||||||
match instr.imm_u32() {
|
match instr.imm_u32() {
|
||||||
Ok(v) => Some(v),
|
Ok(v) => Some(v),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
@ -255,15 +254,9 @@ impl Linker {
|
|||||||
patch_u32_at(&mut combined_code, imm_start, &|_| global_func_idx);
|
patch_u32_at(&mut combined_code, imm_start, &|_| global_func_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Branches are strictly function-relative. Do NOT relocate.
|
// Branches are strictly function-relative. Do NOT relocate or inspect immediates.
|
||||||
// The emitter encodes `target_rel = label - func_start` and the verifier enforces it.
|
// The emitter encodes `target_rel = label - func_start` and the verifier enforces it.
|
||||||
OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue => {
|
OpCode::Jmp | OpCode::JmpIfFalse | OpCode::JmpIfTrue => { /* no-op */ }
|
||||||
let _ = imm_u32_opt.ok_or_else(|| LinkError::IncompatibleSymbolSignature(format!(
|
|
||||||
"Invalid branch immediate at pc {}",
|
|
||||||
pc - code_offset
|
|
||||||
)))?;
|
|
||||||
// Intentionally no patch here.
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -640,4 +633,76 @@ mod tests {
|
|||||||
let cjmp_patched = u32::from_le_bytes(result.rom[cjmp_imm_off..cjmp_imm_off+4].try_into().unwrap());
|
let cjmp_patched = u32::from_le_bytes(result.rom[cjmp_imm_off..cjmp_imm_off+4].try_into().unwrap());
|
||||||
assert_eq!(cjmp_patched, 0);
|
assert_eq!(cjmp_patched, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_jump_link_order_invariance() {
|
||||||
|
// Same setup as previous test, but link order is [m2, m1]
|
||||||
|
let key1 = ProjectKey { name: "m1".into(), version: "1.0.0".into() };
|
||||||
|
let id1 = ProjectId(0);
|
||||||
|
let step1 = BuildStep { project_id: id1, project_key: key1.clone(), project_dir: "".into(), target: BuildTarget::Main, sources: vec![], deps: BTreeMap::new() };
|
||||||
|
|
||||||
|
let mut code1 = Vec::new();
|
||||||
|
code1.extend_from_slice(&(OpCode::Add as u16).to_le_bytes());
|
||||||
|
code1.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||||
|
let m1 = CompiledModule {
|
||||||
|
project_id: id1,
|
||||||
|
project_key: key1.clone(),
|
||||||
|
target: BuildTarget::Main,
|
||||||
|
exports: BTreeMap::new(),
|
||||||
|
imports: vec![],
|
||||||
|
const_pool: vec![],
|
||||||
|
code: code1.clone(),
|
||||||
|
function_metas: vec![FunctionMeta { code_offset: 0, code_len: code1.len() as u32, ..Default::default() }],
|
||||||
|
debug_info: None,
|
||||||
|
symbols: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
let key2 = ProjectKey { name: "m2".into(), version: "1.0.0".into() };
|
||||||
|
let id2 = ProjectId(1);
|
||||||
|
let step2 = BuildStep { project_id: id2, project_key: key2.clone(), project_dir: "".into(), target: BuildTarget::Main, sources: vec![], deps: BTreeMap::new() };
|
||||||
|
|
||||||
|
let mut code2 = Vec::new();
|
||||||
|
let jmp_pc = code2.len() as u32; // where opcode will be placed
|
||||||
|
code2.extend_from_slice(&(OpCode::Jmp as u16).to_le_bytes());
|
||||||
|
code2.extend_from_slice(&0u32.to_le_bytes());
|
||||||
|
|
||||||
|
code2.extend_from_slice(&(OpCode::PushBool as u16).to_le_bytes());
|
||||||
|
code2.push(1u8);
|
||||||
|
let cjmp_pc = code2.len() as u32;
|
||||||
|
code2.extend_from_slice(&(OpCode::JmpIfTrue as u16).to_le_bytes());
|
||||||
|
code2.extend_from_slice(&0u32.to_le_bytes());
|
||||||
|
|
||||||
|
code2.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||||
|
|
||||||
|
let m2 = CompiledModule {
|
||||||
|
project_id: id2,
|
||||||
|
project_key: key2.clone(),
|
||||||
|
target: BuildTarget::Main,
|
||||||
|
exports: BTreeMap::new(),
|
||||||
|
imports: vec![],
|
||||||
|
const_pool: vec![],
|
||||||
|
code: code2.clone(),
|
||||||
|
function_metas: vec![FunctionMeta { code_offset: 0, code_len: code2.len() as u32, ..Default::default() }],
|
||||||
|
debug_info: None,
|
||||||
|
symbols: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Link with order [m2, m1]
|
||||||
|
let result = Linker::link(vec![m2, m1], vec![step2, step1]).unwrap();
|
||||||
|
|
||||||
|
// Module 2 is now at offset 0
|
||||||
|
let module2_offset = 0usize;
|
||||||
|
|
||||||
|
// Verify that the JMP immediate remains function-relative (0), no relocation applied
|
||||||
|
let jmp_abs_pc = module2_offset + jmp_pc as usize;
|
||||||
|
let jmp_imm_off = jmp_abs_pc + 2; // skip opcode
|
||||||
|
let jmp_patched = u32::from_le_bytes(result.rom[jmp_imm_off..jmp_imm_off+4].try_into().unwrap());
|
||||||
|
assert_eq!(jmp_patched, 0);
|
||||||
|
|
||||||
|
// Verify that the conditional JMP immediate also remains function-relative (0)
|
||||||
|
let cjmp_abs_pc = module2_offset + cjmp_pc as usize;
|
||||||
|
let cjmp_imm_off = cjmp_abs_pc + 2;
|
||||||
|
let cjmp_patched = u32::from_le_bytes(result.rom[cjmp_imm_off..cjmp_imm_off+4].try_into().unwrap());
|
||||||
|
assert_eq!(cjmp_patched, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user