use crate::elf::AllowedRange;
use crate::elf::RelocationKind;
use crate::elf::RelocationKindInfo;
use crate::elf::RelocationSize;
use crate::elf::Sign;
use crate::relaxation::RelocationModifier;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelaxationKind {
MovIndirectToLea,
MovIndirectToAbsolute,
RexMovIndirectToAbsolute(u8),
RexAddIndirectToAbsolute(u8),
RexSubIndirectToAbsolute(u8),
RexCmpIndirectToAbsolute(u8),
CallIndirectToRelative,
JmpIndirectToRelative,
NoOp,
TlsGdToLocalExec,
TlsGdToLocalExecLarge,
TlsLdToLocalExec,
TlsLdToLocalExecNoPlt,
TlsLdToLocalExec64,
TlsGdToInitialExec,
TlsDescToLocalExec(u8),
TlsDescToInitialExec,
SkipTlsDescCall,
}
impl RelaxationKind {
pub fn apply(self, section_bytes: &mut [u8], offset_in_section: &mut u64, addend: &mut i64) {
let offset = *offset_in_section as usize;
match self {
RelaxationKind::MovIndirectToLea => {
section_bytes[offset - 2] = 0x8d;
}
RelaxationKind::MovIndirectToAbsolute => {
section_bytes[offset - 2] = 0xc7;
let mod_rm = &mut section_bytes[offset - 1];
*mod_rm = (*mod_rm >> 3) & 0x7 | 0xc0;
*addend = 0;
}
RelaxationKind::RexMovIndirectToAbsolute(inst_offset) => {
let rex = section_bytes[offset - 3];
if inst_offset == 3 {
section_bytes[offset - 3] = (rex & !4) | ((rex & 4) >> 2);
} else if inst_offset == 4 {
section_bytes[offset - 3] = (rex & !0x44) | ((rex & 0x44) >> 2);
}
section_bytes[offset - 2] = 0xc7;
let mod_rm = &mut section_bytes[offset - 1];
*mod_rm = (*mod_rm >> 3) & 0x7 | 0xc0;
*addend = 0;
}
RelaxationKind::RexAddIndirectToAbsolute(inst_offset) => {
if inst_offset == 3 {
let rex = section_bytes[offset - 3];
section_bytes[offset - 3] = (rex & !4) | ((rex & 4) >> 2);
} else if inst_offset == 4 {
let rex = section_bytes[offset - 3];
section_bytes[offset - 3] = (rex & !0x44) | ((rex & 0x44) >> 2);
} else if inst_offset == 6 {
let l5 = &mut section_bytes[offset - 5];
if (*l5 & (1 << 7)) == 0 {
*l5 = (*l5 | (1 << 7)) & !(1 << 5);
}
if (*l5 & (1 << 4)) == 0 {
*l5 = *l5 | (1 << 4) | (1 << 3);
}
}
section_bytes[offset - 2] = 0x81;
let mod_rm = &mut section_bytes[offset - 1];
*mod_rm = (*mod_rm >> 3) & 0x7 | 0xc0;
*addend = 0;
}
RelaxationKind::RexSubIndirectToAbsolute(inst_offset) => {
let rex = section_bytes[offset - 3];
if inst_offset == 3 {
section_bytes[offset - 3] = (rex & !4) | ((rex & 4) >> 2);
} else if inst_offset == 4 {
section_bytes[offset - 3] = (rex & !0x44) | ((rex & 0x44) >> 2);
}
section_bytes[offset - 2] = 0x81;
let mod_rm = &mut section_bytes[offset - 1];
*mod_rm = (*mod_rm >> 3) & 0x7 | 0xe8;
*addend = 0;
}
RelaxationKind::RexCmpIndirectToAbsolute(inst_offset) => {
let rex = section_bytes[offset - 3];
if inst_offset == 3 {
section_bytes[offset - 3] = (rex & !4) | ((rex & 4) >> 2);
} else if inst_offset == 4 {
section_bytes[offset - 3] = (rex & !0x44) | ((rex & 0x44) >> 2);
}
section_bytes[offset - 2] = 0x81;
let mod_rm = &mut section_bytes[offset - 1];
*mod_rm = (*mod_rm >> 3) & 0x7 | 0xf8;
*addend = 0;
}
RelaxationKind::CallIndirectToRelative => {
section_bytes[offset - 2..offset].copy_from_slice(&[0x67, 0xe8]);
}
RelaxationKind::JmpIndirectToRelative => {
section_bytes[offset - 2..offset + 4].copy_from_slice(&[0xe9, 0, 0, 0, 0, 0x90]);
*offset_in_section -= 1; }
RelaxationKind::TlsGdToLocalExec => {
section_bytes[offset - 4..offset + 8].copy_from_slice(&[
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, 0x48, 0x8d, 0x80, ]);
*offset_in_section += 8;
*addend = 0;
}
RelaxationKind::TlsGdToLocalExecLarge => {
section_bytes[offset - 3..offset + 19].copy_from_slice(&[
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, 0x48, 0x8d, 0x80, 0, 0, 0, 0, 0x66, 0x0f, 0x1f, 0x44, 0, 0, ]);
*offset_in_section += 9;
*addend = 0;
}
RelaxationKind::TlsGdToInitialExec => {
section_bytes[offset - 4..offset + 8].copy_from_slice(&[
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, 0x48, 0x03, 0x05,
]);
*offset_in_section += 8;
}
RelaxationKind::TlsLdToLocalExec => {
section_bytes[offset - 3..offset + 9].copy_from_slice(&[
0x66, 0x66, 0x66, 0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0,
]);
*offset_in_section += 5;
}
RelaxationKind::TlsLdToLocalExecNoPlt => {
section_bytes[offset - 3..offset + 10].copy_from_slice(&[
0x66, 0x66, 0x66, 0x66, 0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0,
]);
*offset_in_section += 5;
}
RelaxationKind::TlsLdToLocalExec64 => {
section_bytes[offset - 3..offset + 19].copy_from_slice(&[
0x66, 0x66, 0x66, 0x66, 0x2e, 0x0f, 0x1f, 0x84, 0, 0, 0, 0, 0,
0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0,
]);
*offset_in_section += 15;
}
RelaxationKind::TlsDescToLocalExec(inst_offset) => {
let rex = section_bytes[offset - 3];
let modrm = section_bytes[offset - 1];
let rex_r = (rex >> 2) & 1;
let reg = (modrm >> 3) & 0x7;
let rex = if inst_offset == 3 {
if rex_r == 0 { 0x48 } else { 0x49 }
} else if inst_offset == 4 {
if rex_r == 0 { 0x18 } else { 0x19 }
} else {
return;
};
section_bytes[offset - 3..offset + 4].copy_from_slice(&[
rex,
0xc7,
0xc0 | reg,
0,
0,
0,
0,
]);
*addend = 0;
}
RelaxationKind::TlsDescToInitialExec => {
let rex = section_bytes[offset - 3];
let modrm = section_bytes[offset - 1];
let rex_r = (rex >> 2) & 1;
let reg = (modrm >> 3) & 0x7;
let rex = if rex_r == 0 { 0x48 } else { 0x4c };
section_bytes[offset - 3..offset + 4].copy_from_slice(&[
rex,
0x8b,
0x05 | reg << 3,
0,
0,
0,
0,
]);
}
RelaxationKind::SkipTlsDescCall => {
section_bytes[offset..offset + 2].copy_from_slice(&[
0x66, 0x90,
]);
}
RelaxationKind::NoOp => {}
}
}
#[must_use]
pub fn next_modifier(&self) -> RelocationModifier {
match self {
RelaxationKind::TlsGdToInitialExec
| RelaxationKind::TlsGdToLocalExec
| RelaxationKind::TlsGdToLocalExecLarge
| RelaxationKind::TlsLdToLocalExec
| RelaxationKind::TlsLdToLocalExecNoPlt
| RelaxationKind::TlsLdToLocalExec64 => RelocationModifier::SkipNextRelocation,
RelaxationKind::MovIndirectToLea
| RelaxationKind::MovIndirectToAbsolute
| RelaxationKind::RexMovIndirectToAbsolute(_)
| RelaxationKind::RexAddIndirectToAbsolute(_)
| RelaxationKind::RexSubIndirectToAbsolute(_)
| RelaxationKind::RexCmpIndirectToAbsolute(_)
| RelaxationKind::CallIndirectToRelative
| RelaxationKind::JmpIndirectToRelative
| RelaxationKind::TlsDescToLocalExec(_)
| RelaxationKind::TlsDescToInitialExec
| RelaxationKind::NoOp
| RelaxationKind::SkipTlsDescCall => RelocationModifier::Normal,
}
}
}
type RelocSizeAndRange = (RelocationSize, AllowedRange);
const RELOC_8_BYTE_UNSIGNED: RelocSizeAndRange = (
RelocationSize::ByteSize(8),
AllowedRange::from_byte_size(8, Sign::Unsigned),
);
const RELOC_8_BYTE_SIGNED: RelocSizeAndRange = (
RelocationSize::ByteSize(8),
AllowedRange::from_byte_size(8, Sign::Signed),
);
const RELOC_4_BYTE_UNSIGNED: RelocSizeAndRange = (
RelocationSize::ByteSize(4),
AllowedRange::from_byte_size(4, Sign::Unsigned),
);
const RELOC_4_BYTE_SIGNED: RelocSizeAndRange = (
RelocationSize::ByteSize(4),
AllowedRange::from_byte_size(4, Sign::Signed),
);
const RELOC_2_BYTE_SIGNED: RelocSizeAndRange = (
RelocationSize::ByteSize(2),
AllowedRange::from_byte_size(2, Sign::Signed),
);
const RELOC_1_BYTE_SIGNED: RelocSizeAndRange = (
RelocationSize::ByteSize(1),
AllowedRange::from_byte_size(1, Sign::Signed),
);
const RELOC_NONE: RelocSizeAndRange = (RelocationSize::ByteSize(0), AllowedRange::no_check());
#[must_use]
#[inline(always)]
pub const fn relocation_from_raw(r_type: u32) -> Option<RelocationKindInfo> {
let (kind, (size, range)) = match r_type {
object::elf::R_X86_64_64 => (RelocationKind::Absolute, RELOC_8_BYTE_UNSIGNED),
object::elf::R_X86_64_PC32 => (RelocationKind::Relative, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_PC64 => (RelocationKind::Relative, RELOC_8_BYTE_SIGNED),
object::elf::R_X86_64_GOT32 => (RelocationKind::GotRelGotBase, RELOC_4_BYTE_UNSIGNED),
object::elf::R_X86_64_GOT64 => (RelocationKind::GotRelGotBase, RELOC_8_BYTE_UNSIGNED),
object::elf::R_X86_64_GOTOFF64 => (RelocationKind::SymRelGotBase, RELOC_8_BYTE_SIGNED),
object::elf::R_X86_64_PLT32 => (RelocationKind::PltRelative, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_PLTOFF64 => (RelocationKind::PltRelGotBase, RELOC_8_BYTE_SIGNED),
object::elf::R_X86_64_GOTPCREL => (RelocationKind::GotRelative, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_GOTPC64 => (RelocationKind::Relative, RELOC_8_BYTE_SIGNED),
object::elf::R_X86_64_GOTPC32 => (RelocationKind::Relative, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_32 => (RelocationKind::Absolute, RELOC_4_BYTE_UNSIGNED),
object::elf::R_X86_64_32S => (RelocationKind::Absolute, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_16 => (RelocationKind::Absolute, RELOC_2_BYTE_SIGNED),
object::elf::R_X86_64_PC16 => (RelocationKind::Relative, RELOC_2_BYTE_SIGNED),
object::elf::R_X86_64_8 => (RelocationKind::Absolute, RELOC_1_BYTE_SIGNED),
object::elf::R_X86_64_PC8 => (RelocationKind::Relative, RELOC_1_BYTE_SIGNED),
object::elf::R_X86_64_TLSGD => (RelocationKind::TlsGd, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_TLSLD => (RelocationKind::TlsLd, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_DTPOFF32 => (RelocationKind::DtpOff, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_DTPOFF64 => (RelocationKind::DtpOff, RELOC_8_BYTE_SIGNED),
object::elf::R_X86_64_GOTTPOFF
| object::elf::R_X86_64_CODE_4_GOTTPOFF
| object::elf::R_X86_64_CODE_5_GOTTPOFF
| object::elf::R_X86_64_CODE_6_GOTTPOFF => (RelocationKind::GotTpOff, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_GOTPCRELX
| object::elf::R_X86_64_REX_GOTPCRELX
| object::elf::R_X86_64_CODE_4_GOTPCRELX
| object::elf::R_X86_64_CODE_5_GOTPCRELX
| object::elf::R_X86_64_CODE_6_GOTPCRELX => {
(RelocationKind::GotRelative, RELOC_4_BYTE_SIGNED)
}
object::elf::R_X86_64_TPOFF32 => (RelocationKind::TpOff, RELOC_4_BYTE_SIGNED),
object::elf::R_X86_64_GOTPC32_TLSDESC
| object::elf::R_X86_64_CODE_4_GOTPC32_TLSDESC
| object::elf::R_X86_64_CODE_5_GOTPC32_TLSDESC
| object::elf::R_X86_64_CODE_6_GOTPC32_TLSDESC => {
(RelocationKind::TlsDesc, RELOC_4_BYTE_SIGNED)
}
object::elf::R_X86_64_TLSDESC_CALL => (RelocationKind::TlsDescCall, RELOC_NONE),
object::elf::R_X86_64_NONE => (RelocationKind::None, RELOC_NONE),
_ => return None,
};
Some(RelocationKindInfo {
kind,
size,
mask: None,
range,
alignment: 1,
bias: 0,
thunkable: false,
})
}