带移位的ARM“添加”指令

逆向工程 拆卸 部件 手臂
2021-06-16 03:02:51

我试图了解 ARMadd与 shift 是如何实现的,例如

sym.imp.__libc_start_main :                                                                                                                                                           

.plt:0x000082bc 00c68fe2 add ip, pc, 0, 12; after execution ip=0x82c4
.plt:0x000082c0 08ca8ce2 add ip, ip, 8, 20; after execution ip=0x102c4
.plt:0x000082c4 48fdbce5 ldr pc, [ip, 0xd48]!

我想知道这条线

.plt:0x000082c0 08ca8ce2 add ip, ip, 8, 20;

它将添加#0x8000到 ip 寄存器。我的问题是为什么#0x8000

我假设它将是:

ip = ip + (8<<20)

所以,0x800000但它更像是

ip = ip + (8<<(20-8))

这是为什么?我总是必须从班次中减去 8 吗?

2个回答

是 32 位系统上循环移位

循环移位

在计算机编程中,循环移位(或按位旋转)是移位操作数的所有位的移位运算符。与算术移位不同,循环移位不保留数字的符号位或将数字的指数与其有效数(有时称为尾数)区分开来。与逻辑移位不同,空位位置不是用零填充,而是用移出序列的位填充。

理解代码

第一行:
这被简单地翻译成add ip, pc因为旋转操作#0仍然是 0。
所以它实际上是IP = PC + (0 << 12) = PC + 0

第二行:
让我们拆开操作码并理解有问题的行:
由于字节顺序,操作码应该这样读取e28cca08

  1. e - 始终执行此指令
  2. 28 - 立即添加
  3. c - Rd 是ip
  4. c - Rn 是ip
  5. a 08 - 8 右旋转 20

事情是,它不是8<<20,而是8<<(32-12)因为我们在一个 32 位系统上,它是一个循环移位。

这是一个 C 代码,它显示了基于Wikipedia示例的循环移位

#include <stdint.h>  // for uint32_t, to get 32bit-wide rotates, regardless of the size of int.
#include <limits.h>  // for CHAR_BIT

uint32_t rotl32 (uint32_t value, unsigned int count) {
    const unsigned int mask = (CHAR_BIT*sizeof(value)-1);
    count &= mask;
    return (value<<count) | (value>>( (-count) & mask ));
}

uint32_t rotr32 (uint32_t value, unsigned int count) {
    const unsigned int mask = (CHAR_BIT*sizeof(value)-1);
    count &= mask;
    return (value>>count) | (value<<( (-count) & mask ));
}

int main()
{
    printf("Result: 0x%x\n",rotr32(8,20));
    return 0;
}

代码将输出:

Result: 0x8000

arm官方文档中提出的 手臂1

当s=1且RD=R15(PC)时,该指令用于保存状态寄存器CPSR,不做计算 手臂2