我对汇编很新,我想知道如何将输出写入stderr。我知道您可以访问C标准库函数,如printf,以打印到控制台。但我无法弄清楚如何打印到stderr。我试图使用fprintf,但我只是猜测参数,我不知道如何指定stderr作为文件指针。感谢。
编辑:根据这个建议,我试过了:
.586
.model small,c
.stack 100h
.data
msg db 'test', 0Ah
.code
includelib MSVCRT
extrn fprintf:near
extrn exit:near
public main
main proc
push offset msg
push 2 ;specify stderr
call fprintf ;print to stderr
push 0
call exit ;exit status code 0
main endp
end main
但它只是导致程序崩溃。还有其他建议吗?
答案 0 :(得分:8)
您使用的是MSVCRT dll中的fprintf吗?
第一个参数是指向流的指针。以下是如何在汇编中使用fprintf。 此外,从Assembly调用C函数时,需要在每次调用参数后调整堆栈。
另外,BIGGIE ......你的字符串是NOT NULL终止的!您必须NULL终止字符串,这是函数查找字符串长度的方式。 不确定您正在使用什么汇编程序,但这是您在MASM中执行此操作的方法:
include masm32rt.inc
_iobuf STRUCT
_ptr DWORD ?
_cnt DWORD ?
_base DWORD ?
_flag DWORD ?
_file DWORD ?
_charbuf DWORD ?
_bufsiz DWORD ?
_tmpfname DWORD ?
_iobuf ENDS
FILE TYPEDEF _iobuf
.data
msg db 'test', 0Ah, 0
.data?
stdin dd ?
stdout dd ?
stderr dd ?
.code
start:
call crt___p__iob
mov stdin,eax
add eax,SIZEOF(FILE)
mov stdout,eax
add eax,SIZEOF(FILE)
mov stderr,eax
push offset msg
push eax
call crt_fprintf
add esp, 4 * 2
push 0
call crt_exit
end start
答案 1 :(得分:1)
我来到这里是因为我在Debian Linux下写stderr时遇到了麻烦。我使用yasm,因为我正在阅读Ray Seyfarth的书。我正在编写Intel语法并组装elf64格式和dwarf2调试协议。
我第一次尝试使用fprintf遇到了分段错误。我看着stdio的内部。我设法编写了相当于一行的程序:
int main()
{
fprintf(stderr, "%d\n", 3);
}
但是当我完全修改它时,我又开始出现分段错误。我的目标是使装配相当于:
int main(int argc, char* argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s n\n", argv[0]);
} else {
/* program code */
}
}
我的最终解决方案是使用sprintf()调用,然后使用文件描述符进行write()系统调用。还需要调用strlen()。此策略应该适用于可以从汇编代码调用C库函数的任何环境。这是一些示例代码。如果没有给出参数或参数太多,则打印用法消息并退出程序。如果提供了一个参数,则将其转换为整数并打印。
segment .text
global main
extern printf, atoi, sprintf, strlen, write
main:
.argc equ 0
.argv equ 4
segment .data
.f1 db "Usage: %s n", 0x0a, 0
.f2 db "%d", 0x0a, 0
segment .bss
.buf resb 255
segment .text
push rbp
mov rbp, rsp
sub rsp, 16
; Save the arguments.
mov [rsp+.argc], rdi
mov [rsp+.argv], rsi
; if (argc != 2)
cmp rdi, 2
jne .usage
; Convert argv[1] to an integer, then print it.
; We could have printed argv[1] as a string,
; but in an actual program we might want to do
; n = atoi(argv[1])
; because we are passing a number on the command line.
; printf("%d\n", atoi(argv[1]))
; Get argv[1].
mov rax, [rsp+.argv]
mov rdi, [rax+8] ; rdi gets *(argv + 1), or argv[1].
call atoi ; Return value in eax.
lea rdi, [.f2]
mov esi, eax
xor eax, eax
call printf
jmp .done
.usage:
; Get argv[0].
mov rax, [rsp+.argv]
mov rax, [rax]
; sprintf(buf, "Usage: %s n\n", argv[0])
lea rdi, [.buf]
lea rsi, [.f1]
mov rdx, rax
xor eax, eax
call sprintf
; len = strlen(buf);
lea rdi, [.buf]
call strlen ; string length is placed in eax.
; write(2, buf, strlen(buf))
mov edi, 2 ; fd for stderr
lea rsi, [.buf]
mov edx, eax
call write
.done:
leave
ret
假设上面的代码包含在progname.asm中,则汇编并链接如下:
yasm -f elf64 -g dwarf2 -l progname.lst progname.asm
gcc progname.o -o progname