瞬间生锈

2020-05-03 17:05:34

标签:Rust Use std::OS::RAW::{c_int,c_void};使用std::sync::atom::{AtomicU64,Ording::SeqCst};使用std::time::Instant;#[repr(C)]struct timespec{TV_sec:U64,TV_Nsec:U64,}Static Time_Counter:AtomicU64=AtomicU64::New(0);#[no_mangle]struct timespec{TV_sec:u64,tv_nsec:u64,}static time_Counter:AtomicU64=AtomicU64::New(0);#[no_manger。fn lock_gettime(clk_id:*mut c_void,tp:*mut timespec)->;c_int{不安全{let next_tick=time_counter。FETCH_ADD(1,SeqCst);(*tp).tv_sec=Next_tick;(*tp).tv_nsec=0;0}}fn main(){let a=Instant::Now();let b=Instant::Now();让.。

首先,让我们从一段值得在推特上发布的代码中学到这一点,让它在MacOS上的大部分时间里更具生产性,并支持跨平台。让我们添加对它的支持:

#[cfg(all(unix,not(target_os=";macOS";))]use std::OS::raw::{c_int,c_void};use std::sync::atom::{AtomicU64,order::SeqCst};use std::time::Instant;#[cfg(all(unix,not(target_os=";MacOS";))]]#[repr(C)]struct timespec{TV_sec:U64,TV_nsec:U64,}static time_counter:AtomicU64=AtomicU64::new(0);#[cfg(all(unix,not(target_os=";macOS";)]#[no_mangle]extern";C";fn lock_gettime(_clk_id:*mut。C_INT{不安全{让NEXT_TICK.。

计算机提供的时钟源允许程序对经过的时间进行假设。如果您所需要的只是测量某个时刻a和b之间经过的时间,则Rust libstd为您提供了Instant,这是对单调不递减时钟的测量(很抱歉地通知您,缺少单调递增时钟的保证)。瞬间是一种不透明的东西,只会让你了解它们之间的区别,从而产生一段时间。很好……。

Instant是如何实现的?这取决于您运行代码的操作系统。幸运的是,文档列出了(当前)用于获取时间信息的系统调用。

但是我们如何替换操作系统的功能呢?原来函数只是一个名称,链接器试图找出它们背后的代码在哪里,然后指向它。

如果我们查看没有任何#[NO_MANGLE]extern";C&34;函数的代码的更简单版本,我们可以看到这些函数来自哪里:

(MacOS)❯nm-a./Instant|rg mach_Absolute_Time U_Mach_Absolute_Time(LINUX)❯nm-a即时|rg Clock_Gettime U Clock_GetTime@@GLIBC_2.17.。

NM列出了目标文件中的符号。U在那里代表未定义:二进制文件不知道它来自哪里(尽管在Linux中,它给了它一个略微不同的名称,让我们猜测会发生什么)。有关此符号的详细信息将在稍后填写,一旦您的程序加载并将符号解析为动态库(我相信@fasterthan lime的Making Our Executable Packer系列将提供更多的详细信息,我。

所以现在让我们试着从我们的代码中公开一个同名的函数。注意:我在这里只做MacOS部分,它在Linux上的作用是一样的。

它根本不会出现在最终的二进制文件中,Rustc Right完全抱怨道:

即使设置为pub也不起作用。默认情况下,函数名称等符号会损坏;一些附加信息也会编码到名称中,同时这会确保名称的唯一性(另请参阅名称损坏)。您可以使用#[no_mangle]属性在Rust中禁用此功能。所以我们应用了这一点。

我们正在尝试覆盖根据C.C定义的函数,该函数的调用约定与Rust略有不同。这是它们各自的ABI的一部分:如何将数据传入和传出函数。在Rust中,您可以使用extern;name标记来定义使用的ABI。所以我们添加了这一点。

(MacOS)❯nm-a./即时|rg mach_Absolute_time0000000100000c20 T_mach_Absolute_Time(Linux)❯nm-a即时|rg CLOCK_Gettime0000000000005250 T CLOCK_GETTIME。

现在我们知道符号是在二进制(";T-符号在文本(代码)部分。";)中的某个位置定义的,也是在位置(0000000100000c20/0000000000005250)中定义的。

如果此程序正在运行,它仍需要解析未定义的符号.。但这一次我们的函数不再是未定义的,因为stdlib只调用各自名称后面的任何内容,所以它现在调用我们的代码!

是的,这几乎适用于其他库或libc定义的几乎所有函数:

使用std::fs::file;使用std::OS::raw::{c_char,c_int};#[no_mangle]extern";C";fn open(_path:*mut c_char,_flag:c_int)->;c_int{-1}fn main(){File::open(";/etc/passwd";)。UnWrap();}。

❯rustc file.rs❯./filethread';main';在`Err`值:OS{CODE:0,KIND:OTHER,MESSAGE:";UNDEFINED ERROR:0";}';,file.rs:10:5注意:使用`RUST_BACKTRACE=1`环境变量运行以显示回溯。