Получить возвращаемое значение Linux системного вызова с G CC встроенной сборкой - PullRequest
2 голосов
/ 23 февраля 2020

Следующий макрос может выполнять системный вызов 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"                     \
  );                                         \
}

Можно вызывать его как glib c wrapper 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);
}

* 1009 Что делать, если я хочу получить доступ к возвращаемому значению, однако? Я думаю, что это записывается в 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);                   \
}

Ответы [ 2 ]

1 голос
/ 27 февраля 2020

Обновлено после @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;
}

ps Если по каким-то причинам вы хотите использовать макрос, а не встроенную функцию, вы должны использовать другой синтаксис:

#define m_lstat(_path, _fs) \
({                          \
    long _rax = 6;          \
    asm volatile (          \
        "syscall"           \
        : "+a" (_rax)       \
        : "D" (_path), "S" (_fs) \
        : "rcx", "r11", "memory" \
    );                      \
    _rax;                   \
})
0 голосов
/ 23 февраля 2020

Если вы удалите 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

Итак, вы получите возвращаемое значение в ret (mov DWORD PTR [rbp-28], eax), и, как вы сказали, возвращаемое значение находится в %rax. Следовательно, вы должны иметь возможность доступа к ret, как к любой нормальной переменной.

PS. Надеюсь, вы не возражаете против синтаксиса Intel, смешанного с %reg,% означает регистр в противоположность переменной. Я знаю, что не будет переменной rax, но чтобы сделать ее более читабельной, я использовал это украшение.

...