Skip to content

Commit 11c9ee1

Browse files
committed
Add support for non-contiguous blocks to find_pc_partial_function
This change adds an optional output parameter BLOCK to find_pc_partial_function. If BLOCK is non-null, then *BLOCK will be set to the address of the block corresponding to the function symbol if such a symbol was found during lookup. Otherwise it's set to the NULL value. Callers may wish to use the block information to determine whether the block contains any non-contiguous ranges. The caller may also iterate over or examine those ranges. When I first started looking at the broken stepping behavior associated with functions w/ non-contiguous ranges, I found that I could "fix" the problem by disabling the find_pc_partial_function cache. It would sometimes happen that the PC passed in would be between the low and high cache values, but would be in some other function that happens to be placed in between the ranges for the cached function. This caused incorrect values to be returned. So dealing with this cache turns out to be very important for fixing this problem. I explored three different ways of dealing with the cache. My first approach was to clear the cache when a block was encountered with more than one range. This would cause the non-cache pathway to be executed on the next call to find_pc_partial_function. Another approach, which I suspect is slightly faster, checks to see whether the PC is within one of the ranges associated with the cached block. If so, then the cached values can be used. It falls back to the original behavior if there is no cached block. The current approach, suggested by Simon Marchi, is to restrict the low/high pc values recorded for the cache to the beginning and end of the range containing the PC value under consideration. This allows us to retain the simple (and fast) test for determining whether the memoized (cached) values apply to the PC passed to find_pc_partial_function. Another choice that had to be made regards setting *ADDRESS and *ENDADDR. There are three possibilities which might make sense: 1) *ADDRESS and *ENDADDR represent the lowest and highest address of the function. 2) *ADDRESS and *ENDADDR are set to the start and end address of the range containing the entry pc. 3) *ADDRESS and *ENDADDR are set to the start and end address of the range in which PC is found. An earlier version of this patch implemented option #1. I found out that it's not very useful though and, in fact, returns results that are incorrect when used in the context of determining the start and end of the function for doing prologue analysis. While debugging a function in which the entry pc was in the second range (of a function containing two non-contiguous ranges), I noticed that amd64_skip_prologue called find_pc_partial_function - the returned start address was set to the beginning of the first range. This is incorrect for this function. What was also interesting was that this first invocation of find_pc_partial_function correctly set the cache for the PC on which it had been invoked, but a slightly later call from skip_prologue_using_sal could not use this cached value because it was now being used to lookup the very lowest address of the function - which is in a range not containing the entry pc. Option #2 is attractive as it would provide a desirable result when used in the context of prologue analysis. However, many callers, including some which do prologue analysis want the condition *ADDRESS <= PC < *ENDADDR to hold. This will not be the case when find_pc_partial_function is called on a PC that's in a non-entry-pc range. A later patch to this series adds find_function_entry_range_from_pc as a wrapper of find_pc_partial_function. Option #3 causes the *ADDRESS <= PC < *ENDADDR property to hold. If find_pc_partial_function is called with a PC that's within entry pc's range, then it will correctly return the limits of that range. So, if the result of a minsym search is passed to find_pc_partial_function to find the limits, then correct results will be achieved. Returned limits (for prologue analysis) won't be correct when PC is within some other (non-entry-pc) range. I don't yet know how big of a problem this might be; I'm guessing that it won't be a serious problem - if a compiler generates functions which have non-contiguous ranges, then it also probably generates DWARF2 CFI which makes a lot of the old prologue analysis moot. I've implemented option #3 for this version of the patch. I don't see any regressions for x86-64. Moreover, I don't expect to see regressions for other targets either simply because find_pc_partial_function behaves the same as it did before for the contiguous address range case. That said, there may be some adjustments needed if GDB encounters a function requiring prologue analysis which occupies non-contiguous ranges. gdb/ChangeLog: * symtab.h (find_pc_partial_function): Add new parameter `block'. * blockframe.c (cache_pc_function_block): New static global. (clear_pc_function_cache): Clear cache_pc_function_block. (find_pc_partial_function): Move comment to symtab.h. Add support for non-contiguous blocks.
1 parent 8cad375 commit 11c9ee1

File tree

3 files changed

+121
-23
lines changed

3 files changed

+121
-23
lines changed

gdb/ChangeLog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
block.c (make_blockranges): New function.
1111
* dwarf2read.c (dwarf2_record_block_ranges): Fill in BLOCK_RANGES
1212
for block.
13+
* symtab.h (find_pc_partial_function): Add new parameter `block'.
14+
* blockframe.c (cache_pc_function_block): New static global.
15+
(clear_pc_function_cache): Clear cache_pc_function_block.
16+
(find_pc_partial_function): Move comment to symtab.h. Add
17+
support for non-contiguous blocks.
1318

1419
2018-08-24 Pedro Alves <[email protected]>
1520
Simon Marchi <[email protected]>

gdb/blockframe.c

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,35 @@ find_pc_sect_containing_function (CORE_ADDR pc, struct obj_section *section)
165165
return block_containing_function (bl);
166166
}
167167

168-
/* These variables are used to cache the most recent result
169-
of find_pc_partial_function. */
168+
/* These variables are used to cache the most recent result of
169+
find_pc_partial_function.
170+
171+
The addresses cache_pc_function_low and cache_pc_function_high
172+
record the range in which PC was found during the most recent
173+
successful lookup. When the function occupies a single contiguous
174+
address range, these values correspond to the low and high
175+
addresses of the function. (The high address is actually one byte
176+
beyond the last byte of the function.) For a function with more
177+
than one (non-contiguous) range, the range in which PC was found is
178+
used to set the cache bounds.
179+
180+
When determining whether or not these cached values apply to a
181+
particular PC value, PC must be within the range specified by
182+
cache_pc_function_low and cache_pc_function_high. In addition to
183+
PC being in that range, cache_pc_section must also match PC's
184+
section. See find_pc_partial_function() for details on both the
185+
comparison as well as how PC's section is determined.
186+
187+
The other values aren't used for determining whether the cache
188+
applies, but are used for setting the outputs from
189+
find_pc_partial_function. cache_pc_function_low and
190+
cache_pc_function_high are used to set outputs as well. */
170191

171192
static CORE_ADDR cache_pc_function_low = 0;
172193
static CORE_ADDR cache_pc_function_high = 0;
173194
static const char *cache_pc_function_name = 0;
174195
static struct obj_section *cache_pc_function_section = NULL;
196+
static const struct block *cache_pc_function_block = nullptr;
175197

176198
/* Clear cache, e.g. when symbol table is discarded. */
177199

@@ -182,24 +204,14 @@ clear_pc_function_cache (void)
182204
cache_pc_function_high = 0;
183205
cache_pc_function_name = (char *) 0;
184206
cache_pc_function_section = NULL;
207+
cache_pc_function_block = nullptr;
185208
}
186209

187-
/* Finds the "function" (text symbol) that is smaller than PC but
188-
greatest of all of the potential text symbols in SECTION. Sets
189-
*NAME and/or *ADDRESS conditionally if that pointer is non-null.
190-
If ENDADDR is non-null, then set *ENDADDR to be the end of the
191-
function (exclusive), but passing ENDADDR as non-null means that
192-
the function might cause symbols to be read. This function either
193-
succeeds or fails (not halfway succeeds). If it succeeds, it sets
194-
*NAME, *ADDRESS, and *ENDADDR to real information and returns 1.
195-
If it fails, it sets *NAME, *ADDRESS and *ENDADDR to zero and
196-
returns 0. */
197-
198-
/* Backward compatibility, no section argument. */
210+
/* See symtab.h. */
199211

200212
int
201213
find_pc_partial_function (CORE_ADDR pc, const char **name, CORE_ADDR *address,
202-
CORE_ADDR *endaddr)
214+
CORE_ADDR *endaddr, const struct block **block)
203215
{
204216
struct obj_section *section;
205217
struct symbol *f;
@@ -241,17 +253,62 @@ find_pc_partial_function (CORE_ADDR pc, const char **name, CORE_ADDR *address,
241253
if (compunit_symtab != NULL)
242254
{
243255
/* Checking whether the msymbol has a larger value is for the
244-
"pathological" case mentioned in print_frame_info. */
256+
"pathological" case mentioned in stack.c:find_frame_funname.
257+
258+
We use BLOCK_ENTRY_PC instead of BLOCK_START_PC for this
259+
comparison because the minimal symbol should refer to the
260+
function's entry pc which is not necessarily the lowest
261+
address of the function. This will happen when the function
262+
has more than one range and the entry pc is not within the
263+
lowest range of addresses. */
245264
f = find_pc_sect_function (mapped_pc, section);
246265
if (f != NULL
247266
&& (msymbol.minsym == NULL
248-
|| (BLOCK_START (SYMBOL_BLOCK_VALUE (f))
267+
|| (BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (f))
249268
>= BMSYMBOL_VALUE_ADDRESS (msymbol))))
250269
{
251-
cache_pc_function_low = BLOCK_START (SYMBOL_BLOCK_VALUE (f));
252-
cache_pc_function_high = BLOCK_END (SYMBOL_BLOCK_VALUE (f));
270+
const struct block *b = SYMBOL_BLOCK_VALUE (f);
271+
253272
cache_pc_function_name = SYMBOL_LINKAGE_NAME (f);
254273
cache_pc_function_section = section;
274+
cache_pc_function_block = b;
275+
276+
/* For blocks occupying contiguous addresses (i.e. no gaps),
277+
the low and high cache addresses are simply the start
278+
and end of the block.
279+
280+
For blocks with non-contiguous ranges, we have to search
281+
for the range containing mapped_pc and then use the start
282+
and end of that range.
283+
284+
This causes the returned *ADDRESS and *ENDADDR values to
285+
be limited to the range in which mapped_pc is found. See
286+
comment preceding declaration of find_pc_partial_function
287+
in symtab.h for more information. */
288+
289+
if (BLOCK_CONTIGUOUS_P (b))
290+
{
291+
cache_pc_function_low = BLOCK_START (b);
292+
cache_pc_function_high = BLOCK_END (b);
293+
}
294+
else
295+
{
296+
int i;
297+
for (i = 0; i < BLOCK_NRANGES (b); i++)
298+
{
299+
if (BLOCK_RANGE_START (b, i) <= mapped_pc
300+
&& mapped_pc < BLOCK_RANGE_END (b, i))
301+
{
302+
cache_pc_function_low = BLOCK_RANGE_START (b, i);
303+
cache_pc_function_high = BLOCK_RANGE_END (b, i);
304+
break;
305+
}
306+
}
307+
/* Above loop should exit via the break. */
308+
gdb_assert (i < BLOCK_NRANGES (b));
309+
}
310+
311+
255312
goto return_cached_value;
256313
}
257314
}
@@ -281,6 +338,7 @@ find_pc_partial_function (CORE_ADDR pc, const char **name, CORE_ADDR *address,
281338
cache_pc_function_name = MSYMBOL_LINKAGE_NAME (msymbol.minsym);
282339
cache_pc_function_section = section;
283340
cache_pc_function_high = minimal_symbol_upper_bound (msymbol);
341+
cache_pc_function_block = nullptr;
284342

285343
return_cached_value:
286344

@@ -311,6 +369,9 @@ find_pc_partial_function (CORE_ADDR pc, const char **name, CORE_ADDR *address,
311369
*endaddr = cache_pc_function_high;
312370
}
313371

372+
if (block != nullptr)
373+
*block = cache_pc_function_block;
374+
314375
return 1;
315376
}
316377

gdb/symtab.h

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1694,10 +1694,42 @@ extern struct symbol *find_pc_sect_containing_function
16941694

16951695
extern struct symbol *find_symbol_at_address (CORE_ADDR);
16961696

1697-
/* lookup function from address, return name, start addr and end addr. */
1698-
1699-
extern int find_pc_partial_function (CORE_ADDR, const char **, CORE_ADDR *,
1700-
CORE_ADDR *);
1697+
/* Finds the "function" (text symbol) that is smaller than PC but
1698+
greatest of all of the potential text symbols in SECTION. Sets
1699+
*NAME and/or *ADDRESS conditionally if that pointer is non-null.
1700+
If ENDADDR is non-null, then set *ENDADDR to be the end of the
1701+
function (exclusive). If the optional parameter BLOCK is non-null,
1702+
then set *BLOCK to the address of the block corresponding to the
1703+
function symbol, if such a symbol could be found during the lookup;
1704+
nullptr is used as a return value for *BLOCK if no block is found.
1705+
This function either succeeds or fails (not halfway succeeds). If
1706+
it succeeds, it sets *NAME, *ADDRESS, and *ENDADDR to real
1707+
information and returns 1. If it fails, it sets *NAME, *ADDRESS
1708+
and *ENDADDR to zero and returns 0.
1709+
1710+
If the function in question occupies non-contiguous ranges,
1711+
*ADDRESS and *ENDADDR are (subject to the conditions noted above) set
1712+
to the start and end of the range in which PC is found. Thus
1713+
*ADDRESS <= PC < *ENDADDR with no intervening gaps (in which ranges
1714+
from other functions might be found).
1715+
1716+
This property allows find_pc_partial_function to be used (as it had
1717+
been prior to the introduction of non-contiguous range support) by
1718+
various tdep files for finding a start address and limit address
1719+
for prologue analysis. This still isn't ideal, however, because we
1720+
probably shouldn't be doing prologue analysis (in which
1721+
instructions are scanned to determine frame size and stack layout)
1722+
for any range that doesn't contain the entry pc. Moreover, a good
1723+
argument can be made that prologue analysis ought to be performed
1724+
starting from the entry pc even when PC is within some other range.
1725+
This might suggest that *ADDRESS and *ENDADDR ought to be set to the
1726+
limits of the entry pc range, but that will cause the
1727+
*ADDRESS <= PC < *ENDADDR condition to be violated; many of the
1728+
callers of find_pc_partial_function expect this condition to hold. */
1729+
1730+
extern int find_pc_partial_function (CORE_ADDR pc, const char **name,
1731+
CORE_ADDR *address, CORE_ADDR *endaddr,
1732+
const struct block **block = nullptr);
17011733

17021734
/* Return the type of a function with its first instruction exactly at
17031735
the PC address. Return NULL otherwise. */

0 commit comments

Comments
 (0)