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