Skip to content

Commit 59bb9b9

Browse files
authored
[lldb] Expose discontinuous functions through SBFunction::GetRanges (#117532)
SBFunction::GetEndAddress doesn't really make sense for discontinuous functions, so I'm declaring it deprecated. GetStartAddress sort of makes sense, if one uses it to find the functions entry point, so I'm keeping that undeprecated. I've made the test a Shell tests because these make it easier to create discontinuous functions regardless of the host os and architecture. They do make testing the python API harder, but I think I've managed to come up with something not entirely unreasonable.
1 parent fed3a9b commit 59bb9b9

File tree

7 files changed

+198
-20
lines changed

7 files changed

+198
-20
lines changed

lldb/include/lldb/API/SBAddressRangeList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class LLDB_API SBAddressRangeList {
4545
private:
4646
friend class SBBlock;
4747
friend class SBProcess;
48+
friend class SBFunction;
4849

4950
lldb_private::AddressRangeListImpl &ref() const;
5051

lldb/include/lldb/API/SBFunction.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class LLDB_API SBFunction {
4343

4444
lldb::SBAddress GetStartAddress();
4545

46+
LLDB_DEPRECATED_FIXME("Not compatible with discontinuous functions.",
47+
"GetRanges()")
4648
lldb::SBAddress GetEndAddress();
4749

4850
lldb::SBAddressRangeList GetRanges();

lldb/include/lldb/Core/AddressRangeListImpl.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ class AddressRangeListImpl {
2424
public:
2525
AddressRangeListImpl();
2626

27-
AddressRangeListImpl(const AddressRangeListImpl &rhs) = default;
28-
29-
AddressRangeListImpl &operator=(const AddressRangeListImpl &rhs);
27+
explicit AddressRangeListImpl(AddressRanges ranges)
28+
: m_ranges(std::move(ranges)) {}
3029

3130
size_t GetSize() const;
3231

lldb/include/lldb/Symbol/Function.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,11 @@ class Function : public UserID, public SymbolContextScope {
444444

445445
Function *CalculateSymbolContextFunction() override;
446446

447+
/// DEPRECATED: Use GetAddressRanges instead.
447448
const AddressRange &GetAddressRange() { return m_range; }
448449

450+
const AddressRanges &GetAddressRanges() const { return m_ranges; }
451+
449452
lldb::LanguageType GetLanguage() const;
450453
/// Find the file and line number of the source location of the start of the
451454
/// function. This will use the declaration if present and fall back on the

lldb/source/API/SBFunction.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "lldb/API/SBAddressRange.h"
1111
#include "lldb/API/SBProcess.h"
1212
#include "lldb/API/SBStream.h"
13+
#include "lldb/Core/AddressRangeListImpl.h"
1314
#include "lldb/Core/Disassembler.h"
1415
#include "lldb/Core/Module.h"
1516
#include "lldb/Symbol/CompileUnit.h"
@@ -153,10 +154,11 @@ SBAddress SBFunction::GetEndAddress() {
153154

154155
SBAddress addr;
155156
if (m_opaque_ptr) {
156-
addr_t byte_size = m_opaque_ptr->GetAddressRange().GetByteSize();
157-
if (byte_size > 0) {
158-
addr.SetAddress(m_opaque_ptr->GetAddressRange().GetBaseAddress());
159-
addr->Slide(byte_size);
157+
llvm::ArrayRef<AddressRange> ranges = m_opaque_ptr->GetAddressRanges();
158+
if (!ranges.empty()) {
159+
// Return the end of the first range, use GetRanges to get all ranges.
160+
addr.SetAddress(ranges.front().GetBaseAddress());
161+
addr->Slide(ranges.front().GetByteSize());
160162
}
161163
}
162164
return addr;
@@ -166,11 +168,8 @@ lldb::SBAddressRangeList SBFunction::GetRanges() {
166168
LLDB_INSTRUMENT_VA(this);
167169

168170
lldb::SBAddressRangeList ranges;
169-
if (m_opaque_ptr) {
170-
lldb::SBAddressRange range;
171-
(*range.m_opaque_up) = m_opaque_ptr->GetAddressRange();
172-
ranges.Append(std::move(range));
173-
}
171+
if (m_opaque_ptr)
172+
ranges.ref() = AddressRangeListImpl(m_opaque_ptr->GetAddressRanges());
174173

175174
return ranges;
176175
}

lldb/source/Core/AddressRangeListImpl.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@ using namespace lldb_private;
1313

1414
AddressRangeListImpl::AddressRangeListImpl() : m_ranges() {}
1515

16-
AddressRangeListImpl &
17-
AddressRangeListImpl::operator=(const AddressRangeListImpl &rhs) {
18-
if (this == &rhs)
19-
return *this;
20-
m_ranges = rhs.m_ranges;
21-
return *this;
22-
}
23-
2416
size_t AddressRangeListImpl::GetSize() const { return m_ranges.size(); }
2517

2618
void AddressRangeListImpl::Reserve(size_t capacity) {
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# REQUIRES: x86
2+
3+
# RUN: split-file %s %t
4+
# RUN: llvm-mc -triple x86_64-pc-linux -filetype=obj %t/input.s -o %t/input.o
5+
# RUN: %lldb %t/input.o -o "command script import %t/script.py" -o exit | FileCheck %s
6+
7+
# CHECK: Found 1 function(s).
8+
# CHECK: foo: [input.o[0x0-0x7), input.o[0x7-0xe), input.o[0x14-0x1b), input.o[0x1b-0x1c)]
9+
10+
#--- script.py
11+
import lldb
12+
13+
def __lldb_init_module(debugger, internal_dict):
14+
target = debugger.GetSelectedTarget()
15+
sym_ctxs = target.FindFunctions("foo")
16+
print(f"Found {len(sym_ctxs)} function(s).")
17+
for ctx in sym_ctxs:
18+
fn = ctx.function
19+
print(f"{fn.name}: {fn.GetRanges()}")
20+
21+
#--- input.s
22+
# An example of a function which has been split into two parts. Roughly
23+
# corresponds to this C code.
24+
# int baz();
25+
# int bar() { return 47; }
26+
# int foo(int flag) { return flag ? bar() : baz(); }
27+
# The function bar has been placed "in the middle" of foo.
28+
29+
.text
30+
31+
.type foo,@function
32+
foo:
33+
.cfi_startproc
34+
cmpl $0, %edi
35+
je foo.__part.2
36+
jmp foo.__part.1
37+
.cfi_endproc
38+
.Lfoo_end:
39+
.size foo, .Lfoo_end-foo
40+
41+
foo.__part.1:
42+
.cfi_startproc
43+
callq bar
44+
jmp foo.__part.3
45+
.Lfoo.__part.1_end:
46+
.size foo.__part.1, .Lfoo.__part.1_end-foo.__part.1
47+
.cfi_endproc
48+
49+
bar:
50+
.cfi_startproc
51+
movl $47, %eax
52+
retq
53+
.cfi_endproc
54+
.Lbar_end:
55+
.size bar, .Lbar_end-bar
56+
57+
foo.__part.2:
58+
.cfi_startproc
59+
callq baz
60+
jmp foo.__part.3
61+
.Lfoo.__part.2_end:
62+
.size foo.__part.2, .Lfoo.__part.2_end-foo.__part.2
63+
.cfi_endproc
64+
65+
foo.__part.3:
66+
.cfi_startproc
67+
retq
68+
.Lfoo.__part.3_end:
69+
.size foo.__part.3, .Lfoo.__part.3_end-foo.__part.3
70+
.cfi_endproc
71+
72+
73+
.section .debug_abbrev,"",@progbits
74+
.byte 1 # Abbreviation Code
75+
.byte 17 # DW_TAG_compile_unit
76+
.byte 1 # DW_CHILDREN_yes
77+
.byte 37 # DW_AT_producer
78+
.byte 8 # DW_FORM_string
79+
.byte 19 # DW_AT_language
80+
.byte 5 # DW_FORM_data2
81+
.byte 17 # DW_AT_low_pc
82+
.byte 1 # DW_FORM_addr
83+
.byte 85 # DW_AT_ranges
84+
.byte 35 # DW_FORM_rnglistx
85+
.byte 116 # DW_AT_rnglists_base
86+
.byte 23 # DW_FORM_sec_offset
87+
.byte 0 # EOM(1)
88+
.byte 0 # EOM(2)
89+
.byte 2 # Abbreviation Code
90+
.byte 46 # DW_TAG_subprogram
91+
.byte 0 # DW_CHILDREN_no
92+
.byte 17 # DW_AT_low_pc
93+
.byte 1 # DW_FORM_addr
94+
.byte 18 # DW_AT_high_pc
95+
.byte 1 # DW_FORM_addr
96+
.byte 3 # DW_AT_name
97+
.byte 8 # DW_FORM_string
98+
.byte 0 # EOM(1)
99+
.byte 0 # EOM(2)
100+
.byte 3 # Abbreviation Code
101+
.byte 46 # DW_TAG_subprogram
102+
.byte 0 # DW_CHILDREN_no
103+
.byte 85 # DW_AT_ranges
104+
.byte 35 # DW_FORM_rnglistx
105+
.byte 64 # DW_AT_frame_base
106+
.byte 24 # DW_FORM_exprloc
107+
.byte 3 # DW_AT_name
108+
.byte 8 # DW_FORM_string
109+
.byte 0 # EOM(1)
110+
.byte 0 # EOM(2)
111+
.byte 0 # EOM(3)
112+
113+
.section .debug_info,"",@progbits
114+
.Lcu_begin0:
115+
.long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
116+
.Ldebug_info_start0:
117+
.short 5 # DWARF version number
118+
.byte 1 # DWARF Unit Type
119+
.byte 8 # Address Size (in bytes)
120+
.long .debug_abbrev # Offset Into Abbrev. Section
121+
.byte 1 # Abbrev [1] DW_TAG_compile_unit
122+
.asciz "Hand-written DWARF" # DW_AT_producer
123+
.short 29 # DW_AT_language
124+
.quad 0 # DW_AT_low_pc
125+
.byte 1 # DW_AT_ranges
126+
.long .Lrnglists_table_base0 # DW_AT_rnglists_base
127+
.byte 2 # Abbrev [2] DW_TAG_subprogram
128+
.quad bar # DW_AT_low_pc
129+
.quad .Lbar_end # DW_AT_high_pc
130+
.asciz "bar" # DW_AT_name
131+
.byte 3 # Abbrev [3] DW_TAG_subprogram
132+
.byte 0 # DW_AT_ranges
133+
.byte 1 # DW_AT_frame_base
134+
.byte 86
135+
.asciz "foo" # DW_AT_name
136+
.byte 0 # End Of Children Mark
137+
.Ldebug_info_end0:
138+
139+
.section .debug_rnglists,"",@progbits
140+
.long .Ldebug_list_header_end0-.Ldebug_list_header_start0 # Length
141+
.Ldebug_list_header_start0:
142+
.short 5 # Version
143+
.byte 8 # Address size
144+
.byte 0 # Segment selector size
145+
.long 2 # Offset entry count
146+
.Lrnglists_table_base0:
147+
.long .Ldebug_ranges0-.Lrnglists_table_base0
148+
.long .Ldebug_ranges1-.Lrnglists_table_base0
149+
.Ldebug_ranges0:
150+
.byte 6 # DW_RLE_start_end
151+
.quad foo
152+
.quad .Lfoo_end
153+
.byte 6 # DW_RLE_start_end
154+
.quad foo.__part.1
155+
.quad .Lfoo.__part.1_end
156+
.byte 6 # DW_RLE_start_end
157+
.quad foo.__part.2
158+
.quad .Lfoo.__part.2_end
159+
.byte 6 # DW_RLE_start_end
160+
.quad foo.__part.3
161+
.quad .Lfoo.__part.3_end
162+
.byte 0 # DW_RLE_end_of_list
163+
.Ldebug_ranges1:
164+
.byte 6 # DW_RLE_start_end
165+
.quad bar
166+
.quad .Lbar_end
167+
.byte 6 # DW_RLE_start_end
168+
.quad foo.__part.1
169+
.quad .Lfoo.__part.1_end
170+
.byte 6 # DW_RLE_start_end
171+
.quad foo.__part.2
172+
.quad .Lfoo.__part.2_end
173+
.byte 6 # DW_RLE_start_end
174+
.quad foo.__part.3
175+
.quad .Lfoo.__part.3_end
176+
.byte 6 # DW_RLE_start_end
177+
.quad foo
178+
.quad .Lfoo_end
179+
.byte 0 # DW_RLE_end_of_list
180+
.Ldebug_list_header_end0:
181+
182+
.section ".note.GNU-stack","",@progbits

0 commit comments

Comments
 (0)