diff --git a/lldb/source/Symbol/CompileUnit.cpp b/lldb/source/Symbol/CompileUnit.cpp --- a/lldb/source/Symbol/CompileUnit.cpp +++ b/lldb/source/Symbol/CompileUnit.cpp @@ -335,6 +335,37 @@ } else { line_entry.range.GetBaseAddress().CalculateSymbolContext(&sc, resolve_scope); + // Sometimes debug info is bad and isn't able to resolve the line entry's + // address back to the same compile unit and/or line entry. If the compile + // unit changed, then revert back to just the compile unit and line entry. + // Prior to this fix, the above code might end up not being able to lookup + // the address, and then it would clear compile unit and the line entry in + // the symbol context and the breakpoint would fail to get set even though + // we have a valid line table entry in this compile unit. The address + // lookup can also end up finding another function in another compiler + // unit if the DWARF has overlappging address ranges. So if we end up with + // no compile unit or a different one after the above function call, + // revert back to the same results as if resolve_scope was set exactly to + // eSymbolContextLineEntry. + if (sc.comp_unit != this) { + if (sc.comp_unit == nullptr && sc.module_sp) { + // Only report an error if we don't map back to any compile unit. With + // link time optimizations, the debug info might have many compile + // units that have the same address range due to function outlining + // or other link time optimizations. If the compile unit is NULL, then + // address resolving is completely failing and more deserving of an + // error message the user can see. + sc.module_sp->ReportError( + "unable to resolve a line table file address 0x%" PRIx64 " back " + "to a compile unit, please file a bug and attach the address " + "and file.", line_entry.range.GetBaseAddress().GetFileAddress()); + } + // Revert to equivalent of resolve_scope == eSymbolContextLineEntry. + sc.comp_unit = this; + sc.function = nullptr; + sc.block = nullptr; + sc.line_entry = line_entry; + } } sc_list.Append(sc); diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py --- a/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py +++ b/lldb/test/API/functionalities/breakpoint/breakpoint_command/TestBreakpointCommand.py @@ -106,6 +106,44 @@ 'Incorrectly resolved a breakpoint using full path "%s" with ' 'debug info that has relative path with matching suffix' % (path)) + @skipIf(oslist=["windows"]) + @no_debug_info_test + def test_breakpoints_with_bad_aranges(self): + """ + Test that we can set breakpoints in a file that has an invalid + .debug_aranges. Older versions of LLDB would find a line entry + in the line table and then would use the start address of the line + entry to do an address lookup on the entry from the line table. If + this address to symbol context lookup would fail, due to a bad + .debug_aranges, it would cause the breakpoint to not get resolved. + Verify that even in these conditions we are able to resolve a + breakpoint. + + The "bad_aranges.yaml" contains a line table that is: + + Line table for /tmp/ab/main.cpp in `a.out + 0x0000000100003f94: /tmp/ab/main.cpp:1 + 0x0000000100003fb0: /tmp/ab/main.cpp:2:3 + 0x0000000100003fb8: /tmp/ab/main.cpp:2:3 + + The .debug_aranges has one range for this compile unit that is + invalid: [0x0000000200003f94-0x0000000200003fb8). This will cause + the resolving of the addresses to fail. + """ + src_dir = self.getSourceDir() + yaml_path = os.path.join(src_dir, "bad_aranges.yaml") + yaml_base, ext = os.path.splitext(yaml_path) + obj_path = self.getBuildArtifact("a.out") + self.yaml2obj(yaml_path, obj_path) + + # Create a target with the object file we just created from YAML + target = self.dbg.CreateTarget(obj_path) + src_path = '/tmp/ab/main.cpp' + bkpt = target.BreakpointCreateByLocation(src_path, 2) + self.assertTrue(bkpt.GetNumLocations() > 0, + 'Couldn\'t resolve breakpoint using "%s" in executate "%s" with ' + 'debug info that has a bad .debug_aranges section' % (src_path, self.getBuildArtifact("a.out"))) + def setUp(self): # Call super's setUp().