DW_AT_entry_pc for methods in (artefical) copy declaration of structures
If a class from another unit is used in the current unit, FPC may write a partial copy of that class's debug info.
E.g. if using var a: TStringList; in unit Foo
- On LINUX
- If
classeshas debug info: No copy. Instead a reference to the existing CU - If
classesdoes NOT have debug info: (partial) Debug info for TStringList is written toFoo
- If
- On WINDOWS
- (partial) Debug info for TStringList is always written to
Foo
- (partial) Debug info for TStringList is always written to
In that partial debug info, methods do not have any address info (DW_AT_low_pc, DW_AT_high_pc, DW_AT_Entry_pc).
The absence of DW_AT_low_pc, DW_AT_high_pc is ok.
- After all, if
classesdoes not have debug info, then the debugger should behave (for stepping and stack) as if the function does not exist. - And if
classeshas debug info, then declaring info for the same block of memory twice may not be a good idea.
On the other hand, the type TStringList is used in foo, which is why other debug info about TStringList is correctly included.
Now that we have a debugger that can call functions as part of watche evaluation, it is highly desirable that methods of TStringList can be called too.
- for virtual methods that works, because the dwarf "copy declaration" has the DW_AT_vtable_elem_location
- for non virtual methods, the debugger finds a function without address info.
It would be good if that address info could be made available.
As indicated having both DW_AT_low_pc and DW_AT_high_pc may not be desirable.
- Having only DW_AT_low_pc would be an option.
- So is using DW_AT_entry_pc
Either one would be ok for FpDebug.
I found the following relevat bits in DWARF-3 (but please double check
2.17 Code Addresses and Ranges
Any debugging information entry describing an entity that has a machine code address or range of machine code addresses, which includes compilation units, module initialization, subroutines, ordinary blocks, try/catch blocks, labels and the like, may have
• A DW_AT_low_pc attribute for a single address,
• A DW_AT_low_pc and DW_AT_high_pc pair of attributes for a single contiguous range of addresses,
The "entry address" is a single address. However a subroutine itself is not a single address, but a range. Therefore I suggest DW_AT_entry_pc.
2.17.1 Single Address
When there is a single address associated with an entity, such as a label or alternate entry point of a subprogram, the entry has a DW_AT_low_pc attribute whose value is the relocated address for the entity.
While the DW_AT_entry_pc attribute might also seem appropriate for this purpose, historically the DW_AT_low_pc attribute was used before the DW_AT_entry_pc was introduced (in DWARF Version 3). There is insufficient reason to change this
The address we would include is not an alternate entry point, it is the normal entry point.
So the question is, would this subroutine considered an entity with a "single address associated"?
Or should it be considered, that it has a low/high which just aren't specified.
An "alternate entry point" would be DW_TAG_entry_point, which indeed is documented to use DW_AT_low_pc.
In DWARF-5 the part about While the DW_AT_entry_pc is no longer included.
2.18 Entry Address
The entry or first executable instruction generated for an entity, if applicable, is often the lowest addressed instruction of a contiguous range of instructions. In other cases, the entry address needs to be specified explicitly.
Any debugging information entry describing an entity that has a range of code addresses, which includes compilation units, module initialization, subroutines, ordinary blocks, try/catch blocks, and the like, may have a DW_entry_pc attribute to indicate the first executable instruction within that range of addresses. The value of the DW_AT_entry_pc attribute is a relocated address. If no DW_AT_entry_pc attribute is present, then the entry address is assumed to be the same as the value of the DW_AT_low_pc attribute, if present; otherwise, the entry address is unknown.
2.18 IMHO matches our situation. We have a contiguous range of instructions. We are at the lowest addressed, which would mean DW_AT_low_pc, but we don't (want to) specify the entire range, which could IMHO make this readable either way.
3.3.3 Subroutine and Entry Point Locations
A subroutine entry may have either a DW_AT_low_pc and DW_AT_high_pc pair of attributes or a DW_AT_ranges attribute whose values encode the contiguous or non-contiguous address ranges, respectively, of the machine instructions generated for the subroutine (see Section 2.17).
A subroutine entry may also have a DW_AT_entry_pc attribute whose value is the address of the first executable instruction of the subroutine (see Section 2.18). An entry point has a DW_AT_low_pc attribute whose value is the relocated address of the first machine instruction generated for the entry point.
While the DW_AT_entry_pc attribute might also seem appropriate for this purpose, historically the DW_AT_low_pc attribute was used before the DW_AT_entry_pc was introduced (in DWARF Version 3). There is insufficient reason to change this.
A subroutine entry representing a subroutine declaration that is not also a definition does not have code address or range attributes.
3.3.3 says DW_AT_low_pc and DW_AT_high_pc pair, but we don't want a pair (at least I suggest we shouldn't want)
It also says: A subroutine entry representing a subroutine declaration that is not also a definition does not have code address or range attributes., which IMHO certainly means at least DW_AT_low_pc and DW_AT_high_pc. And only maybe would also relate to DW_AT_entry_pc.
I tested the patch on Linux and Windows.
program cu;
uses classes;
var l: TStringList;
begin
l := TStringList.Create;
l.Add('123');
l.Add('abc');
l.Add('...'); // break here // add watch (function eval enabled): l.GetSorted()
end.
l.GetSorted() is not virtual.
- On Linux: Compiling against an RTL without dwarf, it can't be called. With the patch it can be called.
- On Window: It cant be called at all (without the patch) as the debugger always finds the partial TStringList in the current unit first. With the patch it works.
get is virtual. So a watch l.get()
I have a 2nd test app, on which the internal linker on Windows sets the address to $00000000. Compiling with -Xe however works.
See the attached need_xe.zip. Methods of TFoo (copied DWARF) in the main program "dwarf_prop_test" CU have an address of zero.
So there may be an issue in the internal linker on Windows. However the addition is still an improvement. And, anyone needing it, but hitting the linker issue, can use -Xe