しょしんしゃのためのhello world

349 Views

August 24, 23

スライド概要

2019年に、Linuxでhello worldをビルドして動かしたときの記録

profile-image

Embedded Linux Hobbyists

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

しょしんしゃのためのhello world wata2ki

2.

はじめに • 今日は、プログラミング初心者が最初に挑戦するhello worldについて発表します • いつもネタが濃いとかマニアックと言われるので、しょしんしゃネタに挑戦してみました • スクリプト言語はとても難しいので、使う言語はC言語です • では、はじめてみましょう!

3.
[beta]
Hello world
まずは、ソースコードを書きます
#include <stdio.h>
int main(int argc, char *args[])
{
printf("Hello, world!\n");
return 0;
}

gccでビルドします

gcc helloworld.c -o helloworld
実際に動かしてみましょう
strace ./helloworld

4.
[beta]
実行結果
execve("./helloworld", ["./helloworld"], 0x7ffdf3cb7e40 /* 26 vars */) = 0
brk(NULL)
= 0x56098a5fc000
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)
= -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=91632, ...}) = 0
mmap(NULL, 91632, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f25389fb000
close(3)
= 0
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f25389f9000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25383fa000
mprotect(0x7f25385e1000, 2097152, PROT_NONE) = 0
mmap(0x7f25387e1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f25387e1000
mmap(0x7f25387e7000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f25387e7000
close(3)
= 0
arch_prctl(ARCH_SET_FS, 0x7f25389fa4c0) = 0
mprotect(0x7f25387e1000, 16384, PROT_READ) = 0
mprotect(0x560988b70000, 4096, PROT_READ) = 0
mprotect(0x7f2538a12000, 4096, PROT_READ) = 0
munmap(0x7f25389fb000, 91632)
= 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)
= 0x56098a5fc000
brk(0x56098a61d000)
= 0x56098a61d000
write(1, "Hello, world!\n", 14Hello, world!
)
= 14
exit_group(0)
= ?

5.

では本題 • 実行結果について • • • その実行ファイルを実行したときにコールされるシステムコールを、コンソールログに出力したもの straceというコマンドを使っています システムコールは、Linuxカーネルの機能を使うときにコールする処理です

6.
[beta]
それでは解説
Hello worldに関係する部分は、実はこれだけ
#include <stdio.h>
int main(int argc, char *args[])
{
printf("Hello, world!\n");
return 0;
}

fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)
= 0x56098a5fc000
brk(0x56098a61d000)
= 0x56098a61d000
write(1, "Hello, world!\n", 14Hello, world!
)
= 14

• printfは、標準出力に対してフォー
マット文字列を出力するライブラリ
関数

• 標準出力はファイルディスクリプタ1
番と決まっている
• 1番のファイルが存在した後brkシス
テムコールでメモリを確保
• brkはmallocなどの動的メモリ確
保の裏でコールされる
• 最後に、文字列を1番に書き込み

7.
[beta]
それでは解説
バッファを持たない標準エラー出力を使ったらどうなるのか?
#include <stdio.h>
int main(int argc, char *args[])
{
fprintf(stderr,"Hello, world!\n");
return 0;
}

write(2, "Hello, world!\n", 14Hello, world!
)
= 14

fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)
= 0x56098a5fc000
brk(0x56098a61d000)
= 0x56098a61d000
write(1, "Hello, world!\n", 14Hello, world!
)
= 14

標準エラー出力の場合
標準出力の場合

8.
[beta]
では、他の部分は何なの?!

実行ファイルを実行するときに最初に
コールするシステムコール

execve("./helloworld", ["./helloworld"], 0x7ffdf3cb7e40 /* 26 vars */) = 0
brk(NULL)
= 0x56098a5fc000
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)
= -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=91632, ...}) = 0
mmap(NULL, 91632, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f25389fb000
close(3)
= 0
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f25389f9000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25383fa000
mprotect(0x7f25385e1000, 2097152, PROT_NONE) = 0
mmap(0x7f25387e1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f25387e1000
mmap(0x7f25387e7000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f25387e7000
close(3)
= 0
arch_prctl(ARCH_SET_FS, 0x7f25389fa4c0) = 0
mprotect(0x7f25387e1000, 16384, PROT_READ) = 0
mprotect(0x560988b70000, 4096, PROT_READ) = 0
mprotect(0x7f2538a12000, 4096, PROT_READ) = 0
munmap(0x7f25389fb000, 91632)
= 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)
= 0x56098a5fc000
brk(0x56098a61d000)
= 0x56098a61d000
write(1, "Hello, world!\n", 14Hello, world!
)
= 14
実行ファイルが終了するとき(プロセスが終了するとき)
exit_group(0)
= ?

にコールされるシステムコール

9.
[beta]
では、他の部分は何なの?!
execve("./helloworld", ["./helloworld"], 0x7ffdf3cb7e40 /* 26 vars */) = 0
brk(NULL)
= 0x56098a5fc000
ダイナミックリンカの処理
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)
= -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=91632, ...}) = 0
mmap(NULL, 91632, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f25389fb000
close(3)
= 0
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f25389f9000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25383fa000
mprotect(0x7f25385e1000, 2097152, PROT_NONE) = 0
mmap(0x7f25387e1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f25387e1000
mmap(0x7f25387e7000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f25387e7000
close(3)
= 0
arch_prctl(ARCH_SET_FS, 0x7f25389fa4c0) = 0
mprotect(0x7f25387e1000, 16384, PROT_READ) = 0
mprotect(0x560988b70000, 4096, PROT_READ) = 0
mprotect(0x7f2538a12000, 4096, PROT_READ) = 0
munmap(0x7f25389fb000, 91632)
= 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)
= 0x56098a5fc000
brk(0x56098a61d000)
= 0x56098a61d000
write(1, "Hello, world!\n", 14Hello, world!
)
= 14
exit_group(0)
= ?

10.

ダイナミックリンカとは? • Linuxを初めとしたほとんどのOSでは、実行ファイルが共通で使うライブラリは、実行ファイル の中(スタティックリンク)には置かずに、実行時にくっつける(ダイナミックリンク)する • メリット • • • ライブラリのバグ修正を行う場合に、スタティックリンクの場合はすべての実行ファイルを新しくしないといけない が、ダイナミックリンクの場合はライブラリのファイルを更新するだけで済む 変数で使うメモリは各プロセスで個別に持つ必要があるが、全プロセス共通で良いプログラムコードと定数 (.text, .const)は1つで済むため、メモリ使用量が削減できる このメカニズムを実現するのがダイナミックリンカ(ld.so)

11.
[beta]
では、何をしているのか見てみよう
execve("./helloworld", ["./helloworld"], 0x7ffdf3cb7e40 /*
26 vars */) = 0
brk(NULL)
= 0x56098a5fc000
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such
file or directory)
access("/etc/ld.so.preload", R_OK)
= -1 ENOENT (No such
file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=91632, ...}) = 0
mmap(NULL, 91632, PROT_READ, MAP_PRIVATE, 3, 0) =
0x7f25389fb000
close(3)
= 0
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such
file or directory)
# cat /proc/30628/maps
555555554000-555555555000 r-xp 00000000 08:01 1213967
555555754000-555555756000 rw-p 00000000 08:01 1213967
7ffff7dd5000-7ffff7dfc000 r-xp 00000000 08:01 2102427
7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0
7ffff7ffc000-7ffff7ffe000 rw-p 00027000 08:01 2102427
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0

この時点での、helloworldのメモリ
マップ
execveシステムコールで、実行ファ
イルとダイナミックリンカが、展開
されている

/home/watatuki/demo/helloworld3
/home/watatuki/demo/helloworld3
/lib/x86_64-linux-gnu/ld-2.27.so
[vvar]
[vdso]
/lib/x86_64-linux-gnu/ld-2.27.so
[stack]
[vsyscall]

注. ログとメモリマップを同時にとれないので、アドレスがずれています

12.
[beta]
では、何をしているのか見てみよう
execve("./helloworld", ["./helloworld"], 0x7ffdf3cb7e40 /*
26 vars */) = 0
brk(NULL)
= 0x56098a5fc000
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such
file or directory)
access("/etc/ld.so.preload", R_OK)
= -1 ENOENT (No such
file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=91632, ...}) = 0
mmap(NULL, 91632, PROT_READ, MAP_PRIVATE, 3, 0) =
0x7f25389fb000
close(3)
= 0
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such
file or directory)
# cat /proc/30628/maps
555555554000-555555555000 r-xp 00000000 08:01 1213967
555555754000-555555756000 rw-p 00000000 08:01 1213967
7ffff7dd5000-7ffff7dfc000 r-xp 00000000 08:01 2102427
7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0
7ffff7ffc000-7ffff7ffe000 rw-p 00027000 08:01 2102427
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0

これは動的メモリ(ヒープ)の初期位
置を取得している
ダイナミックリンクライブラリは、
ファイル名しか実行フィルに記録さ
れていないので、それがどこ(/lib?
/usr/lib?)にあるのかを記録した
キャッシュファイルを探している
/home/watatuki/demo/helloworld3
/home/watatuki/demo/helloworld3
/lib/x86_64-linux-gnu/ld-2.27.so
[vvar]
[vdso]
/lib/x86_64-linux-gnu/ld-2.27.so
[stack]
[vsyscall]

注. ログとメモリマップを同時にとれないので、アドレスがずれています

13.
[beta]
では、何をしているのか見てみよう
execve("./helloworld", ["./helloworld"], 0x7ffdf3cb7e40 /*
26 vars */) = 0
brk(NULL)
= 0x56098a5fc000
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such
file or directory)
access("/etc/ld.so.preload", R_OK)
= -1 ENOENT (No such
file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=91632, ...}) = 0
mmap(NULL, 91632, PROT_READ, MAP_PRIVATE, 3, 0) =
0x7f25389fb000
close(3)
= 0
access("/etc/ld.so.nohwcap", F_OK)
= -1 ENOENT (No such
file or directory)
555555554000-555555555000 r-xp 00000000 08:01 1213967
555555754000-555555756000 rw-p 00000000 08:01 1213967
7ffff7dd5000-7ffff7dfc000 r-xp 00000000 08:01 2102427
7ffff7fe0000-7ffff7ff7000 r--p 00000000 08:01 2362199
7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0
7ffff7ffc000-7ffff7ffe000 rw-p 00027000 08:01 2102427
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0

このmmap終了後、ld.so.cacheとい
うダイナミックリンカのキャッシュ
ファイルがマップされる
/home/watatuki/demo/helloworld3
/home/watatuki/demo/helloworld3
/lib/x86_64-linux-gnu/ld-2.27.so
/etc/ld.so.cache (deleted)
[vvar]
[vdso]
/lib/x86_64-linux-gnu/ld-2.27.so
[stack]
[vsyscall]

注. ログとメモリマップを同時にとれないので、アドレスがずれています

14.
[beta]
では、何をしているのか見てみよう(続き1)
libc(システムコールや標準関数を提供するライブラリ)をメモリ上に展開
access("/etc/ld.so.nohwcap", F_OK)
= -1
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f25389f9000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f25383fa000
mprotect(0x7f25385e1000, 2097152, PROT_NONE) = 0
mmap(0x7f25387e1000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f25387e1000
mmap(0x7f25387e7000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f25387e7000
close(3)
= 0
555555554000-555555555000 r-xp 00000000 08:01 1213967
/home/watatuki/demo/helloworld3
555555754000-555555756000 rw-p 00000000 08:01 1213967
/home/watatuki/demo/helloworld3
7ffff79e4000-7ffff7bcb000 r-xp 00000000 08:01 2102455
/lib/x86_64-linux-gnu/libc-2.27.so
7ffff7bcb000-7ffff7dcb000 ---p 001e7000 08:01 2102455
/lib/x86_64-linux-gnu/libc-2.27.so
7ffff7dcb000-7ffff7dd1000 rw-p 001e7000 08:01 2102455
/lib/x86_64-linux-gnu/libc-2.27.so
7ffff7dd1000-7ffff7dd5000 rw-p 00000000 00:00 0
7ffff7dd5000-7ffff7dfc000 r-xp 00000000 08:01 2102427
/lib/x86_64-linux-gnu/ld-2.27.so
7ffff7fde000-7ffff7fe0000 rw-p 00000000 00:00 0
7ffff7fe0000-7ffff7ff7000 r--p 00000000 08:01 2362199
/etc/ld.so.cache (deleted)
7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0
[vvar]
7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0
[vdso]
7ffff7ffc000-7ffff7ffe000 rw-p 00027000 08:01 2102427
/lib/x86_64-linux-gnu/ld-2.27.so
7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0
[stack]
注.
ログとメモリマップを同時にとれないので、のアドレスがずれています
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0
[vsyscall]

15.

では、何をしているのか見てみよう(続き2) libc(システムコールや標準関数を提供するライブラリ)をメモリ上に展開 close(3) = 0 arch_prctl(ARCH_SET_FS, 0x7f25389fa4c0) = 0 mprotect(0x7f25387e1000, 16384, PROT_READ) = 0 mprotect(0x560988b70000, 4096, PROT_READ) = 0 mprotect(0x7f2538a12000, 4096, PROT_READ) = 0 munmap(0x7f25389fb000, 91632) = 0 たぶんダイナミックリンクテーブルを作り終わったの で、テーブルの領域を書き込み禁止に変更してる いらなくなった、ダイナミックリンカキャッシュを破 棄 555555554000-555555555000 r-xp 00000000 08:01 1213967 /home/watatuki/demo/helloworld3 555555754000-555555755000 r--p 00000000 08:01 1213967 /home/watatuki/demo/helloworld3 555555755000-555555756000 rw-p 00001000 08:01 1213967 /home/watatuki/demo/helloworld3 7ffff79e4000-7ffff7bcb000 r-xp 00000000 08:01 2102455 /lib/x86_64-linux-gnu/libc-2.27.so 7ffff7bcb000-7ffff7dcb000 ---p 001e7000 08:01 2102455 /lib/x86_64-linux-gnu/libc-2.27.so 7ffff7dcb000-7ffff7dcf000 r--p 001e7000 08:01 2102455 /lib/x86_64-linux-gnu/libc-2.27.so 7ffff7dcf000-7ffff7dd1000 rw-p 001eb000 08:01 2102455 /lib/x86_64-linux-gnu/libc-2.27.so 7ffff7dd1000-7ffff7dd5000 rw-p 00000000 00:00 0 7ffff7dd5000-7ffff7dfc000 r-xp 00000000 08:01 2102427 /lib/x86_64-linux-gnu/ld-2.27.so 7ffff7fde000-7ffff7fe0000 rw-p 00000000 00:00 0 7ffff7fe0000-7ffff7ff7000 r--p 00000000 08:01 2362476 /etc/ld.so.cache (deleted) 7ffff7ff7000-7ffff7ffa000 r--p 00000000 00:00 0 [vvar] 7ffff7ffa000-7ffff7ffc000 r-xp 00000000 00:00 0 [vdso] 7ffff7ffc000-7ffff7ffd000 r--p 00027000 08:01 2102427 /lib/x86_64-linux-gnu/ld-2.27.so 7ffff7ffd000-7ffff7ffe000 rw-p 00028000 08:01 2102427 /lib/x86_64-linux-gnu/ld-2.27.so 7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00注. 0 ログとメモリマップを同時にとれないので、のアドレスがずれています [vsyscall]

16.

まとめ • 今日は、hallo worldがどんな動作をしているのか説明してみました • 今回はシステムコールに着目してみました • これが、スクリプト言語だともっとすごいことに。。。 • しょしんしゃ向けでも、こんなとらえかたがあると知ってもらえたらうれしいです!