获取带有GCC内联程序集的Linux系统调用的返回值



以下宏可以执行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, %%raxn"                       
"movq %1, %%rdin"                       
"movq %2, %%rsin"                       
"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 %dn", fs0.st_size);
struct stat fs1;  lstat(  "a.out", &fs1);  printf("nbytes %dn", 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, %%raxn"                       
"movq %1, %%rdin"                       
"movq %2, %%rsin"                       
"syscall"                                
:"=m"(ret)                               
:"m"(_rax), "m"(_path), "m"(_fs)         
:"rax", "rdi", "rsi"                     
);                                         
printf("ret %dn", ret);                   
}

更新了以下@PeterOrders@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 = %zdn", p, ret, fs.st_size);
ret = m_lstat(p, &fs);
printf("m_lstat: %s: ret = %ld, size = %zdn", p, ret, fs.st_size);
return 0;
}

p.s.如果出于某些原因,您想使用宏,而不是内联函数,则应该使用另一种语法:

#define m_lstat(_path, _fs) 
({                          
long _rax = 6;          
asm volatile (          
"syscall"           
: "+a" (_rax)       
: "D" (_path), "S" (_fs) 
: "rcx", "r11", "memory" 
);                      
_rax;                   
})

如果删除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();(返回打印(做准备

.LC0:
.string "test.txt"
.LC1:
.string "ret %dn"
(...)
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 %dn"
mov     eax, 0 // %eax = 0 // ?
call    printf

因此,您可以在ret(mov DWORD PTR [rbp-28], eax(中获得返回值,正如您所说,返回值在%rax中。因此,您应该能够像访问任何正常变量一样访问ret

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

最新更新