「30日でできる！OS自作入門」三日目(harib00a〜harib00j)をUbuntu18.04/NASMで作る

#はじめに
[「30日でできる！OS自作入門」二日目](https://qiita.com/furble/items/9cf3ac3023a746a22aee)の続きです。

#環境
```bash
$ uname -a
Linux furble 5.3.0-45-generic #37~18.04.1-Ubuntu SMP Fri Mar 27 15:58:10 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

$ nasm -v
NASM version 2.13.02

$ qemu-system-i386 --version
QEMU emulator version 2.11.1(Debian 1:2.11+dfsg-1ubuntu7.23)
Copyright (c) 2003-2017 Fabrice Bellard and the QEMU Project developers

$ gcc --version
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ mcopy --version
mcopy (GNU mtools) 4.0.18
configured with the following options: enable-xdf disable-vold disable-new-vold disable-debug enable-raw-term 

```

#harib00a
```Nasm:ipl.asm
; haribote-ipl
; TAB=4

		ORG		0x7c00			; このプログラムがどこに読み込まれるのか

; 以下は標準的なFAT12フォーマットフロッピーディスクのための記述

		JMP		entry
		DB		0x90
		DB		"HARIBOTE"		; ブートセクタの名前を自由に書いてよい（8バイト）
		DW		512				; 1セクタの大きさ（512にしなければいけない）
		DB		1				; クラスタの大きさ（1セクタにしなければいけない）
		DW		1				; FATがどこから始まるか（普通は1セクタ目からにする）
		DB		2				; FATの個数（2にしなければいけない）
		DW		224				; ルートディレクトリ領域の大きさ（普通は224エントリにする）
		DW		2880			; このドライブの大きさ（2880セクタにしなければいけない）
		DB		0xf0			; メディアのタイプ（0xf0にしなければいけない）
		DW		9				; FAT領域の長さ（9セクタにしなければいけない）
		DW		18				; 1トラックにいくつのセクタがあるか（18にしなければいけない）
		DW		2				; ヘッドの数（2にしなければいけない）
		DD		0				; パーティションを使ってないのでここは必ず0
		DD		2880			; このドライブ大きさをもう一度書く
		DB		0,0,0x29		; よくわからないけどこの値にしておくといいらしい
		DD		0xffffffff		; たぶんボリュームシリアル番号
		DB		"HARIBOTEOS "	; ディスクの名前（11バイト）
		DB		"FAT12   "		; フォーマットの名前（8バイト）
		TIMES	18 DB 0				; とりあえず18バイトあけておく

; プログラム本体

entry:
		MOV		AX,0			; レジスタ初期化
		MOV		SS,AX
		MOV		SP,0x7c00
		MOV		DS,AX

; ディスクを読む

		MOV		AX,0x0820
		MOV		ES,AX
		MOV		CH,0			; シリンダ0
		MOV		DH,0			; ヘッド0
		MOV		CL,2			; セクタ2

		MOV		AH,0x02			; AH=0x02 : ディスク読み込み
		MOV		AL,1			; 1セクタ
		MOV		BX,0
		MOV		DL,0x00			; Aドライブ
		INT		0x13			; ディスクBIOS呼び出し
		JC		error

; 読み終わったけどとりあえずやることないので寝る

fin:
		HLT						; 何かあるまでCPUを停止させる
		JMP		fin				; 無限ループ

error:
		MOV		SI,msg
putloop:
		MOV		AL,[SI]
		ADD		SI,1			; SIに1を足す
		CMP		AL,0
		JE		fin
		MOV		AH,0x0e			; 一文字表示ファンクション
		MOV		BX,15			; カラーコード
		INT		0x10			; ビデオBIOS呼び出し
		JMP		putloop
msg:
		DB		0x0a, 0x0a		; 改行を2つ
		DB		"load error"
		DB		0x0a			; 改行
		DB		0

		TIMES	0x7dfe-0x7c00-($-$$)	DB 0	; 0x7dfeまでを0x00で埋める命令

		DB		0x55, 0xaa
```
```:Makefile
MAKE     = make -r
NASM     = nasm

# デフォルト動作
default :
	$(MAKE) img

# ファイル生成規則
ipl.bin : ipl.asm Makefile
	$(NASM) ipl.asm -o ipl.bin -l ipl.lst

haribote.img : ipl.bin Makefile
	cat ipl.bin > haribote.img

# コマンド
asm :
	$(MAKE) ipl.bin

img :
	$(MAKE) haribote.img

run :
	$(MAKE) img
	qemu-system-i386 -drive file=haribote.img,format=raw,if=floppy

clean :
	-rm ipl.bin $(IGN_ERR)
	-rm ipl.lst $(IGN_ERR)

src_only :
	$(MAKE) clean
	-rm haribote.img $(IGN_ERR)
```
#harib00b〜harib00d
新しく変更する箇所無し。

#harib00e
[30日でできる！OS自作入門（３日目）](https://qiita.com/pollenjp/items/8fcb9573cdf2dc6e2668#5-os%E6%9C%AC%E4%BD%93%E3%82%92%E6%9B%B8%E3%81%8D%E5%A7%8B%E3%82%81%E3%82%8Bp57)
[OS自作入門 onLinux 3日目 - Handwriting](http://lv4.hateblo.jp/entry/2011/10/21/222521)
を参考に、Makefileを変更します。

```bash:Makefile
MAKE     = make -rm
NASM     = nasm

# デフォルト動作
default :
	$(MAKE) img

# ファイル生成規則
ipl.bin : ipl.asm Makefile
	$(NASM) ipl.asm -o ipl.bin -l ipl.lst

haribote.sys : haribote.asm Makefile
	$(NASM) haribote.asm -o haribote.sys -l haribote.lst

haribote.img : ipl.bin haribote.sys Makefile
	mformat -f 1440 -C -B ipl.bin -i haribote.img ::
	mcopy haribote.sys -i haribote.img ::


# コマンド
asm :
	$(MAKE) ipl.bin

img :
	$(MAKE) haribote.img

run :
	$(MAKE) img
	qemu-system-i386 -drive file=haribote.img,format=raw,if=floppy

clean :
	-rm *.bin
	-rm *.lst
	-rm *.sys

src_only :
	$(MAKE) clean
	-rm haribote.img
```
haribote.asmをharibote.sysにアセンブラして、

```
mformat -f 1440 -C -B ipl.bin -i haribote.img ::
mcopy haribote.sys -i haribote.img :
```
でimgファイルに変換しています。
ここでmcopyはMS-DOSとLinuxという異なるファイルシステム間でファイルをコピーするコマンドですが、 $ man mcopyで調べると-iは存在しません。でもこの-iを抜くと

```bash
...
mcopy haribote.sys haribote.img ::
Drive '::' not supported
Cannot initialize '::'
Bad target ::
Makefile:16: recipe for target 'haribote.img' failed
...
```
とエラーが出るため意味はあるようですが、その意味はネットで調べても分かりませんでした。


#harib00f〜harib00h
新しく変更する箇所無し。

#harib00i
asmhead.asmとipl10.asmは新しい変更箇所はありません。

omake/tolsrc/bim2hrb/bim2hrb.cには

```:bim2hrb.c
[ .bimファイルの構造 ]

+ 0 : .textサイズ
+ 4 : ファイル中の.textスタートアドレス（0x24）
+ 8 : メモリロード時の.textスタートアドレス（0x24）
+12 : .dataサイズ
+16 : ファイル中の.dataスタートアドレス
+20 : メモリロード時の.dataスタートアドレス
+24 : エントリポイント
+28 : bss領域のバイト数
+36 : コード

[ .hrbファイルの構造 ]

+ 0 : stack+.data+heap の大きさ（4KBの倍数）
+ 4 : シグネチャ "Hari"
+ 8 : mmarea の大きさ（4KBの倍数）
+12 : スタック初期値＆.data転送先
+16 : .dataのサイズ
+20 : .dataの初期値列がファイルのどこにあるか
+24 : 0xe9000000
+28 : エントリアドレス-0x20
+32 : heap領域（malloc領域）開始アドレス
```
とあります。
[30日でできる！OS自作入門（３日目）](https://qiita.com/pollenjp/items/8fcb9573cdf2dc6e2668#9-c%E8%A8%80%E8%AA%9E%E5%B0%8E%E5%85%A5p63---harib00i)より、

```:har.ld
/* https://vanya.jp.net/os/haribote.html#hrb */
OUTPUT_FORMAT("binary");

SECTIONS
{
.head 0x0 : {
LONG(64 * 1024)  /*  0 : stack+.data+heap の大きさ（4KBの倍数） */
LONG(0x69726148)      /*  4 : シグネチャ "Hari" */
LONG(0)               /*  8 : mmarea の大きさ（4KBの倍数） */
LONG(0x310000)        /* 12 : スタック初期値＆.data転送先 */
LONG(SIZEOF(.data))   /* 16 : .dataサイズ */
LONG(LOADADDR(.data)) /* 20 : .dataの初期値列のファイル位置 */
LONG(0xE9000000)      /* 24 : 0xE9000000 */
LONG(HariMain - 0x20) /* 28 : エントリアドレス - 0x20 */
LONG(0)               /* 32 : heap領域（malloc領域）開始アドレス */
}

.text : { *(.text) }

.data 0x310000 : AT ( ADDR(.text) + SIZEOF(.text) ) {
*(.data)
*(.rodata*)
*(.bss)
}

/DISCARD/ : { *(.eh_frame) }
}
```

[tools/nask - hrb-wiki](http://hrb.osask.jp/wiki/?tools/nask)を参考に

```diff:diff-asmhead.asm
$ diff -u projects/03_day/harib00i/asmhead.nas tolset/harib00i/asmhead.asm
--- projects/03_day/harib00i/asmhead.nas	2020-03-25 17:15:31.015930660 +0900
+++ tolset/harib00i/asmhead.asm	2020-04-05 16:00:03.896527952 +0900
@@ -1,6 +1,5 @@
 ; haribote-os boot asm
 ; TAB=4
-
 BOTPAK	EQU		0x00280000		; bootpackのロード先
 DSKCAC	EQU		0x00100000		; ディスクキャッシュの場所
 DSKCAC0	EQU		0x00008000		; ディスクキャッシュの場所（リアルモード）
@@ -55,8 +54,6 @@
 
 ; プロテクトモード移行
 
-[INSTRSET "i486p"]				; 486の命令まで使いたいという記述
-
 		LGDT	[GDTR0]			; 暫定GDTを設定
 		MOV		EAX,CR0
 		AND		EAX,0x7fffffff	; bit31を0にする（ページング禁止のため）
@@ -131,9 +128,9 @@
 		RET
 ; memcpyはアドレスサイズプリフィクスを入れ忘れなければ、ストリング命令でも書ける
 
-		ALIGNB	16
+		ALIGNB	16, DB 0
 GDT0:
-		RESB	8				; ヌルセレクタ
+		TIMES	8 DB 0				; ヌルセレクタ
 		DW		0xffff,0x0000,0x9200,0x00cf	; 読み書き可能セグメント32bit
 		DW		0xffff,0x0000,0x9a28,0x0047	; 実行可能セグメント32bit（bootpack用）
 
@@ -142,5 +139,5 @@
 		DW		8*3-1
 		DD		GDT0
 
-		ALIGNB	16
+		ALIGNB	16, DB 0
 bootpack:
```

[30日でできる！OS自作入門（３日目）[Ubuntu16.04/NASM]](https://qiita.com/pollenjp/items/8fcb9573cdf2dc6e2668#10-htl%E3%81%97%E3%81%9F%E3%81%84p66---harib00j)に従いMakefileを書いて実行すると、

```bash
$ make img
...
gcc -march=i486 -m32 -nostdlib -T har.ld bootpack.c -o bootpack.hrb
/tmp/ccf8eTvw.o: 関数 `HariMain' 内:
bootpack.c:(.text+0x9): `_GLOBAL_OFFSET_TABLE_' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
Makefile:16: recipe for target 'bootpack.hrb' failed
...
```
とエラーが出ます。
[Linuxの共有ライブラリ呼び出し(呼び出し側編) - 情弱ログ](https://sugawarayusuke.hatenablog.com/entry/2018/03/06/020921)を参考に、gccでhrbファイルを作る際、PIEという機能を明示的に無効化することで正常に実行できるようになりました。

```diff:diff-Makefile
$ diff -u harib00h/Makefile harib00i/Makefile 
--- harib00h/Makefile	2020-04-01 23:27:38.462692773 +0900
+++ harib00i/Makefile	2020-04-05 12:16:51.070304035 +0900
@@ -9,8 +9,14 @@
 ipl10.bin : ipl10.asm Makefile
 	$(NASM) ipl10.asm -o ipl10.bin -l ipl10.lst
 
-haribote.sys : haribote.asm Makefile
-	$(NASM) haribote.asm -o haribote.sys -l haribote.lst
+asmhead.bin : asmhead.asm Makefile
+	$(NASM) asmhead.asm -o asmhead.bin -l asmhead.lst
+
+bootpack.hrb : bootpack.c har.ld Makefile
+	gcc -fno-pie -no-pie -march=i486 -m32 -nostdlib -T har.ld bootpack.c -o bootpack.hrb
+
+haribote.sys : asmhead.bin bootpack.hrb Makefile
+	cat asmhead.bin bootpack.hrb > haribote.sys
 
 haribote.img : ipl10.bin haribote.sys Makefile
 	mformat -f 1440 -C -B ipl10.bin -i haribote.img ::
@@ -29,9 +35,7 @@
 	qemu-system-i386 -drive file=haribote.img,format=raw,if=floppy
 
 clean :
-	-rm *.bin
-	-rm *.lst
-	-rm *.sys
+	-rm *.bin *.lst *.sys *.hrb
 
 src_only :
 	$(MAKE) clean
```

#harib00j
[30日でできる！OS自作入門（３日目）](https://qiita.com/pollenjp/items/8fcb9573cdf2dc6e2668#10-htl%E3%81%97%E3%81%9F%E3%81%84p66---harib00j)と[tools/nask - hrb-wiki](http://hrb.osask.jp/wiki/?tools/nask)より、

```Nasm:nasmfunc.asm
GLOBAL	io_hlt			; このプログラムに含まれる関数名
io_hlt:	; void io_hlt(void);
		HLT
		RET
```
だけの記述で動きました。元ファイルでは\_io_hltとアンダーバーが付いていましたが、以下のエラーが出るので削除しました。

```bash
$ make run
...
gcc -fno-pie -no-pie -march=i486 -m32 -nostdlib -T har.ld bootpack.c nasmfunc.o -o bootpack.hrb
/tmp/ccEMYSGG.o: 関数 `HariMain' 内:
bootpack.c:(.text+0x7): `io_hlt' に対する定義されていない参照です
collect2: error: ld returned 1 exit status
Makefile:19: recipe for target 'bootpack.hrb' failed
...
```

また、bootpack.cのextern void io_hlt(void);のexternは無くても問題ありませんでした。