#はじめに
「30日でできる!OS自作入門」二日目の続きです。
#環境
$ 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
; 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
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自作入門(3日目)
OS自作入門 onLinux 3日目 - Handwriting
を参考に、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を抜くと
...
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には
[ .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自作入門(3日目)より、
/* 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) }
}
$ 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自作入門(3日目)[Ubuntu16.04/NASM]に従いMakefileを書いて実行すると、
$ 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の共有ライブラリ呼び出し(呼び出し側編) - 情弱ログを参考に、gccでhrbファイルを作る際、PIEという機能を明示的に無効化することで正常に実行できるようになりました。
$ 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自作入門(3日目)とtools/nask - hrb-wikiより、
GLOBAL io_hlt ; このプログラムに含まれる関数名
io_hlt: ; void io_hlt(void);
HLT
RET
だけの記述で動きました。元ファイルでは_io_hltとアンダーバーが付いていましたが、以下のエラーが出るので削除しました。
$ 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は無くても問題ありませんでした。