会社で「入門 モダンLinux」の輪読会が発生しているので、読んだ中で重要であることを書く。
1章 Linuxの入門
1.1 モダンな環境とは何か? 1.2 これまでのLinuxの歴史 1.3 なぜオペレーティングシステムなのか? 1.4 Linuxディストリビューション 1.5 リソースの可視性 1.6 Linuxの全体像 1.7 まとめ
各節で重要なこと
- 1.1 モダンな環境とは、古典的なPCとは異なり、モバイルデバイスをはじめ、Linuxが動くような全ての環境。
- 1.2 (省略)
- 1.3 カーネルはハードウェアを抽象化し、APIを提供するもの。つまり、具体的には、syscallを提供できる状態にするもの。LinuxカーネルはOSである。
- 1.4 Linuxディストリビューションは、カーネル、パッケージ+パッケージマネージャー、ファイルシステム、ブートローダー、その他ユーザーソフトウェアをまとめたものを指す。
- 1.5 ”リソースとはソフトウェア実行の支援のために使用できるすべてのもの”。”Linuxでは「すべてがファイルである」”のは、これらのリソースにアクセスできるということ。同一マシン上でも、OSの制御によって、リソースの見え方は異なること。
- 1.6 仮想化やコンテナ化といった高度な技術は、Linuxのベーシックな機能のもとに成り立っていること。
- 1.7 (省略)
何故OSはCPUアーキテクチャの相違をカバーできないのか
OSの概念を読んだ後は、「Linuxがハードウェアの差異を吸収するのであれば、何故 x86(x86_64) と ARM(aarch64) のCPUのアーキテクチャの差異をカバーできないのか」という疑問が生じる。
私の結論は、以下の2行に集約される。
- OSは、実行ファイル、つまりバイナリによって構成されている
- CPUに実行させたい命令の塊であるバイナリは、CPUアーキテクチャと1対1の関係にあるため、抽象化できない※
当然といえば当然の話であるが、知識としては知っていたものの、確認が不足していた部分なので、今回検証してみた。
※の点は、後述のQEMUなどの技術によって吸収できるという反論はあるかもしれない。しかしそれは、ある命令セットAで実行できるように、命令セットBのバイナリを命令セットAとして翻訳した結果であるので、該当しない。
命令セットの差異の確認
命令セットに差異があるということは、バイナリが異なるということである。 機械語に翻訳する前のアセンブラの差異を見ることによって、人間にもわかるような差異が発生する。
ソースコードは、ハローワールドを実行する hello.c
を以下とする。尚、命令が異なることさえわかればいいので、動的リンクを採用し、アセンブラのコード量を抑えている。
$ cat hello.c #include <stdio.h> int main(){ printf("hello world"); }
x86_64
$ cc -S -O0 -march=x86-64 hello.c $ cat hello.s .file "hello.c" .text .section .rodata .LC0: .string "hello world" .text .globl main .type main, @function main: .LFB0: .cfi_startproc endbr64 pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 leaq .LC0(%rip), %rax movq %rax, %rdi movl $0, %eax call printf@PLT movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0" .section .note.GNU-stack,"",@progbits .section .note.gnu.property,"a" .align 8 .long 1f - 0f .long 4f - 1f .long 5 0: .string "GNU" 1: .align 8 .long 0xc0000002 .long 3f - 2f 2: .long 0x3 3: .align 8 4:
aarch64
$ aarch64-linux-gnu-gcc -S -O0 hello.c $ cat hello.s .arch armv8-a .file "hello.c" .text .section .rodata .align 3 .LC0: .string "hello world" .text .align 2 .global main .type main, %function main: .LFB0: .cfi_startproc stp x29, x30, [sp, -16]! .cfi_def_cfa_offset 16 .cfi_offset 29, -16 .cfi_offset 30, -8 mov x29, sp adrp x0, .LC0 add x0, x0, :lo12:.LC0 bl printf mov w0, 0 ldp x29, x30, [sp], 16 .cfi_restore 30 .cfi_restore 29 .cfi_def_cfa_offset 0 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0" .section .note.GNU-stack,"",@progbits
参考文献
命令セット - Wikipedia
AArch64 - Wikipedia
RISC-V - Wikipedia
x86_64環境上でのARM64組み込みバイナリのクロスコンパイルに関するメモ - satumaimo_10の備忘録
x86, Arm, PowerPC のアセンブリコードを簡単に生成・比較する - Qiita
x86_64のUbuntuでC/C++のソースコードをARM/ARM64用にクロスコンパイルしてQEMUで実行する方法のまとめ - Qiita
11日目: [x86] アーキテクチャとバイナリ解析最速マスター - しかくいさんかく