Unicorn 2 is failng to allow the transition from USR32 to SVC32 using direct register access.
No emulated code is involved - the system state does not change. It is as if the unicorn environment is enforcing the emulated environment's restrictions on the CPSR.
Source environment
I built unicorn on the dev branch at sha 558fb9c155e739444636dfdc434a536cc1a7a79f.
This is after the fix for https://github.com/unicorn-engine/unicorn/issues/1494 (which relates to corruption of banked registers on a mode change).
This has been tested on OSX system on Intel hardware (10.14.6)
Failure mode
The failures appears to be that once you are in USR32, an unprivileged, you are unable programatically to enter a privileged mode. This is necessary for a number of reasons, not least of which is providing entry to SVC32 when a SWI call is made, or IRQ32 when an interrupt is being forced.
What are we testing?
We want to set up the state of the system, and observe that we can freely change moves.
Sequence of tests
Set up SVC32 mode
CPSR = 0x40000093 (SVC32)
sp_svc = 0x12345678
Dump the registers (they look fine)
Set up UND32 mode
CPSR = 0x4000009b (UND32)
SPSR = 0x40000093 (SVC32)
sp_und = 0xDEAD0000
Dump the registers (they look fine)
Set up USR32 mode
CPSR = 0x40000090 (USR32)
sp_usr = 0x0010000
Dump the registers (they look fine)
Reenter SVC32 mode
CPSR = 0x40000093 (SVC32)
Dump the registers (the mode has not changed - we're still in USR32 with USR32 registers)
What is the problem?
The emulation environment should be able to be manipulated in ways that would not be allowed for the emulated system. This allows the operating system to initialise its state and begin execution of code. At present it is unable to execute any system calls because once in USR32 we can never get out.
Test code
Test code which exhibits this problem:
#!/usr/bin/env python
# Sample code for ARM of Unicorn. Nguyen Anh Quynh <aquynh@gmail.com>
# Python sample ported by Loi Anh Tuan <loianhtuan@gmail.com>
import sys
from unicorn import *
from unicorn.arm_const import *
reg_map = [
UC_ARM_REG_R0,
UC_ARM_REG_R1,
UC_ARM_REG_R2,
UC_ARM_REG_R3,
UC_ARM_REG_R4,
UC_ARM_REG_R5,
UC_ARM_REG_R6,
UC_ARM_REG_R7,
UC_ARM_REG_R8,
UC_ARM_REG_R9,
UC_ARM_REG_R10,
UC_ARM_REG_R11,
UC_ARM_REG_R12,
UC_ARM_REG_SP,
UC_ARM_REG_LR,
UC_ARM_REG_PC,
]
arm_names = [
'r0', 'r1', 'r2', 'r3',
'r4', 'r5', 'r6', 'r7',
'r8', 'r9', 'r10', 'r11',
'r12', 'sp', 'lr', 'pc'
]
def dump_registers(uc):
print("Registers: ")
for rn in range(0, 16):
value = uc.reg_read(reg_map[rn])
sys.stdout.write(" %3s : 0x%08x" % (arm_names[rn], value))
if rn % 4 == 3:
sys.stdout.write("\n")
print(" CPSR = 0x{:08x}".format(uc.reg_read(UC_ARM_REG_CPSR)))
print(" SPSR = 0x{:08x}".format(uc.reg_read(UC_ARM_REG_SPSR)))
# Test ARM
def test_arm():
print("Testing under Unicorn : {!r}".format(uc_version()))
print("Header version: {!r}".format((UC_VERSION_MAJOR, UC_VERSION_MINOR, UC_VERSION_EXTRA)))
print("Changing ARM modes")
try:
# Initialize emulator in ARM mode
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# initialize machine registers in different modes
mu.reg_write(UC_ARM_REG_CPSR, 0x40000093) # Current mode = SVC32 mode
mu.reg_write(UC_ARM_REG_R13, 0x12345678) # SVC stack value
print("--- Should be in SVC32, with R13 = 0x12345678")
dump_registers(mu)
mu.reg_write(UC_ARM_REG_CPSR, 0x4000009b) # Current mode = UND32 mode
mu.reg_write(UC_ARM_REG_SPSR, 0x40000093) # Saved mode = SVC32 mode
mu.reg_write(UC_ARM_REG_R13, 0xDEAD0000) # UND stack value
print("--- Should be in UND32, with R13 = 0xDEAD0000")
dump_registers(mu)
mu.reg_write(UC_ARM_REG_CPSR, 0x40000090) # Current mode = USR32 mode
mu.reg_write(UC_ARM_REG_R13, 0x0010000) # USR stack value
print("--- Should be in UND32, with R13 = 0x00010000")
dump_registers(mu)
mu.reg_write(UC_ARM_REG_CPSR, 0x40000093) # Current mode = SVC32 mode
print("--- Should be in SVC32, with R13 = 0x12345678")
dump_registers(mu)
except UcError as e:
print("ERROR: %s" % e)
if __name__ == '__main__':
test_arm()
Failing output (Unicorn 2)
This produces the following output on Unicorn 2 (failing output):
Unicorn 2 is failng to allow the transition from USR32 to SVC32 using direct register access. No emulated code is involved - the system state does not change. It is as if the unicorn environment is enforcing the emulated environment's restrictions on the CPSR.
Source environment
I built unicorn on the dev branch at sha 558fb9c155e739444636dfdc434a536cc1a7a79f. This is after the fix for https://github.com/unicorn-engine/unicorn/issues/1494 (which relates to corruption of banked registers on a mode change). This has been tested on OSX system on Intel hardware (10.14.6)
Failure mode
The failures appears to be that once you are in USR32, an unprivileged, you are unable programatically to enter a privileged mode. This is necessary for a number of reasons, not least of which is providing entry to SVC32 when a SWI call is made, or IRQ32 when an interrupt is being forced.
What are we testing?
We want to set up the state of the system, and observe that we can freely change moves.
Sequence of tests
What is the problem?
The emulation environment should be able to be manipulated in ways that would not be allowed for the emulated system. This allows the operating system to initialise its state and begin execution of code. At present it is unable to execute any system calls because once in USR32 we can never get out.
Test code
Test code which exhibits this problem:
Failing output (Unicorn 2)
This produces the following output on Unicorn 2 (failing output):
The final register dump should have sp = 0x12345678, and CPSR = 0x40000093
Successful output (Unicorn 1)