Since Swift compiles to native code, why not try writing a kernel module on it? All interested in please under the cut!
Swift is a cross-platform programming language that began its history in July 2010 as a personal project of Chris Luttner, father of such famous projects as LLVM and Clang. In 2013, Swift became an important task for the Apple Development Tools development team. Version 1.0 was released on September 9, 2014. On December 3, 2015, the compiler and other tools were published under the Apache 2.0 license. Development is conducted on GitHub . The project is designed to create a fast, secure, concise and modern language.
Becoming a free software compiler made me interested in them. One of the first experiments was the creation of the Hellowhold for bare iron on Swift . However, I was not the first. Kevin Lange, an employee of Yelp and author of the amateur Unix-like OS Toaru , published something similar a few days earlier.
Not so long ago, I noticed rust.ko , an example of a Linux kernel module on Rust, and asked the question "Is it possible to do the same, but on Swift?" As it turned out, you can!
Unfortunately, the Linux kernel code is heavily tied to macros and GCC-specific extensions, so you can't do without C in this case.
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);
Consider this code in order.
#include <linux/init.h> #include <linux/module.h>
init.h contains the __init and __exit macros , which place the functions in the .init.text and .exit.text sections, respectively. module.h is needed for module_init and module_exit , indicating initialization and exit functions, as well as for specifying information about the module.
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Roman Zhikharevich"); MODULE_DESCRIPTION("a little bit swifty Linux kernel module"); MODULE_VERSION("0.1");
The information about the module is indicated here. Here everything speaks for itself.
int swift_main(void); static int __init swift_init(void) { return swift_main(); } static void __exit swift_exit(void) {}
Specify the signature of the function swift_main, written in Swift, and then call it. The swift_exit function is empty, since there is nothing to free.
module_init(swift_init); module_exit(swift_exit);
Specify initialization and exit functions.
@_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 }
Disassemble again in order.
@_silgen_name("swift_main")
The @_silgen_name attribute allows you to manually set the symbol name. If this is not done, the name will be "mangled" - __TF1n10swift_mainFT_Vs5Int32 , that is, it will store information about types - to call this from C is inconvenient.
let msg: StaticString = "\u{1}6Linux + Swift = < >\n" // Emoji
A message is declared here that is of type StaticString and does not require runtime support. It is worth explaining the fragment "\ u {1} 6" . The fact is that to transmit the log level of the message to the printk function, you need to use the KERN_INFO macro, but it is not available in Swift. Fortunately, it has fairly simple content ( 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))
Display a message!
return 0
"Initialization" of the module was successful!
I compiled the Swift code under macOS Sierra using scary crutches, and the soda code on Raspberry Pi running Void Linux . However, the following is not difficult to adapt for other setups.
First you need to create conditions for the assembly of the module.
pi $ mkdir -p ~/projects/swift.ko pi $ cd ~/projects/swift.ko pi $ nano shim.c pi $ nano Makefile
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
Now the most interesting is Swift.
mac $ nano import.h
import.h
int printk(const char *msg);
It is important to note that the signature is not entirely correct, but we cannot work with va_list under these conditions, so we have to cheat.
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
Let me explain what is happening here. The problem is that the Swift compiler does not know how to generate code for non-standard purposes. But it can Clang! The solution is to switch the Swift compiler to the LLVM bitcode generation mode, and then feed it to the Clang. The last command moves swift_main to the .init.text section. It remains only to send the object file to the Raspberry Pi:
mac $ scp main.o pi@pi:~/projects/swift.ko/main.o_shipped
We assemble the module!
pi $ make
Of course, from the language here, alas, only the syntax. To use all the possibilities, you need to make considerable efforts to port Swift runtime to the Linux kernel. It is also not very clear whether it makes any sense to write such a low-level code in Swift. Linus Torvalds probably would not approve .
In any case, happy hacking!
Source: https://habr.com/ru/post/305530/
All Articles