[clang] [llvm] [X86] Enhance kCFI type IDs with a 3-bit arity indicator. (PR #117121)
Scott Constable via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 26 12:48:33 PST 2024
scottconstable wrote:
> > Flag guarding this feature seems like it would also be good for any existing C users - for example, if trying to build a kernel module intended to load against a kernel image built with an older `clang`, you need to select the same type ID projection that the kernel did.
>
> +1 to this. Adding support for this to the Rust compiler shouldn't be a problem and @maurer or I could take a look at it. However, I wonder if the arity information should be from the high level information (i.e., from the function declarations/definitions) instead of from the lower level calling convention used/code generated (and possibly also affected by/after optimizations). This would ensure compatibility with any calling convention and any other possible differences that may come up later when using cross-language KCFI. (The KCFI encoding is based on high level type information from the function declarations/definitions.)
The implementation in this PR does use high-level type information (but now that I look back at what I had written, I think my original PR description was a bit mis-leading). Here is an extended version of the arity table, with an additional column to show what the implementation is intended to do:
| Arity Indicator | Description | Implementation |
| --------------- | --------------- | --------------- |
| 0 | 0 parameters | 0 parameters |
| 1 | 1 parameter in RDI | 1 address-width (or smaller) parameter and no other parameters |
| 2 | 2 parameters in RDI and RSI | 2 address-width (or smaller) parameters and no other parameters |
| 3 | 3 parameters in RDI, RSI, and RDX | 3 address-width (or smaller) parameters and no other parameters |
| 4 | 4 parameters in RDI, RSI, RDX, and RCX | 4 address-width (or smaller) parameters and no other parameters |
| 5 | 5 parameters in RDI, RSI, RDX, RCX, and R8 | 5 address-width (or smaller) parameters and no other parameters |
| 6 | 6 parameters in RDI, RSI, RDX, RCX, R8, and R9 | 6 address-width (or smaller) parameters and no other parameters |
| 7 | At least one parameter may be passed on the stack | The function type does not qualify as arity 0-6 |
Hence, this implementation uses high-level type information from clang (`CodeGenModule.cpp`) or LLVM (`ModuleUtils.cpp`) to infer a better approximation of the default x86-64 calling convention. For example, if the implementation were to instead use the number of parameters as a proxy for the arity, then this could permit a scenario where a register that is dead at the call site becomes live at the call target, e.g.:
```C
// test.c
struct S {
int *p1;
int *p2;
};
int foo(struct S s) { // 1 parameter
return *s.p1 + *s.p2;
}
```
But then the struct gets decomposed into two separate registers by the compiler:
```
clang -O1 test.c -S -o test.S; cat test.S
.type foo, at function
foo: # @foo
.cfi_startproc
# %bb.0: # %entry
movl (%rsi), %eax
addl (%rdi), %eax
retq
```
Thus, if an arity-1 function type's hash collides with `foo`'s hash, then a dead `RSI` register at an arity-1 caller could become live at the target `foo`.
https://github.com/llvm/llvm-project/pull/117121
More information about the llvm-commits
mailing list