## 簡述 近年來 Rust 程式語言興起,憑藉其優秀的效能表現、記憶體安全以及開發者體驗在程式語言界取得一席之地。而在 Linux 核心中 Rust 也是近期的熱門話題,Linux 6.1 新增了 kernel crate,讓開發者可以用 Rust 開發核心,特別是驅動程式,在 Linux 6.15 有了第一個被加入 Linux 核心程式碼的 Rust 驅動程式。 另一方面,有部分研究在研究如何將 C 程式碼自動轉為 Rust 程式碼,最具規模的便是 [C2Rust](https://c2rust.com)。看到 C2Rust 我們不禁思考,以 C 語言寫成的 Linux 核心程式碼是否可以使用 C2Rust 轉為 Rust 程式碼?因此本篇研究嘗試使用 C2Rust 轉換部分 Linux 核心程式碼。 我們首先練習轉譯簡單的 hello world,接著嘗試 `lib/uuid.c`,最後嘗試轉換 [SeKVM](https://www.usenix.org/conference/usenixsecurity21/presentation/li-shih-wei) 。 結果發現核心程式碼中的 `<linux/kernel.h>` 中包含 C2Rust 無法轉譯的 C 語法,因此無法完成全自動轉換。 ## 環境設定 ### 參考資料 1. [Quick Start — The Linux Kernel documentation](https://docs.kernel.org/rust/quick-start.html) 2. [Rust version policy - Rust for Linux](https://rust-for-linux.com/rust-version-policy) ### 版本資訊 1. Linux 6.12 mainline 2. `rustc` & `cargo` 1.80.1(官方文件要求 1.78+) 3. `bindgen` 0.69.1 4. `clang` 18.1.3 ### 設定 Rust 工具鏈 在 `linux/` 目錄中 1. `rustup override set 1.80` 2. `rustup component add rust-src` 3. `cargo install --locked bindgen-cli --version 0.69.1` ### 啟用 Rust 支援 1. `make LLVM=1 defconfig` 2. `make LLVM=1 rustavailable`:確定成功再執行下一步 3. `scripts/config -e CONFIG_RUST` ### 編譯全部 為了方便之後測試,先完整編譯核心映像檔一次 `make LLVM=1 Image -j$(nproc)`。 ## 轉譯並執行 Hello World ### 第一步:插入 C 程式碼 因為 Linux 核心中的程式碼大多都較為複雜,沿襲資訊界傳統,我們從最簡單的 hello world 開始,在 Linux 開機時插入一個自訂訊息。 ```c= // lib/mytest.c #include <linux/kernel.h> #include <linux/init.h> void mytest_hello(void) { pr_info("Hello from mytest.c\n"); } ``` 修改 `lib/Makefile` 將我們的檔案插入核心中。 ```=38 buildid.o objpool.o union_find.o lib-y += mytest.o lib-$(CONFIG_PRINTK) += dump_stack.o ``` 最後到 `init/main.c` 中呼叫這個 hello world 函式。 ```c=1102 kcsan_init(); extern void mytest_hello(void); mytest_hello(); /* Do the rest non-__init'ed, we're now alive */ rest_init(); ``` 全部完成後我們先測試核心是否能夠如預期運作: 1. 編譯核心:`make LLVM=1 Image -j$(nproc)` 2. 啟動核心 3. 測試是否有印出訊息:`dmesg | grep mytest` ### 第二步:轉譯為 Rust 語言 我們首先取得 `mytest.c` 的編譯參數,讓 `c2rust` 知道如何轉換: ```sh rm lib/mytest.o intercept-build make LLVM=1 lib/mytest.o ``` 此時目錄下會產生 `compile_commands.json` 檔案,我們可以提供 `c2rust` 這個檔案來轉換程式碼: ```sh c2rust transpile compile_commands.json mv lib/mytest.rs lib/rmytest.rs ``` > [!Note] 為什麽要重新命名為 `rmytest.rs`? > 因為若我們在 Makefile 中加入 `mytest.o`,KBuild 會自動搜尋 `mytest.c` 或是 `mytest.rs` 編譯,如果我們同時擁有 `mytest.c` 與 `mytest.rs`,我們會不確定 Kbuild 選擇哪一個檔案編譯,所以這裡直接將檔案重新命名,對於 Rust 程式碼我們使用 `rmytest.o` 以和 `mytest.o` 做區隔。 接著是本專案的瓶頸,`c2rust` 會產生 `libc` 綁定而不是使用 `core::ffi`,因此需要修改 `lib/rmytest.rs`,將所有的 `libc` 替換為 `core::ffi`、註解掉 `compile_error!()` 巨集,以及為 `_printk()` 加上 unsafe 區塊。 ```rust #![allow( dead_code, mutable_transmutes, non_camel_case_types, non_snake_case, non_upper_case_globals, unused_assignments, unused_mut )] extern "C" { fn _printk(fmt: *const core::ffi::c_char, _: ...) -> core::ffi::c_int; } #[no_mangle] pub unsafe extern "C" fn mytest_hello() { ({ unsafe { _printk(b"\x016Hello from mytest.c\n\0" as *const u8 as *const core::ffi::c_char); } // compile_error!("Function call expression is not supposed to be used") }); } ``` 然後修改 `lib/Makefile`: ```=40 # lib-y += mytest.o lib-y += rmytest.o ``` 最後重新編譯核心即可。 ## 轉譯並執行 `lib/uuid.c` ### 第一步:轉換與編譯 我們首先取得 `lib/uuid.c` 的編譯參數,讓 `c2rust` 知道如何轉換: ```sh rm lib/uuid.o intercept-build make LLVM=1 lib/uuid.o ``` 此時目錄下會產生 `compile_commands.json` 檔案,我們可以提供 `c2rust` 這個檔案來轉換程式碼: ```sh c2rust transpile compile_commands.json mv lib/uuid.rs lib/ruuid.rs ``` 編輯 `lib/ruuid.rs` 檔案: 0. 可以選擇先使用 `rustfmt lib/ruuid.rs` 先格式化檔案 1. 將所有 `libc` 替換為 `core::ffi` 2. 為所有 unsafe 函式的本體套上 unsafe block 3. 註解掉與 `run_static_initializers` 相關的程式碼區塊,見下方轉換問題三 然後修改 `lib/Makefile`,把 `uuid.o` 替換為 `ruuid.o`。 最後重新編譯核心即可。 ### 第二步:測試 我們想要確定核心確實有使用轉換後的程式,因此在 `lib/ruuid.rs` 中加入: ```rust extern "C" { fn hex_to_bin(ch: core::ffi::c_uchar) -> core::ffi::c_int; static _ctype: [core::ffi::c_uchar; 0]; fn get_random_bytes(buf: *mut core::ffi::c_void, len: size_t); // 加入 _printk() fn _printk(fmt: *const core::ffi::c_char, _: ...) -> core::ffi::c_int; } ``` ```rust #[no_mangle] pub unsafe extern "C" fn uuid_gen(mut bu: *mut uuid_t) { unsafe { __uuid_gen_common(((*bu).b).as_mut_ptr()); (*bu) .b[6 as core::ffi::c_int as usize] = ((*bu).b[6 as core::ffi::c_int as usize] as core::ffi::c_int & 0xf as core::ffi::c_int | 0x40 as core::ffi::c_int) as __u8; // 加入 printk() _printk(b"\x016Generated Random UUID.\n\0" as *const u8 as *const core::ffi::c_char); } } ``` 重新編譯開機後,使用 `dmesg | grep Generated` 就會看到核心確實有使用這個程式。 ## 轉譯 SeKVM(沒有編譯進核心) [SeKVM](https://www.usenix.org/conference/usenixsecurity21/presentation/li-shih-wei) 採用模組化的寫法,我們期望它更容易被 C2Rust 轉譯,因此我們嘗試轉換這個核心模組。因為在 KrustVM 研究中將 SeKVM 移植到 Linux 5.15,且 KrustVM 是 SeKVM 的 Rust 語言實作,所以我們嘗試轉換 SeKVM 並與 KrustVM 比較。這裡使用的核心版本是 5.15,此時 KBuild 還不支援 Rust,所以本實驗沒有將其編譯進核心。 我們先從最簡單的程式碼開始轉換起,我選擇 SeKVM 中的 `BootAux.c`,詳細的實驗過程請見 [C2Rust on SeKVM:BootAux.c](/Q9tIBLzEQlyTstvpJWMKhw)。 > 從實驗的結果發現: > 1. C2Rust 產生的錯誤大部分來自於核心的標頭而不是 `hypsec.h` 中直接定義的內容,這表示 C2Rust 無法處理的程式碼大部分都存在於核心標頭中。 > 2. C2Rust 會先對 C 程式碼進行預處理,其中的 `#ifdef` 不會被保留在 Rust 程式碼中,從第三階段的結果可以看到 `BootAux.c` 中原本有兩種程式碼,但是轉換後只剩一種。 我們現在知道錯誤來自於核心標頭,因此進一步研究是哪部分的核心標頭讓 C2Rust 無法成功轉譯程式碼,詳細的實驗過程請見 [C2Rust 無法處理的核心標頭檔](/SDMcAZuSQSCo8gihRnnLug)。 > 實驗結果發現 GNU C 語法以及部分內嵌組合語言是 C2Rust 無法轉譯的程式碼,而此段程式碼存在於 `<linux/kernel.h>` 中,大量的核心程式碼都會直接或間接引用這個標頭檔,因此要自動轉譯 Linux 核心至 Rust 語言是困難的,更別說可能還有其他無法轉譯的程式碼。 `<linux/kernel.h>` 是核心中重要的標頭,我們猜測 RFL 與 KrustVM 都會使用到這個標頭,但是卻能夠順利運行,因此我們想要知道 RFL 與 KrustVM 是如何辦到的,詳細的實驗過程請見 [RFL 如何處理標頭問題](/CkM6Z66FQWWW3Lz7Ja7cVA)。 > 實驗發現 RFL 不需要處理這個問題,因為它直接透過 bindgen 使用 C 語言提供的功能。另外也探究了 SeKVM 的 Rust 版本 KrustVM 如何處理該問題,結果和 RFL 一樣,它直接呼叫 C 語言的 FFI 介面。因此 C2Rust 轉譯失敗的原因是它會嘗試轉譯直到最底層的所有程式,而 RFL 與 KrustVM 都將最底層交給 C 語言。 ## 注意事項 1. 使用 `intercept-build LLVM=1 make path/to/file.o` 時需要避免 Make 產生 nothing to do,否則 `compile_commands.json` 會輸出空白內容。解決方法是在執行前移除 `path/to/file.o`。 2. 使用 `c2rust transpile compile_commands.json` 時需要確保目標的 Rust 檔案不存在,否則 C2Rust 不會轉譯程式碼。 3. 產生 `compile_commands.json` 所使用的 Make 參數必須加上 `LLVM=1`,因為 `c2rust` 只接受 Clang 參數,不支援某些 GCC 參數。 ## 轉換問題 ### 1. C2Rust 無法將 libc 改為使用 core::ffi C2Rust 官方的文件過於老舊,其中沒有提到關於 `libc` 與 `core::ffi` 的設定,執行檔中附帶的說明也沒有這一方面的資訊。因為目前遇到的 `libc` 使用情境多為引用 C 語言的資料型態,所以目前的解法是手動將 `libc` 替換為 `core::ffi`。 ### 2. 轉換後的 unsafe 函式中會呼叫 unsafe 函式 這個問題會導致編譯失敗,因此需要手動在那些被呼叫的 unsafe 函式周圍包上 unsafe 區塊,當然一般而言需要更精細地處理,不過在這個情況我們只需要讓它運作即可。目前 C2Rust 文件也沒有這方面的資訊,因此當前的解法是為每個 unsafe 函式本體套上 unsafe 區塊。 ### 3. 連結 `.discard` 問題 `c2rust` 轉換 `lib/uuid.c` 時會產生如下程式碼: ```rust unsafe extern "C" fn run_static_initializers() { unsafe { __UNIQUE_ID___addressable_guid_null152 = &guid_null as *const guid_t as uintptr_t as *mut core::ffi::c_void; __UNIQUE_ID___addressable_uuid_null153 = &uuid_null as *const uuid_t as uintptr_t as *mut core::ffi::c_void; __UNIQUE_ID___addressable_generate_random_uuid154 = ::core::mem::transmute::< Option<unsafe extern "C" fn(*mut core::ffi::c_uchar) -> ()>, uintptr_t, >(Some( generate_random_uuid as unsafe extern "C" fn(*mut core::ffi::c_uchar) -> (), )) as *mut core::ffi::c_void; __UNIQUE_ID___addressable_generate_random_guid155 = ::core::mem::transmute::< Option<unsafe extern "C" fn(*mut core::ffi::c_uchar) -> ()>, uintptr_t, >(Some( generate_random_guid as unsafe extern "C" fn(*mut core::ffi::c_uchar) -> (), )) as *mut core::ffi::c_void; __UNIQUE_ID___addressable_guid_gen156 = ::core::mem::transmute::<Option<unsafe extern "C" fn(*mut guid_t) -> ()>, uintptr_t>( Some(guid_gen as unsafe extern "C" fn(*mut guid_t) -> ()), ) as *mut core::ffi::c_void; __UNIQUE_ID___addressable_uuid_gen157 = ::core::mem::transmute::<Option<unsafe extern "C" fn(*mut uuid_t) -> ()>, uintptr_t>( Some(uuid_gen as unsafe extern "C" fn(*mut uuid_t) -> ()), ) as *mut core::ffi::c_void; __UNIQUE_ID___addressable_uuid_is_valid158 = ::core::mem::transmute::< Option<unsafe extern "C" fn(*const core::ffi::c_char) -> bool_0>, uintptr_t, >(Some( uuid_is_valid as unsafe extern "C" fn(*const core::ffi::c_char) -> bool_0, )) as *mut core::ffi::c_void; __UNIQUE_ID___addressable_guid_parse159 = ::core::mem::transmute::< Option<unsafe extern "C" fn(*const core::ffi::c_char, *mut guid_t) -> core::ffi::c_int>, uintptr_t, >(Some( guid_parse as unsafe extern "C" fn(*const core::ffi::c_char, *mut guid_t) -> core::ffi::c_int, )) as *mut core::ffi::c_void; __UNIQUE_ID___addressable_uuid_parse160 = ::core::mem::transmute::< Option<unsafe extern "C" fn(*const core::ffi::c_char, *mut uuid_t) -> core::ffi::c_int>, uintptr_t, >(Some( uuid_parse as unsafe extern "C" fn(*const core::ffi::c_char, *mut uuid_t) -> core::ffi::c_int, )) as *mut core::ffi::c_void; } } #[used] #[cfg_attr(target_os = "linux", link_section = ".init_array")] #[cfg_attr(target_os = "windows", link_section = ".CRT$XIB")] #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] static INIT_ARRAY: [unsafe extern "C" fn(); 1] = [run_static_initializers]; ``` 這些程式碼與 C 程式中的 `EXPORT_SYMBOL()` 有關,是為了讓 `kallsyms` 可以查詢函式記憶體位址等用途而存在,而最後的 `INIT_ARRAY` 會產生以下的連結錯誤: ``` ld.lld: error: relocation refers to a symbol in a discarded section: ruuid::__UNIQUE_ID___addressable_guid_null152 >>> defined in vmlinux.a(lib/ruuid.o) >>> referenced by ruuid.rs:273 (lib/ruuid.rs:273) >>> lib/ruuid.o:(ruuid::run_static_initializers) in archive vmlinux.a >>> referenced by ruuid.rs:273 (lib/ruuid.rs:273) >>> lib/ruuid.o:(ruuid::run_static_initializers) in archive vmlinux.a ... ``` 目前的解決方法是註解這些程式碼區塊,暫時不使用 `EXPORT_SYMBOL()` 功能。 ## 參考資料 - 其他轉譯器 - [NishanthSpShetty/crust: C/C++ to Rust transpiler](https://github.com/NishanthSpShetty/crust):功能不足(沒有函式指標),久未更新。 - [Rust — The Linux Kernel documentation](https://docs.kernel.org/rust/index.html):核心中 Rust 子模組文件。 - [Introduction - C2Rust Manual](https://c2rust.com/manual):C2Rust 手冊,過時沒什麼參考價值。 - [Transpiling A Kernel Module to Rust: The Good, the Bad and the Ugly :: Immunant, Inc](https://immunant.com/blog/2020/06/kernel_modules/):主要是轉換驅動程式而不是核心程式,不過也可以作為參考。 - [C2Rust Linux Kernel Module尝试【译】](https://retriever.hashnode.dev/c2rust-linux-kernel-module):上文翻譯版。 - 文獻論文 - [[2401.08264v1] Towards a Transpiler for C/C++ to Safer Rust](https://arxiv.org/abs/2401.08264v1):主要是轉譯 C++,沒有開源。 ## 補充資料 :::spoiler `lib/ruuid.rs` 完整程式碼 ```rust #![allow( dead_code, mutable_transmutes, non_camel_case_types, non_snake_case, non_upper_case_globals, unused_assignments, unused_mut )] extern "C" { fn hex_to_bin(ch: core::ffi::c_uchar) -> core::ffi::c_int; static _ctype: [core::ffi::c_uchar; 0]; fn get_random_bytes(buf: *mut core::ffi::c_void, len: size_t); fn _printk(fmt: *const core::ffi::c_char, _: ...) -> core::ffi::c_int; } pub type __u8 = core::ffi::c_uchar; pub type u8_0 = __u8; pub type C2RustUnnamed = core::ffi::c_uint; pub const true_0: C2RustUnnamed = 1; pub const false_0: C2RustUnnamed = 0; pub type __kernel_ulong_t = core::ffi::c_ulong; pub type __kernel_size_t = __kernel_ulong_t; pub type bool_0 = bool; pub type uintptr_t = core::ffi::c_ulong; pub type size_t = __kernel_size_t; #[derive(Copy, Clone)] #[repr(C)] pub struct guid_t { pub b: [__u8; 16], } #[derive(Copy, Clone)] #[repr(C)] pub struct uuid_t { pub b: [__u8; 16], } #[no_mangle] pub static mut guid_null: guid_t = guid_t { b: [0; 16] }; #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_guid_null152: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; #[no_mangle] pub static mut uuid_null: uuid_t = uuid_t { b: [0; 16] }; #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_uuid_null153: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; #[no_mangle] pub static mut guid_index: [u8_0; 16] = [ 3 as core::ffi::c_int as u8_0, 2 as core::ffi::c_int as u8_0, 1 as core::ffi::c_int as u8_0, 0 as core::ffi::c_int as u8_0, 5 as core::ffi::c_int as u8_0, 4 as core::ffi::c_int as u8_0, 7 as core::ffi::c_int as u8_0, 6 as core::ffi::c_int as u8_0, 8 as core::ffi::c_int as u8_0, 9 as core::ffi::c_int as u8_0, 10 as core::ffi::c_int as u8_0, 11 as core::ffi::c_int as u8_0, 12 as core::ffi::c_int as u8_0, 13 as core::ffi::c_int as u8_0, 14 as core::ffi::c_int as u8_0, 15 as core::ffi::c_int as u8_0, ]; #[no_mangle] pub static mut uuid_index: [u8_0; 16] = [ 0 as core::ffi::c_int as u8_0, 1 as core::ffi::c_int as u8_0, 2 as core::ffi::c_int as u8_0, 3 as core::ffi::c_int as u8_0, 4 as core::ffi::c_int as u8_0, 5 as core::ffi::c_int as u8_0, 6 as core::ffi::c_int as u8_0, 7 as core::ffi::c_int as u8_0, 8 as core::ffi::c_int as u8_0, 9 as core::ffi::c_int as u8_0, 10 as core::ffi::c_int as u8_0, 11 as core::ffi::c_int as u8_0, 12 as core::ffi::c_int as u8_0, 13 as core::ffi::c_int as u8_0, 14 as core::ffi::c_int as u8_0, 15 as core::ffi::c_int as u8_0, ]; #[no_mangle] pub unsafe extern "C" fn generate_random_uuid(mut uuid: *mut core::ffi::c_uchar) { unsafe { get_random_bytes( uuid as *mut core::ffi::c_void, 16 as core::ffi::c_int as size_t, ); *uuid.offset(6 as core::ffi::c_int as isize) = (*uuid.offset(6 as core::ffi::c_int as isize) as core::ffi::c_int & 0xf as core::ffi::c_int | 0x40 as core::ffi::c_int) as core::ffi::c_uchar; *uuid.offset(8 as core::ffi::c_int as isize) = (*uuid.offset(8 as core::ffi::c_int as isize) as core::ffi::c_int & 0x3f as core::ffi::c_int | 0x80 as core::ffi::c_int) as core::ffi::c_uchar; } } #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_generate_random_uuid154: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; #[no_mangle] pub unsafe extern "C" fn generate_random_guid(mut guid: *mut core::ffi::c_uchar) { unsafe { get_random_bytes( guid as *mut core::ffi::c_void, 16 as core::ffi::c_int as size_t, ); *guid.offset(7 as core::ffi::c_int as isize) = (*guid.offset(7 as core::ffi::c_int as isize) as core::ffi::c_int & 0xf as core::ffi::c_int | 0x40 as core::ffi::c_int) as core::ffi::c_uchar; *guid.offset(8 as core::ffi::c_int as isize) = (*guid.offset(8 as core::ffi::c_int as isize) as core::ffi::c_int & 0x3f as core::ffi::c_int | 0x80 as core::ffi::c_int) as core::ffi::c_uchar; } } #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_generate_random_guid155: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; unsafe extern "C" fn __uuid_gen_common(mut b: *mut __u8) { unsafe { get_random_bytes( b as *mut core::ffi::c_void, 16 as core::ffi::c_int as size_t, ); *b.offset(8 as core::ffi::c_int as isize) = (*b.offset(8 as core::ffi::c_int as isize) as core::ffi::c_int & 0x3f as core::ffi::c_int | 0x80 as core::ffi::c_int) as __u8; } } #[no_mangle] pub unsafe extern "C" fn guid_gen(mut lu: *mut guid_t) { unsafe { __uuid_gen_common(((*lu).b).as_mut_ptr()); (*lu).b[7 as core::ffi::c_int as usize] = ((*lu).b[7 as core::ffi::c_int as usize] as core::ffi::c_int & 0xf as core::ffi::c_int | 0x40 as core::ffi::c_int) as __u8; } } #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_guid_gen156: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; #[no_mangle] pub unsafe extern "C" fn uuid_gen(mut bu: *mut uuid_t) { unsafe { __uuid_gen_common(((*bu).b).as_mut_ptr()); (*bu).b[6 as core::ffi::c_int as usize] = ((*bu).b[6 as core::ffi::c_int as usize] as core::ffi::c_int & 0xf as core::ffi::c_int | 0x40 as core::ffi::c_int) as __u8; _printk(b"\x016Generated Random UUID.\n\0" as *const u8 as *const core::ffi::c_char); } } #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_uuid_gen157: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; #[no_mangle] pub unsafe extern "C" fn uuid_is_valid(mut uuid: *const core::ffi::c_char) -> bool_0 { unsafe { let mut i: core::ffi::c_uint = 0; i = 0 as core::ffi::c_int as core::ffi::c_uint; while i < 36 as core::ffi::c_int as core::ffi::c_uint { if i == 8 as core::ffi::c_int as core::ffi::c_uint || i == 13 as core::ffi::c_int as core::ffi::c_uint || i == 18 as core::ffi::c_int as core::ffi::c_uint || i == 23 as core::ffi::c_int as core::ffi::c_uint { if *uuid.offset(i as isize) as core::ffi::c_int != '-' as i32 { return false_0 as core::ffi::c_int != 0; } } else if !(*_ctype .as_ptr() .offset(*uuid.offset(i as isize) as core::ffi::c_uchar as core::ffi::c_int as isize) as core::ffi::c_int & (0x4 as core::ffi::c_int | 0x40 as core::ffi::c_int) != 0 as core::ffi::c_int) { return false_0 as core::ffi::c_int != 0; } i = i.wrapping_add(1); i; } return true_0 as core::ffi::c_int != 0; } } #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_uuid_is_valid158: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; unsafe extern "C" fn __uuid_parse( mut uuid: *const core::ffi::c_char, mut b: *mut __u8, mut ei: *const u8_0, ) -> core::ffi::c_int { unsafe { static mut si: [u8_0; 16] = [ 0 as core::ffi::c_int as u8_0, 2 as core::ffi::c_int as u8_0, 4 as core::ffi::c_int as u8_0, 6 as core::ffi::c_int as u8_0, 9 as core::ffi::c_int as u8_0, 11 as core::ffi::c_int as u8_0, 14 as core::ffi::c_int as u8_0, 16 as core::ffi::c_int as u8_0, 19 as core::ffi::c_int as u8_0, 21 as core::ffi::c_int as u8_0, 24 as core::ffi::c_int as u8_0, 26 as core::ffi::c_int as u8_0, 28 as core::ffi::c_int as u8_0, 30 as core::ffi::c_int as u8_0, 32 as core::ffi::c_int as u8_0, 34 as core::ffi::c_int as u8_0, ]; let mut i: core::ffi::c_uint = 0; if !uuid_is_valid(uuid) { return -(22 as core::ffi::c_int); } i = 0 as core::ffi::c_int as core::ffi::c_uint; while i < 16 as core::ffi::c_int as core::ffi::c_uint { let mut hi: core::ffi::c_int = hex_to_bin( *uuid.offset((si[i as usize] as core::ffi::c_int + 0 as core::ffi::c_int) as isize) as core::ffi::c_uchar, ); let mut lo: core::ffi::c_int = hex_to_bin( *uuid.offset((si[i as usize] as core::ffi::c_int + 1 as core::ffi::c_int) as isize) as core::ffi::c_uchar, ); *b.offset(*ei.offset(i as isize) as isize) = (hi << 4 as core::ffi::c_int | lo) as __u8; i = i.wrapping_add(1); i; } return 0 as core::ffi::c_int; } } #[no_mangle] pub unsafe extern "C" fn guid_parse( mut uuid: *const core::ffi::c_char, mut u: *mut guid_t, ) -> core::ffi::c_int { unsafe { return __uuid_parse(uuid, ((*u).b).as_mut_ptr(), guid_index.as_ptr()); } } #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_guid_parse159: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; #[no_mangle] pub unsafe extern "C" fn uuid_parse( mut uuid: *const core::ffi::c_char, mut u: *mut uuid_t, ) -> core::ffi::c_int { unsafe { return __uuid_parse(uuid, ((*u).b).as_mut_ptr(), uuid_index.as_ptr()); } } #[used] #[link_section = ".discard.addressable"] static mut __UNIQUE_ID___addressable_uuid_parse160: *mut core::ffi::c_void = 0 as *const core::ffi::c_void as *mut core::ffi::c_void; // unsafe extern "C" fn run_static_initializers() { // unsafe { // __UNIQUE_ID___addressable_guid_null152 = // &guid_null as *const guid_t as uintptr_t as *mut core::ffi::c_void; // __UNIQUE_ID___addressable_uuid_null153 = // &uuid_null as *const uuid_t as uintptr_t as *mut core::ffi::c_void; // __UNIQUE_ID___addressable_generate_random_uuid154 = ::core::mem::transmute::< // Option<unsafe extern "C" fn(*mut core::ffi::c_uchar) -> ()>, // uintptr_t, // >(Some( // generate_random_uuid as unsafe extern "C" fn(*mut core::ffi::c_uchar) -> (), // )) as *mut core::ffi::c_void; // __UNIQUE_ID___addressable_generate_random_guid155 = ::core::mem::transmute::< // Option<unsafe extern "C" fn(*mut core::ffi::c_uchar) -> ()>, // uintptr_t, // >(Some( // generate_random_guid as unsafe extern "C" fn(*mut core::ffi::c_uchar) -> (), // )) as *mut core::ffi::c_void; // __UNIQUE_ID___addressable_guid_gen156 = // ::core::mem::transmute::<Option<unsafe extern "C" fn(*mut guid_t) -> ()>, uintptr_t>( // Some(guid_gen as unsafe extern "C" fn(*mut guid_t) -> ()), // ) as *mut core::ffi::c_void; // __UNIQUE_ID___addressable_uuid_gen157 = // ::core::mem::transmute::<Option<unsafe extern "C" fn(*mut uuid_t) -> ()>, uintptr_t>( // Some(uuid_gen as unsafe extern "C" fn(*mut uuid_t) -> ()), // ) as *mut core::ffi::c_void; // __UNIQUE_ID___addressable_uuid_is_valid158 = ::core::mem::transmute::< // Option<unsafe extern "C" fn(*const core::ffi::c_char) -> bool_0>, // uintptr_t, // >(Some( // uuid_is_valid as unsafe extern "C" fn(*const core::ffi::c_char) -> bool_0, // )) as *mut core::ffi::c_void; // __UNIQUE_ID___addressable_guid_parse159 = ::core::mem::transmute::< // Option<unsafe extern "C" fn(*const core::ffi::c_char, *mut guid_t) -> core::ffi::c_int>, // uintptr_t, // >(Some( // guid_parse // as unsafe extern "C" fn(*const core::ffi::c_char, *mut guid_t) -> core::ffi::c_int, // )) as *mut core::ffi::c_void; // __UNIQUE_ID___addressable_uuid_parse160 = ::core::mem::transmute::< // Option<unsafe extern "C" fn(*const core::ffi::c_char, *mut uuid_t) -> core::ffi::c_int>, // uintptr_t, // >(Some( // uuid_parse // as unsafe extern "C" fn(*const core::ffi::c_char, *mut uuid_t) -> core::ffi::c_int, // )) as *mut core::ffi::c_void; // } // } // #[used] // #[cfg_attr(target_os = "linux", link_section = ".init_array")] // #[cfg_attr(target_os = "windows", link_section = ".CRT$XIB")] // #[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")] // static INIT_ARRAY: [unsafe extern "C" fn(); 1] = [run_static_initializers]; ``` :::