x86linux gasでhello world
ちょっとだけやってほとんど忘れてるgasの記憶確認としてHello Worldを書いてみました。
関数なし版
セクション、基本的なx86 asmの使い方、エントリポイント、システムコールwrite&exitを呼ぶ、アドレッシング
# puts hello world # build: as hello.s -o hello.o && ld hello.o -o hello # check: ./hello ; echo $? #=> 13 (output length) .section .data # static value section message: .ascii "Hello World!\n\0" .section .text # code section .equ SYSCALL, 0x80 .equ EXIT, 1 .equ WRITE, 4 .equ STDOUT, 1 .globl _start # global symbol: _start _start: # entry point movl $message, %ecx # buf = message movl $0, %edx # init len = 0 calc_len: cmpb $0, (%ecx, %edx) # buf[len] == 0 je puts incl %edx # len++ jmp calc_len puts: movl $STDOUT, %ebx # fd = stdout movl $WRITE, %eax # syscall 4: write int $SYSCALL # write(fd, buf, len) exit: movl %eax, %ebx # val = write(...): length of output movl $EXIT, %eax # syscall 1: exit int $SYSCALL # exit(val)
関数化版
C規約関数の呼び出し方、呼び出され方、スタックポインタ、ベースポインタ
# puts hello world # build: as hello.s -o hello.o && ld hello.o -o hello # check: ./hello ; echo $? #=> 13 (output length) .section .data # static value section message: .ascii "Hello World!\n\0" .section .text # code section .equ SYSCALL, 0x80 .equ EXIT, 1 .equ WRITE, 4 .equ STDOUT, 1 .globl exit exit: pushl %ebp movl %esp, %ebp movl 8(%ebp), %ebx movl $EXIT, %eax int $SYSCALL popl %ebp ret .globl strlen strlen: pushl %ebp # enter func magic1 movl %esp, %ebp # enter func magic2 movl 8(%ebp), %edx # 8(%ebp): first int32 param movl $0, %eax # eax: set return value .strlen_loop: cmpb $0, (%edx, %eax) je .strlen_exit addl $1, %eax jmp .strlen_loop .strlen_exit: popl %ebp # exit func magic1 ret # exit func magic2 .globl puts puts: pushl %ebp movl %esp, %ebp subl $4, %esp movl 8(%ebp), %eax # eax = *(ebp+8) movl %eax, (%esp) # *esp = eax call strlen addl $4, %esp movl %eax, %edx # eax: return val of strlen movl 8(%ebp), %ecx movl $STDOUT, %ebx movl $WRITE, %eax int $SYSCALL popl %ebp ret .globl _start _start: subl $4, %esp # shift esp by args size movl $message, (%esp) # set first arg call puts # call func addl $4, %esp # unshift esp subl $4, %esp movl %eax, (%esp) call exit addl $4, %esp
付録: gccインラインアセンブラ版
int strlen(char* str) { int len = 0; while (*str++ != '\0') len++; return len; } int puts(char* str) { int ret, len; len = strlen(str); __asm__("movl %2, %%edx;" "movl %1, %%ecx;" "movl $1, %%ebx;" "movl $4, %%eax;" "int $0x80;" "movl %%eax, %0;" :"=r" (ret) :"r" (str), "r"(len) :"%eax", "%ebx", "%ecx", "%edx"); return ret; } void exit0(int v) { __asm__("movl $1, %%eax;" "int $0x80;" : :"b" (v) :"%eax"); return; } int main() { exit0(puts("Hello World!\n")); return 0; }
4つのレジスタを使うwriteでは、直接eaxなどに変数を割り当てできないっぽい("=a"(ret)とかくとコンパイルエラーが出る)。
あと、stdlibとかぶるexitという名前は使えませんでした。
cファイルからアセンブリコードに変換するには、
gcc -S hello.c -o hello.c.s