Skip to content

Commit 952b680

Browse files
authored
Support stepping through Darwin "branch islands" (#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.
1 parent 616489e commit 952b680

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
@@ -26,6 +26,7 @@
2626
#include "lldb/Target/Thread.h"
2727
#include "lldb/Target/ThreadPlanCallFunction.h"
2828
#include "lldb/Target/ThreadPlanRunToAddress.h"
29+
#include "lldb/Target/ThreadPlanStepInstruction.h"
2930
#include "lldb/Utility/DataBuffer.h"
3031
#include "lldb/Utility/DataBufferHeap.h"
3132
#include "lldb/Utility/LLDBLog.h"
@@ -923,15 +924,15 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
923924
if (current_symbol != nullptr) {
924925
std::vector<Address> addresses;
925926

927+
ConstString current_name =
928+
current_symbol->GetMangled().GetName(Mangled::ePreferMangled);
926929
if (current_symbol->IsTrampoline()) {
927-
ConstString trampoline_name =
928-
current_symbol->GetMangled().GetName(Mangled::ePreferMangled);
929930

930-
if (trampoline_name) {
931+
if (current_name) {
931932
const ModuleList &images = target_sp->GetImages();
932933

933934
SymbolContextList code_symbols;
934-
images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeCode,
935+
images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode,
935936
code_symbols);
936937
for (const SymbolContext &context : code_symbols) {
937938
Address addr = context.GetFunctionOrSymbolAddress();
@@ -945,8 +946,8 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
945946
}
946947

947948
SymbolContextList reexported_symbols;
948-
images.FindSymbolsWithNameAndType(
949-
trampoline_name, eSymbolTypeReExported, reexported_symbols);
949+
images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported,
950+
reexported_symbols);
950951
for (const SymbolContext &context : reexported_symbols) {
951952
if (context.symbol) {
952953
Symbol *actual_symbol =
@@ -968,7 +969,7 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
968969
}
969970

970971
SymbolContextList indirect_symbols;
971-
images.FindSymbolsWithNameAndType(trampoline_name, eSymbolTypeResolver,
972+
images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver,
972973
indirect_symbols);
973974

974975
for (const SymbolContext &context : indirect_symbols) {
@@ -1028,6 +1029,23 @@ DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread,
10281029
thread_plan_sp = std::make_shared<ThreadPlanRunToAddress>(
10291030
thread, load_addrs, stop_others);
10301031
}
1032+
// One more case we have to consider is "branch islands". These are regular
1033+
// TEXT symbols but their names end in .island plus maybe a .digit suffix.
1034+
// They are to allow arm64 code to branch further than the size of the
1035+
// address slot allows. We just need to single-instruction step in that
1036+
// case.
1037+
static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$";
1038+
static RegularExpression g_branch_island_regex(g_branch_island_pattern);
1039+
1040+
bool is_branch_island = g_branch_island_regex.Execute(current_name);
1041+
if (!thread_plan_sp && is_branch_island) {
1042+
thread_plan_sp = std::make_shared<ThreadPlanStepInstruction>(
1043+
thread,
1044+
/* step_over= */ false, /* stop_others */ false, eVoteNoOpinion,
1045+
eVoteNoOpinion);
1046+
LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.",
1047+
current_name);
1048+
}
10311049
} else {
10321050
LLDB_LOGF(log, "Could not find symbol for step through.");
10331051
}
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)