ARM指令和机器码对应关系:动态生成可执行指令
汇编经过编译器生成机器可以执行的机器码。由于有一段指令需要动态生成,所以简单看了一下汇编和机器码之间的关系。做了记录方便后面查找。
大部分情况下,写C/ASM,经由工具链生成可执行机器码。所以主要功能先由C/ASM编写,然后根据需求改变部分机器码。
首先研究一下《Instruction Set Assembly Guide for Armv7 and earlier Arm® architectures Version 2.0 Reference Guide》和《Arm A-profile A32/T32 Instruction Set Architecture》。
1. Conditional instruction
A32每条指令的bit[31:28]表示需要满足的条件,以PSTATE.NZCV作为输入检查条件是否满足。
// ConditionHolds() // ================ // Return TRUE iff COND currently holds--如果当前条件满足,返回TRUE。 boolean ConditionHolds(bits(4) cond) // Evaluate base condition. boolean result; case cond<3:1> of--根据cond[3:1]以及PSTATE返回result的值。 when '000' result = (PSTATE.Z == '1'); // EQ or NE when '001' result = (PSTATE.C == '1'); // CS or CC when '010' result = (PSTATE.N == '1'); // MI or PL when '011' result = (PSTATE.V == '1'); // VS or VC when '100' result = (PSTATE.C == '1' && PSTATE.Z == '0'); // HI or LS when '101' result = (PSTATE.N == PSTATE.V); // GE or LT when '110' result = (PSTATE.N == PSTATE.V && PSTATE.Z == '0'); // GT or LE when '111' result = TRUE; // AL // Condition flag values in the set '111x' indicate always true // Otherwise, invert condition if necessary. if cond<0> == '1' && cond != '1111' then--如果cond[0]=1,或者cond[3:0]!=1111,则result取反。 result = !result; return result;
所以不同的cond[3:0]检查的不同的PSTATE.NZCV标志位,结合Condition code suffixes对应关系如下:
cond[3:0 | PSTATE | Suffix | Meaning |
0000 | PSTATE.Z == '1' | EQ | Equal |
0001 | !(PSTATE.Z == '1') | NE | Not equal |
0010 | PSTATE.C == '1' | CS | Carry set(identical to HS) |
0011 | !(PSTATE.C == '1') | CC | Cary clear(identical to LO) |
0100 | PSTATE.N == '1' | MI | Minus or negative result |
0101 | !(PSTATE.N == '1' ) | PL | Positive or zero result |
0110 | PSTATE.V == '1' | VS | Overflow |
0111 | !(PSTATE.V == '1' ) | VC | No overflow |
1000 | PSTATE.C == '1' && PSTATE.Z == '0' | HI | Unsigned higher |
1001 | !(PSTATE.C == '1' && PSTATE.Z == '0') | LS | Unsigned lower or same |
1010 | PSTATE.N == PSTATE.V | GE | Signed greater than or equal |
1011 | !(PSTATE.N == PSTATE.V) | LT | Signed less than |
1100 | (PSTATE.N == PSTATE.V) && PSTATE.Z == '0' | GT | Signed greater than |
1101 | !((PSTATE.N == PSTATE.V) && PSTATE.Z == '0') | LE | Signed len than or equal |
1110 | TRUE | AL | Alaways(this is the default) |
1111 | TRUE | AL | Alaways(this is the default) |
以blx为例,如何从blx对应到机器码
BLX (register)是无条件跳转到(register)指向的地址。其指令机器码为:
其中cond[3:0]=1110,所以blx (register)的bit[31:4] = 1110 0001 0010 1111 1111 1111 0011,bit[3:0]为寄存器序号,从0~15。其中r15即为pc寄存器,结果不可预测。
所以blx r3的机器码即为0xE12FFF30 | 0x3 = 0xE12FFF33。
设置跳转地址到寄存器,然后跳转到地址。
如果要对如下指令,使用不固定寄存器跳转到不固定地址:
mov r3, #0x0 movt r3, #0x8000 blx r3
编译后的机器码为:
根据MOV指令机器码说明,这里对应A1 S==0的情形。如果是imm16则对应A2机器码。
所以mov r3, #0x0的机器码为0xE3A00(cond+S+...)3(Rd)000(imm12) = 0xE3A03000。
如果是mov r3, #0xffff,机器码则为0xE30(cond...)+F(imm4)+3(Rd)+FFF(imm12) = 0xE30F3FFF。
movt改变寄存器的高16位数值,所以movt r3, #0x8000对应的机器码即为:0xE34(cond...)8(imm4)3(Rd)000(imm12) = 0xE3883000。
所以可以通过任意寄存器跳转到任意地址生成机器码。
void update_instructions(unsigned int addr, unsigned int reg, unsigned int *instructions)
{ unsigned int * intr p = instructions;
if(reg >= 15) { return;
} //mov r3,#0x0
if(addr & 0x0000F000) {
*intr p++ = 0xE3A00000 \
|(addr & 0x0000F000) << 4 \
|(reg << 12) \
|(addr & 0x00000FFF);
} else {
*intr _p++ = 0xE3000000 \
|(reg << 12) \
|(addr & 0x00000FFF);
}
//movt r3,#0x8000
*intr p++ = 0xE3400000 \ |(addr & 0xF0000000) >> 12 \ |(reg << 12) \
|(addr & 0x0FFF0000) >> 16; //blx r3
*intr p = 0xE12FFF30 |reg;
}
为什么blx label地址范围+/-32MB,而blx <reg>不受限制
从blx (immediate)的机器码可知,imm24被扩展到Signed 26位,所以blx (immediate)的跳转空间是64MB,在当前指令位置的前后32MB内。
而blx <reg>跳转的地址存于寄存器中,寄存器的位宽足够覆盖整个地址空间。