unicorn-engine / unicorn

Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
http://www.unicorn-engine.org
GNU General Public License v2.0
7.54k stars 1.33k forks source link

ARM Stack Pointer reset on CPSR write #1984

Open OBarronCS opened 2 months ago

OBarronCS commented 2 months ago

Using Unicorn to emulate an Arm binary, I noticed a behavior where the stack pointer in the emulator is being reset to 0 if the CPSR register is written after the SP register.

Here's a minimal example:

import unicorn as U

uc = U.Uc(U.UC_ARCH_ARM, U.UC_MODE_ARM)

uc.reg_write(U.arm_const.UC_ARM_REG_SP,100)
# This returns 100, as expected
print(uc.reg_read(U.arm_const.UC_ARM_REG_SP))

# Now, write something to the CPSR register
uc.reg_write(U.arm_const.UC_ARM_REG_CPSR,0x30)

# Reading from stack pointer returns 0, instead of 100
print(uc.reg_read(U.arm_const.UC_ARM_REG_SP))

Assuming this is not expected behavior in Arm, the SP register being reset may be a bug in the writing of the CPSR register. In my testing, other registers (PC or other general purpose registers) do not reset upon the CPSR being written - only the SP has this behavior.

OBarronCS commented 2 months ago

I think I have the root cause - I noticed that the default value of CPSR (when read immediately after initialization) is 0x400001d3. The bottom bits indicate supervisor mode. Arm has banked registers - SP being one of them - which means that each processor mode (supervisor, user, etc) has it's own distinct physical SP register. When you switch modes (say from supervisor to user), the SP is saved somewhere, and then restored upon your mode switching back to user mode.

Might this be configurable?