Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce new relocation for landing pad #452

Open
wants to merge 3 commits into
base: complex-label-lp
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
179 changes: 178 additions & 1 deletion riscv-elf.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,9 @@ Description:: Additional information about the relocation
<| S - P
.2+| 65 .2+| TLSDESC_CALL .2+| Static | .2+| Annotate call to TLS descriptor resolver function, `%tlsdesc_call(address of %tlsdesc_hi)`, for relaxation purposes only
<|
.2+| 66-190 .2+| *Reserved* .2+| - | .2+| Reserved for future standard use
.2+| 66 .2+| LPAD .2+| Static | .2+| Annotates the landing pad instruction inserted at the beginning of the function.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the relocation need not be restricted to annotate lpads as function beginnings. Lpads in the middle of functions can also be annotated by this relocation, given that the relocation's symbol field maybe empty.

<|
.2+| 67-190 .2+| *Reserved* .2+| - | .2+| Reserved for future standard use
<|
.2+| 191 .2+| VENDOR .2+| Static | .2+| Paired with a vendor-specific relocation and must be placed immediately before it, indicates which vendor owns the relocation.
<|
Expand Down Expand Up @@ -1210,6 +1212,7 @@ The defined processor-specific section types are listed in <<rv-section-type>>.
| Name | Value | Attributes

| SHT_RISCV_ATTRIBUTES | 0x70000003 | none
| SHT_RISCV_LADING_PAD_INFO | 0x70000004 | none
|===

==== Special Sections
Expand All @@ -1224,12 +1227,16 @@ The defined processor-specific section types are listed in <<rv-section-type>>.
| Name | Type | Attributes

| .riscv.attributes | SHT_RISCV_ATTRIBUTES | none
| .riscv.lpadinfo | SHT_RISCV_LADING_PAD_INFO | none
| .riscv.jvt | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR
| .note.gnu.property | SHT_NOTE | SHF_ALLOC
|===

+++.riscv.attributes+++ names a section that contains RISC-V ELF attributes.

+++.riscv.lpadinfo+++ names a section that contains RISC-V landing pad
information, which used for generating PLT and also can be used for debugging.

+++.riscv.jvt+++ is a linker-created section to store table jump
target addresses. The minimum alignment of this section is 64 bytes.

Expand Down Expand Up @@ -1568,6 +1575,79 @@ the `Zicfilp` extension. An executable or shared library with this bit set is
required to generate PLTs with the landing pad (`lpad`) instruction, and all
label are set to a value which hashed from its function signature.

=== Landing Pad Information Section (`.riscv.lpadinfo`)
kito-cheng marked this conversation as resolved.
Show resolved Hide resolved

The landing pad information section is named `.riscv.lpadinfo`. The section
type (`sh_type`) is `SHT_RISCV_LANDING_PAD_INFO`, and the section flags
(`sh_flags`) should be 0.

The landing pad information section is a section that contains the information
to generate PLT with landing pads. The static linker is required to use the
landing pad values provided by this section when generating lpad instructions
Comment on lines +1585 to +1586

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The static linker is required to use the landing pad values provided by this section when generating lpad instructions

The information contained in .riscv.lpadinfo is not limited to generating lpad insns. It's also used for generating lui t2, <lpad_value> in PLT, so the better wording is "The static linker is required to use the landing pad values provided by this section when generating PLT entries ..."

for functions listed in this section, unless otherwise specified by the user
(e.g. through command line options) or the unlabeled CFI scheme is selected.

This section is consist by the entries of the following structure:

```
typedef struct
{
Elf32_Word lpi_sym; /* Symbol index */
Elf32_Word lpi_value; /* Landing pad value for the symbol */
} Elf32_Lpadinfo;
kito-cheng marked this conversation as resolved.
Show resolved Hide resolved

typedef struct
{
Elf64_Word lpi_sym; /* Symbol index */
Elf64_Word lpi_value; /* Landing pad value for the symbol */
kito-cheng marked this conversation as resolved.
Show resolved Hide resolved
} Elf64_Lpadinfo;
```

The `lpi_name` field is the index into the string table for the symbol name,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lpi_name field is the index into the string table for the symbol name

You forget to update this sentence. My updated wording would be:

The lpi_sym field refers to a symbol by indexing into in the symbol table. The indexed symbol table is .dynsym for shared libraries, and .symtab for other ELF files.

and the `lpi_value` field is the landing pad value for the symbol.

Every global or weak function symbol, as well as undefined global or weak
symbols expected to be functions, must have an entry in `.riscv.lpadinfo`.

This section can be discard after static linking stage.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you mentioned that "Every symbol with global or weak bind must has a corresponding entry", I think it implies that the lpad labels are provided by the object file that defines the function, right? If this is the case, we can't discard this section after static linking when creating a shared library, since library users would expect to find lpad labels later when linking against this share library.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still know the signature/landing pad label value when we reference to a symbol which is undefined yet, because we always need declare the prototype in the source code.

"Every symbol with global or weak bind must has a corresponding entry" -> we didn't exclude the undefined symbol, so we can link to the shared library without that info

Copy link

@mylai-mtk mylai-mtk Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If labels can be provided by the object that uses but doesn't define the function, why require labels to be there in the defining object ("Every symbol with global or weak bind must has a corresponding entry")? For the sake of checking if the use-site and define-site agree on the same lpad label?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's still better to put the .riscv.lpadinfo section in shared libraries. I can come up with two possible cases in which the C language prototype is hard to obtain:

Case 1: When writing assembly by hand, the pseudo call or jump instruction is used. These instructions would cause the CALL_PLT relocation to be generated in the resulting relocatable, which would lead to PLT entries being generated by the static linker.

Case 2: Compiler inserts calls to builtins or instrumentations. These extra function calls are often not inserted at the C source level, but at the compiler IR level. This makes knowing the C prototype of the called target hard as there would not be a C language data structure to represent the called target in the AST.

In these cases, it's not easy to obtain the C language prototype, as the only assumption in these cases is that there would be a defined symbol to which the linker can resolve the called target. Considering these cases, I would still prefer to have the shared libraries provide the lpad labels for functions defined by them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you, so...I just say can be not must be here, that make it strip-able, but it's also legal to leave here

Copy link

@mylai-mtk mylai-mtk Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, if this is the intention, I would suggest you make it clear that it means the section is strip-able, and does not mean that the section can be discarded under all circumstances after static linkage. I believe if a user strips his binary, he should know the consequence, so marking a section that is only strip-able under certain situations strip-able is acceptable.

My concern is that if this sentence is considered by a linker implementation to be the spec of linker behavior that uniformly allows the section to be dropped after static linkage, the spec is flawed, since when producing shared libraries, the section cannot be dropped or otherwise the library could risk linkage failure to relocatables.


Static linkers should merge landing pad values from objects using the following
logic:
1. Definitive Landing Pad Value Determination:
- If a resolved symbol has both a zero and a non-zero landing pad value coming
from objects that define the symbol (globally or weakly), the non-zero
landing pad value is adopted as the "definitive landing pad value" for the
symbol.
- If all landing pad values for the symbol are zero, the definitive lpad value
for the symbol is 0.
- If the symbol-defining objects provide more than one non-zero landing pad
value for a resolved symbol, an error should be emitted.
2. Referencing Landing Pad Values:
- For symbols referenced (but not defined) by objects, the landing pad values
provided by these objects are termed "referencing landing pad values."
- Static linkers should emit an error if the referencing landing pad values
for a symbol conflict with each other, i.e., they are different.
3. Merging Rules for Zero and Non-Zero Values:
- The merging logic between zero and non-zero landing pad values does not
apply to referencing landing pad values, as these are expected to be
generated through C declarations, where only non-zero landing pad values
are expected.
4. Final Landing Pad Value Determination:
- The final landing pad value is determined by merging definitive and
referencing landing pad values as follows:
- If the definitive landing pad value exists and is non-zero, the final
landing pad value is the definitive landing pad value.
- If the referencing landing pad value exists, the final landing pad value
is the referencing landing pad value.
- If the definitive landing pad value exists but is 0, the final landing
pad value is 0.
- If neither definitive nor referencing landing pad values exist, the final
landing pad value is unknown. If the linker needs to generate a PLT entry
for the symbol in this case, it must emit an error.

NOTE: Dynamic linker may also check with same symbol has the same landing pad
value when loading shared object.

=== Mapping Symbol

The section can have a mixture of code and data or code with different ISAs.
Expand All @@ -1582,6 +1662,7 @@ A number of symbols, named mapping symbols, describe the boundaries.
| $x.<any>
| $x<ISA> .2+| Start of a sequence of instructions with <ISA> extension.
| $x<ISA>.<any>
| $s<function-signature-string> | Marker for the landing pad or landing pad value setup instruction.
|===

The mapping symbol should set the type to `STT_NOTYPE`, binding to `STB_LOCAL`,
Expand Down Expand Up @@ -1612,6 +1693,12 @@ is not enough for the disassembler to disassemble the `rv64gcv` version
correctly. Specifying ISA string appropriately with the two memcpy instruction
mapping symbols helps the disassembler to disassemble instructions correctly.

The mapping symbol for the landing pad (`$s`) provides additional information
Copy link

@mylai-mtk mylai-mtk Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The optional ISA information, when present, will be used until the next instruction mapping symbol.

This existing spec for the interpretation of mapping symbols conflicts with the lpad mapping symbol.

for the labeled landing pad scheme. This mapping symbol may be generated when
setting up a landing pad value (e.g., `auipc t2, %lpad_hash("FvvE")` will

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you mean lui t2, %lpad_hash(...)?

BTW, the condition under which this mapping symbol can be emitted could actually be summarized as "if there's a %lpad_hash(...), an lpad mapping symbol is emitted". Would you mind to restructure the spec around the summarized condition so that it's more versatile compared to the current spec that explicitly constrains the emission condition of the symbol to the 2 listed "intentions"?

generate `$sFvvE`) or when emitting a landing pad instruction (e.g.,
`lpad %lpad_hash("FvvE")` will generate `$sFvvE`).

== Label Value Compuatation for Function Signature based Scheme Landing Pad

The label value for the function signature-based labeling scheme landing pad is
Expand Down Expand Up @@ -2317,6 +2404,96 @@ instructions. It is recommended to initialize `jvt` CSR immediately after
csrw jvt, a0
----

==== Landing Pad Relaxation

Target Relocation::: R_RISCV_LPAD

Description:: This relaxation type allows the `lpad` instruction to be removed.
However, if `R_RISCV_RELAX` is not present, the `lpad` instruction can only be
replaced with a sequence of `nop` instructions of the same length as the
original instruction.

Description:: This relaxation type can relax lpad instruction into a none,
which removed the lpad instruction.
This relaxation type can be performed even without `R_RISCV_RELAX`,
but the linker should pad nop instruction to the same length of the original
instruction sequence.
kito-cheng marked this conversation as resolved.
Show resolved Hide resolved

Condition:: This relaxation can only be applied if the symbol is **NOT**

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current relocation refers to the mapping symbol, but I guess this condition is not made on the mapping symbol, but on the function symbol?

exported to the dynamic symbol table and is only referenced by `R_RISCV_CALL`
or `R_RISCV_CALL_PLT` relocations. If the symbol is exported or referenced by
other relocations, relaxation cannot be performed.

Relaxation::
- Lpad instruction associated with `R_RISCV_LPAD` can be removed.
- Lpad instruction associated with `R_RISCV_LPAD` can be replaced with nop
instruction if the relacation isn't paired with `R_RISCV_RELAX`.

Example::
+
--
Relaxation candidate:
[,asm]
----
lpad 0x123 # R_RISCV_LPAD, R_RISCV_RELAX
----

Relaxation result:
[,asm]
----
# No instruction
----
Can be relaxed into `nop` if no `R_RISCV_RELAX` is paired with `R_RISCV_LPAD`.
[,asm]
----
nop
----
--

==== Landing Pad Scheme Relaxation

Target Relocation::: R_RISCV_LPAD

Description:: This relaxation type allows an `lpad` instruction to be relaxed
into `lpad 0`, which is a universal landing pad that ignores the label value
comparison. This relaxation is used when the label value is not computed
correctly.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what would be the cases where a label may be computed incorrectly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some legacy programs don’t properly declare function prototypes before calling them. In these cases, the compiler will infer a function prototype based on the language standards, but it often ends up being incorrect. One common example is dhrystone[1]. In most versions you find online, Func_2 isn’t declared before it’s called, so the compiler will assume the prototype is int Func_2(char*, char*), but the correct prototype is actually void Func_2(char[31], char[31]).

[1] https://github.com/sifive/benchmark-dhrystone/blob/master/dhry_1.c#L164

Another common potential issue in C is with qsort. Function pointers can be compatible but not perfectly match the expected type. For example, here’s how qsort is declared:

void qsort(void* ptr, size_t count, size_t size, int (*comp)(const void*, const void*));

But in practice, you can pass in a compatible, but not exactly matching, type for the comparison function, and it works in most cases:

#include <stdlib.h>

int compare(int *a, int *b)  // The signature isn’t int (*)(const void*, const void*)
{
    return *(int *)a - *(int *)b;
}

void foo(int *x, size_t count, size_t size)
{
    qsort(x, count, size, compare);  // But in practice, this works fine
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But how is the linker expected to know the incorrectness so it can perform this relaxation?

The Zicfilp mechanism is employed when issuing an indirect call through function pointers, and when calling functions through PLT:

In the first case (indirect calls through pointers), to know that an lpad insn needs to be relaxed to lpad 0 due to the original label being incorrect, the linker would need to know where the pointer points to, so the caller's label (the "correct" one) can be checked against the callee's label (the lpad insn). I'm not sure if this is the scenario you're targeting, but if it is, I think this (knowing where the pointer points to, or knowing where the call would come from) is an expectation too high for linkers. Besides, in this scenario, I would also wonder how linkers are expected to retrieve the caller's label (the "correct" one) so it can make the comparison?

In the second case (calls through PLT), the indirect call happens in the PLT, which is generated by linkers. The label which linkers use to generate PLT would come from the addend of the LPAD relocation, which should contain the same label as the one in its referenced lpad insn, so there would be no chance of mismatch and thus incorrectness identifiable by linkers.

The above is my guess and understanding of the intended usage of this relaxation. If we're not on the same page, please do let me know.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linker never know (or not always know), and also that's not the right layer to analysis (or guess:P ), so I expect that relaxation should only enabled when user pass something like -z force-simple-landing-pad-scheme to linker.


Condition:: This relaxation can be performed without `R_RISCV_RELAX`, and
should not be enabled by default. The user must explicitly enable this
relaxation. Additionally, if this relaxation is applied, it must be applied
consistently to all `R_RISCV_LPAD` relocations in the entire binary.

Relaxation::
- Lpad instruction associated with `R_RISCV_LPAD` will be replaced with
`lpad 0`.
- The GNU property must be adjusted to reflect the use of this relaxation.
- The format of the PLT entries must also be adjusted accordingly.

Example::
+
--
Relaxation candidate:
[,asm]
----
lpad 0x123 # R_RISCV_LPAD
----

Relaxation result:
[,asm]
----
lpad 0
----
--

NOTE: This relaxation is designed to be compatible with legacy programs that
may not declare the function signature correctly.

NOTE: Dependent shared libraries will not undergo the corresponding
transformation. Therefore, if this Landing Pad Scheme Relaxation is used in a
dynamically linked environment, ensure that all dependent shared libraries are
rebuilt with the corresponding version.

[bibliography]
== References

Expand Down
Loading