1""" 2Test that breakpoint by symbol name works correctly with dynamic libs. 3""" 4 5from __future__ import print_function 6 7 8import os 9import re 10import lldb 11from lldbsuite.test.decorators import * 12from lldbsuite.test.lldbtest import * 13from lldbsuite.test import lldbutil 14 15 16@skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently 17class LoadUnloadTestCase(TestBase): 18 19 mydir = TestBase.compute_mydir(__file__) 20 21 NO_DEBUG_INFO_TESTCASE = True 22 23 def setUp(self): 24 # Call super's setUp(). 25 TestBase.setUp(self) 26 self.setup_test() 27 # Invoke the default build rule. 28 self.build() 29 # Find the line number to break for main.cpp. 30 self.line = line_number( 31 'main.cpp', 32 '// Set break point at this line for test_lldb_process_load_and_unload_commands().') 33 self.line_d_function = line_number( 34 'd.cpp', '// Find this line number within d_dunction().') 35 36 def setup_test(self): 37 lldbutil.mkdir_p(self.getBuildArtifact("hidden")) 38 if not self.platformIsDarwin(): 39 if not lldb.remote_platform and "LD_LIBRARY_PATH" in os.environ: 40 self.runCmd( 41 "settings set target.env-vars " + 42 self.dylibPath + 43 "=" + 44 os.environ["LD_LIBRARY_PATH"] + 45 ":" + 46 self.getBuildDir()) 47 else: 48 if lldb.remote_platform: 49 wd = lldb.remote_platform.GetWorkingDirectory() 50 else: 51 wd = self.getBuildDir() 52 self.runCmd( 53 "settings set target.env-vars " + 54 self.dylibPath + 55 "=" + 56 wd) 57 58 def copy_shlibs_to_remote(self, hidden_dir=False): 59 """ Copies the shared libs required by this test suite to remote. 60 Does nothing in case of non-remote platforms. 61 """ 62 if lldb.remote_platform: 63 ext = 'so' 64 if self.platformIsDarwin(): 65 ext = 'dylib' 66 67 shlibs = ['libloadunload_a.' + ext, 'libloadunload_b.' + ext, 68 'libloadunload_c.' + ext, 'libloadunload_d.' + ext] 69 wd = lldb.remote_platform.GetWorkingDirectory() 70 cwd = os.getcwd() 71 for f in shlibs: 72 err = lldb.remote_platform.Put( 73 lldb.SBFileSpec(self.getBuildArtifact(f)), 74 lldb.SBFileSpec(os.path.join(wd, f))) 75 if err.Fail(): 76 raise RuntimeError( 77 "Unable copy '%s' to '%s'.\n>>> %s" % 78 (f, wd, err.GetCString())) 79 if hidden_dir: 80 shlib = 'libloadunload_d.' + ext 81 hidden_dir = os.path.join(wd, 'hidden') 82 hidden_file = os.path.join(hidden_dir, shlib) 83 err = lldb.remote_platform.MakeDirectory(hidden_dir) 84 if err.Fail(): 85 raise RuntimeError( 86 "Unable to create a directory '%s'." % hidden_dir) 87 err = lldb.remote_platform.Put( 88 lldb.SBFileSpec(os.path.join('hidden', shlib)), 89 lldb.SBFileSpec(hidden_file)) 90 if err.Fail(): 91 raise RuntimeError( 92 "Unable copy 'libloadunload_d.so' to '%s'.\n>>> %s" % 93 (wd, err.GetCString())) 94 95 def setSvr4Support(self, enabled): 96 self.runCmd( 97 "settings set plugin.process.gdb-remote.use-libraries-svr4 {enabled}".format( 98 enabled="true" if enabled else "false" 99 ) 100 ) 101 102 # libloadunload_d.so does not appear in the image list because executable 103 # dependencies are resolved relative to the debuggers PWD. Bug? 104 @expectedFailureAll(oslist=["linux"]) 105 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 106 @not_remote_testsuite_ready 107 @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently 108 @expectedFailureNetBSD 109 def test_modules_search_paths(self): 110 """Test target modules list after loading a different copy of the library libd.dylib, and verifies that it works with 'target modules search-paths add'.""" 111 if self.platformIsDarwin(): 112 dylibName = 'libloadunload_d.dylib' 113 else: 114 dylibName = 'libloadunload_d.so' 115 116 # The directory with the dynamic library we did not link to. 117 new_dir = os.path.join(self.getBuildDir(), "hidden") 118 119 old_dylib = os.path.join(self.getBuildDir(), dylibName) 120 new_dylib = os.path.join(new_dir, dylibName) 121 exe = self.getBuildArtifact("a.out") 122 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 123 124 self.expect("target modules list", 125 substrs=[old_dylib]) 126 # self.expect("target modules list -t 3", 127 # patterns = ["%s-[^-]*-[^-]*" % self.getArchitecture()]) 128 # Add an image search path substitution pair. 129 self.runCmd( 130 "target modules search-paths add %s %s" % 131 (self.getBuildDir(), new_dir)) 132 133 self.expect("target modules search-paths list", 134 substrs=[self.getBuildDir(), new_dir]) 135 136 self.expect( 137 "target modules search-paths query %s" % 138 self.getBuildDir(), 139 "Image search path successfully transformed", 140 substrs=[new_dir]) 141 142 # Obliterate traces of libd from the old location. 143 os.remove(old_dylib) 144 # Inform (DY)LD_LIBRARY_PATH of the new path, too. 145 env_cmd_string = "settings set target.env-vars " + self.dylibPath + "=" + new_dir 146 if self.TraceOn(): 147 print("Set environment to: ", env_cmd_string) 148 self.runCmd(env_cmd_string) 149 self.runCmd("settings show target.env-vars") 150 151 remove_dyld_path_cmd = "settings remove target.env-vars " + self.dylibPath 152 self.addTearDownHook( 153 lambda: self.dbg.HandleCommand(remove_dyld_path_cmd)) 154 155 self.runCmd("run") 156 157 self.expect( 158 "target modules list", 159 "LLDB successfully locates the relocated dynamic library", 160 substrs=[new_dylib]) 161 162 # libloadunload_d.so does not appear in the image list because executable 163 # dependencies are resolved relative to the debuggers PWD. Bug? 164 @expectedFailureAll(oslist=["linux"]) 165 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 166 @expectedFailureAndroid # wrong source file shows up for hidden library 167 @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently 168 @skipIfDarwinEmbedded 169 @expectedFailureNetBSD 170 def test_dyld_library_path(self): 171 """Test (DY)LD_LIBRARY_PATH after moving libd.dylib, which defines d_function, somewhere else.""" 172 self.copy_shlibs_to_remote(hidden_dir=True) 173 174 exe = self.getBuildArtifact("a.out") 175 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 176 177 # Shut off ANSI color usage so we don't get ANSI escape sequences 178 # mixed in with stop locations. 179 self.dbg.SetUseColor(False) 180 181 if self.platformIsDarwin(): 182 dylibName = 'libloadunload_d.dylib' 183 dsymName = 'libloadunload_d.dylib.dSYM' 184 else: 185 dylibName = 'libloadunload_d.so' 186 187 # The directory to relocate the dynamic library and its debugging info. 188 special_dir = "hidden" 189 if lldb.remote_platform: 190 wd = lldb.remote_platform.GetWorkingDirectory() 191 else: 192 wd = self.getBuildDir() 193 194 old_dir = wd 195 new_dir = os.path.join(wd, special_dir) 196 old_dylib = os.path.join(old_dir, dylibName) 197 198 remove_dyld_path_cmd = "settings remove target.env-vars " \ 199 + self.dylibPath 200 self.addTearDownHook( 201 lambda: self.dbg.HandleCommand(remove_dyld_path_cmd)) 202 203 # For now we don't track (DY)LD_LIBRARY_PATH, so the old 204 # library will be in the modules list. 205 self.expect("target modules list", 206 substrs=[os.path.basename(old_dylib)], 207 matching=True) 208 209 lldbutil.run_break_set_by_file_and_line( 210 self, "d.cpp", self.line_d_function, num_expected_locations=1) 211 # After run, make sure the non-hidden library is picked up. 212 self.expect("run", substrs=["return", "700"]) 213 214 self.runCmd("continue") 215 216 # Add the hidden directory first in the search path. 217 env_cmd_string = ("settings set target.env-vars %s=%s" % 218 (self.dylibPath, new_dir)) 219 if not self.platformIsDarwin(): 220 env_cmd_string += ":" + wd 221 self.runCmd(env_cmd_string) 222 223 # This time, the hidden library should be picked up. 224 self.expect("run", substrs=["return", "12345"]) 225 226 @expectedFailureAll( 227 bugnumber="llvm.org/pr25805", 228 hostoslist=["windows"], 229 triple='.*-android') 230 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 231 @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently 232 def test_lldb_process_load_and_unload_commands(self): 233 self.setSvr4Support(False) 234 self.run_lldb_process_load_and_unload_commands() 235 236 @expectedFailureAll( 237 bugnumber="llvm.org/pr25805", 238 hostoslist=["windows"], 239 triple='.*-android') 240 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 241 @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently 242 def test_lldb_process_load_and_unload_commands_with_svr4(self): 243 self.setSvr4Support(True) 244 self.run_lldb_process_load_and_unload_commands() 245 246 def run_lldb_process_load_and_unload_commands(self): 247 """Test that lldb process load/unload command work correctly.""" 248 self.copy_shlibs_to_remote() 249 250 exe = self.getBuildArtifact("a.out") 251 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 252 253 # Break at main.cpp before the call to dlopen(). 254 # Use lldb's process load command to load the dylib, instead. 255 256 lldbutil.run_break_set_by_file_and_line( 257 self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) 258 259 self.runCmd("run", RUN_SUCCEEDED) 260 261 ctx = self.platformContext 262 dylibName = ctx.shlib_prefix + 'loadunload_a.' + ctx.shlib_extension 263 localDylibPath = self.getBuildArtifact(dylibName) 264 if lldb.remote_platform: 265 wd = lldb.remote_platform.GetWorkingDirectory() 266 remoteDylibPath = lldbutil.join_remote_paths(wd, dylibName) 267 else: 268 remoteDylibPath = localDylibPath 269 270 # Make sure that a_function does not exist at this point. 271 self.expect( 272 "image lookup -n a_function", 273 "a_function should not exist yet", 274 error=True, 275 matching=False, 276 patterns=["1 match found"]) 277 278 # Use lldb 'process load' to load the dylib. 279 self.expect( 280 "process load %s --install=%s" % (localDylibPath, remoteDylibPath), 281 "%s loaded correctly" % dylibName, 282 patterns=[ 283 'Loading "%s".*ok' % re.escape(localDylibPath), 284 'Image [0-9]+ loaded']) 285 286 # Search for and match the "Image ([0-9]+) loaded" pattern. 287 output = self.res.GetOutput() 288 pattern = re.compile("Image ([0-9]+) loaded") 289 for l in output.split(os.linesep): 290 #print("l:", l) 291 match = pattern.search(l) 292 if match: 293 break 294 index = match.group(1) 295 296 # Now we should have an entry for a_function. 297 self.expect( 298 "image lookup -n a_function", 299 "a_function should now exist", 300 patterns=[ 301 "1 match found .*%s" % 302 dylibName]) 303 304 # Use lldb 'process unload' to unload the dylib. 305 self.expect( 306 "process unload %s" % 307 index, 308 "%s unloaded correctly" % 309 dylibName, 310 patterns=[ 311 "Unloading .* with index %s.*ok" % 312 index]) 313 314 self.runCmd("process continue") 315 316 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 317 def test_load_unload(self): 318 self.setSvr4Support(False) 319 self.run_load_unload() 320 321 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 322 def test_load_unload_with_svr4(self): 323 self.setSvr4Support(True) 324 self.run_load_unload() 325 326 def run_load_unload(self): 327 """Test breakpoint by name works correctly with dlopen'ing.""" 328 self.copy_shlibs_to_remote() 329 330 exe = self.getBuildArtifact("a.out") 331 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 332 333 # Break by function name a_function (not yet loaded). 334 lldbutil.run_break_set_by_symbol( 335 self, "a_function", num_expected_locations=0) 336 337 self.runCmd("run", RUN_SUCCEEDED) 338 339 # The stop reason of the thread should be breakpoint and at a_function. 340 self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, 341 substrs=['stopped', 342 'a_function', 343 'stop reason = breakpoint']) 344 345 # The breakpoint should have a hit count of 1. 346 self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, 347 substrs=[' resolved, hit count = 1']) 348 349 # Issue the 'contnue' command. We should stop agaian at a_function. 350 # The stop reason of the thread should be breakpoint and at a_function. 351 self.runCmd("continue") 352 353 # rdar://problem/8508987 354 # The a_function breakpoint should be encountered twice. 355 self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, 356 substrs=['stopped', 357 'a_function', 358 'stop reason = breakpoint']) 359 360 # The breakpoint should have a hit count of 2. 361 self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, 362 substrs=[' resolved, hit count = 2']) 363 364 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 365 @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently 366 @expectedFailureAll(archs="aarch64", oslist="linux", 367 bugnumber="https://bugs.llvm.org/show_bug.cgi?id=27806") 368 def test_step_over_load(self): 369 self.setSvr4Support(False) 370 self.run_step_over_load() 371 372 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 373 @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently 374 @expectedFailureAll(archs="aarch64", oslist="linux", 375 bugnumber="https://bugs.llvm.org/show_bug.cgi?id=27806") 376 def test_step_over_load_with_svr4(self): 377 self.setSvr4Support(True) 378 self.run_step_over_load() 379 380 def run_step_over_load(self): 381 """Test stepping over code that loads a shared library works correctly.""" 382 self.copy_shlibs_to_remote() 383 384 exe = self.getBuildArtifact("a.out") 385 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 386 387 # Break by function name a_function (not yet loaded). 388 lldbutil.run_break_set_by_file_and_line( 389 self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) 390 391 self.runCmd("run", RUN_SUCCEEDED) 392 393 # The stop reason of the thread should be breakpoint and at a_function. 394 self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, 395 substrs=['stopped', 396 'stop reason = breakpoint']) 397 398 self.runCmd( 399 "thread step-over", 400 "Stepping over function that loads library") 401 402 # The stop reason should be step end. 403 self.expect("thread list", "step over succeeded.", 404 substrs=['stopped', 405 'stop reason = step over']) 406 407 # We can't find a breakpoint location for d_init before launching because 408 # executable dependencies are resolved relative to the debuggers PWD. Bug? 409 @expectedFailureAll(oslist=["linux"], triple=no_match('aarch64-.*-android')) 410 @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support 411 @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently 412 @expectedFailureNetBSD 413 def test_static_init_during_load(self): 414 """Test that we can set breakpoints correctly in static initializers""" 415 self.copy_shlibs_to_remote() 416 417 exe = self.getBuildArtifact("a.out") 418 self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) 419 420 a_init_bp_num = lldbutil.run_break_set_by_symbol( 421 self, "a_init", num_expected_locations=0) 422 b_init_bp_num = lldbutil.run_break_set_by_symbol( 423 self, "b_init", num_expected_locations=0) 424 d_init_bp_num = lldbutil.run_break_set_by_symbol( 425 self, "d_init", num_expected_locations=1) 426 427 self.runCmd("run", RUN_SUCCEEDED) 428 429 self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, 430 substrs=['stopped', 431 'd_init', 432 'stop reason = breakpoint %d' % d_init_bp_num]) 433 434 self.runCmd("continue") 435 self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, 436 substrs=['stopped', 437 'b_init', 438 'stop reason = breakpoint %d' % b_init_bp_num]) 439 self.expect("thread backtrace", 440 substrs=['b_init', 441 'dlopen', 442 'main']) 443 444 self.runCmd("continue") 445 self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, 446 substrs=['stopped', 447 'a_init', 448 'stop reason = breakpoint %d' % a_init_bp_num]) 449 self.expect("thread backtrace", 450 substrs=['a_init', 451 'dlopen', 452 'main']) 453