1 //! Memory operands to instructions. 2 3 use alloc::string::{String, ToString}; 4 5 use crate::api::{AsReg, CodeSink, Constant, KnownOffset, Label, TrapCode}; 6 use crate::gpr::{self, NonRspGpr, Size}; 7 use crate::rex::{Disp, RexPrefix, encode_modrm, encode_sib}; 8 9 /// x64 memory addressing modes. 10 #[derive(Copy, Clone, Debug, PartialEq)] 11 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] 12 pub enum Amode<R: AsReg> { 13 ImmReg { 14 base: R, 15 simm32: AmodeOffsetPlusKnownOffset, 16 trap: Option<TrapCode>, 17 }, 18 ImmRegRegShift { 19 base: R, 20 index: NonRspGpr<R>, 21 scale: Scale, 22 simm32: AmodeOffset, 23 trap: Option<TrapCode>, 24 }, 25 RipRelative { 26 target: DeferredTarget, 27 }, 28 } 29 30 impl<R: AsReg> Amode<R> { 31 /// Return the [`TrapCode`] associated with this [`Amode`], if any. 32 pub fn trap_code(&self) -> Option<TrapCode> { 33 match self { 34 Amode::ImmReg { trap, .. } | Amode::ImmRegRegShift { trap, .. } => *trap, 35 Amode::RipRelative { .. } => None, 36 } 37 } 38 39 /// Return the [`RexPrefix`] for each variant of this [`Amode`]. 40 #[must_use] 41 pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix { 42 match self { 43 Amode::ImmReg { base, .. } => { 44 RexPrefix::mem_op(enc_reg, base.enc(), has_w_bit, uses_8bit) 45 } 46 Amode::ImmRegRegShift { base, index, .. } => { 47 RexPrefix::three_op(enc_reg, index.enc(), base.enc(), has_w_bit, uses_8bit) 48 } 49 Amode::RipRelative { .. } => RexPrefix::two_op(enc_reg, 0, has_w_bit, uses_8bit), 50 } 51 } 52 53 /// Emit the ModR/M, SIB, and displacement suffixes as needed for this 54 /// `Amode`. 55 pub(crate) fn encode_rex_suffixes( 56 &self, 57 sink: &mut impl CodeSink, 58 enc_reg: u8, 59 bytes_at_end: u8, 60 evex_scaling: Option<i8>, 61 ) { 62 emit_modrm_sib_disp(sink, enc_reg, self, bytes_at_end, evex_scaling); 63 } 64 65 /// Return the registers for encoding the `b` and `x` bits (e.g., in a VEX 66 /// prefix). 67 /// 68 /// During encoding, the `b` bit is set by the topmost bit (the fourth bit) 69 /// of either the `reg` register or, if this is a memory address, the `base` 70 /// register. The `x` bit is set by the `index` register, when used. 71 pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) { 72 match self { 73 Amode::ImmReg { base, .. } => (Some(base.enc()), None), 74 Amode::ImmRegRegShift { base, index, .. } => (Some(base.enc()), Some(index.enc())), 75 Amode::RipRelative { .. } => (None, None), 76 } 77 } 78 } 79 80 /// A 32-bit immediate for address offsets. 81 #[derive(Clone, Copy, Debug, PartialEq)] 82 pub struct AmodeOffset(i32); 83 84 impl AmodeOffset { 85 pub const ZERO: AmodeOffset = AmodeOffset::new(0); 86 87 #[must_use] 88 pub const fn new(value: i32) -> Self { 89 Self(value) 90 } 91 92 #[must_use] 93 pub fn value(self) -> i32 { 94 self.0 95 } 96 } 97 98 impl From<i32> for AmodeOffset { 99 fn from(value: i32) -> Self { 100 Self(value) 101 } 102 } 103 104 impl core::fmt::LowerHex for AmodeOffset { 105 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 106 // This rather complex implementation is necessary to match how 107 // `capstone` pretty-prints memory immediates. 108 if self.0 == 0 { 109 return Ok(()); 110 } 111 if self.0 < 0 { 112 write!(f, "-")?; 113 } 114 if self.0 > 9 || self.0 < -9 { 115 write!(f, "0x")?; 116 } 117 let abs = match self.0.checked_abs() { 118 Some(i) => i, 119 None => -2_147_483_648, 120 }; 121 core::fmt::LowerHex::fmt(&abs, f) 122 } 123 } 124 125 /// An [`AmodeOffset`] immediate with an optional known offset. 126 /// 127 /// Cranelift does not know certain offsets until emission time. To accommodate 128 /// Cranelift, this structure stores an optional [`KnownOffset`]. The following 129 /// happens immediately before emission: 130 /// - the [`KnownOffset`] is looked up, mapping it to an offset value 131 /// - the [`AmodeOffset`] value is added to the offset value 132 #[derive(Copy, Clone, Debug, PartialEq)] 133 pub struct AmodeOffsetPlusKnownOffset { 134 pub simm32: AmodeOffset, 135 pub offset: Option<KnownOffset>, 136 } 137 138 impl AmodeOffsetPlusKnownOffset { 139 pub const ZERO: AmodeOffsetPlusKnownOffset = AmodeOffsetPlusKnownOffset { 140 simm32: AmodeOffset::ZERO, 141 offset: None, 142 }; 143 144 /// # Panics 145 /// 146 /// Panics if the sum of the immediate and the known offset value overflows. 147 #[must_use] 148 pub fn value(&self, sink: &impl CodeSink) -> i32 { 149 let known_offset = match self.offset { 150 Some(offset) => sink.known_offset(offset), 151 None => 0, 152 }; 153 known_offset 154 .checked_add(self.simm32.value()) 155 .expect("no wrapping") 156 } 157 } 158 159 impl core::fmt::LowerHex for AmodeOffsetPlusKnownOffset { 160 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 161 if let Some(offset) = self.offset { 162 write!(f, "<offset:{offset}>+")?; 163 } 164 core::fmt::LowerHex::fmt(&self.simm32, f) 165 } 166 } 167 168 /// For RIP-relative addressing, keep track of the [`CodeSink`]-specific target. 169 #[derive(Copy, Clone, Debug, PartialEq)] 170 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] 171 pub enum DeferredTarget { 172 Label(Label), 173 Constant(Constant), 174 None, 175 } 176 177 impl<R: AsReg> core::fmt::Display for Amode<R> { 178 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 179 let pointer_width = Size::Quadword; 180 match self { 181 Amode::ImmReg { simm32, base, .. } => { 182 // Note: size is always 8; the address is 64 bits, 183 // even if the addressed operand is smaller. 184 let base = base.to_string(Some(pointer_width)); 185 write!(f, "{simm32:x}({base})") 186 } 187 Amode::ImmRegRegShift { 188 simm32, 189 base, 190 index, 191 scale, 192 .. 193 } => { 194 let base = base.to_string(Some(pointer_width)); 195 let index = index.to_string(pointer_width); 196 let shift = scale.shift(); 197 if shift > 1 { 198 write!(f, "{simm32:x}({base}, {index}, {shift})") 199 } else { 200 write!(f, "{simm32:x}({base}, {index})") 201 } 202 } 203 Amode::RipRelative { .. } => write!(f, "(%rip)"), 204 } 205 } 206 } 207 208 /// The scaling factor for the index register in certain [`Amode`]s. 209 #[derive(Copy, Clone, Debug, PartialEq)] 210 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] 211 pub enum Scale { 212 One, 213 Two, 214 Four, 215 Eight, 216 } 217 218 impl Scale { 219 /// Create a new [`Scale`] from its hardware encoding. 220 /// 221 /// # Panics 222 /// 223 /// Panics if `enc` is not a valid encoding for a scale (0-3). 224 #[must_use] 225 pub fn new(enc: u8) -> Self { 226 match enc { 227 0b00 => Scale::One, 228 0b01 => Scale::Two, 229 0b10 => Scale::Four, 230 0b11 => Scale::Eight, 231 _ => panic!("invalid scale encoding: {enc}"), 232 } 233 } 234 235 /// Return the hardware encoding of this [`Scale`]. 236 fn enc(&self) -> u8 { 237 match self { 238 Scale::One => 0b00, 239 Scale::Two => 0b01, 240 Scale::Four => 0b10, 241 Scale::Eight => 0b11, 242 } 243 } 244 245 /// Return how much this [`Scale`] will shift the value in the index 246 /// register of the SIB byte. 247 /// 248 /// This is useful for pretty-printing; when encoding, one usually needs 249 /// [`Scale::enc`]. 250 fn shift(&self) -> u8 { 251 1 << self.enc() 252 } 253 } 254 255 /// A general-purpose register or memory operand. 256 #[derive(Copy, Clone, Debug, PartialEq)] 257 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] 258 #[allow( 259 clippy::module_name_repetitions, 260 reason = "'GprMem' indicates this has GPR and memory variants" 261 )] 262 pub enum GprMem<R: AsReg, M: AsReg> { 263 Gpr(R), 264 Mem(Amode<M>), 265 } 266 267 impl<R: AsReg, M: AsReg> GprMem<R, M> { 268 /// Pretty-print the operand. 269 pub fn to_string(&self, size: Size) -> String { 270 match self { 271 GprMem::Gpr(gpr) => gpr.to_string(Some(size)), 272 GprMem::Mem(amode) => amode.to_string(), 273 } 274 } 275 276 /// Return the [`RexPrefix`] for each variant of this [`GprMem`]. 277 #[must_use] 278 pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix { 279 match self { 280 GprMem::Gpr(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit), 281 GprMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit), 282 } 283 } 284 285 /// Emit the ModR/M, SIB, and displacement suffixes for this [`GprMem`]. 286 pub(crate) fn encode_rex_suffixes( 287 &self, 288 sink: &mut impl CodeSink, 289 enc_reg: u8, 290 bytes_at_end: u8, 291 evex_scaling: Option<i8>, 292 ) { 293 match self { 294 GprMem::Gpr(gpr) => { 295 sink.put1(encode_modrm(0b11, enc_reg & 0b111, gpr.enc() & 0b111)); 296 } 297 GprMem::Mem(amode) => { 298 amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling); 299 } 300 } 301 } 302 303 /// Same as `XmmMem::encode_bx_regs`, but for `GprMem`. 304 pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) { 305 match self { 306 GprMem::Gpr(reg) => (Some(reg.enc()), None), 307 GprMem::Mem(amode) => amode.encode_bx_regs(), 308 } 309 } 310 } 311 312 impl<R: AsReg, M: AsReg> From<R> for GprMem<R, M> { 313 fn from(reg: R) -> GprMem<R, M> { 314 GprMem::Gpr(reg) 315 } 316 } 317 318 impl<R: AsReg, M: AsReg> From<Amode<M>> for GprMem<R, M> { 319 fn from(amode: Amode<M>) -> GprMem<R, M> { 320 GprMem::Mem(amode) 321 } 322 } 323 324 /// An XMM register or memory operand. 325 #[derive(Copy, Clone, Debug)] 326 #[cfg_attr(any(test, feature = "fuzz"), derive(arbitrary::Arbitrary))] 327 #[allow( 328 clippy::module_name_repetitions, 329 reason = "'XmmMem' indicates this has Xmm and memory variants" 330 )] 331 pub enum XmmMem<R: AsReg, M: AsReg> { 332 Xmm(R), 333 Mem(Amode<M>), 334 } 335 336 impl<R: AsReg, M: AsReg> XmmMem<R, M> { 337 /// Pretty-print the operand. 338 pub fn to_string(&self) -> String { 339 match self { 340 XmmMem::Xmm(xmm) => xmm.to_string(None), 341 XmmMem::Mem(amode) => amode.to_string(), 342 } 343 } 344 345 /// Return the [`RexPrefix`] for each variant of this [`XmmMem`]. 346 #[must_use] 347 pub(crate) fn as_rex_prefix(&self, enc_reg: u8, has_w_bit: bool, uses_8bit: bool) -> RexPrefix { 348 match self { 349 XmmMem::Xmm(rm) => RexPrefix::two_op(enc_reg, rm.enc(), has_w_bit, uses_8bit), 350 XmmMem::Mem(amode) => amode.as_rex_prefix(enc_reg, has_w_bit, uses_8bit), 351 } 352 } 353 354 /// Emit the ModR/M, SIB, and displacement suffixes for this [`XmmMem`]. 355 pub(crate) fn encode_rex_suffixes( 356 &self, 357 sink: &mut impl CodeSink, 358 enc_reg: u8, 359 bytes_at_end: u8, 360 evex_scaling: Option<i8>, 361 ) { 362 match self { 363 XmmMem::Xmm(xmm) => { 364 sink.put1(encode_modrm(0b11, enc_reg & 0b111, xmm.enc() & 0b111)); 365 } 366 XmmMem::Mem(amode) => { 367 amode.encode_rex_suffixes(sink, enc_reg, bytes_at_end, evex_scaling); 368 } 369 } 370 } 371 372 /// Return the registers for encoding the `b` and `x` bits (e.g., in a VEX 373 /// prefix). 374 /// 375 /// During encoding, the `b` bit is set by the topmost bit (the fourth bit) 376 /// of either the `reg` register or, if this is a memory address, the `base` 377 /// register. The `x` bit is set by the `index` register, when used. 378 pub(crate) fn encode_bx_regs(&self) -> (Option<u8>, Option<u8>) { 379 match self { 380 XmmMem::Xmm(reg) => (Some(reg.enc()), None), 381 XmmMem::Mem(amode) => amode.encode_bx_regs(), 382 } 383 } 384 } 385 386 impl<R: AsReg, M: AsReg> From<R> for XmmMem<R, M> { 387 fn from(reg: R) -> XmmMem<R, M> { 388 XmmMem::Xmm(reg) 389 } 390 } 391 392 impl<R: AsReg, M: AsReg> From<Amode<M>> for XmmMem<R, M> { 393 fn from(amode: Amode<M>) -> XmmMem<R, M> { 394 XmmMem::Mem(amode) 395 } 396 } 397 398 /// Emit the ModRM/SIB/displacement sequence for a memory operand. 399 pub fn emit_modrm_sib_disp<R: AsReg>( 400 sink: &mut impl CodeSink, 401 enc_g: u8, 402 mem_e: &Amode<R>, 403 bytes_at_end: u8, 404 evex_scaling: Option<i8>, 405 ) { 406 match *mem_e { 407 Amode::ImmReg { simm32, base, .. } => { 408 let enc_e = base.enc(); 409 let mut imm = Disp::new(simm32.value(sink), evex_scaling); 410 411 // Most base registers allow for a single ModRM byte plus an 412 // optional immediate. If rsp is the base register, however, then a 413 // SIB byte must be used. 414 let enc_e_low3 = enc_e & 7; 415 if enc_e_low3 == gpr::enc::RSP { 416 // Displacement from RSP is encoded with a SIB byte where 417 // the index and base are both encoded as RSP's encoding of 418 // 0b100. This special encoding means that the index register 419 // isn't used and the base is 0b100 with or without a 420 // REX-encoded 4th bit (e.g. rsp or r12) 421 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100)); 422 sink.put1(0b00_100_100); 423 imm.emit(sink); 424 } else { 425 // If the base register is rbp and there's no offset then force 426 // a 1-byte zero offset since otherwise the encoding would be 427 // invalid. 428 if enc_e_low3 == gpr::enc::RBP { 429 imm.force_immediate(); 430 } 431 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, enc_e & 7)); 432 imm.emit(sink); 433 } 434 } 435 436 Amode::ImmRegRegShift { 437 simm32, 438 base, 439 index, 440 scale, 441 .. 442 } => { 443 let enc_base = base.enc(); 444 let enc_index = index.enc(); 445 446 // Encoding of ModRM/SIB bytes don't allow the index register to 447 // ever be rsp. Note, though, that the encoding of r12, whose three 448 // lower bits match the encoding of rsp, is explicitly allowed with 449 // REX bytes so only rsp is disallowed. 450 assert!(enc_index != gpr::enc::RSP); 451 452 // If the offset is zero then there is no immediate. Note, though, 453 // that if the base register's lower three bits are `101` then an 454 // offset must be present. This is a special case in the encoding of 455 // the SIB byte and requires an explicit displacement with rbp/r13. 456 let mut imm = Disp::new(simm32.value(), evex_scaling); 457 if enc_base & 7 == gpr::enc::RBP { 458 imm.force_immediate(); 459 } 460 461 // With the above determined encode the ModRM byte, then the SIB 462 // byte, then any immediate as necessary. 463 sink.put1(encode_modrm(imm.m0d(), enc_g & 7, 0b100)); 464 sink.put1(encode_sib(scale.enc(), enc_index & 7, enc_base & 7)); 465 imm.emit(sink); 466 } 467 468 Amode::RipRelative { target } => { 469 // RIP-relative is mod=00, rm=101. 470 sink.put1(encode_modrm(0b00, enc_g & 7, 0b101)); 471 472 // Inform the code sink about the RIP-relative `target` at the 473 // current offset, emitting a `LabelUse`, a relocation, or etc as 474 // appropriate. 475 sink.use_target(target); 476 477 // N.B.: some instructions (XmmRmRImm format for example) 478 // have bytes *after* the RIP-relative offset. The 479 // addressed location is relative to the end of the 480 // instruction, but the relocation is nominally relative 481 // to the end of the u32 field. So, to compensate for 482 // this, we emit a negative extra offset in the u32 field 483 // initially, and the relocation will add to it. 484 sink.put4(-(i32::from(bytes_at_end)) as u32); 485 } 486 } 487 } 488