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 6 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
97 changes: 95 additions & 2 deletions 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. The addend indicates the label value of the landing pad, and the symbol value is the address of the mapping symbol for the function signature, which will have the same address as the function.
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the "label value of the landing pad?

Choose a reason for hiding this comment

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

Is this relocation only for the func-sig scheme? Based on its description, it looks like so, but the following LPAD relaxation that removes the lpad insn seems also applicable to the unlabeled scheme.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That should also work for unlabeled scheme as well, let me think how to make it clearly.

<|
.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 @@ -1582,6 +1584,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 instruction. This should only be used with the function signature-based scheme and should be placed only at the beginning of the function.
Copy link

@mylai-mtk mylai-mtk Oct 17, 2024

Choose a reason for hiding this comment

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

I don't quite get the purpose of this mapping symbol: It looks like the only reference to these symbols come from the LPAD relocation, but for what? The LPAD relocation already have the 20-bit label stored in its addend, and its link to this %s mapping symbol provides only the additional information of the signature string, which is not needed (as of now) to link the object files.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

riscv/riscv-cfi#151 (comment)

It's kinda debugging propose only, so it safe to strip like all other mapping symbols

Copy link

@mylai-mtk mylai-mtk Oct 22, 2024

Choose a reason for hiding this comment

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

If the purpose is to display function signatures when disassembling, this mechanism seems a bit incomplete (?) I suppose since the relocation is a static one, it would not stay in the binary after static linking, thus if a user disassembles a linked ELF, it's still the label numbers instead of signatures that get displayed?

Update: Assuming it's relying on the mapping symbol having the same address as the lpad insn to associate an lpad insn to a function signature (so that the signature can be displayed when disassembling a linked binary), why do relocations refer to these symbols?

|===

The mapping symbol should set the type to `STT_NOTYPE`, binding to `STB_LOCAL`,
Expand Down Expand Up @@ -2008,7 +2011,7 @@ NOTE: Tag_RISCV_x3_reg_usage is treated as 0 if it is not present.

Relaxation::
- The `auipc` instruction associated with `R_RISCV_GOT_HI20` can be
removed if the symbol is absolute.
removes if the symbol is absolute.

- The instruction or instructions associated with `R_RISCV_PCREL_LO12_I`
can be rewritten to either `c.li` or `addi` to materialize the symbol's
Expand Down Expand Up @@ -2317,6 +2320,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**
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