使用GCC内联汇编获取Linux syscall的返回值

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

以下宏可以执行lstat系统调用。

#include <stdint.h>

#define m_lstat(PATH, FS){                   \
  long         _rax  = 6;  /*sys_newlstat*/  \
  uint8_t*     _path = PATH;                 \
  struct stat* _fs   = FS;                   \
  asm volatile(                              \
    "movq %0, %%rax\n"                       \
    "movq %1, %%rdi\n"                       \
    "movq %2, %%rsi\n"                       \
    "syscall"                                \
    :                                        \
    :"m"(_rax), "m"(_path), "m"(_fs)         \
    :"rax", "rdi", "rsi"                     \
  );                                         \
}

可以像glibc包装器lstat那样调用它:

#include <sys/stat.h>
#include <stdio.h>

int main(){
  struct stat fs0;  m_lstat("a.out", &fs0);  printf("nbytes %d\n", fs0.st_size);
  struct stat fs1;  lstat(  "a.out", &fs1);  printf("nbytes %d\n", fs1.st_size);
}

但是,如果我想访问返回值怎么办?我认为它已写入rax,但我不知道如何从C代码中检索它...

例如,以下操作不是

#define m_lstat(PATH, FS){                   \
  long         _rax  = 6;  /*sys_newlstat*/  \
  u8*          _path = PATH;                 \
  struct stat* _fs   = FS;                   \
  int ret;                                   \
  asm volatile(                              \
    "movq %0, %%rax\n"                       \
    "movq %1, %%rdi\n"                       \
    "movq %2, %%rsi\n"                       \
    "syscall"                                \
    :"=m"(ret)                               \
    :"m"(_rax), "m"(_path), "m"(_fs)         \
    :"rax", "rdi", "rsi"                     \
  );                                         \
  printf("ret %d\n", ret);                   \
}
linux gcc x86-64 system-calls inline-assembly
2个回答
0
投票

在@PeterCordes @MichaelPetch评论之后更新

只需使用适当的约束

inline long m_lstat(char *_path, struct stat *_fs)
{
    long _rax = 6;

    asm volatile(
        "syscall"
        : "+a" (_rax)
        : "D" (_path), "S" (_fs) 
        : "rcx", "r11", /* used by syscall */
          "memory"      /* barrier for _path and _fs */
    );

    return _rax;
}

此代码

struct stat s;
char foo[] = "foo";

long test()
{
    return m_lstat(foo, &s);
}

产生

test:
    movl    $6, %eax
    leaq    foo(%rip), %rdi
    leaq    s(%rip), %rsi
#APP
# 10 "m_lst.c" 1
    syscall
# 0 "" 2
#NO_APP
    ret

我使用此代码进行测试,一切正常

int main(int argc, char **argv)
{
    struct stat fs;
    long ret;
    char *p = argv[ argc >= 2 ];

    ret = lstat(p, &fs);
    printf("lstat:   %s: ret = %ld, size = %zd\n", p, ret, fs.st_size);

    ret = m_lstat(p, &fs);
    printf("m_lstat: %s: ret = %ld, size = %zd\n", p, ret, fs.st_size);

    return 0;
}

-1
投票

如果删除printf();呼叫,则会收到以下消息:

.LC0:
        .string "test.txt"
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 56
.LBB2: // m_lstats
        mov     QWORD PTR [rbp-8], 6 // _rax  = 6;
        mov     QWORD PTR [rbp-16], OFFSET FLAT:.LC0 // _path = PATH;
        lea     rax, [rbp-176] // _fs = FS;
        mov     QWORD PTR [rbp-24], rax
        mov     rax, QWORD PTR [rbp-8] // %rax = _rax
        mov     rdx, QWORD PTR [rbp-16] // %rdx = _path
        mov     rsi, QWORD PTR [rbp-24] // %rsi = _fs
        mov     rdi, rdx // %rdi = _path
        syscall // call
        mov     DWORD PTR [rbp-28], eax // ret = %eax (low %rax)
.LBE2: // back to main
        mov     eax, 0 // return 0; // @ main()
        leave
        ret

其余所有只是准备printf();(返回printf)

.LC0:
        .string "test.txt"
.LC1:
        .string "ret %d\n"
(...)
        syscall // call
        mov     DWORD PTR [rbp-28], eax // (*)ret = %eax (low %rax)
        mov     eax, DWORD PTR [rbp-28] // %esi = &ret
        mov     esi, eax //
        mov     edi, OFFSET FLAT:.LC1 // %edi = &"ret %d\n"
        mov     eax, 0 // %eax = 0 // ?
        call    printf

所以您确实在retmov DWORD PTR [rbp-28], eax)中得到了返回值,正如您所说的,返回值在%rax中。因此,您应该能够像访问任何普通变量一样访问ret

PS。希望您不要介意与%reg混合使用的intel语法,%表示与变量相反的寄存器。我知道不会有rax变量,但是为了使它更具可读性,我使用了这种装饰。

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