1import lldb 2import binascii 3import os.path 4from lldbsuite.test.lldbtest import * 5from lldbsuite.test.decorators import * 6from lldbsuite.test.gdbclientutils import * 7from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase 8 9 10class TestGDBRemoteClient(GDBRemoteTestBase): 11 12 class gPacketResponder(MockGDBServerResponder): 13 registers = [ 14 "name:rax;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;ehframe:0;dwarf:0;", 15 "name:rbx;bitsize:64;offset:8;encoding:uint;format:hex;set:General Purpose Registers;ehframe:3;dwarf:3;", 16 "name:rcx;bitsize:64;offset:16;encoding:uint;format:hex;set:General Purpose Registers;ehframe:2;dwarf:2;generic:arg4;", 17 "name:rdx;bitsize:64;offset:24;encoding:uint;format:hex;set:General Purpose Registers;ehframe:1;dwarf:1;generic:arg3;", 18 "name:rdi;bitsize:64;offset:32;encoding:uint;format:hex;set:General Purpose Registers;ehframe:5;dwarf:5;generic:arg1;", 19 "name:rsi;bitsize:64;offset:40;encoding:uint;format:hex;set:General Purpose Registers;ehframe:4;dwarf:4;generic:arg2;", 20 "name:rbp;bitsize:64;offset:48;encoding:uint;format:hex;set:General Purpose Registers;ehframe:6;dwarf:6;generic:fp;", 21 "name:rsp;bitsize:64;offset:56;encoding:uint;format:hex;set:General Purpose Registers;ehframe:7;dwarf:7;generic:sp;", 22 ] 23 24 def qRegisterInfo(self, num): 25 try: 26 return self.registers[num] 27 except IndexError: 28 return "E45" 29 30 def readRegisters(self): 31 return len(self.registers) * 16 * '0' 32 33 def readRegister(self, register): 34 return "0000000000000000" 35 36 def test_connect(self): 37 """Test connecting to a remote gdb server""" 38 target = self.createTarget("a.yaml") 39 process = self.connect(target) 40 self.assertPacketLogContains(["qProcessInfo", "qfThreadInfo"]) 41 42 def test_attach_fail(self): 43 error_msg = "mock-error-msg" 44 45 class MyResponder(MockGDBServerResponder): 46 # Pretend we don't have any process during the initial queries. 47 def qC(self): 48 return "E42" 49 50 def qfThreadInfo(self): 51 return "OK" # No threads. 52 53 # Then, when we are asked to attach, error out. 54 def vAttach(self, pid): 55 return "E42;" + binascii.hexlify(error_msg.encode()).decode() 56 57 self.server.responder = MyResponder() 58 59 target = self.dbg.CreateTarget("") 60 process = self.connect(target) 61 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected]) 62 63 error = lldb.SBError() 64 target.AttachToProcessWithID(lldb.SBListener(), 47, error) 65 self.assertEquals(error_msg, error.GetCString()) 66 67 def test_launch_fail(self): 68 class MyResponder(MockGDBServerResponder): 69 # Pretend we don't have any process during the initial queries. 70 def qC(self): 71 return "E42" 72 73 def qfThreadInfo(self): 74 return "OK" # No threads. 75 76 # Then, when we are asked to attach, error out. 77 def A(self, packet): 78 return "E47" 79 80 self.server.responder = MyResponder() 81 82 target = self.createTarget("a.yaml") 83 process = self.connect(target) 84 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, [lldb.eStateConnected]) 85 86 error = lldb.SBError() 87 target.Launch(lldb.SBListener(), None, None, None, None, None, 88 None, 0, True, error) 89 self.assertEquals("'A' packet returned an error: 71", error.GetCString()) 90 91 def test_read_registers_using_g_packets(self): 92 """Test reading registers using 'g' packets (default behavior)""" 93 self.dbg.HandleCommand( 94 "settings set plugin.process.gdb-remote.use-g-packet-for-reading true") 95 self.addTearDownHook(lambda: 96 self.runCmd("settings set plugin.process.gdb-remote.use-g-packet-for-reading false")) 97 self.server.responder = self.gPacketResponder() 98 target = self.createTarget("a.yaml") 99 process = self.connect(target) 100 101 self.assertEquals(1, self.server.responder.packetLog.count("g")) 102 self.server.responder.packetLog = [] 103 self.read_registers(process) 104 # Reading registers should not cause any 'p' packets to be exchanged. 105 self.assertEquals( 106 0, len([p for p in self.server.responder.packetLog if p.startswith("p")])) 107 108 def test_read_registers_using_p_packets(self): 109 """Test reading registers using 'p' packets""" 110 self.dbg.HandleCommand( 111 "settings set plugin.process.gdb-remote.use-g-packet-for-reading false") 112 self.server.responder = self.gPacketResponder() 113 target = self.createTarget("a.yaml") 114 process = self.connect(target) 115 116 self.read_registers(process) 117 self.assertNotIn("g", self.server.responder.packetLog) 118 self.assertGreater( 119 len([p for p in self.server.responder.packetLog if p.startswith("p")]), 0) 120 121 def test_write_registers_using_P_packets(self): 122 """Test writing registers using 'P' packets (default behavior)""" 123 self.server.responder = self.gPacketResponder() 124 target = self.createTarget("a.yaml") 125 process = self.connect(target) 126 127 self.write_registers(process) 128 self.assertEquals(0, len( 129 [p for p in self.server.responder.packetLog if p.startswith("G")])) 130 self.assertGreater( 131 len([p for p in self.server.responder.packetLog if p.startswith("P")]), 0) 132 133 def test_write_registers_using_G_packets(self): 134 """Test writing registers using 'G' packets""" 135 136 class MyResponder(self.gPacketResponder): 137 def readRegister(self, register): 138 # empty string means unsupported 139 return "" 140 141 self.server.responder = MyResponder() 142 target = self.createTarget("a.yaml") 143 process = self.connect(target) 144 145 self.write_registers(process) 146 self.assertEquals(0, len( 147 [p for p in self.server.responder.packetLog if p.startswith("P")])) 148 self.assertGreater(len( 149 [p for p in self.server.responder.packetLog if p.startswith("G")]), 0) 150 151 def read_registers(self, process): 152 self.for_each_gpr( 153 process, lambda r: self.assertEquals("0x0000000000000000", r.GetValue())) 154 155 def write_registers(self, process): 156 self.for_each_gpr( 157 process, lambda r: r.SetValueFromCString("0x0000000000000000")) 158 159 def for_each_gpr(self, process, operation): 160 registers = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters() 161 self.assertGreater(registers.GetSize(), 0) 162 regSet = registers[0] 163 numChildren = regSet.GetNumChildren() 164 self.assertGreater(numChildren, 0) 165 for i in range(numChildren): 166 operation(regSet.GetChildAtIndex(i)) 167 168 def test_launch_A(self): 169 class MyResponder(MockGDBServerResponder): 170 def __init__(self, *args, **kwargs): 171 self.started = False 172 return super().__init__(*args, **kwargs) 173 174 def qC(self): 175 if self.started: 176 return "QCp10.10" 177 else: 178 return "E42" 179 180 def qfThreadInfo(self): 181 if self.started: 182 return "mp10.10" 183 else: 184 return "E42" 185 186 def qsThreadInfo(self): 187 return "l" 188 189 def A(self, packet): 190 self.started = True 191 return "OK" 192 193 def qLaunchSuccess(self): 194 if self.started: 195 return "OK" 196 return "E42" 197 198 self.server.responder = MyResponder() 199 200 target = self.createTarget("a.yaml") 201 # NB: apparently GDB packets are using "/" on Windows too 202 exe_path = self.getBuildArtifact("a").replace(os.path.sep, '/') 203 exe_hex = binascii.b2a_hex(exe_path.encode()).decode() 204 process = self.connect(target) 205 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, 206 [lldb.eStateConnected]) 207 208 target.Launch(lldb.SBListener(), 209 ["arg1", "arg2", "arg3"], # argv 210 [], # envp 211 None, # stdin_path 212 None, # stdout_path 213 None, # stderr_path 214 None, # working_directory 215 0, # launch_flags 216 True, # stop_at_entry 217 lldb.SBError()) # error 218 self.assertTrue(process, PROCESS_IS_VALID) 219 self.assertEqual(process.GetProcessID(), 16) 220 221 self.assertPacketLogContains([ 222 "A%d,0,%s,8,1,61726731,8,2,61726732,8,3,61726733" % ( 223 len(exe_hex), exe_hex), 224 ]) 225 226 def test_launch_vRun(self): 227 class MyResponder(MockGDBServerResponder): 228 def __init__(self, *args, **kwargs): 229 self.started = False 230 return super().__init__(*args, **kwargs) 231 232 def qC(self): 233 if self.started: 234 return "QCp10.10" 235 else: 236 return "E42" 237 238 def qfThreadInfo(self): 239 if self.started: 240 return "mp10.10" 241 else: 242 return "E42" 243 244 def qsThreadInfo(self): 245 return "l" 246 247 def vRun(self, packet): 248 self.started = True 249 return "T13" 250 251 def A(self, packet): 252 return "E28" 253 254 self.server.responder = MyResponder() 255 256 target = self.createTarget("a.yaml") 257 # NB: apparently GDB packets are using "/" on Windows too 258 exe_path = self.getBuildArtifact("a").replace(os.path.sep, '/') 259 exe_hex = binascii.b2a_hex(exe_path.encode()).decode() 260 process = self.connect(target) 261 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, 262 [lldb.eStateConnected]) 263 264 process = target.Launch(lldb.SBListener(), 265 ["arg1", "arg2", "arg3"], # argv 266 [], # envp 267 None, # stdin_path 268 None, # stdout_path 269 None, # stderr_path 270 None, # working_directory 271 0, # launch_flags 272 True, # stop_at_entry 273 lldb.SBError()) # error 274 self.assertTrue(process, PROCESS_IS_VALID) 275 self.assertEqual(process.GetProcessID(), 16) 276 277 self.assertPacketLogContains([ 278 "vRun;%s;61726731;61726732;61726733" % (exe_hex,) 279 ]) 280 281 def test_launch_QEnvironment(self): 282 class MyResponder(MockGDBServerResponder): 283 def qC(self): 284 return "E42" 285 286 def qfThreadInfo(self): 287 return "E42" 288 289 def vRun(self, packet): 290 self.started = True 291 return "E28" 292 293 self.server.responder = MyResponder() 294 295 target = self.createTarget("a.yaml") 296 process = self.connect(target) 297 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, 298 [lldb.eStateConnected]) 299 300 target.Launch(lldb.SBListener(), 301 [], # argv 302 ["PLAIN=foo", 303 "NEEDSENC=frob$", 304 "NEEDSENC2=fr*ob", 305 "NEEDSENC3=fro}b", 306 "NEEDSENC4=f#rob", 307 "EQUALS=foo=bar", 308 ], # envp 309 None, # stdin_path 310 None, # stdout_path 311 None, # stderr_path 312 None, # working_directory 313 0, # launch_flags 314 True, # stop_at_entry 315 lldb.SBError()) # error 316 317 self.assertPacketLogContains([ 318 "QEnvironment:PLAIN=foo", 319 "QEnvironmentHexEncoded:4e45454453454e433d66726f6224", 320 "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62", 321 "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62", 322 "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62", 323 "QEnvironment:EQUALS=foo=bar", 324 ]) 325 326 def test_launch_QEnvironmentHexEncoded_only(self): 327 class MyResponder(MockGDBServerResponder): 328 def qC(self): 329 return "E42" 330 331 def qfThreadInfo(self): 332 return "E42" 333 334 def vRun(self, packet): 335 self.started = True 336 return "E28" 337 338 def QEnvironment(self, packet): 339 return "" 340 341 self.server.responder = MyResponder() 342 343 target = self.createTarget("a.yaml") 344 process = self.connect(target) 345 lldbutil.expect_state_changes(self, self.dbg.GetListener(), process, 346 [lldb.eStateConnected]) 347 348 target.Launch(lldb.SBListener(), 349 [], # argv 350 ["PLAIN=foo", 351 "NEEDSENC=frob$", 352 "NEEDSENC2=fr*ob", 353 "NEEDSENC3=fro}b", 354 "NEEDSENC4=f#rob", 355 "EQUALS=foo=bar", 356 ], # envp 357 None, # stdin_path 358 None, # stdout_path 359 None, # stderr_path 360 None, # working_directory 361 0, # launch_flags 362 True, # stop_at_entry 363 lldb.SBError()) # error 364 365 self.assertPacketLogContains([ 366 "QEnvironmentHexEncoded:504c41494e3d666f6f", 367 "QEnvironmentHexEncoded:4e45454453454e433d66726f6224", 368 "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62", 369 "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62", 370 "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62", 371 "QEnvironmentHexEncoded:455155414c533d666f6f3d626172", 372 ]) 373 374 def test_detach_no_multiprocess(self): 375 class MyResponder(MockGDBServerResponder): 376 def __init__(self): 377 super().__init__() 378 self.detached = None 379 380 def qfThreadInfo(self): 381 return "10200" 382 383 def D(self, packet): 384 self.detached = packet 385 return "OK" 386 387 self.server.responder = MyResponder() 388 target = self.dbg.CreateTarget('') 389 process = self.connect(target) 390 process.Detach() 391 self.assertEqual(self.server.responder.detached, "D") 392 393 def test_detach_pid(self): 394 class MyResponder(MockGDBServerResponder): 395 def __init__(self, test_case): 396 super().__init__() 397 self.test_case = test_case 398 self.detached = None 399 400 def qSupported(self, client_supported): 401 self.test_case.assertIn("multiprocess+", client_supported) 402 return "multiprocess+;" + super().qSupported(client_supported) 403 404 def qfThreadInfo(self): 405 return "mp400.10200" 406 407 def D(self, packet): 408 self.detached = packet 409 return "OK" 410 411 self.server.responder = MyResponder(self) 412 target = self.dbg.CreateTarget('') 413 process = self.connect(target) 414 process.Detach() 415 self.assertRegex(self.server.responder.detached, r"D;0*400") 416 417 def test_signal_gdb(self): 418 class MyResponder(MockGDBServerResponder): 419 def qSupported(self, client_supported): 420 return "PacketSize=3fff;QStartNoAckMode+" 421 422 def haltReason(self): 423 return "S0a" 424 425 def cont(self): 426 return self.haltReason() 427 428 self.server.responder = MyResponder() 429 430 self.runCmd("platform select remote-linux") 431 target = self.createTarget("a.yaml") 432 process = self.connect(target) 433 434 self.assertEqual(process.threads[0].GetStopReason(), 435 lldb.eStopReasonSignal) 436 self.assertEqual(process.threads[0].GetStopDescription(100), 437 'signal SIGBUS') 438 439 def test_signal_lldb_old(self): 440 class MyResponder(MockGDBServerResponder): 441 def qSupported(self, client_supported): 442 return "PacketSize=3fff;QStartNoAckMode+" 443 444 def qHostInfo(self): 445 return "triple:61726d76372d756e6b6e6f776e2d6c696e75782d676e75;" 446 447 def QThreadSuffixSupported(self): 448 return "OK" 449 450 def haltReason(self): 451 return "S0a" 452 453 def cont(self): 454 return self.haltReason() 455 456 self.server.responder = MyResponder() 457 458 self.runCmd("platform select remote-linux") 459 target = self.createTarget("a.yaml") 460 process = self.connect(target) 461 462 self.assertEqual(process.threads[0].GetStopReason(), 463 lldb.eStopReasonSignal) 464 self.assertEqual(process.threads[0].GetStopDescription(100), 465 'signal SIGUSR1') 466 467 def test_signal_lldb(self): 468 class MyResponder(MockGDBServerResponder): 469 def qSupported(self, client_supported): 470 return "PacketSize=3fff;QStartNoAckMode+;native-signals+" 471 472 def qHostInfo(self): 473 return "triple:61726d76372d756e6b6e6f776e2d6c696e75782d676e75;" 474 475 def haltReason(self): 476 return "S0a" 477 478 def cont(self): 479 return self.haltReason() 480 481 self.server.responder = MyResponder() 482 483 self.runCmd("platform select remote-linux") 484 target = self.createTarget("a.yaml") 485 process = self.connect(target) 486 487 self.assertEqual(process.threads[0].GetStopReason(), 488 lldb.eStopReasonSignal) 489 self.assertEqual(process.threads[0].GetStopDescription(100), 490 'signal SIGUSR1') 491 492 def do_siginfo_test(self, platform, target_yaml, raw_data, expected): 493 class MyResponder(MockGDBServerResponder): 494 def qSupported(self, client_supported): 495 return "PacketSize=3fff;QStartNoAckMode+;qXfer:siginfo:read+" 496 497 def qXferRead(self, obj, annex, offset, length): 498 if obj == "siginfo": 499 return raw_data, False 500 else: 501 return None, False 502 503 def haltReason(self): 504 return "T02" 505 506 def cont(self): 507 return self.haltReason() 508 509 self.server.responder = MyResponder() 510 511 self.runCmd("platform select " + platform) 512 target = self.createTarget(target_yaml) 513 process = self.connect(target) 514 515 siginfo = process.threads[0].GetSiginfo() 516 self.assertSuccess(siginfo.GetError()) 517 518 for key, value in expected.items(): 519 self.assertEqual(siginfo.GetValueForExpressionPath("." + key) 520 .GetValueAsUnsigned(), 521 value) 522 523 524 def test_siginfo_linux_amd64(self): 525 data = ( 526 # si_signo si_errno si_code 527 "\x11\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" 528 # __pad0 si_pid si_uid 529 "\x00\x00\x00\x00\xbf\xf7\x0b\x00\xe8\x03\x00\x00" 530 # si_status 531 "\x0c\x00\x00\x00" + "\x00" * 100) 532 expected = { 533 "si_signo": 17, # SIGCHLD 534 "si_errno": 0, 535 "si_code": 1, # CLD_EXITED 536 "_sifields._sigchld.si_pid": 784319, 537 "_sifields._sigchld.si_uid": 1000, 538 "_sifields._sigchld.si_status": 12, 539 "_sifields._sigchld.si_utime": 0, 540 "_sifields._sigchld.si_stime": 0, 541 } 542 self.do_siginfo_test("remote-linux", "basic_eh_frame.yaml", 543 data, expected) 544 545 def test_siginfo_linux_i386(self): 546 data = ( 547 # si_signo si_errno si_code 548 "\x11\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" 549 # si_pid si_uid si_status 550 "\x49\x43\x07\x00\xe8\x03\x00\x00\x0c\x00\x00\x00" 551 + "\x00" * 104) 552 expected = { 553 "si_signo": 17, # SIGCHLD 554 "si_errno": 0, 555 "si_code": 1, # CLD_EXITED 556 "_sifields._sigchld.si_pid": 475977, 557 "_sifields._sigchld.si_uid": 1000, 558 "_sifields._sigchld.si_status": 12, 559 "_sifields._sigchld.si_utime": 0, 560 "_sifields._sigchld.si_stime": 0, 561 } 562 self.do_siginfo_test("remote-linux", "basic_eh_frame-i386.yaml", 563 data, expected) 564 565 def test_siginfo_freebsd_amd64(self): 566 data = ( 567 # si_signo si_errno si_code 568 "\x0b\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00" 569 # si_pid si_uid si_status 570 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 571 # si_addr 572 "\x76\x98\xba\xdc\xfe\x00\x00\x00" 573 # si_status si_trapno 574 "\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00" 575 + "\x00" * 36) 576 577 expected = { 578 "si_signo": 11, # SIGSEGV 579 "si_errno": 0, 580 "si_code": 1, # SEGV_MAPERR 581 "si_addr": 0xfedcba9876, 582 "_reason._fault._trapno": 12, 583 } 584 self.do_siginfo_test("remote-freebsd", "basic_eh_frame.yaml", 585 data, expected) 586