010205.1 W R. Brender Representation Discontiguous scopes

Location lists and discontiguous scopes share a common problem. The proposals
of 001101.1 and 001130.1 attempted to address that problem for both of them,
although those proposals talked mostly about ranges lists because they are
the simpler of the two (having no location associated with each list entry)
and the problem was originally noticed in the context of thinking about ranges
lists.

After two attempts, it appears that trying to separate location lists and
discontiguous scopes into two independent proposals doesn't work. So,
following is a single proposal to cover them both. (Note: this proposal
merely merges both parts of my 24 January email into a single proposal.)

PROBLEM STATEMENT
-----------------

The DWARF V2 design of location lists for representing the locations of
variables that change over time is not adequate to deal with variables
declared in functions whose code is split into multiple program (text)
sections. Similarly for discontiguous scopes.

Recall that in DWARF V2, location lists are defined in Sections 2.4,
2.4.6 and 7.7.2. In DWARF V2.1 Draft 5DW they are defined in Figure 3
(see specifically loclistptr), Sections 2.5, 2.5.4, 7.5.4 (see
specifically loclistptr) and 7.7.3. In both cases they are referenced
from attributes, most notably DW_AT_location, whose value is interpreted
as an offset in the .debug_loc section where the beginning of a location
list is found.

Recall that in DWARF V2.1 Draft 5DW, discontiguous scopes are defined in
Figure 3 (see specifically rangelistptr), Sections 2.6.3, 7.5.4 (see
specifically rangelistptr) and 7.24. A range list is referenced from
a DW_ranges attribute whose value is interpreted as an offset in the
.debug_ranges section where the beginning of a ranges list is found.

A location list consists of a sequence of location list entries, where
each entry consists of

    - A beginning address
    - An ending address
    - A location expression

The two addresses are specified to be "relative to the base address of
the compilation unit referencing this location list". (An attractive
consequence of this formulation is the two addresses need not be relocated
at linktime or loadtime, which has both size and system performance
benefits.)

In DWARF V2, there are two problems with this formulation. The first is
that in a compilation that is split into more than one program section,
it is ambiguous which program section establishes "the base address".
DWARF V2.1 Section 3.1 resolves this problem as follows:

    "The base address of a compilation is defined as follows: If a
    DW_AT_entry_pc is given for the unit, the base address is the
    value of that attribute. Otherwise, if a DW_AT_low_pc attribute
    is specified, the base address is the value of that attribute.
    If neither is present, then the base address is undefined and
    any DWARF [debugging information] entry or structure defined
    in terms of the base address of that compilation is not valid."

(Section 2.5.4 would benefit from a reference to this definition.)

The second problem is more subtle and is the problem that this proposal
seeks to correct.

The second problem derives from the definition of the beginning and
ending addresses as relative to the base address, which is to say
as the difference (offset) of the given address minus (relative to)
the base address. That is, for a location list entry with beginning
address BEG, ending address END, location register R0, and base address
BASE, the location list entry is represented as

    (BEG - BASE, END - BASE, <location expression for register R0>)

If there is only a single text program section, then there is no problem:
BEG and BASE are at known offsets relative to the beginning of that section
and the difference is easily computed by the compiler. However, if BEG and
END are defined in one program section and BASE is defined in another,
then there is a problem: Few object file representations in common use
today provide relocation commands that singly or in combination are able
to compute the required difference and use it to initialize storage at
link time (or possibly even load time).

    As a key example, ELF for the IA-32 architecture does not provide such
    relocation capability.

Without such relocation capability, optimizing compilers that split
modules and functions into more than one program section cannot implement
DWARF location lists.

Similarly, a ranges list consists of a sequence of ranges list entries,
where each entry consists of

    - A beginning address
    - An ending address

The two addresses are specified to be "relative to the base address of
the compilation unit referencing this ranges list".

A ranges list is just like a location list in its use of relative
addresses to define the beginning and ending addresses of each entry of
the list. As a result, it shares all of the problems of location lists when
multiple sections containing generated machine code are involved.

The discussion that follows is presented in terms of location lists; however
it should be understood that it applies to ranges lists as well. Moreover,
the proposed textural changes do cover both location lists and ranges lists.

A detailed example that illustrates how this problem comes about can be
found at the end of this proposal.


PROPOSAL
--------

This proposal has two main parts:

  - Define an attribute DW_AT_base_addresses, which may be used only
    for the compilation unit DIE, and whose value is a sequence of
    addresses where each address is the beginning address of one of
    the text program sections that hold the machine code.

  - Define a means to associate one or more entries in a location list or
    ranges list with one of the base addresses from the DW_AT_base_addresses
    attribute, and use that particular base address in computing the
    relative addresses for those entries.


DW_AT_base_addresses

This attribute is used only on a compilation unit DIE and is used in
combination with DW_AT_ranges (and instead of DW_AT_low_pc and
DW_AT_high_pc). The value of this attribute is a sequence of addresses,
represented using class block, where each address is the beginning address
of one of the (text) sections that contain some of the machine code for
the compilation unit.

DW_AT_ranges and DW_AT_base_addresses are used when the machine code
for the compilation unit is contained in discontiguous ranges of
addresses. This will always be the case when the code is contained
in multiple, independently relocated text sections; moreover, it can
also apply when there is only a single program section with contains
both instructions and data, where the instructions are non-contiguous.

The order of addresses is arbitrary but the actual order is used in
the encoding of location lists as explained below. Let the first
address be called the "first base address", the second called the
"second base address", and so on.

When DW_AT_base_addresses is not present, then the value of DW_AT_low_pc
is called the "first base address", or for simplicity just "the base
address".

This proposal replaces the use of DW_AT_entry_pc in establishing the
base address of a compilation unit.

Relating a location list entry to "its" base address

First, recall that the addresses BEG and END of a relocation list entry
must satisfy the relationship BEG < END. (The end address is defined as
one past the last address in the range, so even for a range consisting
of one address ADR we have ADR < ADR + 1.) Since the two relative
addresses must logically be relative to the same base address, we can
define the low relative address L = BEG - BASE and the high relative
address H = END - BASE, and it immediately follows that L < H.

As a consequence, any relative address pair (L, H) that does not satisfy
L < H can be used to encode something other than a "normal" location
list entry. DWARF already exploits this! The pair (0, 0), that is, a pair
of zeros, identifies the end of a list.

Define a "base address selection entry" to consist of the pair (N, 0)
where N > 0; it specifies that subsequent entries in the same location
list are defined relative to the N'th base address (as specified in the
DW_AT_base_addresses attribute of the referencing compilation unit).
This selection applies to subsequent location list entries until a
new base address selection entry is encountered. The first location
list entry is assumed to be relative to the first base address (so that
an initial (1,0) is redundant and need not be present).

There is no need to append a fake DWARF expression (eg, a 2-byte count
of zero) in a (misguided) attempt to make the base address selection
entry "look like" a location entry. (The (0, 0) end of list entry does
not have a pseudo-location either.) The ability of dumpers to scan and
interpret the .debug_loc section independent of other information is not
compromised by this omission.

Finally, adjust the definition of location list to be a sequence of
entries, each of which is either a base address selection entry or
a location list entry, followed by an end of list entry.


DOCUMENT CHANGES
----------------

A) In Figure 2 (Section 2.2), add DW_AT_base_addresses.

B) In Section 2.5.4, replace "Each entry in a location list consists
   of:" together with the following bullets with (note: | helps mark the
   actual changes)

|     "Each entry in a location list is either a base address selection
|     entry (see Section 3.1.2) or a location list entry. A location
      list entry consists of:

|     1. A beginning address. This address is relative to the applicable
         base address of the compilation unit referencing this location
         list. It marks the beginning of the address range over which
         the location is valid.
|     2. An ending address, again relative to the applicable base
         address of the compilation unit referencing this location list.
         It marks the first address past the end of the address range
         over which the location is valid.
      3. A location expression describing the location of the object
         over the range specified by the beginning and ending address.

|     "The applicable base address of a location list entry is determined
|     by the closest preceding base address selection entry in the
|     same location list. If there is no such selection entry, then the
|     applicable base address is the first (possibly only) base address
|     of the compilation unit (see Section 3.1.1).

|     "<italics>In the case of a compilation unit where all of the
|     machine code is contained in a single contiguous section, no base
|     address selection entry is ever needed.</italics>"

C) In Section 2.16.3, replace "Each entry in a range list consists
   of:" together with the following bullets with (note: | helps mark
   the actual changes)

|     "Each entry in a range list is either a base address selection
|     entry (see Section 3.1.2) or a range list entry. A range list
      entry consists of:

|     1. A beginning address. This address is relative to the applicable
      base address of the compilation unit referencing this range
      list.
|     2. An ending address, again relative to the applicable base
      address of the compilation unit referencing this range list.

|     "The applicable base address of a range list entry is determined
|     by the closest preceding base address selection entry in the
|     same ranges list. If there is no such selection entry, then the
|     applicable base address is the first (possibly only) base address
|     of the compilation unit (see Section 3.1.1).

|     <italics>"In the case of a compilation unit where all of the
|     machine code is contained in a single contiguous section, no base
|     address selection entry is ever needed.</italics>"

D) In Section 3.1, bullet 1, replace the bullet with the following:

      "Either a DW_AT_low_pc and DW_AT_high_pc pair of attributes or
      a DW_AT_ranges and DW_AT_base_addresses pair of attributes whose
      values encode the contiguous or non-contiguous address ranges,
      respectively, of the machine instructions generated for the
      compilation unit.

      "DW_AT_low_pc, DW_AT_high_pc and DW_AT_ranges attributes are
      described in Section 2.16.3. The DW_AT_base_addresses attribute
      is described later in Section 3.1.1."

E) In Section 3.1, replace the last paragraph (which defines the base
   address of a compilation unit) with the following:

      "3.1.1 Base Addresses

      "A compilation unit may have one or more base addresses, which are
      specified by the DW_AT_low_pc or DW_AT_base_addresses attribute
      of the compilation unit entry. If neither DW_AT_low_pc nor
      DW_AT_base_addresses is present, then the base address(es) is
      (are) undefined and any DWARF debugging information entry or
      related structure defined in terms of the base address(es) of
      the compilation unit is invalid."

      "If the DW_AT_low_pc attribute is given, then the value of the
      first (and only) base address of the compilation unit is the
      value of that attribute.

      "The value of a DW_AT_base_addresses attribute is a sequence of
      addresses represented using class block, where each address is
      the beginning address of one of the independently relocatable
      sections that contain machine code of the compilation unit.
      There is one address for each section. The first base address
      of the compilation unit is the value of the first address in
      the block, the second base address of the compilation unit is
      the value of the second address in the block, and so on.

      "<italics>There may be only one such address if the machine
      code has multiple discontiguous ranges of instructions, all
      generated in a single section (possibly separated by program
      data or other system information).</italics>

      "3.1.2 Base Address Selection

      "Location lists (see Section 2.5.4) and range lists (see
      Section 2.16.3) may include one or more base address selection
      entries.

      "A base address selection entry consists of:

      "1. A base address index. The value N indicates that the N'th
          base address of the referencing compilation unit is the
          appropriate base address for use in interpreting the beginning
          and ending relative addresses of subsequent entries of
          the location list or ranges list. The value N must not
          be zero or larger than the number of base addresses defined
          for the referencing compilation unit.

          <italics>A base address selection entry affects only the
          list in which it is contained.</italics>

      "2. Zero.

          <italics>The pair (N, 0) cannot be part of a location
          list entry or be a ranges list entry because N and 0
          do not constitute a valid range of addresses (for which
          the beginning address is necessarily less than the
          ending address). The pair (N, 0) cannot be an end of list
          marker because N cannot be 0 in a base address selection
          entry.</italics>

      "If a compilation unit has only a single base address, then
      no base address selection entry is ever required in a location
      list or ranges list of that compilation unit."


F) In Section 7.7.3, replace the current two paragraphs with (note:
   | helps mark the actual changes)

|     "Each entry in a location list is either a base address
|     selection entry or a location list entry.

|     "A base address selection entry consists of two integers.
|     The integers are the same size as used by DW_FORM_addr on the
|     target machine.

|     "A location list entry consists of two relative addresses
      followed by a 2-byte length, followed by a block of contiguous
      bytes. The length specifies the number of bytes in the block
      that follows. The two addresses are the same size as used
      by DW_FORM_addr on the target machine.

G) In Section 7.24, replace the current two paragraphs with (note:
   | helps mark the actual changes)

|     "Each entry in a ranges list is either a base address
|     selection entry or a ranges list entry.

|     "A base address selection entry consists of two integers.
|     The integers are the same size as used by DW_FORM_addr on the
|     target machine.

|     "A ranges list entry consists of two relative addresses.
      The addresses are the same size as used by DW_FORM_addr on
      the target machine.

DISCUSSION
----------

There are a variety of ways to address the problem described, but an
important constraint is that the solution is upward compatible with
the DWARF V2 specification of location lists. The proposal does that.

This proposal solves the problem in a way that retains the property that
no relocations are needed for the .debug_loc section.

It has been suggested that it is desirable for whatever solution is
proposed to be first implemented as a vendor extension, as a way of
proving the viability and correctness of the proposal. While desirable,
it is not possible for me to do so. Of the systems available to me:
  - Alpha VMS supports generating code in multiple sections that are
    independently relocated as late as load time. The proprietary debug
    symbol table representation supports the equivalent of location
    lists in a way that uses real address and length
    pairs rather than relative addresses. That approach does not share
    the DWARF problem.
  - Alpha Tru64 does not support generating code in multiple sections
    that are independently relocated (it uses a variation of COFF);
    neither does it use ELF.
  - Alpha Linux supports generating code in multiple sections that are
    independently relocated at load time, using ELF and DWARF. The
    compilers, however, do not currently support this (treating the
    underlying ELF object format in a manner similar to Tru64 COFF).


ALTERNATIVES CONSIDERED
-----------------------

Here is how this proposal compares to the earlier proposals 001101.1
and 001130.1.

Compared to 001101.1:
  - This proposal and 001101.1 are identical in the way that the
    so-called base address selection entry is used to select the
    appropriate base address for subsequent entries in a location
    list.
  - This proposal defines the DW_AT_base_addresses attribute to
    establish the sequence of base addresses for the unit; 001101.1
    uses the DWARF V2 defined .debug_aranges section to provide that
    information.
  - This proposal uses the DW_AT_ranges attribute on a compilation
    unit to specify the set of ranges for a discontiguous compilation
    unit; 001101.1 did not need (nor allow) DW_AT_ranges on a
    compilation unit because the full range information is already
    available in the .debug_aranges section.
  - This proposal does not change the implementer optional status of
    the .debug_aranges section; 001101.1 requires the use of the
    .debug_aranges section for those (and only those) compilation
    units that generate code in multiple independently relocated
    sections.

Compared to 001130.1:
  - This proposal has a single "central" attribute that specifies
    the set of base addresses for a compilation unit; 001130.1 has
    no central specification.
  - This proposal and 001130.1 both exploit the requirement that
    L < H in a valid location list entry, so that other pairs of
    values can be interpreted in a different way. This proposal
    uses the pair (N, 0) to encode an index that identifies the
    applicable base address; 001130.1 uses the pair (M, B) where
    M is the maximum possible address (either 0xFFFFFFFF or
    0xFFFFFFFFFFFFFFFF) and B is the base address itself to be
    used for subsequent entries.
  - This proposal requires no relocations in the .debug_loc section;
    001130.1 requires a relocation for each occurrence of a base
    address (one for each base address selection entry).

All three proposals drop the use of DW_AT_entry_pc to define "the"
base address of a compilation unit, because the limitation to a single
base address is in fact part of the problem.

One could also explore possible schemes where the control information
that associates a base address with an location list entry is external
to the .debug_loc section. While I played with some such ideas, they
all seemed to be less intuitive and more complicated than any of the
ones mentioned here.


DETAILED PROBLEM DESCRIPTION
----------------------------

Suppose we have an application that looks sort'a like this

---- File application.cxx -----

    void ROUT1 {...}

    void ROUT2 {

        int status = 0;

        while (status) {

            ...hot computes 1...

            if (status) {

                // report the error, should never happen
                //
                ...cold code...

                goto done;
            }

            ...hot computes 2...

            if (status) {

                // report the error, should never happen
                //
                ...cold code...

                goto done;
            }
        }

    done: return;
    }

----------

Suppose the compiler determines (based in clever heuristics, or source
pragmas [not shown], or run-time profile feedback) that the best way to
compile this module is to split the code for ROUT2 into two independent
sections called, say, .text_hot and .text_cold. At the assembler level
the compiled code will look sort'a like this

[*Note: any resemblance to real assembly code for any particular assembler
is mostly amazingly good luck. Hopefully the notion is more or less generic
and suggestive enough to be self-explanatory.]

                      .section   .text_hot
        SECTION$$1:
        ROUT1:         .entry
                      ...some code...

        ROUT2:         .entry
                      ...allocate stack
                      ...initialize status
        ROUT2$$L:
                      ...test status
                      ...hot computes 1

                      beq      ROUT2$$1
                      jmp      COLD$$1
        ROUT2$$1:

                      ...hot computes 2

                      beq      ROUT2$$2
                      jmp      COLD$$2
        ROUT2$$2:

                      br       ROUT2$$L
        ROUT2$$DONE:
                      ...deallocate stack
                      ...return

        SECTION$$1$$END:
                      .end     .text_hot

                      .section .text_cold
        SECTION$$2:
        COLD$$1:
                      ...error reporting code 1 using variable status
                      jmp      ROUT2$$DONE

        COLD$$2:
                      ...error reporting code 2 using variable status
                      jmp      ROUT2$$DONE

        SECTION$$2$$END:
                      .end      .text_cold


Now, given that generated code, what should the DWARF sections look like?

                      .section .debug_abbrev
                      ...yada yada...
                      .end      .debug_abbrev

                      .section .debug_info

                      DW_TAG_compile_unit
                          ...
                          DW_AT_entry_pc(address SECTION$$1)
                          ...
                          DW_TAG_subroutine
                              DW_AT_name("ROUT2")
                              DW_AT_ranges(reference to ROUT2$$RANGES)
                              ...

                      .end      .debug_info

                      .section .debug_loc

        ROUT2$$LOC:          ! Location list for variable 'status' in ROUT2
                      .word      ROUT2 - SECTION$$1                  ! hot part
                      .word      SECTION$$1$$END - SECTION$$1
                      .word      ...<location expression for status>

                      .word      COLD$$1 - SECTION$$1                ! cold part
                      .word      SECTION$$2$$END - SECTION$$1
                      .word      ...<location expression for status>

                      .word 0
                      .word 0

                      .end       .debug_ranges

Consider just the line that reads

                      .word      COLD$$1 - SECTION$$1                ! cold part

This line creates the relative address for the lower bound of one of the
location list entries for status in ROUT2. The compiler surely knows the
offset of COLD$$1 within section .text_cold and it knows offset of SECTION$$1
within section .text_hot. But, the compiler knows nothing about the
relationship of the section .text_hot and .text_cold. That will be
determined later by the linker and/or loader. So, it must use some kind
of relocation directives in the underlying object module representation
to construct the difference between this two locations.

    The PROBLEM is that many object module representations do not
    provide a way to do this. ELF for the IA-32 is an example.

Why subtract SECTION$$1? Because that is the base address specified by the
DW_AT_entry_pc attribute for the compilation unit. It makes no difference
what base address is selected, even one in section .text_cold. The same
problem just shows up again for other entries.

In the solution I propose, we would instead have:

Instead of DW_AT_entry_pc(address SECTION$$1) in the compilation unit we
would have

    DW_AT_base_addresses(address SECTION$$1, address SECTION$$2)

And the location list would look like:

        ROUT2$$LOC:                                      ! assume base 1
                    .word      ROUT2 - SECTION$$1        ! hot part
                    .word      SECTION$$1$$END - SECTION1$$
                    .word      ...<location expression for status>

                    .word      2                          ! select base 2
                    .word      0

                    .word      COLD$$1 - SECTION$$2      ! cold part
                    .word      SECTION$$2$$END - SECTION$$2
                    .word      ...<location expression for status>

                    .word      0
                    .word      0

The first entry in the location list is unchanged.

The two new words are a base address selection entry, which "states" that
following entries are relative to the second base address specified in
the DW_AT_base_addresses attribute of the compilation unit.

The following entry is similar to before but subtracts SECTION$$2
rather than SECTION$$1. Since COLD$$1 and SECTION$$2 are in the same
section (.text_cold), the compiler can compute the difference in their
offsets and establish the contents of that word directly. Similarly for
SECTION$$2$$END and SECTION$$2.

Notice that no relocations of any kind are required for this .debug_loc
section.