💻
システムコールでHelloWorld
Linuxシステムプログラミングを輪読することになり1章担当になりました。
1章なのでなんとなくで軽く読み流してもよかったのですが、せっかくなので少しだけ深ぼってみた。後々の章で説明がありそうなきもするけれど念のため。
i386(32bit)システムでは、ユーザーアプリケーションがソフトウェア割り込み命令intを実行します。割り込み番号は 0x80 (
int 0x80
という命令が実行される)。
これがなんなのか分からなかった。0x80をどうやって送って命令出すんだろう?
アセンブリ言語でHello,Worldしてみる
- アセンブリコードで「int0x80」とはどういう意味ですか?
- [さくっとアセンブリ入門 hello,world編](https://rabbitfoot141.hatenablog.com/entry/2016/05/01/124410
上記のサイトを参考にしつつ少してを加えて変えてみた。実行環境はUbuntu20.04。
アセンブリ言語まで下ると int $0x80
という命令がかかれて意味を理解できた。これを添えて実行するとCPUに割り込んで標準出力させることができる。
.globl _start
_start:
/* $の後が数字なら数字、文字なら変数。%はレジスタ */
movl $4,%eax /* write システムコール番号 */
movl $1,%ebx /* 書き込み先 - 標準出力 */
movl $msg,%ecx /* 書き込むメッセージ */
movl $13,%edx /* メッセージの長さ */
int $0x80 /* システムコール実行 割り込み実行 */
movl $1,%eax /* システムコール終了番号 */
movl $0,%ebx /* 終了状態 */
int $0x80 /* システムコール終了 割り込み実行 */
.data
msg: .asciz "Hello,World\n"
➜ ~ as -o main.o main.S
➜ ~ ld -o main.out main.o
➜ ~ ./main.out
hello,World
➜ ~
C言語とシステムコールでHello,Worldしてみる
こちらはシステムコールでHelloWorldするC言語。write関数なので自動的にeax=4、第一引数の1が標準出力 ebx=1, 出力文字列stringをecx、文字列の長さをedx
先にアセンブリ言語をみておくとwrite関数の引数の意味も見えてくる。
#include<unistd.h>
int main(){
const void *string = "Hello,World!\n";
write(1, string, 13);
return 0;
}
➜ ~ gcc write.c
➜ ~ ./a.out
Hello World!
➜ ~
逆アセンブルしてみる
最初に書いたアセンブリ言語は32bitの書き方なので、64bitでコンパル->逆アセンブルするとだいぶ結果が異なる。int 0x80は見当たらないけど, 0x8がmovされているので、これがどこかで実行されている?のでしょう
int main(){
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 10 sub $0x10,%rsp
const void *string = "Hello Write!\n";
c: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 13 <main+0x13>
13: 48 89 45 f8 mov %rax,-0x8(%rbp)
write(1, string, 13);
17: 48 8b 45 f8 mov -0x8(%rbp),%rax
1b: ba 0d 00 00 00 mov $0xd,%edx
20: 48 89 c6 mov %rax,%rsi
23: bf 01 00 00 00 mov $0x1,%edi
28: e8 00 00 00 00 callq 2d <main+0x2d>
return 0;
2d: b8 00 00 00 00 mov $0x0,%eax
}
32: c9 leaveq
33: c3 retq
まとめ
新しめの日本語情報でHello,Worldする情報があんまりなかったので書き残してみた。なお輪読する「Linuxシステムプログラミング」ではC言語をほうをメインに取り扱うのでアセンブリ言語はもう登場しないと思う……
Discussion