代码之家  ›  专栏  ›  技术社区  ›  Max Sedlusch

ARM V7内联汇编-将C变量移动到寄存器中

  •  0
  • Max Sedlusch  · 技术社区  · 6 月前

    关于ARMV7内联汇编没有问题可以回答我的问题,所以我发了一篇帖子。 我想将C变量的值移动到r0-r2中,反之亦然——从寄存器移动到C变量中。然而,我的代码总是将相同的值-12加载到寄存器中。有人能告诉我如何实现我的目标吗?

    // syscalls.h
    #ifndef SYSCALL_H
    #define SYSCALL_H
    #define STRINGIFY(x) #x
    #define TOSTRING(x) STRINGIFY(x)
    #define SYSCALL_ID_EXIT          1
    #define SYSCALL_ID_CREATE_THREAD 2
    #define SYSCALL_ID_GETC          3
    #define SYSCALL_ID_PUTC          4
    #define SYSCALL_ID_SLEEP         5
    #define SYSCALL_ID_UNDEFINED     6
    #ifndef __ASSEMBLER__
    //...
    
    // syscalls.c
    // ...
    void syscall_create_thread(void (*f) (void *), void * args, unsigned int arg_size){
        unsigned int func = (unsigned int) f;
        unsigned int arguments = (unsigned int) args;
        unsigned int argument_size = (unsigned int) arg_size;
        asm("mov r0, %0" : "=r"(func)::"r0");
        asm("mov r1, %0" : "=r"(arguments)::"r1");
        asm("mov r2, %0" : "=r"(argument_size)::"r2");
        asm("mov r7, #" TOSTRING(SYSCALL_ID_CREATE_THREAD));
        asm("SVC #0");
    }
    
    1 回复  |  直到 6 月前
        1
  •  1
  •   Eugene Sh.    6 月前

    您的特定用例可以简单地完成,如下所示:

    __attribute__((naked))
    void syscall_create_thread(void (*f) (void *), void * args, unsigned int arg_size) 
    {
        asm("mov r7, #" TOSTRING(SYSCALL_ID_CREATE_THREAD) :::"r7");
        asm("SVC #0");
        asm("RET");
    }
    

    这依赖于这样一个事实,即架构的调用约定定义了传递给函数的参数被放置在寄存器中 r0, r1... 由呼叫者。当你制作函数时 naked 您告诉编译器不要为函数生成任何序言/结语,也就是说不要创建堆栈帧、保存被调用者保存的寄存器,甚至添加 ret 指令。但这意味着你也需要格外小心,自己添加缺失的部分。

    以下是最后一个示例代码和生成的程序集,以说服您它是有效的:

    #include <stdlib.h>
    #define STRINGIFY(x) #x
    #define TOSTRING(x) STRINGIFY(x)
    #define SYSCALL_ID_CREATE_THREAD 2
    
    __attribute__((naked))
    void syscall_create_thread(void (*f) (void *), void * args, unsigned int arg_size) 
    {
        asm("mov r7, #" TOSTRING(SYSCALL_ID_CREATE_THREAD) :::"r7");
        asm("SVC #0");
        asm("RET");
    }
    
    
    void callback(void* args)
    {
        return;
    }
    
    void caller() {
        int arg = 8;
        syscall_create_thread(callback, &arg, sizeof (int));
    }
    

    生成的程序集(由我注释):

    callback:
            bx      lr
    syscall_create_thread:
            mov r7, #2
            SVC #0
            RET
    caller:
            str     lr, [sp, #-4]!
            sub     sp, sp, #12
            mov     r3, #8
            str     r3, [sp, #4]
            mov     r2, #4              // r2=sizeof(int)
            add     r1, sp, r2          // r1 = address of the local `arg` variable
            ldr     r0, .L5             // r0 = address of callback function
            bl      syscall_create_thread
            add     sp, sp, #12
            ldr     lr, [sp], #4
            bx      lr
    .L5:
            .word   callback
    

    看看这个 Godbolt

    编辑: 显然,当函数“裸”时 r7 被标记为clobbered,因此它不会像应该的那样“被调用者保存”,因此需要对上述代码进行一些修改,以确保 r7 值在中保存和恢复 syscall_create_thread() 功能。最简单的方法是在非被调用者保存的寄存器中执行此操作,例如 r3 r4 。或者可以在堆栈上完成。