Python ARM Thumb BL指令循环到自身
我正在尝试使用Keystone组装此代码,并使用Unicorn引擎执行它:Python ARM Thumb BL指令循环到自身,python,python-3.x,arm,thumb,Python,Python 3.x,Arm,Thumb,我正在尝试使用Keystone组装此代码,并使用Unicorn引擎执行它: start: add r0, r0, #1 add r1, r1, #2 bl start b start 我认为,bl指令应该将下一条指令的地址保存到lr寄存器,然后跳转到start。因此,这将是一个无限循环,它将1添加到r0,将2添加到r1 显然,我错了,因为bl start转而分支到自身 我正在使用Keystone、Capstone和Unicorn的Python包装器来处理程序集。
start:
add r0, r0, #1
add r1, r1, #2
bl start
b start
我认为,bl
指令应该将下一条指令的地址保存到lr
寄存器,然后跳转到start
。因此,这将是一个无限循环,它将1
添加到r0
,将2
添加到r1
显然,我错了,因为bl start
转而分支到自身
我正在使用Keystone、Capstone和Unicorn的Python包装器来处理程序集。这是我的密码:
import keystone as ks
import capstone as cs
import unicorn as uc
print(f'Keystone {ks.__version__}\nCapstone {cs.__version__}\nUnicorn {uc.__version__}\n')
code = '''
start:
add r0, r0, #1
add r1, r1, #2
bl start
b start
'''
assembler = ks.Ks(ks.KS_ARCH_ARM, ks.KS_MODE_THUMB)
disassembler = cs.Cs(cs.CS_ARCH_ARM, cs.CS_MODE_THUMB)
emulator = uc.Uc(uc.UC_ARCH_ARM, uc.UC_MODE_THUMB)
machine_code, _ = assembler.asm(code)
machine_code = bytes(machine_code)
print(machine_code.hex())
initial_address = 0
for addr, size, mnem, op_str in disassembler.disasm_lite(machine_code, initial_address):
instruction = machine_code[addr:addr + size]
print(f'{addr:04x}|\t{instruction.hex():<8}\t{mnem:<5}\t{op_str}')
emulator.mem_map(initial_address, 1024) # allocate 1024 bytes of memory
emulator.mem_write(initial_address, machine_code) # write the machine code
emulator.hook_add(uc.UC_HOOK_CODE, lambda uc, addr, size, _: print(f'Address: {addr}'))
emulator.emu_start(initial_address | 1, initial_address + len(machine_code), timeout=500)
编辑 我接着用叮当声汇编了这段代码:
; test.s
.text
.syntax unified
.globl start
.p2align 1
.code 16
.thumb_func
start:
add r0, r0, #1
add r1, r1, #2
bl start
b start
使用以下命令:
$ clang -c test.s -target armv7-unknown-linux -o test.bin -mthumb
clang-11: warning: unknown platform, assuming -mfloat-abi=soft
然后用objdump
反汇编test.bin
:
$ objdump -d test.bin
test.bin: file format elf32-littlearm
Disassembly of section .text:
00000000 <start>:
0: 00 f1 01 00 add.w r0, r0, #1
4: 01 f1 02 01 add.w r1, r1, #2
8: ff f7 fe ff bl #-4
c: ff f7 fe bf b.w #-4 <start+0x10>
$
$objdump-d test.bin
bin:文件格式elf32 littlearm
第节的分解。正文:
00000000 :
0:00 f1 01 00 add.w r0,r0,#1
4:01 f1 02 01 add.w r1,r1,#2
8:ff f7 fe ff bl#-4
c:ff f7 fe bf b.w#-4
$
所以bl
的参数实际上是一个偏移量。这是负面的,因为我们在倒退。但是:
对于B
、BL
、CBNZ
和CBZ
指令,PC的值是当前指令的地址加上4个字节
因此,bl#-4
将跳转到(bl的地址)+4字节-4字节,或者,换句话说,再次跳转到它本身
所以,出于某种原因,我不能向后bl
?这里发生了什么以及如何修复它?所有工具“链”链接器都必须处理函数调用或其他到外部资源的操作,您将看到像bl这样的指令编码为分支到自身或分支到零或一些这样的不完整指令(当然是对于外部标签)。与此相切的是,某些版本的clang有时似乎为本地地址编码,有时则不是(在汇编程序级别)。但当链接时,偏移量/地址会被修补(如本例所示)
对象级别的通用clang(所有目标,默认x86主机)3.7给出了正确的指令。3.8没有。这似乎就是这一变化发生的时间。Clang10通用版没有,但是一个手工构建的Clang10.0.0特定于一个目标,在组装时给出了正确的答案
所有这些都是相切的,因为这是在装配时而不是最终输出。当链接时,您会得到正确的答案(到目前为止,OP可能还有其他没有得到正确答案的情况)
这与以前的一对thumb指令0xF7FF、0xFFFE是同一条指令,但对于armv7 ar,它被视为一条指令,不可分割
0xF7FFFFFE
感谢再次查找这个问题,我发现了这个问题,因为我要么知道了,要么忘记了,要么不知道
在ARMv6T2之前,编码T1和T2中的J1和J2均为1,因此分支范围较小。这些指令可以作为两条独立的16位指令执行
我已经在armv7之前的体系结构上演示了这两条指令是相互独立的,并表明它们不是一条指令
无论如何:
与来自gnu的指令相同
8: f7ff fffe bl 0 <start>
8:f7ff fffe bl 0
GNUOne稍好一点,但仍有问题,编码不是BL0,但输出表明了最终的愿望,最后被重新编码,以便在链接时正确
因此,这些工具也可能是理解问题的一部分,因为它们无法以正确的可解码格式表示机器代码。你找到答案了吗?找到一个不同的三元组,它有一个通用的叮当声可以工作?@old_timer,哦,天哪,我构建了一个递归的factorial
函数,它在armv7苹果达尔文
上使用了与叮当声3.7(!)相同的bl
指令,它工作了,但生成的机器代码与这里的机器代码完全不同。我用三种不同版本的Clang为armv7 none-eabi
重建了相同的程序集(遗憾的是,我没有地方运行它),所有这些程序都生成了bl
指令,根据objdump
,该指令将自动跳转。因此,唯一“正确”的版本是由iPhone的那个老铃声生成的。我不知道为什么,你答案中的程序集显示f7ff fffa
作为bl start
的机器代码,但在我的Keystone中,它被交换为fff7 feff
(我想应该是f7ff fffe
,但这仍然是fe
,而不是fa
)。看起来像是一个持久性问题。顺便说一句,我认为Keystone&friends实际上使用了Clang的代码来进行汇编和反汇编,所以我认为将其与“实际的”Clang进行比较是毫无意义的。。。不管怎样,只有老的Clang3.7产生了一些肯定有效的东西。明天,我将在《叮当声》中总结我对被诅咒的bl
指令的研究,并更新这个问题。@old_timer,以下是我的一些发现:。看起来这与结果文件是对象文件或实际可执行文件有关。我想我已经看到了一个关于这个的问题,所以…DOH,没有想到,当然,一些工具链直到链接时才会解析,像这样的指令在任何情况下都肯定是由链接器处理的,所以为什么不让链接器一直这样做,即使汇编器知道答案。这可能很容易测试。很酷,所以这看起来像Keystone中的一个bug。我提交了-也许他们会修复它。谢谢你如此详细的回答!好吧,也许是机器代码的打印,但编码根本不是一个bug。这是典型的行为,并不少见。对于一个使用链接器的编译器来说,这可能很好,但据我所知,Keystone力求独立。我是说,Keystone甚至不能
$ objdump -d test.bin
test.bin: file format elf32-littlearm
Disassembly of section .text:
00000000 <start>:
0: 00 f1 01 00 add.w r0, r0, #1
4: 01 f1 02 01 add.w r1, r1, #2
8: ff f7 fe ff bl #-4
c: ff f7 fe bf b.w #-4 <start+0x10>
$
.thumb
.syntax unified
.thumb_func
start:
add r0, r0, #1
add r1, r1, #2
bl start
b start
clang-3.8 -c so.s -target armv7-unknown-linux -o so.o
clang: warning: unknown platform, assuming -mfloat-abi=soft
arm-none-eabi-objdump -D so.o
so.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <start>:
0: f100 0001 add.w r0, r0, #1
4: f101 0102 add.w r1, r1, #2
8: f7ff fffe bl 0 <start>
c: e7f8 b.n 0 <start>
arm-none-eabi-ld -Ttext=0 so.o -o so.elf
arm-none-eabi-ld: warning: cannot find entry symbol _start; defaulting to 0000000000000000
arm-none-eabi-objdump -d so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <start>:
0: f100 0001 add.w r0, r0, #1
4: f101 0102 add.w r1, r1, #2
8: f7ff fffa bl 0 <start>
c: e7f8 b.n 0 <start>
0008| fff7feff bl #8 ; why not `bl #0`?
8: ff f7 fe ff bl #-4
8: f7ff fffe bl 0 <start>