通过修改LLVM Backend来Clobber X86寄存器

2024-02-28

我正在尝试稍微改变 X86 目标的 LLVM 后端,以产生一些所需的行为。

更具体地说,我想模拟一个像 gcc 的 fcall-used 这样的标志reg option https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#Code-Gen-Options,它指示编译器将被调用者保存的寄存器转换为破坏的寄存器(意味着它可以在函数调用期间更改)。

让我们重点关注r14。我手动破坏寄存器,就像this https://stackoverflow.com/questions/18024672/what-registers-are-preserved-through-a-linux-x86-64-function-call/55207335 answer:

#include <inttypes.h>

uint64_t inc(uint64_t i) {
    __asm__ __volatile__(
        ""
        : "+m" (i)
        :
        : "r14"
    );
    return i + 1;
}

int main(int argc, char **argv) {
    (void)argv;
    return inc(argc);
}

编译和反汇编:

 gcc -std=gnu99 -O3 -ggdb3 -Wall -Wextra -pedantic -o main.out main.c
 objdump -d main.out

拆解包含:

0000000000001150 <inc>:                                                                                                                                                                                            
    1150:       41 56                   push   %r14                                                                                                                                                                
    1152:       48 89 7c 24 f8          mov    %rdi,-0x8(%rsp)                                                                                                                                                     
    1157:       48 8b 44 24 f8          mov    -0x8(%rsp),%rax                                                                                                                                                     
    115c:       41 5e                   pop    %r14                                                                                                                                                                
    115e:       48 83 c0 01             add    $0x1,%rax                                                                                                                                                           
    1162:       c3                      retq                                                                                                                                                                       
    1163:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)                                                                                                                                                
    116a:       00 00 00
    116d:       0f 1f 00                nopl   (%rax)

我们可以在哪里看到r14,因为被篡改,被压入堆栈,然后弹出以恢复其原始值。 现在,重复-fcall-使用-r14 flag:

 gcc -std=gnu99 -O3 -ggdb3 -fcall-used-r14 -Wall -Wextra -pedantic -o main.out main.c
 objdump -d main.out

拆解包含:

0000000000001150 <inc>:                                                                                                                                                                                            
    1150:       48 89 7c 24 f8          mov    %rdi,-0x8(%rsp)
    1155:       48 8b 44 24 f8          mov    -0x8(%rsp),%rax
    115a:       48 83 c0 01             add    $0x1,%rax
    115e:       c3                      retq
    115f:       90                      nop

没有发生推送/弹出的地方。

现在,我修改了一些 LLVM 目标文件,编译了源代码,并将此功能添加到了(?)llc tool:

clang-11 -emit-llvm -S -c main.c -o main.ll
llc-11 main.ll -o main.s

Now, main.s包含:

# %bb.0:
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register %rbp
        pushq   %r14
        .cfi_offset %r14, -24
        movq    %rdi, -16(%rbp)
        #APP
        #NO_APP
        movq    -16(%rbp), %rax
        addq    $1, %rax
        popq    %r14
        popq    %rbp
        .cfi_def_cfa %rsp, 8
        retq

显然,r14仍然是被调用者保存的。

Inside llvm/lib/Target/X86/X86CallingConv.td我修改了以下几行(删除 R14),因为它们似乎是我感兴趣的唯一与 Linux 的 System V ABI 和 C 调用约定相关的行:

def CSR_64 : CalleeSavedRegs<(add R12, R13, R15, RBP)>;
...
def CSR_64_MostRegs : CalleeSavedRegs<(add RBX, RCX, RDX, RSI, RDI, R8, R9, R10,
                                           R11, R12, R13, R15, RBP,
...
def CSR_64_AllRegs_NoSSE : CalleeSavedRegs<(add RAX, RBX, RCX, RDX, RSI, RDI, R8, R9,
                                                R10, R11, R12, R13, R15, RBP)>;

我的问题是:

  • Is X86CallingConv.td我应该修改的唯一文件?我想是的,但也许我错了。
  • 我是否专注于正确的线路?也许这个问题更难回答,但至少一个方向可能会有所帮助。

我在 Debian 10.5 中运行 LLVM 11。

EDIT:

更改行,从“隐藏”定义中删除 R14:

def CSR_SysV64_RegCall_NoSSE : CalleeSavedRegs<(add RBX, RBP, RSP,
                                               (sequence "R%u", 12, 13), R15)>;

正如玛格丽特正确指出的那样,也没有帮助。


事实证明,最小的修改是这一行:

def CSR_64 : CalleeSavedRegs<(add RBX, R12, R13, R15, RBP)>;

问题在于我如何构建源代码。

通过跑步cmake --build .再次原始安装后,llc工具没有全局修改(我认为它会修改,因为我正在构建默认架构 - X86 - 但这是无关紧要的)。所以,我打电话给一个未经修改的llc-11工具。因此,当我跑时:

/path/to/llvm-project/build/bin/lcc main.ll -o main.s

main.s包含:

# %bb.0:
        movq    %rdi, -8(%rsp)
        #APP
        #NO_APP
        movq    -8(%rsp), %rax
        addq    $1, %rax
        retq

这就是我最初想要的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过修改LLVM Backend来Clobber X86寄存器 的相关文章

随机推荐