[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