作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我最近涉足低级编程,想做一个函数somesyscall
接受 (CType rax, CType rbx, CType rcx, CType rdx)
.结构 CType 看起来像:
/*
TYPES:
0 int
1 string
2 bool
*/
typedef struct {
void* val;
int typev;
} CType;
该功能有点困惑,但理论上应该可以工作:
#include <errno.h>
#include <stdbool.h>
#include "ctypes.h"
//define functions to set registers
#define seteax(val) asm("mov %0, %%rax" :: "g" (val) : "%rax")
#define setebx(val) asm("mov %0, %%rbx" :: "g" (val) : "%rbx")
#define setecx(val) asm("mov %0, %%rcx" :: "g" (val) : "%rcx")
#define setedx(val) asm("mov %0, %%rdx" :: "g" (val) : "%rdx")
///////////////////////////////////
#define setregister(value, register) \
switch (value.typev) { \
case 0: { \
register(*((double*)value.val)); \
break; \
} \
case 1: { \
register(*((char**)value.val)); \
break; \
} \
case 2: { \
register(*((bool*)value.val)); \
break; \
} \
}
static inline long int somesyscall(CType a0, CType a1, CType a2, CType a3) {
//set the registers
setregister(a0, seteax);
setregister(a1, setebx);
setregister(a2, setecx);
setregister(a3, setedx);
///////////////////
asm("int $0x80"); //interrupt
//fetch back the rax
long int raxret;
asm("mov %%rax, %0" : "=r" (raxret));
return raxret;
}
当我运行时:
#include "syscall_unix.h"
int main() {
CType rax;
rax.val = 39;
rax.typev = 0;
CType rbx;
rbx.val = 0;
rbx.typev = 0;
CType rcx;
rcx.val = 0;
rcx.typev = 0;
CType rdx;
rdx.val = 0;
rdx.typev = 0;
printf("%ld", somesyscall(rax, rbx, rcx, rdx));
}
并编译(并运行二进制文件)
clang test.c
./a.out
我得到一个段错误。然而,一切似乎都是正确的。我在这里做错什么了吗?
最佳答案
宏扩展后,您将拥有类似
long int raxret;
asm("mov %0, %%rax" :: "g" (a0) : "%rax");
asm("mov %0, %%rbx" :: "g" (a1) : "%rbx");
asm("mov %0, %%rcx" :: "g" (a2) : "%rcx");
asm("mov %0, %%rdx" :: "g" (a3) : "%rdx");
asm("int $0x80");
asm("mov %%rax, %0" : "=r" (raxret));
这不起作用,因为您没有告诉编译器在
rax
语句的序列中不允许将
rbx
、
rcx
、
rdx
和
asm
用于其他内容。例如,寄存器分配器可能决定将
a2
从堆栈复制到
rax
,然后使用
rax
作为
mov %0, %%rcx
指令的输入操作数——破坏您放入
rax
的值。
volatile
,所以前5个不能相对于彼此重新排序,但最后一个可以移动到任何地方。例如,在后面的代码之后移动到编译器发现在寄存器中生成
raxret
很方便的地方它的选择。RAX 可能不再有系统调用返回值——你需要告诉编译器输出来自实际产生它的 asm 语句,而不假设任何寄存器在 asm 语句之间存在。)
int
指令放入 asm 中,并用约束字母表达对寄存器中内容的所有要求:asm volatile ("int $0x80"
: "=a" (raxret) // outputs
: "a" (a0), "b" (a1), "c" (a2), "d" (a3) // pure inputs
: "memory", "r8", "r9", "r10", "r11" // clobbers
// 32-bit int 0x80 system calls in 64-bit code zero R8..R11
// for native "syscall", clobber "rcx", "r11".
);
对于这个简单的示例,这是可能的,但通常并不总是可能的,因为每个寄存器都没有约束字母,尤其是在 x86 以外的 CPU 上。 // use the native 64-bit syscall ABI
// remove the r8..r11 clobbers for 32-bit mode
int
指令放入 asm 中,并用 explicit register variables 表达对什么寄存器的要求: register long rax asm("rax") = a0;
register long rbx asm("rbx") = a1;
register long rcx asm("rcx") = a2;
register long rdx asm("rdx") = r3;
// Note that int $0x80 only looks at the low 32 bits of input regs
// so `uint32_t` would be more appropriate than long
// but really you should just use "syscall" in 64-bit code.
asm volatile ("int $0x80"
: "+r" (rax) // read-write: in=call num, out=retval
: "r" (rbx), "r" (rcx), "r" (rdx) // read-only inputs
: "memory", "r8", "r9", "r10", "r11"
);
return rax;
无论您需要使用哪些寄存器,这都将起作用。它也可能与您尝试用来删除类型的宏更兼容。syscall
rather than int $0x80
,并且参数属于 ABI 标准传入参数寄存器(按顺序为 rdi、rsi、rdx、rcx、r8、r9),而不是 rbx、rcx、 rdx 等。不过,系统调用号仍然在 rax 中。 (使用来自
#include <asm/unistd.h>
或
<sys/syscall.h>
的调用号码,这将适用于您正在编译的模式的 native ABI,另一个在 64 位模式下不使用
int $0x80
的原因。)
volatile
;几乎所有系统调用都以某种方式访问内存。
"memory"
如果您为特定的系统调用编写包装器。
getpid
,其中
call into the VDSO 会更快,以避免往返内核模式并返回,就像 glibc 对适当的系统调用所做的那样。这也适用于带有指针的
clock_gettime
。)
brk(2)
和
getpriority(2)
关于c - 在内联 C 程序集中执行系统调用会导致段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64083032/
我是一名优秀的程序员,十分优秀!