Debian Linuxでstderrに書き込むのに問題があったため、ここに到着しました。私はレイ・ザイファルトの本を読んでいるので、yasmを使用します。私はIntel構文で記述し、elf64形式とdwarf2デバッグプロトコル用にアセンブルしています。
fprintfを使用する最初の試みで、セグメンテーション違反が発生しました。stdioの内部を見ました。私はなんとか1行のプログラムに相当するものを書くことができました:
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ライブラリ関数を呼び出すことができるすべての環境で機能するはずです。ここにいくつかのサンプルコードがあります。引数が指定されていないか、引数が多すぎる場合は、使用法メッセージが出力され、プログラムが終了します。引数が1つ指定されている場合は、整数に変換されて出力されます。
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