GCC:内联汇编中的putchar(char)
溢出,
如何仅使用内联汇编实现putchar(char)过程?我想在x86-64汇编中做到这一点。我这样做的原因是实现我自己的标准库(或至少一部分)。这是我到目前为止的内容:
void putchar(char c){
/* your code here: print character c on stdout */
asm(...);
}
void _start()
{
/* exit system call */
asm("mov $1,%rax;"
"xor %rbx,%rbx;"
"int $0x80"
);
}
我正在编译:
gcc -nostdlib -o putchar putchar.c
感谢您的帮助!
回答:
使用GNU C内联asm时,请 ,而不是使用asm模板中的指令“手动”进行。
对于writechar
和readchar
,我们只需要"syscall"
为模板,用约束来设置所有寄存器输入(与所指向char
的内存的write(2)
系统调用),根据X86-64的Linux系统调用约定(这非常密切与System V ABI的函数调用约定匹配)。
这也使得避免编译器可能保留值的红色区域(比RSP低128个字节)变得容易。您一定不能从内联asm破坏它(因此push
/ pop
除非sub rsp,
128首先使用,否则是不可用的:请参见在C++内联asm中使用基本指针寄存器,以及有关GNUC内联asm的许多有用链接),并且无法告诉编译器您破坏它。您可以使用构建-mno-redzone
,但是在这种情况下,输入/输出操作数要好得多。
我不愿把这些putchar
和getchar
。如果要实现自己的不支持缓冲的stdio,则可以执行此操作,但是某些功能 需要
输入缓冲才能正确实现。例如,scanf
必须检查字符以查看它们是否与格式字符串匹配,如果不匹配则将其保留为“未读”。输出缓冲是可选的。您 可能会 认为_可以_使用创建私有缓冲区及其功能的函数write()
或直接write()
输入指针的函数完全实现stdio 。
回答:
int writechar(char my_char){
int retval; // sys_write uses ssize_t, but we only pass len=1
// so the return value is either 1 on success or -1..-4095 for error
// and thus fits in int
asm volatile("syscall #dummy arg picked %[dummy]\n"
: "=a" (retval) /* output in EAX */
/* inputs: ssize_t read(int fd, const void *buf, size_t count); */
: "D"(1), // RDI = fd=stdout
"S"(&my_char), // RSI = buf
"d"(1) // RDX = length
, [dummy]"m" (my_char) // dummy memory input, otherwise compiler doesn't store the arg
/* clobbered regs */
: "rcx", "r11" // clobbered by syscall
);
// It doesn't matter what addressing mode "m"(my_char) picks,
// as long as it refers to the same memory as &my_char so the compiler actually does a store
return retval;
}
在Godbolt编译器资源管理器上,这可以使用gcc
-O3非常有效地进行编译。
writechar: movb %dil, -4(%rsp) # store my_char into the red-zone
movl $1, %edi
leaq -4(%rsp), %rsi
movl %edi, %edx # optimize because fd = len
syscall # dummy arg picked -4(%rsp)
ret
@ NRZ的测试main
内联也 远
更有效地比在回答不安全(红色区域重挫)版本,服用的事实优势,syscall
让大部分寄存器未修改所以它可以只设置一次即可。
main: movl $97, %r8d # my_char = 'a'
leaq -1(%rsp), %rsi # rsi = &my_char
movl $1, %edx # len
.L6: # do {
movb %r8b, -1(%rsp) # store the char into the buffer
movl %edx, %edi # silly compiler doesn't hoist this out of the loop
syscall #dummy arg picked -1(%rsp)
addl $1, %r8d
cmpb $123, %r8b
jne .L6 # } while(++my_char < 'z'+1)
movb $10, -1(%rsp)
syscall #dummy arg picked -1(%rsp)
xorl %eax, %eax # return 0
ret
回答:
int readchar(void){
int retval;
unsigned char my_char;
asm volatile("syscall #dummy arg picked %[dummy]\n"
/* outputs */
: "=a" (retval)
,[dummy]"=m" (my_char) // tell the compiler the asm dereferences &my_char
/* inputs: ssize_t read(int fd, void *buf, size_t count); */
: "D"(0), // RDI = fd=stdin
"S" (&my_char), // RDI = buf
"d"(1) // RDX = length
: "rcx", "r11" // clobbered by syscall
);
if (retval < 0) // -1 .. -4095 are -errno values
return retval;
return my_char; // else a 0..255 char / byte
}
呼叫者可以通过检查来检查错误c < 0
。
以上是 GCC:内联汇编中的putchar(char) 的全部内容, 来源链接: utcz.com/qa/420906.html