Swift上のLinuxカーネルモジュール





Swiftはネイティブコードにコンパイルされているので、Swiftでカーネルモジュールを作成してみてはどうでしょうか。 興味のある方は猫をリクエストしてください!







Swiftは、LLVMやClangなどの有名なプロジェクトの父であるChris Luttnerの個人プロジェクトとして2010年7月にその歴史を開始したクロスプラットフォームプログラミング言語です。 2013年、SwiftはApple開発ツール開発チームの重要なタスクになりました。 バージョン1.0は2014年9月9日にリリースされました。 2015年12月3日に、コンパイラおよびその他のツールがApache 2.0ライセンスの下で公開されました。 GitHubで開発が進行中です 。 このプロジェクトは、高速で安全、簡潔で現代的な言語を作成することを目的に開発されています。







フリーソフトウェアコンパイラになったことに興味がありました。 最初の実験の1つは、 Swiftで裸鉄の地獄世界を作成することでした。 しかし、私は最初ではありませんでした。 Yelpの従業員であり、 Unixに似アマチュアのToaru OSの著者であるKelin Langeは、数日前に似たようなものを公開しました。







少し前、私はRustのLinuxカーネルモジュールの例であるrust.koに注目し、「同じことをすることはできますが、Swiftでできるか?」 判明したように、できます!







C層



残念ながら、LinuxカーネルコードはマクロとGCC固有の拡張機能に強く結び付けられているため、これをCなしで行うことはできません。







shim.c







#include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("Roman Zhikharevich"); MODULE_DESCRIPTION("a little bit swifty Linux kernel module"); MODULE_VERSION("0.1"); int swift_main(void); static int __init swift_init(void) { return swift_main(); } static void __exit swift_exit(void) {} module_init(swift_init); module_exit(swift_exit);
      
      





このコードを順番に検討してください。







 #include <linux/init.h> #include <linux/module.h>
      
      





init.hにはそれぞれ.init.textおよび.exit.textセクションに関数を配置する__initおよび__exitマクロが含まれていますmodule.hは 、初期化関数と終了関数を指すmodule_initおよびmodule_exitに必要であり、モジュールに関する情報を示すためにも必要です。







 MODULE_LICENSE("GPL"); MODULE_AUTHOR("Roman Zhikharevich"); MODULE_DESCRIPTION("a little bit swifty Linux kernel module"); MODULE_VERSION("0.1");
      
      





モジュールに関する情報がここに示されています。 ここのすべてがそれ自体を物語っています。







 int swift_main(void); static int __init swift_init(void) { return swift_main(); } static void __exit swift_exit(void) {}
      
      





Swiftで記述されたswift_main関数のシグネチャを指定して呼び出します。 リリースするものないため、 swift_exit関数空です。







 module_init(swift_init); module_exit(swift_exit);
      
      





初期化関数と終了関数を指定します。







ギャグ 迅速なコード



 @_silgen_name("swift_main") func swift_main() -> Int32 { let msg: StaticString = "\u{1}6Linux + Swift = < >\n" // Emoji        printk(unsafeBitCast(msg.utf8Start, to: UnsafePointer<Int8>!.self)) return 0 }
      
      





順番に再度分解します。







 @_silgen_name("swift_main")
      
      





@_silgen_name属性を使用すると、キャラクターの名前を手動で指定できます 。 これが行われない場合、名前は " crippled "(英語のマングル-__TF1n10swift_mainFT_Vs5Int32になります。つまり、型に関する情報を保存します-これをCから呼び出すのは不便です。







 let msg: StaticString = "\u{1}6Linux + Swift = < >\n" // Emoji       
      
      





StaticStringタイプのメッセージはここで宣言され、ランタイムからのサポートを必要としません。 フラグメント"\ u {1} 6"を説明する価値があります。 実際、ログレベルメッセージをprintk関数に渡すには、KERN_INFOマクロを使用する必要がありますが、Swiftでは使用できません。 幸いなことに、非常に単純なコンテンツ( include / linux / kern_levels.h )があります:







 ... #define KERN_SOH "\001" /* ASCII Start Of Header */ ... #define KERN_INFO KERN_SOH "6" /* informational */ ...
      
      





 printk(unsafeBitCast(msg.utf8Start, to: UnsafePointer<Int8>!.self))
      
      





メッセージを表示してください!







 return 0
      
      





モジュールの初期化に成功しました!







振る、しかし混ぜない-コードを集める



怖い松葉杖を使用してmacOS SierraのSwiftコードと、 Void Linuxを実行するRaspberry Piのコードを作成しました。 ただし、以下を他の設定に適応させることは難しくありません。







最初に、モジュールのアセンブリの条件を作成する必要があります。







 pi $ mkdir -p ~/projects/swift.ko pi $ cd ~/projects/swift.ko pi $ nano shim.c pi $ nano Makefile
      
      





メイクファイル







 obj-m += swift.o swift-objs := shim.o main.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
      
      





今、楽しい部分はSwiftです。







 mac $ nano import.h
      
      





import.h







 int printk(const char *msg);
      
      





署名が完全に正しいわけではないことに注意することが重要ですが、これらの条件でva_listを操作することはできないため、チートが必要です。







 mac $ xcrun -sdk iphoneos swiftc -import-objc-header import.h -emit-library -emit-bc -target armv7-apple-ios7 main.swift mac $ xcrun -sdk iphoneos clang -target armv7-none-elf -mfloat-abi=soft -O2 -c main.bc mac $ arm-none-eabi-objcopy --rename-section .text=.init.text main.o
      
      





ここで何が起こっているのかを説明します。 問題は、Swiftコンパイラーが非標準目的のコードを生成できないことです。 しかし、それはClangできます! 解決策は、SwiftコンパイラーをLLVMビットコード生成モードにし、それをClangにフィードすることです。 最後のコマンドは、 swift_main.init.textセクションに移動します。 オブジェクトファイルをRaspberry Piに送信するだけです。







 mac $ scp main.o pi@pi:~/projects/swift.ko/main.o_shipped
      
      





モジュールをまとめる!







 pi $ make
      
      





フィニッシュライン







おわりに



もちろん、ここの言語から、悲しいかな、構文のみ。 すべての機能を使用するには、SwiftランタイムをLinuxカーネルに移植するためにかなりの努力をする必要があります。 また、このような低レベルのSwiftコードを作成する意味があるかどうかはあまり明確ではありません。 Linus Torvaldsはおそらく承認しないでしょう







とにかくハッピーハッキング!








All Articles