use crate::{
jump::{relocate, JumpTable},
wasm::ToLSBytes,
Buffer, Error, Result, BUFFER_LIMIT,
};
use opcodes::ShangHai as OpCode;
impl JumpTable {
pub fn relocate(&mut self, buffer: &mut Buffer) -> Result<()> {
self.shift_targets()?;
tracing::trace!("code section offset: 0x{:x}", self.code.offset());
while let Some((pc, jump)) = self.jump.pop_first() {
tracing::trace!("run relocation for {jump:?}");
let offset = relocate::offset(pc)?;
let mut target = self.target(&jump)?;
if jump.is_offset() {
target += pc;
}
relocate::pc(buffer, pc, target, offset)?;
self.shift_label_pc(pc, offset)?;
}
buffer.extend_from_slice(&self.code.finish());
Ok(())
}
}
pub fn offset(original_pc: u16) -> Result<u16> {
let pc = original_pc;
let mut offset = 0;
{
offset += 1;
offset += if pc > 0xfe { 2 } else { 1 }
}
if pc + offset > BUFFER_LIMIT as u16 {
return Err(Error::InvalidPC((pc + offset) as usize));
}
Ok(offset)
}
fn pc(buffer: &mut Buffer, original_pc: u16, target_pc: u16, offset: u16) -> Result<()> {
let original_pc = original_pc as usize;
let mut new_buffer: Buffer = buffer[..original_pc].into();
let rest_buffer: Buffer = buffer[original_pc..].into();
if offset == 2 {
new_buffer.push(OpCode::PUSH1.into());
} else {
new_buffer.push(OpCode::PUSH2.into());
}
let pc_offset = target_pc.to_ls_bytes();
tracing::trace!("push bytes: 0x{:x?} at 0x{:x}", pc_offset, original_pc);
new_buffer.extend_from_slice(&pc_offset);
new_buffer.extend_from_slice(&rest_buffer);
if new_buffer.len() > BUFFER_LIMIT {
return Err(Error::BufferOverflow(new_buffer.len()));
}
*buffer = new_buffer;
Ok(())
}