如何在ARM GCC内联汇编中将单个寄存器指定为约束?

问题描述 投票:4回答:2

在x86内联汇编中我可以写这样的东西:

asm ("cpuid"
            : "=a" (_eax),
              "=b" (_ebx),
              "=c" (_ecx),
              "=d" (_edx)
            : "a" (op));

所以在matchin约束中而不是只写“= r”并让编译器选择寄存器,我可以说我想使用哪个特定寄存器(= a例如使用%eax)

我怎么能为ARM组装做这个? ARM GCC组装手册http://www.ethernut.de/en/documents/arm-inline-asm.html声明我可以使用约束“r”作为VFP浮点寄存器S0-S31之一的通用寄存器R0-R15“w”之一

但是我怎样才能将操作数限制为例如s1?或特定的通用注册?

gcc arm inline-assembly
2个回答
4
投票

我不认为ARM的gcc允许您使用constraints来准确指定要使用的寄存器。但是,您可以使用explicit register variables指定用于存储变量的寄存器:

register int my_variable asm("r0");

1
投票

显式寄存器变量minimal runnable example

这是一个ARMv8 Linux C独立式hello世界,用https://stackoverflow.com/a/3936064/9160762作为一些反汇编分析的例子:

main.c中

#include <inttypes.h>

void _start(void) {
    uint64_t exit_status;

    /* write */
    {
        char msg[] = "hello syscall v8\n";
        uint64_t syscall_return;
        register uint64_t x0 __asm__ ("x0") = 1; /* stdout */
        register char *x1 __asm__ ("x1") = msg;
        register uint64_t x2 __asm__ ("x2") = sizeof(msg);
        register uint64_t x8 __asm__ ("x8") = 64; /* syscall number */
        __asm__ __volatile__ (
            "svc 0;"
            : "+r" (x0)
            : "r" (x1), "r" (x2), "r" (x8)
            : "memory"
        );
        syscall_return = x0;
        exit_status = (syscall_return != sizeof(msg));
    }

    /* exit */
    {
        register uint64_t x0 __asm__ ("x0") = exit_status;
        register uint64_t x8 __asm__ ("x8") = 93;
        __asm__ __volatile__ (
            "svc 0;"
            : "+r" (x0)
            : "r" (x8)
            :
        );
    }
}

GitHub upstream

编译并运行:

sudo apt-get install qemu-user gcc-aarch64-linux-gnu
aarch64-linux-gnu-gcc -O3 -std=c99 -ggdb3 -march=armv8-a -pedantic -Wall -Wextra \
  -ffreestanding -nostdlib -static -o main.out main.c
qemu-aarch64 main.out

输出:

hello syscall v8

拆卸:

aarch64-linux-gnu-objdump -S main.out

输出:

main.out:     file format elf64-littleaarch64


Disassembly of section .text:

0000000000400110 <_start>:
void _start(void) {
    uint64_t exit_status;

    /* write */
    {
        char msg[] = "hello syscall v8\n";
  400110:   90000003    adrp    x3, 400000 <_start-0x110>
  400114:   91056063    add x3, x3, #0x158
void _start(void) {
  400118:   d10083ff    sub sp, sp, #0x20
        uint64_t syscall_return;
        register uint64_t x0 __asm__ ("x0") = 1; /* stdout */
  40011c:   d2800020    mov x0, #0x1                    // #1
        register char *x1 __asm__ ("x1") = msg;
  400120:   910023e1    add x1, sp, #0x8
        register uint64_t x2 __asm__ ("x2") = sizeof(msg);
  400124:   d2800242    mov x2, #0x12                   // #18
        char msg[] = "hello syscall v8\n";
  400128:   a9401464    ldp x4, x5, [x3]
        register uint64_t x8 __asm__ ("x8") = 64; /* syscall number */
  40012c:   d2800808    mov x8, #0x40                   // #64
        char msg[] = "hello syscall v8\n";
  400130:   79402063    ldrh    w3, [x3, #16]
  400134:   a90097e4    stp x4, x5, [sp, #8]
  400138:   790033e3    strh    w3, [sp, #24]
        __asm__ __volatile__ (
  40013c:   d4000001    svc #0x0
            : "+r" (x0)
            : "r" (x1), "r" (x2), "r" (x8)
            : "memory"
        );
        syscall_return = x0;
        exit_status = (syscall_return != sizeof(msg));
  400140:   eb02001f    cmp x0, x2
    }

    /* exit */
    {
        register uint64_t x0 __asm__ ("x0") = exit_status;
        register uint64_t x8 __asm__ ("x8") = 93;
  400144:   d2800ba8    mov x8, #0x5d                   // #93
        register uint64_t x0 __asm__ ("x0") = exit_status;
  400148:   9a9f07e0    cset    x0, ne  // ne = any
        __asm__ __volatile__ (
  40014c:   d4000001    svc #0x0
            : "+r" (x0)
            : "r" (x8)
            :
        );
    }
}
  400150:   910083ff    add sp, sp, #0x20
  400154:   d65f03c0    ret

没有显式寄存器变量的尝试

主要是为了好玩,我试图在不使用寄存器变量的情况下达到相同的结果,但我无法做到。

在任何情况下,代码都会更复杂,所以最好只使用寄存器变量。

这是我最好的尝试:

main.c中

#include <inttypes.h>

void _start(void) {
    uint64_t exit_status;

    /* write */
    {
        char msg[] = "hello syscall v8\n";
        uint64_t syscall_return;
        __asm__ (
            "mov x0, 1;" /* stdout */
            "mov x1, %[msg];"
            "mov x2, %[len];"
            "mov x8, 64;" /* syscall number */
            "svc 0;"
            "mov %[syscall_return], x0;"
            : [syscall_return] "=r" (syscall_return)
            : [msg] "p" (msg),
            [len] "i" (sizeof(msg))
            : "x0", "x1", "x2", "x8", "memory"
        );
        exit_status = (syscall_return != sizeof(msg));
    }

    /* exit */
    __asm__ (
        "mov x0, %[exit_status];"
        "mov x8, 93;" /* syscall number */
        "svc 0;"
        :
        : [exit_status] "r" (exit_status)
        : "x0", "x8"
    );
}

GitHub upstream

拆卸:

main.out:     file format elf64-littleaarch64


Disassembly of section .text:

0000000000400110 <_start>:
void _start(void) {
    uint64_t exit_status;

    /* write */
    {
        char msg[] = "hello syscall v8\n";
  400110:   90000000        adrp    x0, 400000 <_start-0x110>
  400114:   9105a000        add     x0, x0, #0x168
void _start(void) {
  400118:   d10083ff        sub     sp, sp, #0x20
        char msg[] = "hello syscall v8\n";
  40011c:   a9400c02        ldp     x2, x3, [x0]
  400120:   a9008fe2        stp     x2, x3, [sp, #8]
  400124:   79402000        ldrh    w0, [x0, #16]
        uint64_t syscall_return;
        __asm__ (
  400128:   910023e3        add     x3, sp, #0x8
        char msg[] = "hello syscall v8\n";
  40012c:   790033e0        strh    w0, [sp, #24]
        __asm__ (
  400130:   d2800020        mov     x0, #0x1                        // #1
  400134:   aa0303e1        mov     x1, x3
  400138:   d2800242        mov     x2, #0x12                       // #18
  40013c:   d2800808        mov     x8, #0x40                       // #64
  400140:   d4000001        svc     #0x0
  400144:   aa0003e3        mov     x3, x0
            : [syscall_return] "=r" (syscall_return)
            : [msg] "p" (msg),
            [len] "i" (sizeof(msg))
            : "x0", "x1", "x2", "x8", "memory"
        );
        exit_status = (syscall_return != sizeof(msg));
  400148:   f100487f        cmp     x3, #0x12
  40014c:   9a9f07e1        cset    x1, ne  // ne = any
    }

    /* exit */
    __asm__ (
  400150:   aa0103e0        mov     x0, x1
  400154:   d2800ba8        mov     x8, #0x5d                       // #93
  400158:   d4000001        svc     #0x0
        "svc 0;"
        :
        : [exit_status] "r" (exit_status)
        : "x0", "x8"
    );
}
  40015c:   910083ff        add     sp, sp, #0x20
  400160:   d65f03c0        ret

由于以下原因,效率较低:

  • write约束p需要使用中间寄存器x3addsp
  • 我不知道如何在没有额外的mov到输出寄存器的情况下获得系统调用返回状态
  • exit状态通过x1再次移动一次。寄存器变量只是直接计算到x0

在Ubuntu 18.10,GCC 8.2.0,QEMU 2.12中测试。

© www.soinside.com 2019 - 2024. All rights reserved.