import gdb_rsp
+if sys.platform == 'win32':
+ RETURNCODE_KILL = -9
+else:
+ RETURNCODE_KILL = -9 & 0xff
+
+
+NACL_SIGILL = 4
NACL_SIGTRAP = 5
NACL_SIGSEGV = 11
+ [('cpsr', 'I')])
+MIPS_REG_DEFS = [
+ ('zero', 'I'),
+ ('at', 'I'),
+ ('v0', 'I'),
+ ('v1', 'I'),
+ ('a0', 'I'),
+ ('a1', 'I'),
+ ('a2', 'I'),
+ ('a3', 'I'),
+ ('t0', 'I'),
+ ('t1', 'I'),
+ ('t2', 'I'),
+ ('t3', 'I'),
+ ('t4', 'I'),
+ ('t5', 'I'),
+ ('t6', 'I'),
+ ('t7', 'I'),
+ ('s0', 'I'),
+ ('s1', 'I'),
+ ('s2', 'I'),
+ ('s3', 'I'),
+ ('s4', 'I'),
+ ('s5', 'I'),
+ ('s6', 'I'),
+ ('s7', 'I'),
+ ('t8', 'I'),
+ ('t9', 'I'),
+ ('k0', 'I'),
+ ('k1', 'I'),
+ ('global_ptr', 'I'),
+ ('stack_ptr', 'I'),
+ ('frame_ptr', 'I'),
+ ('return_addr', 'I'),
+ ('prog_ctr', 'I'),
+]
+
+
REG_DEFS = {
'x86-32': X86_32_REG_DEFS,
'x86-64': X86_64_REG_DEFS,
'arm': ARM_REG_DEFS,
+ 'mips32': MIPS_REG_DEFS,
}
'x86-32': 'esp',
'x86-64': 'rsp',
'arm': 'r13',
+ 'mips32': 'stack_ptr',
}
'x86-32': 'eip',
'x86-64': 'rip',
'arm': 'r15',
+ 'mips32': 'prog_ctr',
}
(1<<19) | (1<<18) | (1<<17) | (1<<16)) # GE bits
+def UsingQemu():
+ return SEL_LDR_COMMAND[0].endswith('/run_under_qemu_arm')
+
+
def DecodeRegs(reply):
defs = REG_DEFS[ARCH]
names = [reg_name for reg_name, reg_fmt in defs]
def KillProcess(process):
+ if process.returncode is not None:
+ # kill() won't work if we've already wait()'ed on the process.
+ return
try:
process.kill()
except OSError:
return struct.unpack('I', ReadMemory(connection, address, 4))[0]
+def SingleSteppingWorks():
+ # Single-stepping is not yet supported on ARM and MIPS.
+ # TODO(eaeltsin):
+ # http://code.google.com/p/nativeclient/issues/detail?id=2911
+ return ARCH in ('x86-32', 'x86-64')
+
+
class DebugStubTest(unittest.TestCase):
def test_initial_breakpoint(self):
self.assertEquals(registers['r14'], 0xe000000f)
self.assertEquals(registers['cpsr'] & ARM_USER_CPSR_FLAGS_MASK,
(1 << 29) | (1 << 27))
+ elif ARCH == 'mips32':
+ # We skip zero register because it cannot be set.
+ self.assertEquals(registers['at'], 0x11000220)
+ self.assertEquals(registers['v0'], 0x22000330)
+ self.assertEquals(registers['v1'], 0x33000440)
+ self.assertEquals(registers['a0'], 0x44000550)
+ self.assertEquals(registers['a1'], 0x55000660)
+ self.assertEquals(registers['a2'], 0x66000770)
+ self.assertEquals(registers['a3'], 0x77000880)
+ self.assertEquals(registers['t0'], 0x88000990)
+ self.assertEquals(registers['t1'], 0x99000aa0)
+ self.assertEquals(registers['t2'], 0xaa000bb0)
+ self.assertEquals(registers['t3'], 0xbb000cc0)
+ self.assertEquals(registers['t4'], 0xcc000dd0)
+ self.assertEquals(registers['t5'], 0xdd000ee0)
+ self.assertEquals(registers['t6'], 0x0ffffff0)
+ self.assertEquals(registers['t7'], 0x3fffffff)
+ # Skip t8 because it cannot be set by untrusted code.
+ self.assertEquals(registers['s0'], 0x11100222)
+ self.assertEquals(registers['s1'], 0x22200333)
+ self.assertEquals(registers['s2'], 0x33300444)
+ self.assertEquals(registers['s3'], 0x44400555)
+ self.assertEquals(registers['s4'], 0x55500666)
+ self.assertEquals(registers['s5'], 0x66600777)
+ self.assertEquals(registers['s6'], 0x77700888)
+ self.assertEquals(registers['s7'], 0x88800999)
+ self.assertEquals(registers['t9'], 0xaaa00bbb)
+ # Skip k0 and k1 registers, since they can be changed by kernel.
+ self.assertEquals(registers['global_ptr'], 0xddd00eee)
+ self.assertEquals(registers['stack_ptr'], 0x2ee00fff)
+ self.assertEquals(registers['frame_ptr'], 0xfff00000)
+ self.assertEquals(registers['return_addr'], 0x0a0a0a0a)
else:
raise AssertionError('Unknown architecture')
reg_name = 'rdx'
elif ARCH == 'arm':
reg_name = 'r0'
+ elif ARCH == 'mips32':
+ reg_name = 'a0'
else:
raise AssertionError('Unknown architecture')
sample_read_only_regs = ['r15', 'cs', 'ds']
elif ARCH == 'arm':
sample_read_only_regs = []
+ elif ARCH == 'mips32':
+ sample_read_only_regs = ['zero']
else:
raise AssertionError('Unknown architecture')
# breakpoint set at its start address.
reply = connection.RspRequest('c')
if ARCH == 'arm':
+ AssertReplySignal(reply, NACL_SIGILL)
+ elif ARCH == 'mips32':
# The process should have stopped on a BKPT instruction.
AssertReplySignal(reply, NACL_SIGTRAP)
else:
self.CheckReadOnlyRegisters(connection)
def test_jump_to_address_zero(self):
+ if UsingQemu():
+ # This test hangs under qemu-arm.
+ return
with LaunchDebugStub('test_jump_to_address_zero') as connection:
# Continue from initial breakpoint.
reply = connection.RspRequest('c')
self.assertEqual(regs['eflags'] & X86_TRAP_FLAG, 0)
def test_single_step(self):
- if ARCH == 'arm':
- # Skip this test because single-stepping is not supported on ARM.
- # TODO(eaeltsin):
- # http://code.google.com/p/nativeclient/issues/detail?id=2911
+ if not SingleSteppingWorks():
return
with LaunchDebugStub('test_single_step') as connection:
# We expect test_single_step() to stop at a HLT instruction.
def test_vCont(self):
# Basically repeat test_single_step, but using vCont commands.
- if ARCH == 'arm':
- # Skip this test because single-stepping is not supported on ARM.
- # TODO(eaeltsin):
- # http://code.google.com/p/nativeclient/issues/detail?id=2911
+ if not SingleSteppingWorks():
return
with LaunchDebugStub('test_single_step') as connection:
# Test if vCont is supported.
self.assertTrue(reply.startswith('E'))
def test_interrupt(self):
- if ARCH == 'arm':
- # Skip this test because single-stepping is not supported on ARM.
- # TODO(eaeltsin):
- # http://code.google.com/p/nativeclient/issues/detail?id=2911
+ if not SingleSteppingWorks():
return
func_addr = GetSymbols()['test_interrupt']
with LaunchDebugStub('test_interrupt') as connection:
reply = connection.RspRequest(write_command)
self.assertEquals(reply, 'E03')
+ def test_kill(self):
+ sel_ldr = PopenDebugStub('test_exit_code')
+ try:
+ connection = gdb_rsp.GdbRspConnection()
+ # Request killing the target.
+ reply = connection.RspRequest('k')
+ self.assertEquals(reply, 'OK')
+ self.assertEquals(sel_ldr.wait(), RETURNCODE_KILL)
+ finally:
+ KillProcess(sel_ldr)
+
+ def test_detach(self):
+ sel_ldr = PopenDebugStub('test_exit_code')
+ try:
+ connection = gdb_rsp.GdbRspConnection()
+ # Request detaching from the target.
+ # This resumes execution, so we get the nexe's normal exit() status.
+ reply = connection.RspRequest('D')
+ self.assertEquals(reply, 'OK')
+ self.assertEquals(sel_ldr.wait(), 2)
+ finally:
+ KillProcess(sel_ldr)
+
+ def test_disconnect(self):
+ sel_ldr = PopenDebugStub('test_exit_code')
+ try:
+ # Connect and record the instruction pointer.
+ connection = gdb_rsp.GdbRspConnection()
+ # Check something basic responds with sane results.
+ reply = connection.RspRequest('vCont?')
+ self.assertEqual(reply, 'vCont;s;S;c;C')
+ # Store the instruction pointer.
+ registers = DecodeRegs(connection.RspRequest('g'))
+ initial_ip = registers[IP_REG[ARCH]]
+ connection.Close()
+ # Reconnect 5 times.
+ for _ in range(5):
+ connection = gdb_rsp.GdbRspConnection()
+ # Confirm the instruction pointer stays where it was, indicating that
+ # the thread stayed suspended.
+ registers = DecodeRegs(connection.RspRequest('g'))
+ self.assertEquals(registers[IP_REG[ARCH]], initial_ip)
+ connection.Close()
+ finally:
+ KillProcess(sel_ldr)
+
class DebugStubBreakpointTest(unittest.TestCase):
# Skip past the single-byte HLT instruction.
regs[IP_REG[ARCH]] += 1
elif ARCH == 'arm':
- AssertReplySignal(stop_reply, NACL_SIGTRAP)
+ AssertReplySignal(stop_reply, NACL_SIGILL)
bundle_size = 16
assert regs['r15'] % bundle_size == 0, regs['r15']
regs['r15'] += bundle_size
+ elif ARCH == 'mips32':
+ AssertReplySignal(stop_reply, NACL_SIGTRAP)
+ bundle_size = 16
+ assert regs['prog_ctr'] % bundle_size == 0, regs['prog_ctr']
+ regs['prog_ctr'] += bundle_size
else:
raise AssertionError('Unknown architecture')
AssertEquals(connection.RspRequest('G' + EncodeRegs(regs)), 'OK')
return child_thread_id
def test_continuing_thread_with_others_suspended(self):
+ if UsingQemu():
+ # Suspending a running thread doesn't work under qemu-arm, so
+ # disable this test there.
+ return
with LaunchDebugStub('test_suspending_threads') as connection:
symbols = GetSymbols()
child_thread_id = self.WaitForTestThreadsToStart(connection, symbols)
child_thread_val)
def test_single_stepping_thread_with_others_suspended(self):
+ if UsingQemu():
+ # Suspending a running thread doesn't work under qemu-arm, so
+ # disable this test there.
+ return
with LaunchDebugStub('test_suspending_threads') as connection:
symbols = GetSymbols()
child_thread_id = self.WaitForTestThreadsToStart(connection, symbols)