Skip to content

Commit eb96c8c

Browse files
authored
[lldb] Implement (SB)Function::GetInstructions for discontinuous functions (#122933)
The main change is to permit the disassembler class to process/store multiple (discontinuous) ranges of addresses. The result is not ambiguous because each instruction knows its size (in addition to its address), so we can check for discontinuity by looking at whether the next instruction begins where the previous ends. This patch doesn't handle the "disassemble" CLI command, which uses a more elaborate mechanism for disassembling and printing instructions.
1 parent b3924cb commit eb96c8c

File tree

6 files changed

+41
-20
lines changed

6 files changed

+41
-20
lines changed

lldb/include/lldb/Core/Disassembler.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
428428
static lldb::DisassemblerSP
429429
DisassembleRange(const ArchSpec &arch, const char *plugin_name,
430430
const char *flavor, const char *cpu, const char *features,
431-
Target &target, const AddressRange &disasm_range,
431+
Target &target, llvm::ArrayRef<AddressRange> disasm_ranges,
432432
bool force_live_memory = false);
433433

434434
static lldb::DisassemblerSP
@@ -460,7 +460,11 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
460460

461461
size_t ParseInstructions(Target &target, Address address, Limit limit,
462462
Stream *error_strm_ptr,
463-
bool force_live_memory = false);
463+
bool force_live_memory = false) {
464+
m_instruction_list.Clear();
465+
return AppendInstructions(target, address, limit, error_strm_ptr,
466+
force_live_memory);
467+
}
464468

465469
virtual size_t DecodeInstructions(const Address &base_addr,
466470
const DataExtractor &data,
@@ -480,6 +484,9 @@ class Disassembler : public std::enable_shared_from_this<Disassembler>,
480484
const char *flavor) = 0;
481485

482486
protected:
487+
size_t AppendInstructions(Target &target, Address address, Limit limit,
488+
Stream *error_strm_ptr, bool force_live_memory);
489+
483490
// SourceLine and SourceLinesToDisplay structures are only used in the mixed
484491
// source and assembly display methods internal to this class.
485492

lldb/source/API/SBFunction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ SBInstructionList SBFunction::GetInstructions(SBTarget target,
127127
sb_instructions.SetDisassembler(Disassembler::DisassembleRange(
128128
module_sp->GetArchitecture(), nullptr, flavor,
129129
target_sp->GetDisassemblyCPU(), target_sp->GetDisassemblyFeatures(),
130-
*target_sp, m_opaque_ptr->GetAddressRange(), force_live_memory));
130+
*target_sp, m_opaque_ptr->GetAddressRanges(), force_live_memory));
131131
}
132132
}
133133
return sb_instructions;

lldb/source/API/SBInstructionList.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ bool SBInstructionList::GetDescription(Stream &sref) {
151151
FormatEntity::Parse("${addr}: ", format);
152152
SymbolContext sc;
153153
SymbolContext prev_sc;
154+
155+
// Expected address of the next instruction. Used to print an empty line
156+
// for non-contiguous blocks of insns.
157+
std::optional<Address> next_addr;
154158
for (size_t i = 0; i < num_instructions; ++i) {
155159
Instruction *inst =
156160
m_opaque_sp->GetInstructionList().GetInstructionAtIndex(i).get();
@@ -165,10 +169,14 @@ bool SBInstructionList::GetDescription(Stream &sref) {
165169
addr, eSymbolContextEverything, sc);
166170
}
167171

172+
if (next_addr && *next_addr != addr)
173+
sref.EOL();
168174
inst->Dump(&sref, max_opcode_byte_size, true, false,
169175
/*show_control_flow_kind=*/false, nullptr, &sc, &prev_sc,
170176
&format, 0);
171177
sref.EOL();
178+
next_addr = addr;
179+
next_addr->Slide(inst->GetOpcode().GetByteSize());
172180
}
173181
return true;
174182
}

lldb/source/Core/Disassembler.cpp

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -123,22 +123,19 @@ static Address ResolveAddress(Target &target, const Address &addr) {
123123
lldb::DisassemblerSP Disassembler::DisassembleRange(
124124
const ArchSpec &arch, const char *plugin_name, const char *flavor,
125125
const char *cpu, const char *features, Target &target,
126-
const AddressRange &range, bool force_live_memory) {
127-
if (range.GetByteSize() <= 0)
128-
return {};
129-
130-
if (!range.GetBaseAddress().IsValid())
131-
return {};
132-
126+
llvm::ArrayRef<AddressRange> disasm_ranges, bool force_live_memory) {
133127
lldb::DisassemblerSP disasm_sp = Disassembler::FindPluginForTarget(
134128
target, arch, flavor, cpu, features, plugin_name);
135129

136130
if (!disasm_sp)
137131
return {};
138132

139-
const size_t bytes_disassembled = disasm_sp->ParseInstructions(
140-
target, range.GetBaseAddress(), {Limit::Bytes, range.GetByteSize()},
141-
nullptr, force_live_memory);
133+
size_t bytes_disassembled = 0;
134+
for (const AddressRange &range : disasm_ranges) {
135+
bytes_disassembled += disasm_sp->AppendInstructions(
136+
target, range.GetBaseAddress(), {Limit::Bytes, range.GetByteSize()},
137+
nullptr, force_live_memory);
138+
}
142139
if (bytes_disassembled == 0)
143140
return {};
144141

@@ -1092,11 +1089,9 @@ InstructionList::GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr,
10921089
return GetIndexOfInstructionAtAddress(address);
10931090
}
10941091

1095-
size_t Disassembler::ParseInstructions(Target &target, Address start,
1096-
Limit limit, Stream *error_strm_ptr,
1097-
bool force_live_memory) {
1098-
m_instruction_list.Clear();
1099-
1092+
size_t Disassembler::AppendInstructions(Target &target, Address start,
1093+
Limit limit, Stream *error_strm_ptr,
1094+
bool force_live_memory) {
11001095
if (!start.IsValid())
11011096
return 0;
11021097

@@ -1129,7 +1124,7 @@ size_t Disassembler::ParseInstructions(Target &target, Address start,
11291124
return DecodeInstructions(start, data, 0,
11301125
limit.kind == Limit::Instructions ? limit.value
11311126
: UINT32_MAX,
1132-
false, data_from_file);
1127+
/*append=*/true, data_from_file);
11331128
}
11341129

11351130
// Disassembler copy constructor

lldb/source/Symbol/Function.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ lldb::DisassemblerSP Function::GetInstructions(const ExecutionContext &exe_ctx,
488488
if (module_sp && exe_ctx.HasTargetScope()) {
489489
return Disassembler::DisassembleRange(
490490
module_sp->GetArchitecture(), nullptr, nullptr, nullptr, flavor,
491-
exe_ctx.GetTargetRef(), GetAddressRange(), !prefer_file_cache);
491+
exe_ctx.GetTargetRef(), GetAddressRanges(), !prefer_file_cache);
492492
}
493493
return lldb::DisassemblerSP();
494494
}

lldb/test/Shell/ScriptInterpreter/Python/sb_function_ranges.s

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66

77
# CHECK: Found 1 function(s).
88
# CHECK: foo: [input.o[0x0-0xe), input.o[0x14-0x1c)]
9+
# CHECK-NEXT: input.o[0x0]: cmpl $0x0, %edi
10+
# CHECK-NEXT: input.o[0x3]: je 0x14
11+
# CHECK-NEXT: input.o[0x5]: jmp 0x7
12+
# CHECK-NEXT: input.o[0x7]: callq 0xe
13+
# CHECK-NEXT: input.o[0xc]: jmp 0x1b
14+
# CHECK-EMPTY:
15+
# CHECK-NEXT: input.o[0x14]: callq 0x19
16+
# CHECK-NEXT: input.o[0x19]: jmp 0x1b
17+
# CHECK-NEXT: input.o[0x1b]: retq
18+
919

1020
#--- script.py
1121
import lldb
@@ -17,6 +27,7 @@ def __lldb_init_module(debugger, internal_dict):
1727
for ctx in sym_ctxs:
1828
fn = ctx.function
1929
print(f"{fn.name}: {fn.GetRanges()}")
30+
print(fn.GetInstructions(target))
2031

2132
#--- input.s
2233
# An example of a function which has been split into two parts. Roughly

0 commit comments

Comments
 (0)