- Định nghĩa initrd là một đơn vị chương trình mà kernel trực tiếp diễn giải và thực thi, từ đó diễn giải lại Linux như một trình thông dịch
- Dùng
kexec, base64, cpio để tạo nên một bản phân phối Linux đệ quy tự khởi động lại chính nó, trong đó initrd tự thực thi lại chính mình
- Nếu script
/init được để xuất ra ảnh cpio của chính nó thì sẽ hình thành một initrd tự sao chép kiểu Quine
- Giải thích cấu trúc tầng lớp trình thông dịch kéo dài tới tận kernel thông qua cấu trúc thực thi ELF,
ld.so, và binfmt_misc
- Có thể dùng
kexec hoặc QEMU để chạy một Linux khác theo kiểu đệ quy đuôi trên Linux, qua đó mở rộng ranh giới giữa kernel, ảo hóa và trình thông dịch theo cách thực nghiệm
Phân tích ngược rkx.gz và cấu trúc initrd tự đệ quy
- Lệnh
curl https://astrid.tech/rkx.gz | gunzip | sudo sh sẽ tải về và chạy một script shell được mã hóa base64 có kích thước 20MB
- Script kiểm tra quyền root và xác nhận sự tồn tại của
kexec, base64, cpio
- Giải mã dữ liệu base64 để tạo một archive cpio tên
r, rồi trích xuất từ đó ảnh kernel tên k
- Dùng
kexec để nạp k làm kernel và r làm ramdisk rồi thực thi
- Bên trong
r.cpio có các tệp /bin, /init, k; trong đó k là ảnh kernel Linux 6.18.18, còn /init là một script shell
/init mount /proc, đóng gói hệ thống tệp hiện tại thành cpio tại /r, rồi dùng kexec để chạy lại /k và /r
- Kết quả là tạo thành một bản phân phối Linux đệ quy liên tục tự khởi động lại chính nó
Góc nhìn coi kernel Linux là trình thông dịch
- Initrd không chỉ là ramdisk phục vụ khởi động, mà có thể xem là một chương trình được kernel Linux diễn giải và thực thi
- Tương tự
curl | sh hay python3 script.py, initrd cũng là một chương trình đầu vào được kernel thực thi
- Vì vậy, kernel Linux hoạt động như một trình thông dịch diễn giải initrd
- Cấu trúc này tương tự tối ưu hóa đệ quy đuôi (tail-call optimization)
kexec nạp và chạy trong không gian bộ nhớ mới thay vì ghi đè kernel trước đó
- Mỗi kernel không giữ lại trạng thái trước, mà được thay thế bằng một “stack frame” mới
Quine và sự tự sao chép của initrd
- Quine là một chương trình tự in ra chính nó
- Nếu script
/init thực hiện cat /r ở cuối, nó sẽ xuất ra cpio giống hệt chính nó
- Khi đó sẽ hình thành một Quine của trình thông dịch initrd Linux
- Vì mọi tệp đều tồn tại trên
tmpfs trong RAM, nên không phát sinh I/O đĩa thực tế
ELF, ld.so và các tầng lớp trình thông dịch
- Tệp thực thi ELF chứa đường dẫn trình thông dịch (
ld-linux-x86-64.so.2) trong header
- Khi thực thi, kernel chạy
ld.so trước, rồi ld.so nạp các thư viện động của ELF và chạy chương trình
- Vì vậy ELF cũng có thể được xem là một dạng ngôn ngữ được thông dịch
/bin/sh được ld.so diễn giải, còn ld.so thì được kernel trực tiếp diễn giải
ld.so là ELF liên kết tĩnh nên kernel có thể chạy trực tiếp
- Từ đó hình thành trường hợp cơ sở (base case) của tầng lớp trình thông dịch
Thực thi CPIO thông qua binfmt_misc
- Dùng
binfmt_misc có thể thực thi tệp có magic byte nhất định bằng trình thông dịch được chỉ định
- Có thể đăng ký một script dùng QEMU để chạy CPIO như initrd làm trình thông dịch
- QEMU khởi động máy ảo bằng kernel và initrd được chỉ định
- Kết quả là trình thông dịch của tệp CPIO sẽ là kernel Linux đang được QEMU chạy
Trình thông dịch đệ quy và “vòng lặp kỳ lạ nhất”
- Trình thông dịch dựa trên QEMU tạo ra một stack frame môi trường Linux mới
- Đây là cấu trúc chạy một Linux khác trên Linux, có thể lồng đến giới hạn bộ nhớ
- Nếu thay bằng trình thông dịch dựa trên
kexec thì có thể thực thi Linux đệ quy với tối ưu hóa gọi đuôi
- Nếu trong
/init đăng ký binfmt_misc và cấu hình để chạy /r
initrd tự thực thi chính nó sẽ được hoàn thiện
/r là tiến trình init tiếp theo ở định dạng CPIO, và khi chạy sẽ tiếp tục tự diễn giải lại chính mình
Kết luận
- Initrd không chỉ là công cụ khởi động đơn thuần, mà là một đơn vị chương trình được kernel Linux diễn giải
- Với
kexec và binfmt_misc, có thể chạy đệ quy chính Linux như một trình thông dịch
- Cấu trúc này là một khái niệm thử nghiệm phá vỡ ranh giới giữa kernel, ảo hóa, trình thông dịch và chương trình tự sao chép
- Mã nguồn liên quan được công bố tại kho GitHub ifd3f/rekexec
2 bình luận
Đúng là thiếu hiểu biết thì lại càng liều.. Mong là nên tránh những bài viết như thế này.
Ý kiến trên Hacker News
Tôi thấy rất khổ sở khi đọc bài này vì có quá nhiều hiểu nhầm
archive cpio không phải là filesystem. Tác giả đang dùng initramfs, vốn dựa trên tmpfs. Linux có thể giải nén cpio vào tmpfs. Một archive chứa file và thư mục tự nó không phải là chương trình
Chỉ vì thứ gì đó trông giống nhau không có nghĩa là chúng giống hệt nhau. Chương trình nhị phân được chạy trên CPU; nếu có một interpreter thì nó ẩn trong môi trường phần cứng. Điều đó nằm ngoài phạm vi của kernel
Để chạy shell script, bạn cần một shell để diễn giải script đó. Tác giả đã lược bỏ phần này và nhầm lẫn kernel với chương trình shell
Linux có thể được biên dịch mà không cần initramfs hay ramdisk, và vẫn có thể chạy userland của filesystem
Cách nói “Linux initrd interpreter” thực sự là một mô tả sai
Có phải mọi OS đều đóng vai trò interpreter mã máy với đặc quyền kernel không?
Bài này ổn nếu xem “Linux là một interpreter” như một mô hình tinh thần, nhưng nếu hiểu theo nghĩa đen thì sai
Nếu không xét ở mức lệnh CPU, mà xem kernel là thứ điều phối các định dạng thực thi như ELF, script shebang và initramfs, thì hợp lý hơn. Có vẻ sự nhầm lẫn bắt nguồn từ việc trộn lẫn hai nghĩa của từ ‘interpreter’
Điều cốt lõi không phải là phép so sánh có đúng hay không, mà là nó cho thấy khái niệm ‘thực thi’ phụ thuộc vào môi trường đến mức nào
“Mọi thứ đều là interpreter?”
Theta Combinator của Turing
Trong bài trước của loạt bài này, tác giả nói rằng họ không muốn dùng object storage của Contabo nên đã tự tạo image VPS
Tôi nghĩ giữa hai cực: bỏ ra 50 giờ để tiết kiệm 1,50 USD mỗi tháng, và tiêu 250.000 USD cho token, có một điểm cân bằng.
Nếu không kham nổi chi phí hạ tầng, thì có lẽ vấn đề là ở yếu tố xã hội hơn là năng lực kỹ thuật. Việc ám ảnh chuyện chạy Doom bằng curl có vẻ không phải là điều năng suất
Nếu xem
man ld.so, sẽ thấy có ghi rõ dynamic linker được lưu trong section.interpcủa ELF sẽ được chạy. Bản thân tên section này đã rất thú vịLinux rất hữu ích như một giao diện có thể lập trình. Windows cũng làm được, nhưng tôi cảm thấy Linux phù hợp hơn
Tôi nghĩ GUI trên Windows tốt hơn, nhưng GNOME hay KDE cũng bất tiện. Vì vậy tôi dùng fluxbox, icewm, đôi khi là xfce hoặc mate-desktop. Dạo này tôi thích môi trường đơn giản và nhanh. Phần lớn công việc của tôi được xử lý bằng command line và chỉnh sửa mã