ARM64架构ADRP指令地址解析疑问:是否需偏移PC值
ADRP指令地址解析逻辑疑问
我正在查阅ARM64架构下ADRP指令的官方文档,对其中一处实现细节有疑问,以下通过代码示例说明:
ULONG64 uiAddr = 0xfffff80198964c08; // 当前ADRP指令的地址 ULONG32 opcode = 0x900016d0; // ADRP指令编码 // 校验指令合法性 if((opcode & 0x9F000000) != 0x90000000) { // 非法指令 wprintf(L"非法ADRP指令编码: 0x%X\n", opcode); return -1; } ULONG64 imm; imm = (opcode & 0x60000000) >> 29; imm |= (opcode & 0x00FFFFE0) >> 3; // 是否需要执行这一步? // uiAddr += sizeof(opcode); // 将PC偏移指令长度 uiAddr += imm << 12; uiAddr &= ~((0x1LL << 12) - 1); ULONG uiReg = opcode & 0x1F; wprintf(L"ADRP解析结果: x%u=0x%I64X\n", uiReg, uiAddr);
请问上述ADRP指令的地址解析逻辑是否正确?是否需要执行注释中的uiAddr += sizeof(opcode)步骤来偏移PC地址?
回答
1. 必须执行PC偏移步骤
是的,必须加上uiAddr += sizeof(opcode)。ARM64架构中,PC寄存器的值定义为当前指令地址 + 4(因为ARM64指令固定为4字节对齐,PC始终指向当前指令的下一条指令),而ADRP指令的地址计算正是基于这个PC值来做相对偏移的。如果跳过这一步,你会用当前指令的地址而非正确的PC值来计算,最终得到的目标地址会差4字节,导致错误。
2. 原代码的立即数解析逻辑存在错误
原代码中提取立即数的逻辑有问题:
- ADRP的21位立即数由两部分组成:高2位来自指令的
bit29-28,中间16位来自bit20-5,低3位固定为0。 - 原代码用
0x60000000掩码提取的是bit31-30,这是错误的,正确的掩码应该是0x18000000(对应bit29-28),然后右移28位得到高两位的值。
另外,ADRP的立即数是有符号数,需要做符号扩展到64位,否则处理负偏移(即目标地址在当前PC页的前序页)时会计算错误。
修正后的代码示例
ULONG64 uiAddr = 0xfffff80198964c08; // 当前ADRP指令的地址 ULONG32 opcode = 0x900016d0; // ADRP指令编码 // 校验指令合法性 if((opcode & 0x9F000000) != 0x90000000) { wprintf(L"非法ADRP指令编码: 0x%X\n", opcode); return -1; } // 正确提取21位有符号立即数 ULONG64 imm; // 提取bit29-28作为高2位 imm = (opcode & 0x18000000) >> 28; // 提取bit20-5并拼接到低16位 imm |= (opcode & 0x00FFFFE0) >> 3; // 符号扩展21位立即数到64位(处理负偏移) if (imm & (1ULL << 20)) { imm |= ~((1ULL << 21) - 1); } // 必须偏移PC到下一条指令地址 uiAddr += sizeof(opcode); // 计算目标地址:PC对齐到4KB页 + 立即数<<12 uiAddr &= ~((0x1LL << 12) - 1); uiAddr += imm << 12; ULONG uiReg = opcode & 0x1F; wprintf(L"ADRP解析结果: x%u=0x%I64X\n", uiReg, uiAddr);
总结
- 原代码缺少PC偏移步骤,这是核心错误,必须补上。
- 立即数提取的掩码和移位逻辑错误,需要修正。
- 遗漏了有符号立即数的符号扩展,否则负偏移场景下计算结果会出错。
内容的提问来源于stack exchange,提问作者c00000fd
相关产品推荐
相关产品推荐

