Ank*_*zle 1 c ubuntu assembly system-calls
我最近涉足低级编程,并希望制作一个somesyscall接受(CType rax, CType rbx, CType rcx, CType rdx). struct CType 看起来像:
/*
TYPES:
0 int
1 string
2 bool
*/
typedef struct {
void* val;
int typev;
} CType;
Run Code Online (Sandbox Code Playgroud)
该功能有点混乱,但理论上应该可以工作:
#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;
}
Run Code Online (Sandbox Code Playgroud)
当我运行时:
#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));
}
Run Code Online (Sandbox Code Playgroud)
并编译(并运行二进制)
clang test.c
./a.out
Run Code Online (Sandbox Code Playgroud)
我遇到了段错误。然而,一切似乎都是正确的。我在这里做错了什么吗?
宏扩展后,你会得到类似的东西
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));
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为您没有告诉编译器在语句序列期间不允许将rax, rbx, rcx, 和rdx其他内容重用asm。例如,寄存器分配器可能决定a2从堆栈复制到rax然后rax用作mov %0, %%rcx指令的输入操作数——破坏您放入的值rax。
(没有输出的 asm 语句是隐式的,volatile所以前 5 个不能相对于彼此重新排序,但最后一个可以移动到任何地方。例如,在后面的代码之后移动到编译器认为方便raxret在寄存器中生成的位置它的选择。此时 RAX 可能不再具有系统调用返回值 - 您需要告诉编译器输出来自实际生成它的 asm 语句,而不假设任何寄存器在 asm 语句之间存在。)
有两种不同的方法可以告诉编译器不要这样做:
将仅在int在一个汇编指令,并表示所有的在用什么约束字母注册所发生的要求:
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".
);
Run Code Online (Sandbox Code Playgroud)
对于这个简单的示例,这是可能的,但通常并不总是可能的,因为每个寄存器都没有约束字母,尤其是在 x86 以外的 CPU 上。
// use the native 64-bit syscall ABI
// remove the r8..r11 clobbers for 32-bit mode
Run Code Online (Sandbox Code Playgroud)
将仅在int在一个汇编指令,并表达了在什么与什么注册那张要求明确寄存器变量:
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;
Run Code Online (Sandbox Code Playgroud)
无论您需要使用哪些寄存器,这都将起作用。它也可能与您尝试用于擦除类型的宏更兼容。
顺便说一句,如果这是 64 位 x86/Linux,那么您应该使用syscall而不是int $0x80,并且参数属于 ABI 标准传入参数寄存器(按顺序是 rdi、rsi、rdx、rcx、r8、r9),而不是在 rbx、rcx、rdx 等中。不过,系统调用号仍然在 rax 中。(使用#include <asm/unistd.h>或 的电话号码<sys/syscall.h>,这将适用于您正在编译的模式的本机 ABI,这是不在int $0x8064 位模式下使用的另一个原因。)
此外,系统调用指令的 asm 语句应该有一个“内存”破坏并被声明volatile;几乎所有系统调用都以某种方式访问内存。
(作为一个微优化,我想你可以有一个不读取内存、写入内存或修改虚拟地址空间的系统调用列表,并避免它们的内存破坏。这将是一个非常短的列表和我不确定这是否值得麻烦。或者使用如何指示可以使用内联 ASM 参数 * 指向 * 的内存? 中显示的语法来告诉 GCC 可以读取或写入哪个内存,而不是上"memory"撞,如果你编写特定的系统调用包装。
一些无指针情况包括调用 VDSO以避免往返内核模式和返回的getpid速度要快得多,就像 glibc 对适当的系统调用所做的那样。这也适用于which 确实需要指针。)clock_gettime
顺便提一下,注意实际内核接口与 C 库包装器提供的接口不匹配。这通常记录在手册页的 NOTES 部分中,例如 forbrk(2)和getpriority(2)
| 归档时间: |
|
| 查看次数: |
68 次 |
| 最近记录: |