1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

x86でHelloWorld

Last updated at Posted at 2022-12-02

これは東京高専プロコンゼミ① Advent Calendar 2022の2日目の記事です.

hello world

アセンブラでhello worldして,コンピュータのもっと奥深くへと続く階段を降り始めましょう!

x86 GNU Assembler (GAS)

x86 GNU Assembler(x86 GAS)というのはGNUコンパイラ(gccとか)で使われるx86というCPUシリーズのアセンブリです
(この記事ではx86-64です)

code

まずはコードを見てください

hello.s
.intel_syntax noprefix
.global main

main:
	mov rax, 1 # write
	mov rdi, 1 # std 1
	lea rsi, _helloworld # string address
	mov rdx, 10 # length
	syscall

	mov rax, 60 # exit 
	mov rdi, 0
	syscall


.data
_helloworld:
	.asciz "helloworld"

コンパイル・リンク
$gcc -c hello.s -o hello.o && ld -e main -o hello.bin hello.o

ディレクティブ

上から見ていきます.

1行目
.intel_syntax noprefix
.global main

この二つの行ではgccコンパイラに情報を渡しています.
一行目ではintel構文でアセンブラを描くことをコンパイラに宣言しています.

二行目ではmainから処理が始まるということを教えています.

このようにコンパイラにいろいろやってくれるように頼む指令のことをディレクティブ(directives)といいます.
ディレクティブは先頭に.が付きます.

ディレクティブの一覧

ラベル

4行目
main:
	mov rax, 1 # write
	mov rdi, 1 # std 1
	lea rsi, _helloworld # string address

ここで新しく出てきたのがラベルです.

4行目
main:

これがラベルです.ここではラベルを宣言しています.

実そしてここではもう一つラベルが出てきています.

7行目
lea rsi, _helloworld # string address

ここです.ここではラベルを使っています.
このラベルの宣言はもっと後ろでされています.

14行目
.data
_helloworld:
	.asciz "helloworld"

ここです.

ラベルを宣言するときは:をラベルの名前の後につけます.
ラベルを使うときはラベル名を以下のように使えばいいだけです.

7行目
lea rsi, _helloworld # string address

ラベルというのは場所の名前みたいなもので,ラベルが宣言されている場所のアドレスが紐図けられています.
人間がコードを見たり書いたりするときに分かりやすくするためのもので,コンパイラがコンパイル時にラベルアドレスに置き換えてくれます.

レジスタ

4行目
main:
	mov rax, 1 # write
	mov rdi, 1 # std 1
	lea rsi, _helloworld # string address
	mov rdx, 10 # length

これらの行ではレジスタと呼ばれるCPU内部の記憶装置に値を入れています.
レジスタの一覧

命令

このプログラムで出てくる命令は3種類しかないです.

  • mov
    左辺に指定したレジスタに右辺の値を入れる命令です.
mov例
mov rax, 1
  • lea
    左辺に指定したレジスタに右辺の示すアドレスを入れる命令です.
lea rsi, _helloworld #_helloworldがさすアドレスがrsiジスタに入る.
  • syscall

syscall

4行目
main:
	mov rax, 1 # write
	mov rdi, 1 # std 1
	lea rsi, _helloworld # string address
	mov rdx, 10 # length
	syscall

ここではsyscallと呼ばれる.OSが用意してくれているサブルーチンを使うことのできるアセンブリ命令を使っています.

syscallを使うにはraxに使いたいsyscallの番号を入れその他のレジスタに引数を渡します.

syscall番号や引数は以下のリンクを確認してください
syscall一覧

mov rax, 1 # write
mov rdi, 1 # std 1
lea rsi, _helloworld # string address
mov rdx, 10 # length

ここではraxにwriteのsyscall番号である1を入れて,writeを使って標準出力しています.

writeの引数は以下の通りです.

rdi rsi rdx
ファイルディスクリプタ(1で標準出力) 文字列の先頭アドレス 文字列の長さ(byte数)
9行目
syscall

レジスタにsyscall番号,引数を入れたのちsyscall命令を実行することでsyscallを呼び出せます.

11行目
mov rax, 60 # exit 
mov rdi, 0
syscall

ここでのsyscallはプログラムを終了するためexitを呼んでいます.

文字列の確保

14行目
.data
_helloworld:
	.asciz "helloworld"

ここではディレクティブを使っています.

まず.dataでこれより下がデータセクションであることをコンパイラに知らせています.

そしてバイナリ中に文字列を埋め込んでくれる.ascizというディレクティブにより,helloworldという文字列をバイナリ中に埋め込んでいます.

またここではラベルを宣言しています.

_helloworld:

このラベルには.asciiで確保した文字列の先頭アドレスが入っています.

このラベルはmainの

mov rax, 1 # write
mov rdi, 1 # std 1
lea rsi, _helloworld # string address
mov rdx, 10 # length

ここで使われています.

コンパイル・リンク

コンパイル・リンク
$gcc -c hello.s -o hello.o && ld -e main -o hello.bin hello.o

GASなのでgccでコンパイルしてください.
gccでコンパイルだけしているのは普通にコンパイルするとコンパイラがいろいろやってくれるので-cでコンパイルだけしてldでリンクしています(今回はどっちでもいいけどバイナリを覗くときに分かりやすい)
ld-e mainは最初に実行し始めるところ(エントリーポイント)にmainを指定しています.

実行
$./hello.bin

実行結果
image.png

生成された実行形式のファイルを覗いてみましょう.

バイナリを覗く
$objdump -d hello.bin

Hello Computer

おわりに

ありがとうございました.誤り等はコメントで教えてください.

これは東京高専プロコンゼミ① Advent Calendar 2022の2日目の記事です.
このカレンダー,めちゃくちゃ開いていたので急遽日が変わる30分くらい前に投稿するために記事を書き始めました.よって案の定遅刻しています.ゆるして・・・

リンク集

  • Syscall一覧↓

  • brkの使いかた,アセンブラの入門↓

  • アセンブラ入門(少し古め,けどわかりやすい)↓

  • 命令一覧↓

  • Directive 一覧↓

  • 汎用レジスタ一覧↓

  • Opcode/Instruction finder for x86_64

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?