Skip to content

Commit 37f36cb

Browse files
authored
[lldb] Support disassembling discontinuous functions (#126505)
The command already supported disassembling multiple ranges, among other reasons because inline functions can be discontinuous. The main thing that was missing was being able to retrieve the function ranges from the top level function object. The output of the command for the case where the function entry point is not its lowest address is somewhat confusing (we're showing negative offsets), but it is correct.
1 parent 73413bd commit 37f36cb

File tree

4 files changed

+157
-56
lines changed

4 files changed

+157
-56
lines changed

lldb/source/Commands/CommandObjectDisassemble.cpp

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "lldb/Target/SectionLoadList.h"
2222
#include "lldb/Target/StackFrame.h"
2323
#include "lldb/Target/Target.h"
24+
#include <iterator>
2425

2526
static constexpr unsigned default_disasm_byte_size = 32;
2627
static constexpr unsigned default_disasm_num_ins = 4;
@@ -236,35 +237,43 @@ CommandObjectDisassemble::CommandObjectDisassemble(
236237

237238
CommandObjectDisassemble::~CommandObjectDisassemble() = default;
238239

239-
llvm::Error CommandObjectDisassemble::CheckRangeSize(const AddressRange &range,
240-
llvm::StringRef what) {
240+
llvm::Expected<std::vector<AddressRange>>
241+
CommandObjectDisassemble::CheckRangeSize(std::vector<AddressRange> ranges,
242+
llvm::StringRef what) {
243+
addr_t total_range_size = 0;
244+
for (const AddressRange &r : ranges)
245+
total_range_size += r.GetByteSize();
246+
241247
if (m_options.num_instructions > 0 || m_options.force ||
242-
range.GetByteSize() < GetDebugger().GetStopDisassemblyMaxSize())
243-
return llvm::Error::success();
248+
total_range_size < GetDebugger().GetStopDisassemblyMaxSize())
249+
return ranges;
250+
244251
StreamString msg;
245252
msg << "Not disassembling " << what << " because it is very large ";
246-
range.Dump(&msg, &GetTarget(), Address::DumpStyleLoadAddress,
247-
Address::DumpStyleFileAddress);
253+
for (const AddressRange &r : ranges)
254+
r.Dump(&msg, &GetTarget(), Address::DumpStyleLoadAddress,
255+
Address::DumpStyleFileAddress);
248256
msg << ". To disassemble specify an instruction count limit, start/stop "
249257
"addresses or use the --force option.";
250-
return llvm::createStringError(llvm::inconvertibleErrorCode(),
251-
msg.GetString());
258+
return llvm::createStringError(msg.GetString());
252259
}
253260

254261
llvm::Expected<std::vector<AddressRange>>
255262
CommandObjectDisassemble::GetContainingAddressRanges() {
256263
std::vector<AddressRange> ranges;
257-
const auto &get_range = [&](Address addr) {
264+
const auto &get_ranges = [&](Address addr) {
258265
ModuleSP module_sp(addr.GetModule());
259266
SymbolContext sc;
260267
bool resolve_tail_call_address = true;
261268
addr.GetModule()->ResolveSymbolContextForAddress(
262269
addr, eSymbolContextEverything, sc, resolve_tail_call_address);
263270
if (sc.function || sc.symbol) {
264271
AddressRange range;
265-
sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0,
266-
false, range);
267-
ranges.push_back(range);
272+
for (uint32_t idx = 0;
273+
sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol,
274+
idx, false, range);
275+
++idx)
276+
ranges.push_back(range);
268277
}
269278
};
270279

@@ -273,14 +282,14 @@ CommandObjectDisassemble::GetContainingAddressRanges() {
273282
Address symbol_containing_address;
274283
if (target.ResolveLoadAddress(m_options.symbol_containing_addr,
275284
symbol_containing_address)) {
276-
get_range(symbol_containing_address);
285+
get_ranges(symbol_containing_address);
277286
}
278287
} else {
279288
for (lldb::ModuleSP module_sp : target.GetImages().Modules()) {
280289
Address file_address;
281290
if (module_sp->ResolveFileAddress(m_options.symbol_containing_addr,
282291
file_address)) {
283-
get_range(file_address);
292+
get_ranges(file_address);
284293
}
285294
}
286295
}
@@ -292,9 +301,7 @@ CommandObjectDisassemble::GetContainingAddressRanges() {
292301
m_options.symbol_containing_addr);
293302
}
294303

295-
if (llvm::Error err = CheckRangeSize(ranges[0], "the function"))
296-
return std::move(err);
297-
return ranges;
304+
return CheckRangeSize(std::move(ranges), "the function");
298305
}
299306

300307
llvm::Expected<std::vector<AddressRange>>
@@ -304,29 +311,24 @@ CommandObjectDisassemble::GetCurrentFunctionRanges() {
304311
if (!frame) {
305312
if (process) {
306313
return llvm::createStringError(
307-
llvm::inconvertibleErrorCode(),
308-
"Cannot disassemble around the current "
309-
"function without the process being stopped.\n");
310-
} else {
311-
return llvm::createStringError(llvm::inconvertibleErrorCode(),
312-
"Cannot disassemble around the current "
313-
"function without a selected frame: "
314-
"no currently running process.\n");
314+
"Cannot disassemble around the current function without the process "
315+
"being stopped.\n");
315316
}
317+
return llvm::createStringError(
318+
"Cannot disassemble around the current function without a selected "
319+
"frame: no currently running process.\n");
316320
}
317-
SymbolContext sc(
318-
frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol));
319-
AddressRange range;
321+
SymbolContext sc =
322+
frame->GetSymbolContext(eSymbolContextFunction | eSymbolContextSymbol);
323+
std::vector<AddressRange> ranges;
320324
if (sc.function)
321-
range = sc.function->GetAddressRange();
322-
else if (sc.symbol && sc.symbol->ValueIsAddress()) {
323-
range = {sc.symbol->GetAddress(), sc.symbol->GetByteSize()};
324-
} else
325-
range = {frame->GetFrameCodeAddress(), default_disasm_byte_size};
326-
327-
if (llvm::Error err = CheckRangeSize(range, "the current function"))
328-
return std::move(err);
329-
return std::vector<AddressRange>{range};
325+
ranges = sc.function->GetAddressRanges();
326+
else if (sc.symbol && sc.symbol->ValueIsAddress())
327+
ranges.emplace_back(sc.symbol->GetAddress(), sc.symbol->GetByteSize());
328+
else
329+
ranges.emplace_back(frame->GetFrameCodeAddress(), default_disasm_byte_size);
330+
331+
return CheckRangeSize(std::move(ranges), "the current function");
330332
}
331333

332334
llvm::Expected<std::vector<AddressRange>>
@@ -372,19 +374,23 @@ CommandObjectDisassemble::GetNameRanges(CommandReturnObject &result) {
372374

373375
std::vector<AddressRange> ranges;
374376
llvm::Error range_errs = llvm::Error::success();
375-
AddressRange range;
376377
const uint32_t scope =
377378
eSymbolContextBlock | eSymbolContextFunction | eSymbolContextSymbol;
378379
const bool use_inline_block_range = true;
379380
for (SymbolContext sc : sc_list.SymbolContexts()) {
381+
std::vector<AddressRange> fn_ranges;
382+
AddressRange range;
380383
for (uint32_t range_idx = 0;
381384
sc.GetAddressRange(scope, range_idx, use_inline_block_range, range);
382-
++range_idx) {
383-
if (llvm::Error err = CheckRangeSize(range, "a range"))
384-
range_errs = joinErrors(std::move(range_errs), std::move(err));
385-
else
386-
ranges.push_back(range);
387-
}
385+
++range_idx)
386+
fn_ranges.push_back(std::move(range));
387+
388+
if (llvm::Expected<std::vector<AddressRange>> checked_ranges =
389+
CheckRangeSize(std::move(fn_ranges), "a function"))
390+
llvm::move(*checked_ranges, std::back_inserter(ranges));
391+
else
392+
range_errs =
393+
joinErrors(std::move(range_errs), checked_ranges.takeError());
388394
}
389395
if (ranges.empty()) {
390396
if (range_errs)

lldb/source/Commands/CommandObjectDisassemble.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ class CommandObjectDisassemble : public CommandObjectParsed {
100100
llvm::Expected<std::vector<AddressRange>> GetPCRanges();
101101
llvm::Expected<std::vector<AddressRange>> GetStartEndAddressRanges();
102102

103-
llvm::Error CheckRangeSize(const AddressRange &range, llvm::StringRef what);
103+
llvm::Expected<std::vector<AddressRange>>
104+
CheckRangeSize(std::vector<AddressRange> ranges, llvm::StringRef what);
104105

105106
CommandOptions m_options;
106107
};

lldb/source/Symbol/SymbolContext.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,8 @@ bool SymbolContext::GetAddressRange(uint32_t scope, uint32_t range_idx,
351351
}
352352

353353
if ((scope & eSymbolContextFunction) && (function != nullptr)) {
354-
if (range_idx == 0) {
355-
range = function->GetAddressRange();
354+
if (range_idx < function->GetAddressRanges().size()) {
355+
range = function->GetAddressRanges()[range_idx];
356356
return true;
357357
}
358358
}

lldb/test/Shell/Commands/command-disassemble.s

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -82,20 +82,25 @@
8282
# CHECK-NEXT: (lldb) disassemble --name case2
8383
# CHECK-NEXT: command-disassemble.s.tmp`n1::case2:
8484
# CHECK-NEXT: command-disassemble.s.tmp[0x2044] <+0>: int $0x32
85-
# CHECK-NEXT: warning: Not disassembling a range because it is very large [0x0000000000002046-0x0000000000004046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
85+
# CHECK-NEXT: warning: Not disassembling a function because it is very large [0x0000000000002046-0x0000000000004046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
8686
# CHECK-NEXT: (lldb) disassemble --name case3
87-
# CHECK-NEXT: error: Not disassembling a range because it is very large [0x0000000000004046-0x0000000000006046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
88-
# CHECK-NEXT: Not disassembling a range because it is very large [0x0000000000006046-0x0000000000008046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
87+
# CHECK-NEXT: error: Not disassembling a function because it is very large [0x0000000000006046-0x0000000000007046)[0x0000000000009046-0x000000000000a046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
88+
# CHECK-NEXT: Not disassembling a function because it is very large [0x0000000000004046-0x0000000000006046). To disassemble specify an instruction count limit, start/stop addresses or use the --force option.
8989
# CHECK-NEXT: (lldb) disassemble --name case3 --count 3
90+
# CHECK-NEXT: command-disassemble.s.tmp`n2::case3:
91+
# CHECK-NEXT: command-disassemble.s.tmp[0x6046] <-12288>: int $0x2a
92+
# CHECK-NEXT: command-disassemble.s.tmp[0x6048] <-12286>: int $0x2a
93+
# CHECK-NEXT: command-disassemble.s.tmp[0x604a] <-12284>: int $0x2a
94+
# CHECK-EMPTY:
95+
# CHECK-NEXT: command-disassemble.s.tmp`n2::case3:
96+
# CHECK-NEXT: command-disassemble.s.tmp[0x9046] <+0>: int $0x2a
97+
# CHECK-NEXT: command-disassemble.s.tmp[0x9048] <+2>: int $0x2a
98+
# CHECK-NEXT: command-disassemble.s.tmp[0x904a] <+4>: int $0x2a
99+
# CHECK-EMPTY:
90100
# CHECK-NEXT: command-disassemble.s.tmp`n1::case3:
91101
# CHECK-NEXT: command-disassemble.s.tmp[0x4046] <+0>: int $0x2a
92102
# CHECK-NEXT: command-disassemble.s.tmp[0x4048] <+2>: int $0x2a
93103
# CHECK-NEXT: command-disassemble.s.tmp[0x404a] <+4>: int $0x2a
94-
# CHECK-EMPTY:
95-
# CHECK-NEXT: command-disassemble.s.tmp`n2::case3:
96-
# CHECK-NEXT: command-disassemble.s.tmp[0x6046] <+0>: int $0x2a
97-
# CHECK-NEXT: command-disassemble.s.tmp[0x6048] <+2>: int $0x2a
98-
# CHECK-NEXT: command-disassemble.s.tmp[0x604a] <+4>: int $0x2a
99104
# CHECK-EMPTY:
100105

101106

@@ -158,8 +163,97 @@ _ZN2n15case3Ev:
158163
.rept 0x1000
159164
int $42
160165
.endr
166+
.size _ZN2n15case3Ev, .-_ZN2n15case3Ev
161167

162-
_ZN2n25case3Ev:
168+
.L_ZN2n25case3Ev.__part.1:
169+
.rept 0x800
170+
int $42
171+
.endr
172+
.L_ZN2n25case3Ev.__part.1_end:
173+
174+
.Lpadding:
163175
.rept 0x1000
164176
int $42
165177
.endr
178+
179+
_ZN2n25case3Ev:
180+
.rept 0x800
181+
int $42
182+
.endr
183+
.L_ZN2n25case3Ev_end:
184+
185+
.section .debug_abbrev,"",@progbits
186+
.byte 1 # Abbreviation Code
187+
.byte 17 # DW_TAG_compile_unit
188+
.byte 1 # DW_CHILDREN_yes
189+
.byte 37 # DW_AT_producer
190+
.byte 8 # DW_FORM_string
191+
.byte 19 # DW_AT_language
192+
.byte 5 # DW_FORM_data2
193+
.byte 17 # DW_AT_low_pc
194+
.byte 1 # DW_FORM_addr
195+
.byte 85 # DW_AT_ranges
196+
.byte 23 # DW_FORM_sec_offset
197+
.byte 0 # EOM(1)
198+
.byte 0 # EOM(2)
199+
.byte 2 # Abbreviation Code
200+
.byte 57 # DW_TAG_namespace
201+
.byte 1 # DW_CHILDREN_yes
202+
.byte 3 # DW_AT_name
203+
.byte 8 # DW_FORM_string
204+
.byte 0 # EOM(1)
205+
.byte 0 # EOM(2)
206+
.byte 3 # Abbreviation Code
207+
.byte 46 # DW_TAG_subprogram
208+
.byte 0 # DW_CHILDREN_no
209+
.byte 85 # DW_AT_ranges
210+
.byte 23 # DW_FORM_sec_offset
211+
.byte 3 # DW_AT_name
212+
.byte 8 # DW_FORM_string
213+
.byte 110 # DW_AT_linkage_name
214+
.byte 8 # DW_FORM_string
215+
.byte 0 # EOM(1)
216+
.byte 0 # EOM(2)
217+
.byte 0 # EOM(3)
218+
219+
.section .debug_info,"",@progbits
220+
.Lcu_begin0:
221+
.long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
222+
.Ldebug_info_start0:
223+
.short 5 # DWARF version number
224+
.byte 1 # DWARF Unit Type
225+
.byte 8 # Address Size (in bytes)
226+
.long .debug_abbrev # Offset Into Abbrev. Section
227+
.byte 1 # Abbrev DW_TAG_compile_unit
228+
.asciz "Hand-written DWARF" # DW_AT_producer
229+
.short 29 # DW_AT_language
230+
.quad 0 # DW_AT_low_pc
231+
.long .Ldebug_ranges0 # DW_AT_ranges
232+
.byte 2 # Abbrev DW_TAG_namespace
233+
.asciz "n2" # DW_AT_name
234+
.byte 3 # Abbrev DW_TAG_subprogram
235+
.long .Ldebug_ranges0 # DW_AT_ranges
236+
.asciz "case3" # DW_AT_name
237+
.asciz "_ZN2n25case3Ev" # DW_AT_linkage_name
238+
.byte 0 # End Of Children Mark
239+
.byte 0 # End Of Children Mark
240+
.Ldebug_info_end0:
241+
242+
.section .debug_rnglists,"",@progbits
243+
.long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
244+
.Ldebug_list_header_start0:
245+
.short 5 # Version
246+
.byte 8 # Address size
247+
.byte 0 # Segment selector size
248+
.long 2 # Offset entry count
249+
.Lrnglists_table_base0:
250+
.long .Ldebug_ranges0-.Lrnglists_table_base0
251+
.Ldebug_ranges0:
252+
.byte 6 # DW_RLE_start_end
253+
.quad _ZN2n25case3Ev
254+
.quad .L_ZN2n25case3Ev_end
255+
.byte 6 # DW_RLE_start_end
256+
.quad .L_ZN2n25case3Ev.__part.1
257+
.quad .L_ZN2n25case3Ev.__part.1_end
258+
.byte 0 # DW_RLE_end_of_list
259+
.Ldebug_list_header_end0:

0 commit comments

Comments
 (0)