Skip to content

Commit 8cd455a

Browse files
jiminghamJDevlieghere
authored andcommitted
Support stepping through Darwin "branch islands" (llvm#139301)
When an intra-module jump doesn't fit in the immediate branch slot, the Darwin linker inserts "branch island" symbols, and emits code to jump from branch island to branch island till it makes it to the actual function. The previous submissions failed because in that environment the linker was putting the `foo.island` symbol at the same address as the `padding` symbol we we emitting to make our faked-up large binary. This submission jams a byte after the padding symbol so that the other symbols can't overlap it. (cherry picked from commit 952b680)
1 parent 796a0a5 commit 8cd455a

File tree

9 files changed

+119
-7
lines changed

9 files changed

+119
-7
lines changed

lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderDarwin.cpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "lldb/Target/Thread.h"
2828
#include "lldb/Target/ThreadPlanCallFunction.h"
2929
#include "lldb/Target/ThreadPlanRunToAddress.h"
30+
#include "lldb/Target/ThreadPlanStepInstruction.h"
3031
#include "lldb/Utility/DataBuffer.h"
3132
#include "lldb/Utility/DataBufferHeap.h"
3233
#include "lldb/Utility/LLDBLog.h"
@@ -930,15 +931,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
930931
if (current_symbol != nullptr) {
931932
std::vector<Address> addresses;
932933

934+
ConstString current_name =
935+
current_symbol->GetMangled().GetName(Mangled::ePreferMangled);
933936
if (current_symbol->IsTrampoline()) {
934-
ConstString trampoline_name =
935-
current_symbol->GetMangled().GetName(Mangled::ePreferMangled);
936937

937-
if (trampoline_name) {
938+
if (current_name) {
938939
const ModuleList &images = target_sp->GetImages();
939940

940941
SymbolContextList code_symbols;
941-
images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode,
942+
images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode,
942943
code_symbols);
943944
for (const SymbolContext &context : code_symbols) {
944945
AddressRange addr_range;
@@ -955,8 +956,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
955956
}
956957

957958
SymbolContextList reexported_symbols;
958-
images.FindSymbolsWithNameAndType(
959-
trampoline_name, eSymbolTypeReExported, reexported_symbols);
959+
images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported,
960+
reexported_symbols);
960961
for (const SymbolContext &context : reexported_symbols) {
961962
if (context.symbol) {
962963
Symbol *actual_symbol =
@@ -978,7 +979,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
978979
}
979980

980981
SymbolContextList indirect_symbols;
981-
images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver,
982+
images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver,
982983
indirect_symbols);
983984

984985
for (const SymbolContext &context : indirect_symbols) {
@@ -1041,6 +1042,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
10411042
thread_plan_sp = std::make_shared<ThreadPlanRunToAddress>(
10421043
thread, load_addrs, stop_others);
10431044
}
1045+
// One more case we have to consider is "branch islands". These are regular
1046+
// TEXT symbols but their names end in .island plus maybe a .digit suffix.
1047+
// They are to allow arm64 code to branch further than the size of the
1048+
// address slot allows. We just need to single-instruction step in that
1049+
// case.
1050+
static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$";
1051+
static RegularExpression g_branch_island_regex(g_branch_island_pattern);
1052+
1053+
bool is_branch_island = g_branch_island_regex.Execute(current_name);
1054+
if (!thread_plan_sp && is_branch_island) {
1055+
thread_plan_sp = std::make_shared<ThreadPlanStepInstruction>(
1056+
thread,
1057+
/* step_over= */ false, /* stop_others */ false, eVoteNoOpinion,
1058+
eVoteNoOpinion);
1059+
LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.",
1060+
current_name);
1061+
}
10441062
} else {
10451063
LLDB_LOGF(log, "Could not find symbol for step through.");
10461064
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
C_SOURCES := main.c foo.c
2+
CFLAGS_EXTRAS := -std=c99
3+
4+
include Makefile.rules
5+
6+
a.out: main.o padding1.o padding2.o padding3.o padding4.o foo.o
7+
${CC} ${LDFLAGS} foo.o padding1.o padding2.o padding3.o padding4.o main.o -o a.out
8+
9+
%.o: $(SRCDIR)/%.s
10+
${CC} -c $<
11+
12+
#padding1.o: padding1.s
13+
# ${CC} -c $(SRCDIR)/padding1.s
14+
15+
#padding2.o: padding2.s
16+
# ${CC} -c $(SRCDIR)/padding2.s
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""
2+
Make sure that we can step in across an arm64 branch island
3+
"""
4+
5+
6+
import lldb
7+
import lldbsuite.test.lldbutil as lldbutil
8+
from lldbsuite.test.lldbtest import *
9+
from lldbsuite.test.decorators import *
10+
11+
12+
class TestBranchIslandStepping(TestBase):
13+
NO_DEBUG_INFO_TESTCASE = True
14+
15+
@skipUnlessAppleSilicon
16+
def test_step_in_branch_island(self):
17+
"""Make sure we can step in across a branch island"""
18+
self.build()
19+
self.main_source_file = lldb.SBFileSpec("main.c")
20+
self.do_test()
21+
22+
def do_test(self):
23+
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
24+
self, "Set a breakpoint here", self.main_source_file
25+
)
26+
27+
# Make sure that we did manage to generate a branch island for foo:
28+
syms = target.FindSymbols("foo.island", lldb.eSymbolTypeCode)
29+
self.assertEqual(len(syms), 1, "We did generate an island for foo")
30+
# There's a bug in the Xcode 15.2 linker, where it did not emit
31+
# "function starts" entries for the branch island symbols, which
32+
# causes lldb to set the range of the previous non-island symbol to
33+
# encompass the range of the branch island symbols. If we encounter
34+
# that bug, then we won't successfully do the step in. Test for
35+
# that here - if the symbol doesn't round-trip from
36+
# name->address->name then the rest of the test can't pass.
37+
island_sym_ctx = syms[0]
38+
sym_addr = island_sym_ctx.symbol.addr
39+
resolved_name = sym_addr.symbol.name
40+
if resolved_name != "foo.island":
41+
self.skipTest("Encountered overlapping symbol linker bug")
42+
thread.StepInto()
43+
stop_frame = thread.frames[0]
44+
self.assertIn("foo", stop_frame.name, "Stepped into foo")
45+
var = stop_frame.FindVariable("a_variable_in_foo")
46+
self.assertTrue(var.IsValid(), "Found the variable in foo")
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include <stdio.h>
2+
3+
void foo() {
4+
int a_variable_in_foo = 10;
5+
printf("I am foo: %d.\n", a_variable_in_foo);
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
extern void foo();
2+
3+
int main() {
4+
foo(); // Set a breakpoint here
5+
return 0;
6+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.text
2+
_padding1:
3+
.p2align 2
4+
.byte 0x10
5+
.space 120*1024*1024
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.text
2+
_padding2:
3+
.p2align 2
4+
.byte 0x10
5+
.space 120*1024*1024
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.text
2+
_padding3:
3+
.p2align 2
4+
.byte 0x10
5+
.space 120*1024*1024
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.text
2+
_padding4:
3+
.p2align 2
4+
.byte 0x10
5+
.space 120*1024*1024

0 commit comments

Comments
 (0)