なんとな~くしあわせ?の日記

「そしてそれゆえ、知識そのものが力である」 (Nam et ipsa scientia potestas est.) 〜 フランシス・ベーコン

アセンブラを再実装してOS自作入門の4日目に到達

nantonaku-shiawase.hatenablog.com

以前進捗報告したのが2016-06-09、それから5ヶ月ぐらい経っているわけですが、ようやく3日目が終了しました。長かった。

これまでの流れ

  1. harib00iに出てくるアセンブリの実装に手間取る → harib00iの実装 · Issue #4 · HariboteLikeHobbyOS/opennask · GitHub
  2. harib00jに出てくるPEヘッダの出力に手間取る → PE(Portable Executable)ヘッダの出力 · Issue #12 · HariboteLikeHobbyOS/opennask · GitHub
  3. CMakeでqemuを実行するスクリプトを準備しまくる → Mocking cyberbird by Hiroyuki-Nagata · Pull Request #8 · HariboteLikeHobbyOS/opennask · GitHub
  4. 先人のサイトを見つけて、リンカスクリプトを使えばショートカットできることに気づく ← 今

2016/11/23時点では、書籍の3日目までのアセンブラ(harib00jまで)はバイナリに変換できるようになった。
使用できるオペコード(部分的に実装しているものも含む)は以下の通り:

  • ADD, ALIGNB, AND, CALL, CLI, CMP, DB, DD, DW, GLOBAL, HLT, IMUL
  • IN, INT, JAE, JBE, JB, JC, JE, JMP, JNC, JNZ, JZ, LGDT, MOV, NOP
  • OR, ORG, OUT, RET, RESB, SHR, SUB

コンパイル〜img書き込みまでの概要

ここまででようやくC言語をリンクしながら、naskのスクリプトアセンブルしてimgに書き込みという一連の動作が完成しました。以下がオリジナルのosaskと、私が先人のページを見ながらCMakeで動かす動作の比較です。

f:id:panzer-jagdironscrap1:20161123230726p:plain

図からわかるように、まずオリジナルのosaskは様々な独自ツール、独自フォーマットを使っています。

それらは以下のように代用できることがこれまでの先人の知恵によってわかっています。

nask

  • naskは大体の人がnasmもしくはgasで代用しています
  • わたしはオリジナルのアセンブラをそのまま使いたかった*1のでnaskを再実装しつつあります。

obj2bim, bim2hrb

  • これはldで代用できます、そしてldはgccから渡せるので、実質使うコマンドはgccのみになります

edimg

  • GNU謹製のmtoolで代用できます

コンパイル〜img書き込みまでのコマンド

私のopennaskを使うと、imgファイル作成までコマンドで7工程です

$ opennask ipl10.nas ipl.bin
$ opennask asmhead.nas asmhead.bin
$ opennask naskfunc.nas naskfunc.o
$ gcc -m32 -nostdlib -Wl,--oformat=binary bootpack.c naskfunc.o -T os.lds -o boot.bin
$ cat asmhead.bin boot.bin > haribote.sys
$ mformat -f 1440 -l HARIBOTEOS -N 0xffffffff -C -B ipl.bin -i haribote.sys
$ mcopy -i haribote.img haribote.sys

スクリーンショット

harib01a 画面が白くなるはずですがバグのため文字が描画されているっぽい*2

f:id:panzer-jagdironscrap1:20161130000326p:plain

harib01a 画面が白くなった!

f:id:panzer-jagdironscrap1:20161202012557p:plain

harib01d 縞模様

f:id:panzer-jagdironscrap1:20161205222833p:plain

感謝

以下の3サイトに非常にお世話になりました

『30日でできる!OS自作入門』のメモ

今後の展望

  • ポートフォリオみたいな感じで見せられるものを増やす、とりあえず30日目までの全てをコンパイル可能にする
  • TravisでCIを回して常にビルド可能な状態にしておく
  • opennaskをGO言語で書き直してクロスプラットホーム対応する

*1:アセンブラを書き直すのが面倒だったので

*2:asmhead.nasアセンブルする際にミスっていたので修正した

x86 汎用命令 - ModR/M の解説

ModR/Mについては前もちょっと記事を書いたけど、まだちょっと中途半端だったのでもう一度まとめます。

nantonaku-shiawase.hatenablog.com

ModR/Mの実際の例

例えば以下のような命令がある、 /r の意図がみなさんわからないのではないでしょうか?

; IMUL r32, imm32
0x69 /r iw

また、次のような命令、 /7 の意図がみなさんわからないのではないでしょうか?

; CMP r/m8, imm8
0x80 /7 ib

ModR/Mの構造

全体の概要についてはこのページをみたほうがよい

また、このページも役立つ

ModR/Mバイトの構造
  • ModR/Mバイトは画像のような構造になっており、それを指定された場合アセンブラは「mod/reg/rm」の3つの要素を1byteで出力する

https://freestylewiki.xyz/fswiki/wiki.cgi?page=%E3%82%A2%E3%82%BB%E3%83%B3%E3%83%96%E3%83%A9%28ModR%2FM%29&file=modrm%2Epng&action=ATTACH

  • でも実際はregとr/mはそれぞれレジスタを指すので、ほぼ同じようなものだと思っていい
'''/r'''および'''/7'''の意味

解説を Assembly Programming on x86-64 Linux (06) から引用する

この記事で注目している項目を赤字で表現。

記法 解説
/0 - /7 ModR/M バイト の reg フィールドの 0 から 7 の数字はオペコードの拡張用に使われる。r/m フィールドだけをオペランドに使用する。
/r 命令には ModR/M バイトが続き、レジスタオペランドと r/m オペランドの両方を使う。
cb、cw、cd、cp, co, ct オペコードの後に 1 バイト(cb)、2 バイト(cw)、4 バイト(cd)、6 バイト(cp)、 8 バイト(co)または 10 バイト(ct)が続く。
ib, iw, id, io オペコード、ModM/R バイト、または SIB の後に続く 1 バイト(ib)、2 バイト(iw)、4 バイト(id)または 8 バイト(io)の定数(即値)。
+rb, +rw, +rd, +ro + の左側のオペコードに加算される 0 から 7 までのレジスタコード。 結果として 1 バイトのオペコードとなる。
regとr/mで使われるレジスタコード
レジスタコード レジスタ
000 al ax eax
001 cl cx ecx
010 dl dx edx
011 bl bx ebx
100 ah sp esp
101 ch bp ebp
110 dh si esi
111 bh di edi

mod

reg(/0 - /7指定の場合)

  • regは、/0 - /7を指定された場合はそれをそのまま使う

もちろんこれは10進数表記なので、2進数に直すと以下のようになる。ModR/Mバイトのregフィールドはこれで埋まるわけだ。

/0 - /7 2進数表記
/0 000
/1 001
/2 010
/3 011
/4 100
/5 101
/6 110
/7 111

reg(/0 - /7指定の場合)の実例

; 0x80 /7 ib
CMP r/m8, imm8
; 例
CMP CL,18
; オペコードは0x80で確定
; ---------------------
; ModR/Mの値は
; [mod] 11
; [reg] 111
; [r/m] 001
; => "11111001" 
; => 0xf9
; ---------------------
; 18は16進数で0x12
; ---------------------
; よって、以下がアセンブルされると
CMP CL,18
; 以下のバイナリが出力される
0x80, 0xf9, 0x12

reg('''/r'''指定の場合)

つまり、mod2bitに続いて、regr/mが3bitずつ続くことになる

reg(/r指定の場合)の実例

; 0x69 /r iw
IMUL r32, imm32
; 例
IMUL ECX,4608
; オペコードは0x69で確定
; ---------------------
; ModR/Mの値は
; [mod] 11
; [reg] 001
; [r/m] 001
; => "11001001" 
; => 0xc9
; ---------------------
; 4608は16進数で0x1200
; リトルエンディアンのため、 0x00, 0x12と並ぶ
; ---------------------
; よって、以下がアセンブルされると
IMUL ECX,4608
; 以下のバイナリが出力される
0x69, 0xc9, 0x00, 0x12

/r指定の場合のレジスタの細かい指定

さて、実はここまでregとr/mのどちらがどちらのレジスタを指定しているのか気になった人がいるかもしれない。その指摘はめっちゃ正しい。

ここのサイトに答えが書いてある。

The d bit in the opcode determines which operand is the source, and which is the destination:

d=0: MOD R/M <- REG, REG is the source
d=1: REG <- MOD R/M, REG is the destination

"d"bitというのは、1byte(8bit)のデータを先頭から数えて4番目のbitである。*1
つまり、"d"bitが0か1かによってmodとr/mの3bitをそれぞれ入れ替えなければいけない。

C++の疑似コード、std::bitsetを使うとやりやすい

uint8_t op = 0x8e;
std::bitset<8> bsl(op); // オペコード

if (bsl[3]) { // d=0 or 1
  // d=1: REG <- MOD R/M, REG is the destination
} else {
  // d=0: MOD R/M <- REG, REG is the source
}

はじめて読む486、ModRMとかに関してはほとんど役に立たず…

はじめて読む486―32ビットコンピュータをやさしく語る

はじめて読む486―32ビットコンピュータをやさしく語る

*1:わかるか…!こんな細かい仕様…!

Let's Encryptを手動更新

blog.apar.jp

だいたいここの指示にしたがえばよし

こんなエラーが出た。

Cleaning up challenges
Attempting to renew cert from /etc/letsencrypt/renewal/freestylewiki.xyz.conf produced an unexpected error: At least one of the required ports is already taken.. Skipping.

httpdを一回停止させればOK

# systemctl stop httpd

成功時のログ

* Pythonスクリプトがいろいろやってる(秘密鍵更新+CSR作成+証明書作成(中間証明書も))

[root@freestylewiki letsencrypt]# ./certbot-auto renew --force-renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/freestylewiki.xyz.conf
-------------------------------------------------------------------------------
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Renewing an existing certificate
Performing the following challenges:
tls-sni-01 challenge for freestylewiki.xyz
Waiting for verification...
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0001_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0001_csr-certbot.pem

-------------------------------------------------------------------------------
new certificate deployed without reload, fullchain is
/etc/letsencrypt/live/freestylewiki.xyz/fullchain.pem
-------------------------------------------------------------------------------

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/freestylewiki.xyz/fullchain.pem (success)