Skip to content

Memtag ABI: Clarify the semantics of LDG(S) in relocation processing #411

Description

@andreil99

Section 8.4 Relocation Operations could benefit from clarifying the semantics of the LDG(S) operation used in the extended relocation definitions. In particular, it would be helpful to clarify:

  • whether LDG(S) is an abstract semantic operation ("materialize the correct logical tag for S") or whether it normatively requires execution of the architectural ldg instruction;

  • the expected observable behavior of LDG(S) when S refers to an allocation that is not allocation-tagged (i.e. whether the resulting pointer is expected to carry logical tag 0).

The following wording is proposed for the definition of LDG(pointer):

LDG(pointer) denotes the operation of materializing a pointer whose address equals pointer and whose logical tag equals the allocation tag associated with the allocation containing pointer. If the referenced allocation is not allocation-tagged the resulting logical tag is zero. 

Context and details

Section 8.4 Relocation Operations extends the following relocation types:

ELF64 Relocation Base semantics MemtagABI semantics
R_AARCH64_ABS64 S + A LDG(S) + A
R_AARCH64_GLOB_DAT S + A LDG(S) + A
R_AARCH64_RELATIVE Delta + A LDG(Delta + A + *P) - *P

The specification also defines:

"LDG(pointer) is an instruction for the run-time environment to use the ldg instruction on pointer to materialize the correct logical tag for a symbol."

It is not entirely clear whether LDG(S) is intended to be interpreted as an abstract semantic operation or as a normative requirement to execute the architectural ldg instruction.

These relocation types are also generated for function pointers. For example:

using FnT = void();

extern "C" [[gnu::visibility("default")]] void defaultFunction() {}

constinit FnT* defaultFunctionPtr = &defaultFunction; // R_AARCH64_ABS64

In this example, the relocation symbol S is the address of defaultFunction in the executable .text section.

The ARM Architecture Reference Manual defines ldg as a load operation:

accdesc = CreateAccDescLDGSTG(MemOp_LOAD, stzgm, t);  
...  
tag = AArch64_MemTag(address, accdesc);

At the same time, the architecture specifies that reading the allocation tag of a non-allocation-tagged address yields allocation tag 0:

if accdesc.tagaccess && memtagtype == MemTag_AllocationTagged then  
    (memstatus, tag) = PhysMemTagRead(memaddrdesc, accdesc);  
    return (tag, memaddrdesc, memstatus);  
...  
else  
    // Otherwise read the tag as zero  
    tag = '0000';  
    return (tag, memaddrdesc, memstatus);  
end;

The ambiguity arises because ldg cannot be executed on every symbol address. The instruction is defined as a load operation and therefore performs read permission checks. Consequently, executing ldg on an execute-only (XOM) mapping faults.

On the other hand, the library pseudocode for ldg specifies that reading the allocation tag of a non-allocation-tagged address returns allocation tag 0. Therefore, for symbols known not to be allocation-tagged, the observable result of LDG(S) can be obtained without executing the ldg instruction.

It is currently in the spec implicitly, as the spec does not require to always match the LDG(pointer) operation to the ldg instruction.
However defining LDG(S) explicitly as a semantic operation, together with its observable behavior (including the result for non-allocation-tagged memory), would remove any possible ambiguity while allowing implementations to produce the required result in an equivalent manner.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions