This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
Re: RFC: partial symbol table address range generalization
Daniel Jacobowitz <drow@mvista.com> writes:
> I'd like to point you at my thoughts from the first time I noticed this
> behavior:
> <http://sources.redhat.com/ml/gdb/2001-08/msg00161.html>
That's messed up. The compiler produces SO stabs that describe only
the .text section, while happily placing code in other sections. So
the SO stabs' addresses are useless.
If you want to hack on the stabs reader to handle these cases, that
would be great.
> > There is some logic in GDB's lookup functions to cope with overlapping
> > partial symtabs, and they've been working pretty well on our behalf.
> > However, they're fragile, and do break in everyday use. For example,
> > in the executable produced from the source file above, if you try to
> > set a breakpoint on a library routine compiled without debug
> > information, GDB will set the breakpoint in `main' instead. (On some
> > platforms, `_exit' is such a function.)
>
> This particular problem should be avoidable anyway. I would appreciate
> it if you would look at:
> <http://sources.redhat.com/ml/gdb/2001-09/msg00068.html>
Well, okay. But I'd much prefer to see the problem fixed by making
the symbol table contents more accurate than by adding another
heuristic for recognizing insane data. When checks like that get into
the code, they never go away, because nobody's ever really sure
whether the circumstances it was meant to cope with happen any more.
If I can finish up the addrset patch for Dwarf 2, would you be
interested in taking a shot at spiffing up stabs?
> As far as getting this information from the stabs reader, we should be
> able to do it if we know separate "possible" and "definite" ranges, I
> think. I'm not sure if we have enough information to do this. We
> don't for the general case, but we should generally have one "definite"
> range per file (corresponding to the main .text segment). It would be
> nice to at least take this region out of any symtabs that seem to
> encompass it, so that if the main program is built without
> -ffunction-sections, we will behave sanely in the presence of (say)
> libstdc++.a.
I encourage you to take a shot at it. The idea is to have `struct
addrset' support a bunch of groovy operations (like set subtraction,
testing for intersections between two sets, etc.), carefully coded and
gotten right once and for all, that make it easy to do this kind of
sanity checking and refinement. Here's the header file for the code
I've got now; imagine adding the setwise ops you want here, and then
using them in the stabs reader.
/* addrset.h --- interface to `struct addrset' type.
Copyright 2001 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
#ifndef ADDRSET_H
#define ADDRSET_H
/* An address set represents an arbitrary set of CORE_ADDR's. Storage
required is proportional to the smallest number of contiguous
ranges necessary to represent the set. It's efficient to add and
delete address ranges, ask whether a given address is in the range,
and walk the set a range at a time.
We use it to track which pieces of code belong to which `struct
partial_symtab', for example. With C++, the machine code from a
particular .o file no longer appears in the executable file in one
contiguous clump; instead, the .text sections from all .o files
appear first, followed by .gnu.linkonce.t.* sections from all .o
files, holding class methods, destructors, and so on. Since each
method/etc. gets its own .gnu.linkonce.t.* section, there's no real
limit to the number of discontiguous sections a single .o's code
might occupy.
In general, this interface uses pairs of start and end addresses,
both *inclusive*, to describe ranges. This is less graceful in
some ways than using the more traditional start-inclusive,
end-exclusive ranges, or a start-and-length form. However, the
first two each have certain ranges they are unable to represent
clearly:
- How should we represent a range that abuts the top end of the
address space? In start-inclusive, end-inclusive form, we can
simply say (foo, (CORE_ADDR) -1), but in start-inclusive,
end-exclusive form the endpoint can't fit in a CORE_ADDR, and
ends up being zero; ranges of the form (foo, 0) are weird to
think about.
- How should we represent the range that includes the entire
address space? In start-inclusive, end-inclusive form, we can
simply say (0, (CORE_ADDR) -1). In start-inclusive,
end-exclusive form this would be (0, 0), which looks like an
empty range. In start-and-length form, this would also be (0,
0), which has the same problem.
Horribly anal-retentive? Yes, okay, fine. I'm sick and tired of
GDB screwing up at corner cases. At least this one module won't be
at fault. */
struct addrset;
/* Allocate and return a new, empty addrset.
If OBSTACK is non-zero, allocate the addrset's storage in OBSTACK.
Otherwise, allocate its components using xmalloc. */
struct addrset *addrset_new (struct obstack *obstack);
/* Free ADDRSET. It must have been allocated using xmalloc; that is,
the OBSTACK argument to `new_addrset' must have been zero. */
void addrset_free (struct addrset *addrset);
/* Add the addresses from START to END (inclusive!) to ADDRSET. */
void addrset_add (struct addrset *addrset, CORE_ADDR start, CORE_ADDR end);
/* Remove the addresses from START to END (inclusive!) from ADDRSET. */
void addrset_remove (struct addrset *addrset, CORE_ADDR start, CORE_ADDR end);
/* Shift all addresses in ADDRSET by OFFSET. That is, the address (A
+ OFFSET) will be in ADDRSET after this call if and only if the
address A was in ADDRSET before the call. The addition is done
modulo the range of CORE_ADDR.
In general, it's not possible for this function to detect
wraparound: since CORE_ADDR is unsigned, adding a very large
CORE_ADDR value is the only way to shift the set towards lower
addresses. However, this function will signal an error if all the
addresses are not shifted *in the same direction*. That is, if
some addresses wrap around, but others don't, then that's almost
certainly not what was intended. */
void addrset_offset (struct addrset *addrset, CORE_ADDR offset);
/* Return non-zero if ADDR is in ADDRSET, zero otherwise. */
int addrset_in (struct addrset *addrset, CORE_ADDR addr);
/* If ADDR is in ADDRSET, return non-zero and set *START and *END to
the start and end (inclusive) of the largest contiguous range of
addresses in ADDRSET containing ADDR. If ADDR is not in ADDRSET,
return zero. */
int addrset_span (struct addrset *addrset, CORE_ADDR addr,
CORE_ADDR *start, CORE_ADDR *end);
/* You can use the two functions below to traverse an address set, as
follows:
for (addrset_first_range (addrset, &start, &end);
start <= end;
addr = addrset_next_range (addrset, end, &start, &end))
... do something with the range start -- end ...
*/
/* Set *START and *END to the first and last addresses (inclusive) of
the first contiguous range of addresses in ADDRSET. If ADDRSET is
empty, set *START to 1 and *END to zero. */
void addrset_first_range (struct addrset *addrset,
CORE_ADDR *start,
CORE_ADDR *end);
/* Set *START and *END to the first and last addresses (inclusive) of
the first contiguous range of addresses in ADDRSET after AFTER.
(That is, AFTER will not be included in the returned range.)
If there are no addresses in ADDRSET that are > AFTER, then
set *START to 1 and *END to zero. */
void addrset_next_range (struct addrset *addrset,
CORE_ADDR after,
CORE_ADDR *start,
CORE_ADDR *end);
/* Print ADDRSET on OUTFILE, using filtered output and
`print_address_numeric'. */
void addrset_print (struct addrset *addrset, struct ui_file *outfile);
#endif /* ADDRSET_H */