ハイパーニートプログラマーへの道

頑張ったり頑張らなかったり

CrystalでOSを作ってみた

この記事は Crystal Advent Calendar 2016 の25日目の記事です。

OSと言っても、ブートして文字を表示してるだけですが・・・こんなんです。

f:id:noriyo_tcp:20161225004431p:plain

リポジトリはこちら

github.com

軽く自己紹介

  • 普段はフリーランスRailsエンジニア(無職ともいう)
  • OS作りたい、という願望は以前からあった(話すとちょっと長くなるので割愛)
  • アセンブリC言語、Crystal に関しては初心者同然

参考にしたもの

Writing an OS in Rust

そしてRustの部分をCrystalで置き換えればいいやろ、と思っていましたがそんな甘い話があるわけもなく・・・。

ざっくり言うと、blog_osでは、Rust側で rust_main() という関数を用意します。

src/lib.rs

.
.
extern crate rlibc;

#[no_mangle]
pub extern "C" fn rust_main() {
    // ATTENTION: we have a very small stack and no guard page

    let hello = b"Hello World!";
    let color_byte = 0x1f; // white foreground, blue background

    let mut hello_colored = [color_byte; 24];
    for (i, char_byte) in hello.into_iter().enumerate() {
        hello_colored[i*2] = *char_byte;
    }

    // write `Hello World!` to the center of the VGA text buffer
    let buffer_ptr = (0xb8000 + 1988) as *mut _;
    unsafe { *buffer_ptr = hello_colored };

    loop {}
}
.
.

#[no_mangle]付けて pub extern "C" fn rust_main() していると。

それをアセンブリ側からこのように呼び出しています。

arch/x86_64/long_mode_init.asm

global long_mode_start
extern rust_main

section .text
bits 64
long_mode_start:
    ; call rust main
    call rust_main
.
.

全く同じことをCrystalでやろうとしましたが、上手く出来なかった・・・。pub extern "C" みたいなのはどうやるの・・・。name mangling を防ぐのどうするの・・・。

そこで次に参考にしたのはこれです。

crystal_library

Write C static (or shared) libraries in Crystal. This is just a Proof of Concept that exemplifies how to expose a Crystal library to the outside world, eg: C or any language with bindings to C.

ふむ、Crystalで静的(or 共有)ライブラリ作って、それをC言語に食わせればいいんだろうか。

ということはこんな感じ? assembly <- C lang <- Crystal

で、やってみましたが、リンク時に -no-stdlib オプションをつけているので当然、あれもないこれもないでコケまくります。
blog_osでも結局は、rlibc というcrateを導入することで、この問題を回避しています(しかしその後も色々問題は発生するようですが)

http://os.phil-opp.com/set-up-rust.html#rlibc

つ、詰んだ・・・。と思っていたところへ

os-crystal

そのものズバリなプロジェクトを発見します。

ここでは src/x86/loader.cr にて

@[Naked]
fun __entry()
  asm("mov $0, %esp" : : "Z"(0x00100000 + 0x1000));
  asm("push 0");
  asm("push 0");
  asm("call main")
  asm("hlt")
end

インラインアセンブリからシンプルに call main しています。で、呼び出される側は

src/main.cr

require "./x86/loader"
require "./x86/OutputConsole"

puts "Kernel booting with Crystal!"
puts
puts "<3"

こうなっているのですが、不思議なのはmainという関数ではなく、main.cr というファイルが呼び出されている?ということです。
いや、ほんとわかんない。でも確かに呼び出されている。
これで助かった・・・。

TODOs

というわけでまだまだこれからですが、最後は自分を叱って終わりにしたいと思います。

  • アセンブリ部分はほぼblog_osからのパクリじゃないですか。ちゃんと理解しているんですか? 低レベル言語をしっかりと勉強しなさい
  • Crystalの部分はほぼos-crystalからのパクリじゃないですか。なんとかしなさい
  • OSと言っても、ブートして文字出してるだけじゃないですか。カッコつけてオーガニゼーションアカウント取得して、よくわからないままリリースもしてしまっているんだから、行けるところまで行きなさい
  • そもそももっと早くから取り組んでいればこんなことには・・・。 見積もりの甘さと腰の重さをいい加減直しなさい
  • pineさんからgithubでフォローしてもらっているのに、すっかり忘れて放置しているじゃないですか。今すぐフォロバしなさい
  • コミニュティにも参加しなさい。「他人の輪の中に入っていく」という発想がごっそり抜け落ちてしまっているのは致命的です。もういい歳なんだからしっかりしなさい
  • 仕事が全然進んでいない。このままではクライアントに顔向けできないし、何より安心して年を越せないので仕事しなさい

こちらからは以上です・・・。