pub mod aarch64;
pub mod aarch64_errata;
pub mod arm;
pub mod i386;
pub mod loongarch;
pub mod ppc64;
pub mod riscv;
pub mod s390x;
pub mod shrink;
pub mod thunk;
pub mod x86_64;
use crate::args::LinkOptions;
use crate::elf::read::Relocation;
use crate::elf::read::consts::{
EM_AARCH64, EM_LOONGARCH, EM_PPC64, EM_RISCV, EM_X86_64, SHF_EXECINSTR, reloc_name,
};
use crate::target::{Architecture, Target};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Arch {
#[default]
X86_64,
AArch64,
RiscV64,
LoongArch64,
Ppc64,
S390x,
I386,
X32,
RiscV32,
Arm,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Kind {
None,
Abs,
Pc,
Page,
Got,
GotPage,
GotAbs,
GotPageOff,
GotSlotRel,
GotRelax,
GotRel,
GotBasePc,
Size,
TpOff,
DtpOff,
RelaxGotPc,
RelaxGotPcNoPic,
RelaxGotOff,
GdToLe,
LdToLe,
IeToLe,
DescToLe,
DescCallToLe,
GdToIe,
DescToIe,
Add,
Sub,
PageOff,
Relax,
Addend,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum GotKind {
#[default]
Address,
TlsGd,
TpOff,
TlsDesc,
TlsLd,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Width {
None,
W64,
U32,
I32,
Any32,
Any16,
I16,
Any8,
I8,
Field(crate::arch::aarch64::Field),
RiscV(crate::arch::riscv::Field),
LoongArch(crate::arch::loongarch::Field),
Ppc(crate::arch::ppc64::Field),
S390(crate::arch::s390x::Field),
Arm(crate::arch::arm::Field),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Class {
pub kind: Kind,
pub width: Width,
pub slot: GotKind,
pub skip_next: bool,
}
impl Class {
#[must_use]
pub const fn new(kind: Kind, width: Width) -> Self {
Self {
kind,
width,
slot: GotKind::Address,
skip_next: false,
}
}
#[must_use]
pub const fn through(mut self, slot: GotKind) -> Self {
self.slot = slot;
self
}
#[must_use]
pub const fn skipping(mut self) -> Self {
self.skip_next = true;
self
}
#[must_use]
pub fn needs_got(self) -> bool {
matches!(
self.kind,
Kind::Got
| Kind::GotPage
| Kind::GotAbs
| Kind::GotPageOff
| Kind::GotSlotRel
| Kind::GotRelax
)
}
#[must_use]
pub fn uses_got_base(self) -> bool {
matches!(
self.kind,
Kind::GotSlotRel | Kind::GotRel | Kind::GotBasePc | Kind::GotPageOff | Kind::GotRelax
)
}
#[must_use]
pub fn needs_gottpoff(self) -> bool {
matches!(self.kind, Kind::GdToIe | Kind::DescToIe)
|| (self.needs_got() && self.slot == GotKind::TpOff)
}
#[must_use]
pub fn is_tls(self) -> bool {
matches!(
self.kind,
Kind::TpOff
| Kind::DtpOff
| Kind::GdToLe
| Kind::LdToLe
| Kind::IeToLe
| Kind::DescToLe
| Kind::DescCallToLe
| Kind::GdToIe
| Kind::DescToIe
) || (self.needs_got() && self.slot != GotKind::Address)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TlsMode {
LocalExec,
InitialExec,
Dynamic,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ClassifyContext {
pub relax_got: bool,
pub pic: bool,
pub tls: TlsMode,
pub tls_ld: TlsMode,
pub code: bool,
}
impl ClassifyContext {
#[must_use]
pub const fn static_exec(relax_got: bool) -> Self {
Self {
relax_got,
pic: false,
tls: TlsMode::LocalExec,
tls_ld: TlsMode::LocalExec,
code: false,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ClassifyError {
Unsupported,
BadTlsInstruction,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ApplyError {
Overflow,
OutOfBounds,
BadInstruction,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DynKind {
Relative,
Irelative,
JumpSlot,
GlobDat,
Copy,
Abs64,
DtpMod,
DtpOff,
TpOff,
TlsDesc,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct RelaxValues {
pub tpoff: i64,
pub got: u64,
pub got_pc: i64,
pub place: u64,
pub got_base: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Branch {
pub r_type: u32,
pub place: u64,
pub target: u64,
pub st_other: u8,
pub via_stub: bool,
pub slot: Option<u64>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct PltFlags {
pub landing_pad: bool,
pub entry_landing_pad: bool,
pub authenticate: bool,
pub pic: bool,
}
impl Arch {
#[must_use]
pub fn from_machine(e_machine: u16) -> Option<Self> {
match e_machine {
EM_X86_64 => Some(Self::X86_64),
EM_AARCH64 => Some(Self::AArch64),
EM_RISCV => Some(Self::RiscV64),
EM_LOONGARCH => Some(Self::LoongArch64),
EM_PPC64 => Some(Self::Ppc64),
crate::elf::read::consts::EM_386 => Some(Self::I386),
crate::elf::read::consts::EM_S390 => Some(Self::S390x),
crate::elf::read::consts::EM_ARM => Some(Self::Arm),
_ => None,
}
}
#[must_use]
pub fn from_target(target: Target) -> Option<Self> {
match target.arch {
Architecture::X86_64 => Some(Self::X86_64),
Architecture::Aarch64 => Some(Self::AArch64),
Architecture::Riscv64 if target.endian == crate::target::Endianness::Little => {
Some(Self::RiscV64)
}
Architecture::Riscv32 if target.endian == crate::target::Endianness::Little => {
Some(Self::RiscV32)
}
Architecture::LoongArch64 => Some(Self::LoongArch64),
Architecture::X86 => Some(Self::I386),
Architecture::X86_64X32 => Some(Self::X32),
Architecture::Arm if target.endian == crate::target::Endianness::Little => {
Some(Self::Arm)
}
Architecture::PowerPc64 if target.endian == crate::target::Endianness::Little => {
Some(Self::Ppc64)
}
Architecture::S390x => Some(Self::S390x),
_ => None,
}
}
#[must_use]
pub fn of<F: crate::elf::read::ElfFormat>(
options: &LinkOptions,
files: &[super::inputs::ElfInput<'_, F>],
) -> Self {
options
.target
.and_then(Self::from_target)
.or_else(|| Self::of_files(files))
.or_else(|| Self::from_target(crate::elf::target::default_target()))
.unwrap_or_default()
}
#[must_use]
pub fn of_files<F: crate::elf::read::ElfFormat>(
files: &[super::inputs::ElfInput<'_, F>],
) -> Option<Self> {
files.iter().find_map(|file| {
let object = file.object.as_ref()?;
Self::from_machine(object.elf.elf().header().e_machine)
.map(|arch| arch.for_word_size(F::WORD_SIZE))
})
}
#[must_use]
pub fn for_word_size(self, word_size: usize) -> Self {
match self {
Self::RiscV64 if word_size == 4 => Self::RiscV32,
Self::X86_64 if word_size == 4 => Self::X32,
_ => self,
}
}
#[must_use]
#[inline]
pub fn is_riscv(self) -> bool {
matches!(self, Self::RiscV64 | Self::RiscV32)
}
#[must_use]
pub fn incompatible<F: crate::elf::read::ElfFormat>(
self,
files: &[super::inputs::ElfInput<'_, F>],
file: usize,
) -> Option<String> {
if !self.is_riscv() {
return None;
}
let flags = |f: &super::inputs::ElfInput<'_, F>| {
let header = f.object.as_ref()?.elf.elf().header();
(header.e_machine == crate::elf::read::consts::EM_RISCV).then_some(header.e_flags)
};
let (first_index, first) = files
.iter()
.enumerate()
.find_map(|(i, f)| Some((i, flags(f)?)))?;
let this = files.get(file)?;
let what = riscv::incompatible_flags(first, flags(this)?)?;
let first_name = files
.get(first_index)
.map(|f| f.display())
.unwrap_or_default();
Some(format!(
"{}: cannot link object files with different {what} from {first_name}",
this.display()
))
}
#[must_use]
pub fn kind(self) -> crate::elf::read::ElfKind {
match self {
Self::I386 | Self::X32 | Self::RiscV32 | Self::Arm => {
crate::elf::read::ElfKind::Elf32Le
}
Self::S390x => crate::elf::read::ElfKind::Elf64Be,
Self::X86_64 | Self::AArch64 | Self::RiscV64 | Self::LoongArch64 | Self::Ppc64 => {
crate::elf::read::ElfKind::Elf64Le
}
}
}
#[must_use]
#[inline]
pub fn implicit_addend(self, r_type: u32, data: &[u8], offset: u64) -> i64 {
match self {
Self::I386 => i386::implicit_addend(r_type, data, offset),
Self::Arm => arm::implicit_addend(r_type, data, offset),
_ => 0,
}
}
#[must_use]
pub fn uses_rel(self) -> bool {
matches!(self, Self::I386 | Self::Arm)
}
#[must_use]
#[inline]
pub fn is_word(self, width: Width) -> bool {
match self {
Self::I386 | Self::Arm => matches!(width, Width::Any32 | Width::U32 | Width::I32),
Self::X32 => width == Width::U32,
Self::RiscV32 => width == Width::RiscV(crate::arch::riscv::Field::Word32),
_ => width == Width::W64,
}
}
#[must_use]
#[inline]
pub fn got_entry_size(self) -> u64 {
if self == Self::X32 {
8
} else {
self.kind().word_size()
}
}
#[must_use]
pub fn dyn_reloc_size(self) -> u64 {
if self.uses_rel() {
self.kind().rel_size()
} else {
self.kind().rela_size()
}
}
#[must_use]
pub fn machine(self) -> u16 {
match self {
Self::I386 => crate::elf::read::consts::EM_386,
Self::Arm => crate::elf::read::consts::EM_ARM,
Self::X86_64 | Self::X32 => EM_X86_64,
Self::AArch64 => EM_AARCH64,
Self::RiscV64 | Self::RiscV32 => EM_RISCV,
Self::LoongArch64 => EM_LOONGARCH,
Self::Ppc64 => EM_PPC64,
Self::S390x => crate::elf::read::consts::EM_S390,
}
}
#[must_use]
pub fn emulation(self) -> &'static str {
match self {
Self::I386 => "elf_i386",
Self::Arm => "armelf_linux_eabi",
Self::X86_64 => "elf_x86_64",
Self::X32 => "elf32_x86_64",
Self::AArch64 => "aarch64linux",
Self::RiscV64 => "elf64lriscv",
Self::RiscV32 => "elf32lriscv",
Self::LoongArch64 => "elf64loongarch",
Self::Ppc64 => "elf64lppc",
Self::S390x => "elf64_s390",
}
}
#[must_use]
pub fn reloc_name(self, r_type: u32) -> Option<&'static str> {
match self {
Self::LoongArch64 => loongarch::reloc_name(r_type),
Self::S390x => s390x::reloc_name(r_type),
Self::Arm => arm::reloc_name(r_type),
_ => reloc_name(self.machine(), r_type),
}
}
#[must_use]
pub fn reloc_label(self, r_type: u32) -> String {
let r_type = match self {
Self::LoongArch64 => loongarch::base_type(r_type),
Self::Ppc64 => ppc64::base_type(r_type),
_ => r_type,
};
self.reloc_name(r_type)
.map_or_else(|| r_type.to_string(), str::to_owned)
}
#[must_use]
pub fn default_max_page(self) -> u64 {
match self {
Self::I386
| Self::S390x
| Self::X86_64
| Self::X32
| Self::RiscV64
| Self::RiscV32
| Self::Arm => 0x1000,
Self::AArch64 | Self::Ppc64 => 0x1_0000,
Self::LoongArch64 => 0x4000,
}
}
#[must_use]
pub fn default_base(self) -> u64 {
match self {
Self::I386 => 0x0804_8000,
Self::X86_64 | Self::X32 | Self::AArch64 => super::layout::DEFAULT_BASE,
Self::RiscV64 | Self::RiscV32 | Self::Arm => 0x1_0000,
Self::LoongArch64 => 0x1_2000_0000,
Self::Ppc64 => 0x1000_0000,
Self::S390x => 0x100_0000,
}
}
#[must_use]
pub fn got_plt_reserved(self) -> u64 {
match self {
Self::I386 | Self::S390x | Self::Arm => 3,
Self::X86_64 | Self::X32 | Self::AArch64 => 3,
Self::RiscV64 | Self::RiscV32 | Self::LoongArch64 | Self::Ppc64 => 2,
}
}
#[must_use]
pub fn output_flags<F: crate::elf::read::ElfFormat>(
self,
files: &[super::inputs::ElfInput<'_, F>],
) -> u32 {
if self == Self::Ppc64 {
return ppc64::ABI_VERSION;
}
if self == Self::Arm {
return arm::output_flags(arm::attributes::hard_float(files));
}
if self.is_riscv() {
return riscv::output_flags(
files
.iter()
.filter_map(|f| f.object.as_ref())
.map(|o| o.elf.elf().header().e_flags),
);
}
if self != Self::LoongArch64 {
return 0;
}
loongarch::output_flags(files.iter().filter_map(|file| {
let object = file.object.as_ref()?;
let header = object.elf.elf().header();
if header.e_machine != EM_LOONGARCH {
return None;
}
let code = object
.sections
.iter()
.any(|section| section.header.sh_flags & SHF_EXECINSTR != 0);
Some((header.e_flags, code))
}))
}
#[must_use]
pub fn annotates(self) -> bool {
matches!(self, Self::LoongArch64 | Self::Ppc64)
}
#[must_use]
pub fn annotate(self, rel: Relocation, next: Option<&Relocation>) -> Relocation {
if self == Self::Ppc64 {
return ppc64::annotate(rel, next);
}
if self == Self::LoongArch64
&& let Some(next) = next
&& next.r_type == loongarch::R_LARCH_RELAX
&& next.offset == rel.offset
{
return Relocation {
r_type: rel.r_type | loongarch::RELAX_HINT,
..rel
};
}
rel
}
#[must_use]
pub fn page_delta(self, target: u64, place: u64, r_type: u32) -> u64 {
match self {
Self::I386 | Self::S390x | Self::Arm => {
crate::arch::aarch64::page(target).wrapping_sub(crate::arch::aarch64::page(place))
}
Self::LoongArch64 => loongarch::page_delta(target, place, r_type),
Self::X86_64
| Self::X32
| Self::AArch64
| Self::RiscV64
| Self::RiscV32
| Self::Ppc64 => {
let page = crate::arch::aarch64::page;
page(target).wrapping_sub(page(place))
}
}
}
pub fn relax(
self,
out: &mut [u8],
offset: u64,
r_type: u32,
target: u64,
place: u64,
) -> Result<(), ApplyError> {
match self {
Self::I386 | Self::S390x | Self::Arm => Err(ApplyError::BadInstruction),
Self::LoongArch64 => loongarch::relax(out, offset, r_type, target, place),
Self::X86_64
| Self::X32
| Self::AArch64
| Self::RiscV64
| Self::RiscV32
| Self::Ppc64 => Err(ApplyError::BadInstruction),
}
}
#[must_use]
pub fn relaxes(self) -> bool {
shrink::applies(self)
}
#[must_use]
pub fn wide_sysv_hash(self) -> bool {
self == Self::S390x
}
#[must_use]
pub fn got_plt_holds_dynamic(self) -> bool {
self.got_plt_reserved() == 3
}
pub fn check_options(self, options: &LinkOptions) -> crate::error::Result<()> {
if self == Self::Arm && options.emit_relocs {
return Err(crate::error::Error::Unimplemented(
"--emit-relocs for 32-bit Arm (SHT_REL relocations)".into(),
));
}
Ok(())
}
pub fn check_script_options(self, options: &LinkOptions) -> crate::error::Result<()> {
if self == Self::AArch64 && aarch64_errata::enabled(options) {
return Err(crate::error::Error::Unimplemented(
"--fix-cortex-a53-843419 and --fix-cortex-a53-835769 with a linker script".into(),
));
}
Ok(())
}
#[must_use]
pub fn needs_thunks(self) -> bool {
matches!(self, Self::AArch64 | Self::Ppc64 | Self::Arm)
}
#[must_use]
pub fn thunk_size(self) -> u64 {
match self {
Self::Ppc64 => crate::arch::ppc64::THUNK_SIZE,
Self::Arm => crate::arch::arm::THUNK_SIZE,
_ => crate::arch::aarch64::THUNK_SIZE,
}
}
pub fn write_thunk(
self,
out: &mut [u8],
at: u64,
address: u64,
key: u64,
) -> Result<(), ApplyError> {
match self {
Self::Ppc64 => crate::arch::ppc64::write_thunk(out, at, address, key)
.map_err(|_| ApplyError::Overflow),
Self::Arm => arm::write_thunk(out, at, address, key),
_ => crate::arch::aarch64::write_thunk(out, at, address, key)
.map_err(|_| ApplyError::Overflow),
}
}
#[must_use]
pub fn is_thunk_branch(self, r_type: u32) -> bool {
use crate::elf::read::consts::aarch64 as a64;
match self {
Self::I386 | Self::S390x => false,
Self::Arm => arm::is_thunk_branch(r_type),
Self::X86_64 | Self::X32 | Self::RiscV64 | Self::RiscV32 | Self::LoongArch64 => false,
Self::AArch64 => matches!(r_type, a64::R_AARCH64_CALL26 | a64::R_AARCH64_JUMP26),
Self::Ppc64 => ppc64::is_thunk_branch(r_type),
}
}
#[must_use]
pub fn branch_destination(self, branch: Branch) -> u64 {
match self {
Self::Ppc64 => ppc64::branch_destination(branch),
_ => branch.target,
}
}
#[must_use]
pub fn branch_thunk(self, branch: Branch) -> Option<u64> {
match self {
Self::I386 | Self::S390x | Self::Arm => None,
Self::X86_64 | Self::X32 | Self::RiscV64 | Self::RiscV32 | Self::LoongArch64 => None,
Self::AArch64 => (self.is_thunk_branch(branch.r_type)
&& !crate::arch::aarch64::branch_in_range(branch.place, branch.target))
.then_some(branch.target),
Self::Ppc64 => ppc64::branch_thunk(branch),
}
}
pub fn finish_call(
self,
out: &mut [u8],
offset: u64,
branch: Branch,
) -> Result<(), ApplyError> {
match self {
Self::Ppc64 => ppc64::finish_call(out, offset, branch),
_ => Ok(()),
}
}
#[must_use]
pub fn separate_code_by_default(self) -> bool {
matches!(self, Self::X86_64 | Self::X32 | Self::I386)
}
#[must_use]
pub fn default_interpreter(self) -> &'static str {
match self {
Self::I386 => "/lib/ld-linux.so.2",
Self::Arm => arm::INTERPRETER,
Self::X86_64 => "/lib64/ld-linux-x86-64.so.2",
Self::X32 => "/libx32/ld-linux-x32.so.2",
Self::AArch64 => "/lib/ld-linux-aarch64.so.1",
Self::RiscV64 | Self::RiscV32 => {
riscv::interpreter(riscv::EF_RISCV_FLOAT_ABI, self.kind().word_size())
}
Self::LoongArch64 => "/lib64/ld-linux-loongarch-lp64d.so.1",
Self::Ppc64 => "/lib64/ld64.so.2",
Self::S390x => "/lib/ld64.so.1",
}
}
#[must_use]
pub fn tls_variant1(self) -> bool {
matches!(
self,
Self::AArch64
| Self::RiscV64
| Self::RiscV32
| Self::LoongArch64
| Self::Ppc64
| Self::Arm
)
}
#[must_use]
pub fn tcb_size(self) -> u64 {
match self {
Self::I386 | Self::S390x => 0,
Self::X86_64
| Self::X32
| Self::RiscV64
| Self::RiscV32
| Self::LoongArch64
| Self::Ppc64 => 0,
Self::AArch64 => 16,
Self::Arm => 8,
}
}
#[must_use]
pub fn tp_past_tls_start(self) -> Option<u64> {
match self {
Self::Ppc64 => Some(crate::arch::ppc64::TP_OFFSET),
_ => None,
}
}
#[must_use]
pub fn dtv_offset(self) -> u64 {
match self {
Self::Ppc64 => crate::arch::ppc64::DTV_OFFSET,
Self::RiscV64 | Self::RiscV32 => crate::arch::riscv::DTP_OFFSET,
_ => 0,
}
}
#[must_use]
pub fn toc_bias(self) -> Option<u64> {
match self {
Self::Ppc64 => Some(crate::arch::ppc64::TOC_BIAS),
_ => None,
}
}
#[must_use]
pub fn got_header_words(self) -> u64 {
match self {
Self::Ppc64 => 1,
_ => 0,
}
}
#[must_use]
pub fn glink_offset(self) -> Option<u64> {
match self {
Self::Ppc64 => Some(crate::arch::ppc64::GLINK_HEADER_SIZE.wrapping_sub(32)),
_ => None,
}
}
#[must_use]
pub fn has_plt_sec(self, ibt: bool) -> bool {
match self {
Self::I386 | Self::X86_64 | Self::X32 => ibt,
Self::AArch64
| Self::RiscV64
| Self::RiscV32
| Self::LoongArch64
| Self::S390x
| Self::Arm => false,
Self::Ppc64 => true,
}
}
#[must_use]
pub fn plt_sec_entry_size(self, flags: PltFlags) -> u64 {
match self {
Self::Ppc64 => crate::arch::ppc64::PLT_CALL_STUB_SIZE,
_ => self.plt_entry_size(flags),
}
}
#[must_use]
pub fn uses_plt_got(self) -> bool {
!matches!(self, Self::Ppc64 | Self::AArch64 | Self::S390x | Self::Arm)
}
#[must_use]
pub fn irelative_in_rela_plt(self) -> bool {
self != Self::Ppc64
}
#[must_use]
pub fn dyn_reloc(self, kind: DynKind) -> u32 {
use crate::elf::read::consts::{aarch64 as a64, ppc64 as p64, riscv as rv, x86_64 as x64};
match self {
Self::I386 => {
use crate::elf::read::consts::i386 as x86;
match kind {
DynKind::Relative => x86::R_386_RELATIVE,
DynKind::Irelative => x86::R_386_IRELATIVE,
DynKind::JumpSlot => x86::R_386_JUMP_SLOT,
DynKind::GlobDat => x86::R_386_GLOB_DAT,
DynKind::Copy => x86::R_386_COPY,
DynKind::Abs64 => x86::R_386_32,
DynKind::DtpMod => x86::R_386_TLS_DTPMOD32,
DynKind::DtpOff => x86::R_386_TLS_DTPOFF32,
DynKind::TpOff => x86::R_386_TLS_TPOFF,
DynKind::TlsDesc => x86::R_386_TLS_DESC,
}
}
Self::Arm => match kind {
DynKind::Relative => arm::R_ARM_RELATIVE,
DynKind::Irelative => arm::R_ARM_IRELATIVE,
DynKind::JumpSlot => arm::R_ARM_JUMP_SLOT,
DynKind::GlobDat => arm::R_ARM_GLOB_DAT,
DynKind::Copy => arm::R_ARM_COPY,
DynKind::Abs64 => arm::R_ARM_ABS32,
DynKind::DtpMod => arm::R_ARM_TLS_DTPMOD32,
DynKind::DtpOff => arm::R_ARM_TLS_DTPOFF32,
DynKind::TpOff => arm::R_ARM_TLS_TPOFF32,
DynKind::TlsDesc => arm::R_ARM_TLS_DESC,
},
Self::X86_64 | Self::X32 => match kind {
DynKind::Relative => x64::R_X86_64_RELATIVE,
DynKind::Irelative => x64::R_X86_64_IRELATIVE,
DynKind::JumpSlot => x64::R_X86_64_JUMP_SLOT,
DynKind::GlobDat => x64::R_X86_64_GLOB_DAT,
DynKind::Copy => x64::R_X86_64_COPY,
DynKind::Abs64 if self == Self::X32 => x64::R_X86_64_32,
DynKind::Abs64 => x64::R_X86_64_64,
DynKind::DtpMod => x64::R_X86_64_DTPMOD64,
DynKind::DtpOff => x64::R_X86_64_DTPOFF64,
DynKind::TpOff => x64::R_X86_64_TPOFF64,
DynKind::TlsDesc => x64::R_X86_64_TLSDESC,
},
Self::AArch64 => match kind {
DynKind::Relative => a64::R_AARCH64_RELATIVE,
DynKind::Irelative => a64::R_AARCH64_IRELATIVE,
DynKind::JumpSlot => a64::R_AARCH64_JUMP_SLOT,
DynKind::GlobDat => a64::R_AARCH64_GLOB_DAT,
DynKind::Copy => a64::R_AARCH64_COPY,
DynKind::Abs64 => a64::R_AARCH64_ABS64,
DynKind::DtpMod => a64::R_AARCH64_TLS_DTPMOD64,
DynKind::DtpOff => a64::R_AARCH64_TLS_DTPREL64,
DynKind::TpOff => a64::R_AARCH64_TLS_TPREL64,
DynKind::TlsDesc => a64::R_AARCH64_TLSDESC,
},
Self::RiscV64 => match kind {
DynKind::Relative => rv::R_RISCV_RELATIVE,
DynKind::Irelative => rv::R_RISCV_IRELATIVE,
DynKind::JumpSlot => rv::R_RISCV_JUMP_SLOT,
DynKind::GlobDat | DynKind::Abs64 => rv::R_RISCV_64,
DynKind::Copy => rv::R_RISCV_COPY,
DynKind::DtpMod => rv::R_RISCV_TLS_DTPMOD64,
DynKind::DtpOff => rv::R_RISCV_TLS_DTPREL64,
DynKind::TpOff => rv::R_RISCV_TLS_TPREL64,
DynKind::TlsDesc => rv::R_RISCV_TLSDESC,
},
Self::RiscV32 => match kind {
DynKind::Relative => rv::R_RISCV_RELATIVE,
DynKind::Irelative => rv::R_RISCV_IRELATIVE,
DynKind::JumpSlot => rv::R_RISCV_JUMP_SLOT,
DynKind::GlobDat | DynKind::Abs64 => rv::R_RISCV_32,
DynKind::Copy => rv::R_RISCV_COPY,
DynKind::DtpMod => rv::R_RISCV_TLS_DTPMOD32,
DynKind::DtpOff => rv::R_RISCV_TLS_DTPREL32,
DynKind::TpOff => rv::R_RISCV_TLS_TPREL32,
DynKind::TlsDesc => rv::R_RISCV_TLSDESC,
},
Self::LoongArch64 => match kind {
DynKind::Relative => loongarch::R_LARCH_RELATIVE,
DynKind::Irelative => loongarch::R_LARCH_IRELATIVE,
DynKind::JumpSlot => loongarch::R_LARCH_JUMP_SLOT,
DynKind::GlobDat | DynKind::Abs64 => loongarch::R_LARCH_64,
DynKind::Copy => loongarch::R_LARCH_COPY,
DynKind::DtpMod => loongarch::R_LARCH_TLS_DTPMOD64,
DynKind::DtpOff => loongarch::R_LARCH_TLS_DTPREL64,
DynKind::TpOff => loongarch::R_LARCH_TLS_TPREL64,
DynKind::TlsDesc => loongarch::R_LARCH_TLS_DESC64,
},
Self::Ppc64 => match kind {
DynKind::Relative => p64::R_PPC64_RELATIVE,
DynKind::Irelative => p64::R_PPC64_IRELATIVE,
DynKind::JumpSlot => p64::R_PPC64_JMP_SLOT,
DynKind::GlobDat => p64::R_PPC64_GLOB_DAT,
DynKind::Copy => p64::R_PPC64_COPY,
DynKind::Abs64 => p64::R_PPC64_ADDR64,
DynKind::DtpMod => p64::R_PPC64_DTPMOD64,
DynKind::DtpOff => p64::R_PPC64_DTPREL64,
DynKind::TpOff => p64::R_PPC64_TPREL64,
DynKind::TlsDesc => p64::R_PPC64_NONE,
},
Self::S390x => match kind {
DynKind::Relative => s390x::R_390_RELATIVE,
DynKind::Irelative => s390x::R_390_IRELATIVE,
DynKind::JumpSlot => s390x::R_390_JMP_SLOT,
DynKind::GlobDat => s390x::R_390_GLOB_DAT,
DynKind::Copy => s390x::R_390_COPY,
DynKind::Abs64 => s390x::R_390_64,
DynKind::DtpMod => s390x::R_390_TLS_DTPMOD,
DynKind::DtpOff => s390x::R_390_TLS_DTPOFF,
DynKind::TpOff => s390x::R_390_TLS_TPOFF,
DynKind::TlsDesc => s390x::R_390_NONE,
},
}
}
#[must_use]
#[inline(always)]
pub fn is_branch(self, r_type: u32) -> bool {
use crate::elf::read::consts::{aarch64 as a64, ppc64 as p64, x86_64 as x64};
if self == Self::X86_64 {
return matches!(r_type, x64::R_X86_64_PLT32 | x64::R_X86_64_PLT32_BND);
}
match self {
Self::I386 => r_type == crate::elf::read::consts::i386::R_386_PLT32,
Self::Arm => arm::is_branch(r_type),
Self::X86_64 => false, Self::X32 => matches!(r_type, x64::R_X86_64_PLT32 | x64::R_X86_64_PLT32_BND),
Self::Ppc64 => matches!(r_type, p64::R_PPC64_REL24 | p64::R_PPC64_REL24_NOTOC),
Self::AArch64 => matches!(
r_type,
a64::R_AARCH64_CALL26 | a64::R_AARCH64_JUMP26 | a64::R_AARCH64_PLT32
),
Self::RiscV64 | Self::RiscV32 => riscv::is_branch(r_type),
Self::LoongArch64 => loongarch::is_branch(r_type),
Self::S390x => s390x::is_branch(r_type),
}
}
#[inline(always)]
pub fn classify(
self,
r_type: u32,
addend: i64,
data: &[u8],
offset: u64,
context: ClassifyContext,
) -> Result<Class, ClassifyError> {
match self {
Self::I386 => i386::classify(r_type, addend, data, offset, context),
Self::Arm => arm::classify(r_type, context),
Self::X86_64 => x86_64::classify(r_type, addend, data, offset, context),
Self::X32 => x86_64::classify_x32(r_type, addend, data, offset, context),
Self::AArch64 => aarch64::classify(r_type, context),
Self::RiscV64 | Self::RiscV32 => riscv::classify(r_type, context),
Self::LoongArch64 => loongarch::classify(r_type, addend, data, offset, context),
Self::Ppc64 => ppc64::classify(r_type, data, offset, context),
Self::S390x => s390x::classify(r_type, data, offset, context),
}
}
pub fn relax_got(
self,
out: &mut [u8],
offset: u64,
kind: Kind,
value: i64,
) -> Result<(), ApplyError> {
match self {
Self::I386 => i386::relax_got(out, offset, kind, value),
Self::X86_64 => x86_64::relax_got(out, offset, kind, value),
Self::X32 => x86_64::relax_got_x32(out, offset, kind, value),
Self::AArch64
| Self::RiscV64
| Self::RiscV32
| Self::LoongArch64
| Self::Ppc64
| Self::S390x
| Self::Arm => Err(ApplyError::BadInstruction),
}
}
#[inline(never)]
pub fn relax_got_load(
self,
out: &mut [u8],
offset: u64,
r_type: u32,
symbol: u64,
values: RelaxValues,
) -> Result<(), ApplyError> {
match self {
Self::S390x => s390x::relax_got(out, offset, r_type, symbol, values),
_ => Err(ApplyError::BadInstruction),
}
}
pub fn relax_tls(
self,
out: &mut [u8],
offset: u64,
kind: Kind,
r_type: u32,
values: RelaxValues,
) -> Result<(), ApplyError> {
match self {
Self::I386 => i386::relax_tls(out, offset, kind, r_type, values),
Self::X86_64 => x86_64::relax_tls(out, offset, kind, values),
Self::X32 => x86_64::relax_tls_x32(out, offset, kind, values),
Self::AArch64 => aarch64::relax_tls(out, offset, kind, r_type, values),
Self::RiscV64 | Self::RiscV32 | Self::Arm => Err(ApplyError::BadInstruction),
Self::LoongArch64 => loongarch::relax_tls(out, offset, kind, r_type, values),
Self::Ppc64 => ppc64::relax_tls(out, offset, kind, r_type, values),
Self::S390x => s390x::relax_tls(out, offset, kind, r_type, values),
}
}
#[must_use]
pub fn plt_header_size(self, flags: PltFlags) -> u64 {
match self {
Self::I386 => 16,
Self::Arm => arm::PLT_HEADER_SIZE,
Self::X86_64 | Self::X32 => 16,
Self::AArch64 => {
let _ = flags;
32
}
Self::RiscV64 | Self::RiscV32 => riscv::PLT_HEADER_SIZE,
Self::LoongArch64 => loongarch::PLT_HEADER_SIZE,
Self::Ppc64 => crate::arch::ppc64::GLINK_HEADER_SIZE,
Self::S390x => s390x::PLT_HEADER_SIZE,
}
}
#[must_use]
pub fn plt_entry_size(self, flags: PltFlags) -> u64 {
match self {
Self::I386 => i386::PLT_ENTRY_SIZE,
Self::Arm => arm::PLT_ENTRY_SIZE,
Self::X86_64 | Self::X32 => 16,
Self::AArch64 => aarch64::plt_entry_size(flags),
Self::RiscV64 | Self::RiscV32 => riscv::PLT_ENTRY_SIZE,
Self::LoongArch64 => loongarch::PLT_ENTRY_SIZE,
Self::Ppc64 => 4,
Self::S390x => s390x::PLT_ENTRY_SIZE,
}
}
#[must_use]
pub fn plt_got_entry_size(self, flags: PltFlags) -> u64 {
match self {
Self::I386 => {
if flags.landing_pad {
i386::PLT_ENTRY_SIZE
} else {
i386::PLT_GOT_ENTRY_SIZE
}
}
Self::Arm => arm::PLT_ENTRY_SIZE,
Self::X86_64 | Self::X32 if flags.landing_pad => 16,
Self::X86_64 | Self::X32 => 8,
Self::AArch64 => aarch64::plt_entry_size(aarch64::plt_got_flags(flags)),
Self::RiscV64 | Self::RiscV32 => riscv::PLT_ENTRY_SIZE,
Self::LoongArch64 => loongarch::PLT_ENTRY_SIZE,
Self::Ppc64 => crate::arch::ppc64::PLT_CALL_STUB_SIZE,
Self::S390x => s390x::PLT_ENTRY_SIZE,
}
}
#[must_use]
pub fn plt_align(self) -> u64 {
if matches!(self, Self::S390x | Self::Arm) {
4
} else {
16
}
}
#[must_use]
pub fn iplt_align(self) -> u64 {
match self {
Self::I386 => 8,
Self::S390x | Self::Arm => 4,
_ => 16,
}
}
#[must_use]
pub fn iplt_entry_size(self, flags: PltFlags) -> u64 {
match self {
Self::I386 => i386::IPLT_ENTRY_SIZE,
Self::Arm => arm::PLT_ENTRY_SIZE,
Self::X86_64 | Self::X32 => 16,
Self::AArch64 => aarch64::plt_entry_size(flags),
Self::RiscV64 | Self::RiscV32 => riscv::PLT_ENTRY_SIZE,
Self::LoongArch64 => loongarch::PLT_ENTRY_SIZE,
Self::Ppc64 => crate::arch::ppc64::PLT_CALL_STUB_SIZE,
Self::S390x => s390x::PLT_ENTRY_SIZE,
}
}
#[must_use]
pub fn lazy_slot_value(self, plt: u64, entry: u64, flags: PltFlags) -> u64 {
match self {
Self::I386 => {
if flags.landing_pad {
entry
} else {
entry.wrapping_add(6)
}
}
Self::X86_64 | Self::X32 if flags.landing_pad => entry,
Self::X86_64 | Self::X32 => entry.wrapping_add(6),
Self::AArch64 | Self::RiscV64 | Self::RiscV32 | Self::LoongArch64 | Self::Arm => plt,
Self::Ppc64 => 0,
Self::S390x => entry.wrapping_add(crate::arch::s390x::PLT_LAZY_OFFSET),
}
}
pub fn write_plt_header(
self,
out: &mut [u8],
plt: u64,
got_plt: u64,
flags: PltFlags,
) -> Result<(), ApplyError> {
match self {
Self::I386 => i386::write_plt_header(out, got_plt, flags.pic),
Self::Arm => arm::write_plt_header(out, plt, got_plt),
Self::X86_64 | Self::X32 => x86_64::write_plt_header(out, plt, got_plt),
Self::AArch64 => aarch64::write_plt_header(out, plt, got_plt, flags),
Self::RiscV64 | Self::RiscV32 => {
riscv::write_plt_header(out, plt, got_plt, self.kind().word_size())
}
Self::LoongArch64 => loongarch::write_plt_header(out, plt, got_plt),
Self::Ppc64 => ppc64::write_plt_header(out, plt, got_plt),
Self::S390x => s390x::write_plt_header(out, plt, got_plt),
}
}
pub fn write_plt_entry(
self,
out: &mut [u8],
entry: u64,
slot: u64,
index: u32,
plt: u64,
flags: PltFlags,
) -> Result<(), ApplyError> {
match self {
Self::I386 => {
let got = slot.wrapping_sub(u64::from(index).wrapping_add(3).wrapping_mul(4));
i386::write_plt_entry(
out,
entry,
slot,
index,
plt,
got,
flags.pic,
flags.landing_pad,
)
}
Self::Arm => arm::write_plt_entry(out, entry, slot),
Self::X86_64 | Self::X32 => {
x86_64::write_plt_entry(out, entry, slot, index, plt, flags.landing_pad)
}
Self::AArch64 => aarch64::write_plt_entry(out, entry, slot, flags),
Self::RiscV64 | Self::RiscV32 => {
riscv::write_plt_entry(out, entry, slot, self.kind().word_size())
}
Self::LoongArch64 => loongarch::write_plt_entry(out, entry, slot),
Self::Ppc64 => ppc64::write_plt_entry(out, entry, plt),
Self::S390x => s390x::write_plt_entry(out, entry, slot, index, plt),
}
}
pub fn write_plt_jump(
self,
out: &mut [u8],
entry: u64,
slot: u64,
flags: PltFlags,
got_base: u64,
) -> Result<(), ApplyError> {
match self {
Self::I386 => i386::write_plt_jump(out, slot, got_base, flags.pic, flags.landing_pad),
Self::Arm => arm::write_plt_entry(out, entry, slot),
Self::X86_64 | Self::X32 => x86_64::write_plt_jump(out, entry, slot, flags.landing_pad),
Self::AArch64 => {
aarch64::write_plt_entry(out, entry, slot, aarch64::plt_got_flags(flags))
}
Self::RiscV64 | Self::RiscV32 => {
riscv::write_plt_entry(out, entry, slot, self.kind().word_size())
}
Self::LoongArch64 => loongarch::write_plt_entry(out, entry, slot),
Self::Ppc64 => ppc64::write_call_stub(out, slot, got_base),
Self::S390x => Err(ApplyError::BadInstruction),
}
}
pub fn write_iplt(
self,
out: &mut [u8],
stub: u64,
slot_address: u64,
flags: PltFlags,
got_base: u64,
) -> Result<(), ApplyError> {
match self {
Self::I386 => i386::write_iplt(out, slot_address, got_base, flags.pic),
Self::Arm => arm::write_plt_entry(out, stub, slot_address),
Self::X86_64 | Self::X32 => x86_64::write_iplt(out, stub, slot_address),
Self::AArch64 => aarch64::write_plt_entry(out, stub, slot_address, flags),
Self::RiscV64 | Self::RiscV32 => {
riscv::write_plt_entry(out, stub, slot_address, self.kind().word_size())
}
Self::LoongArch64 => loongarch::write_plt_entry(out, stub, slot_address),
Self::Ppc64 => ppc64::write_call_stub(out, slot_address, got_base),
Self::S390x => s390x::write_iplt(out, stub, slot_address),
}
}
pub fn nop_undefined_branch(
self,
out: &mut [u8],
offset: u64,
r_type: u32,
) -> Result<bool, ApplyError> {
match self {
Self::I386 | Self::S390x | Self::Arm => Ok(false),
Self::X86_64 | Self::X32 | Self::RiscV64 | Self::RiscV32 => Ok(false),
Self::LoongArch64 => loongarch::undefined_weak_branch(out, offset, r_type),
Self::AArch64 => aarch64::nop_undefined_branch(out, offset, r_type),
Self::Ppc64 => ppc64::nop_undefined_branch(out, offset, r_type),
}
}
pub fn write_nops(self, out: &mut [u8]) {
match self {
Self::I386 => x86_64::write_nops(out),
Self::Arm => out.fill(0),
Self::X86_64 | Self::X32 => x86_64::write_nops(out),
Self::AArch64 => aarch64::write_nops(out),
Self::RiscV64 | Self::RiscV32 => riscv::write_nops(out),
Self::LoongArch64 => loongarch::write_nops(out),
Self::Ppc64 => ppc64::write_nops(out),
Self::S390x => s390x::write_nops(out),
}
}
}
fn slot<const N: usize>(out: &mut [u8], at: u64) -> Result<&mut [u8; N], ApplyError> {
let start = usize::try_from(at).map_err(|_| ApplyError::OutOfBounds)?;
out.get_mut(start..)
.and_then(|rest| rest.first_chunk_mut::<N>())
.ok_or(ApplyError::OutOfBounds)
}
pub fn write_value(
out: &mut [u8],
offset: u64,
width: Width,
value: u64,
) -> Result<(), ApplyError> {
write_value_as::<crate::elf::read::Little>(out, offset, width, value)
}
pub fn write_value_as<E: crate::elf::read::Endian>(
out: &mut [u8],
offset: u64,
width: Width,
value: u64,
) -> Result<(), ApplyError> {
let signed = value as i64;
match width {
Width::None => {}
Width::W64 => *slot::<8>(out, offset)? = E::put_u64(value),
Width::U32 => {
let v = u32::try_from(value).map_err(|_| ApplyError::Overflow)?;
*slot::<4>(out, offset)? = E::put_u32(v);
}
Width::I32 => {
let v = i32::try_from(signed).map_err(|_| ApplyError::Overflow)?;
*slot::<4>(out, offset)? = E::put_u32(v as u32);
}
Width::Any32 => {
if i32::try_from(signed).is_err() && u32::try_from(value).is_err() {
return Err(ApplyError::Overflow);
}
*slot::<4>(out, offset)? = E::put_u32(value as u32);
}
Width::Any16 => {
if i16::try_from(signed).is_err() && u16::try_from(value).is_err() {
return Err(ApplyError::Overflow);
}
*slot::<2>(out, offset)? = E::put_u16(value as u16);
}
Width::I16 => {
let v = i16::try_from(signed).map_err(|_| ApplyError::Overflow)?;
*slot::<2>(out, offset)? = E::put_u16(v as u16);
}
Width::Any8 => {
if i8::try_from(signed).is_err() && u8::try_from(value).is_err() {
return Err(ApplyError::Overflow);
}
*slot::<1>(out, offset)? = [value as u8];
}
Width::I8 => {
let v = i8::try_from(signed).map_err(|_| ApplyError::Overflow)?;
*slot::<1>(out, offset)? = [v as u8];
}
Width::RiscV(field) => riscv::write_field(out, offset, field, value)?,
Width::Field(field) => {
if field.bytes() == 2 {
let word = slot::<2>(out, offset)?;
let encoded = field
.encode(u32::from(u16::from_le_bytes(*word)), signed)
.map_err(|_| ApplyError::Overflow)?;
*word = (encoded as u16).to_le_bytes();
} else {
let word = slot::<4>(out, offset)?;
let encoded = field
.encode(u32::from_le_bytes(*word), signed)
.map_err(|_| ApplyError::Overflow)?;
*word = encoded.to_le_bytes();
}
}
Width::LoongArch(field) => loongarch::write_field(out, offset, field, value)?,
Width::Ppc(field) => write_ppc64(out, offset, field, signed)?,
Width::S390(field) => s390x::write_field(out, offset, field, value)?,
Width::Arm(field) => write_arm(out, offset, field, signed)?,
}
Ok(())
}
#[inline(never)]
fn write_arm(
out: &mut [u8],
offset: u64,
field: crate::arch::arm::Field,
value: i64,
) -> Result<(), ApplyError> {
let at = usize::try_from(offset).map_err(|_| ApplyError::OutOfBounds)?;
let insn = field.read(out, at).ok_or(ApplyError::OutOfBounds)?;
let encoded = field
.encode(insn, value)
.map_err(|_| ApplyError::Overflow)?;
field.write(out, at, encoded).ok_or(ApplyError::OutOfBounds)
}
#[inline(never)]
fn write_ppc64(
out: &mut [u8],
offset: u64,
field: crate::arch::ppc64::Field,
value: i64,
) -> Result<(), ApplyError> {
use crate::arch::ppc64::EncodeError;
let error = |e: EncodeError| match e {
EncodeError::Overflow => ApplyError::Overflow,
EncodeError::BadInstruction => ApplyError::BadInstruction,
};
if field == crate::arch::ppc64::Field::PcrelOpt {
return ppc64::relax_pcrel_opt(out, offset, value);
}
match field.bytes() {
2 => {
let half = slot::<2>(out, offset)?;
*half = field
.encode16(u16::from_le_bytes(*half), value)
.map_err(error)?
.to_le_bytes();
}
8 => {
let words = slot::<8>(out, offset)?;
let (prefix, suffix) = words.split_at_mut(4);
let old = (u64::from(u32::from_le_bytes([
prefix[0], prefix[1], prefix[2], prefix[3],
])) << 32)
| u64::from(u32::from_le_bytes([
suffix[0], suffix[1], suffix[2], suffix[3],
]));
let encoded = field.encode64(old, value).map_err(error)?;
prefix.copy_from_slice(&((encoded >> 32) as u32).to_le_bytes());
suffix.copy_from_slice(&(encoded as u32).to_le_bytes());
}
_ => {
let word = slot::<4>(out, offset)?;
*word = field
.encode32(u32::from_le_bytes(*word), value)
.map_err(error)?
.to_le_bytes();
}
}
Ok(())
}
pub fn add_value(out: &mut [u8], offset: u64, width: Width, delta: u64) -> Result<(), ApplyError> {
add_value_as::<crate::elf::read::Little>(out, offset, width, delta)
}
pub fn add_value_as<E: crate::elf::read::Endian>(
out: &mut [u8],
offset: u64,
width: Width,
delta: u64,
) -> Result<(), ApplyError> {
match width {
Width::W64 => {
let word = slot::<8>(out, offset)?;
*word = E::put_u64(E::u64(*word).wrapping_add(delta));
}
Width::U32 | Width::I32 | Width::Any32 => {
let word = slot::<4>(out, offset)?;
*word = E::put_u32(E::u32(*word).wrapping_add(delta as u32));
}
Width::Any16 | Width::I16 => {
let word = slot::<2>(out, offset)?;
*word = E::put_u16(E::u16(*word).wrapping_add(delta as u16));
}
Width::Any8 | Width::I8 => {
let [byte] = slot::<1>(out, offset)?;
*byte = byte.wrapping_add(delta as u8);
}
Width::LoongArch(field) => loongarch::add_field(out, offset, field, delta)?,
Width::None
| Width::Field(_)
| Width::RiscV(_)
| Width::Ppc(_)
| Width::S390(_)
| Width::Arm(_) => {
return Err(ApplyError::BadInstruction);
}
}
Ok(())
}
macro_rules! for_each_relocation {
($arch:expr, $relocations:expr, $data:expr, |$rel:ident| $body:block) => {{
let arch: $crate::elf::arch::Arch = $arch;
match $relocations {
$crate::elf::read::Relocations::Rela(relas) => {
if arch.annotates() {
let mut relocations = relas.iter().peekable();
while let Some(current) = relocations.next() {
let $rel = arch.annotate(current, relocations.peek());
$body
}
} else {
for $rel in relas.iter() $body
}
}
$crate::elf::read::Relocations::Rel(rels) => {
let data: &[u8] = $data;
for current in rels.iter() {
let $rel = $crate::elf::read::Relocation {
addend: arch.implicit_addend(current.r_type, data, current.offset),
..current
};
$body
}
}
}
}};
}
pub(crate) use for_each_relocation;
#[must_use]
pub fn width_bytes(width: Width) -> usize {
match width {
Width::None => 0,
Width::W64 => 8,
Width::U32 | Width::I32 | Width::Any32 => 4,
Width::Any16 | Width::I16 => 2,
Width::Any8 | Width::I8 => 1,
Width::Field(field) => field.bytes(),
Width::RiscV(field) => field.bytes(),
Width::LoongArch(field) => field.bytes(),
Width::Ppc(field) => field.bytes(),
Width::S390(field) => field.bytes(),
Width::Arm(field) => field.bytes(),
}
}