about summary refs log tree commit diff
diff options
context:
space:
mode:
authortiif <pekyuan@gmail.com>2024-04-22 06:30:10 +0000
committertiif <pekyuan@gmail.com>2024-04-22 06:30:10 +0000
commitfde24ed5bf66b79d72465f796d862f4241ca71e7 (patch)
treeb27bb9b3d95227d34a00c7d7dc67871097971dcb
parentbc1538dca0b0ef9f257201352e72337a7161b6af (diff)
downloadrust-fde24ed5bf66b79d72465f796d862f4241ca71e7.tar.gz
rust-fde24ed5bf66b79d72465f796d862f4241ca71e7.zip
Add localtime_r shim
-rw-r--r--src/tools/miri/Cargo.lock144
-rw-r--r--src/tools/miri/Cargo.toml1
-rw-r--r--src/tools/miri/src/shims/time.rs78
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs5
-rw-r--r--src/tools/miri/tests/pass-dep/shims/libc-misc.rs45
5 files changed, 273 insertions, 0 deletions
diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock
index 4fb479e1c54..1e6b5502b04 100644
--- a/src/tools/miri/Cargo.lock
+++ b/src/tools/miri/Cargo.lock
@@ -38,6 +38,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
 name = "annotate-snippets"
 version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -107,6 +122,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[package]]
 name = "camino"
 version = "1.1.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -151,6 +172,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
+name = "chrono"
+version = "0.4.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "windows-targets 0.52.3",
+]
+
+[[package]]
 name = "cipher"
 version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -217,6 +250,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "core-foundation-sys"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
 name = "cpufeatures"
 version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -320,6 +359,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
 
 [[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
 name = "indenter"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -373,6 +435,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "js-sys"
+version = "0.3.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
 name = "lazy_static"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -484,6 +555,7 @@ name = "miri"
 version = "0.1.0"
 dependencies = [
  "aes",
+ "chrono",
  "colored",
  "ctrlc",
  "getrandom",
@@ -513,6 +585,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "num-traits"
+version = "0.2.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
 name = "number_prefix"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -965,6 +1046,60 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
+name = "wasm-bindgen"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
+dependencies = [
+ "cfg-if",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
+dependencies = [
+ "bumpalo",
+ "log",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
+
+[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -987,6 +1122,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.3",
+]
+
+[[package]]
 name = "windows-sys"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml
index 9d24d3c6f47..7748d630b12 100644
--- a/src/tools/miri/Cargo.toml
+++ b/src/tools/miri/Cargo.toml
@@ -24,6 +24,7 @@ smallvec = "1.7"
 aes = { version = "0.8.3", features = ["hazmat"] }
 measureme = "11"
 ctrlc = "3.2.5"
+chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
 
 # Copied from `compiler/rustc/Cargo.toml`.
 # But only for some targets, it fails for others. Rustc configures this in its CI, but we can't
diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs
index 1126c900226..dfdf58470d6 100644
--- a/src/tools/miri/src/shims/time.rs
+++ b/src/tools/miri/src/shims/time.rs
@@ -1,5 +1,9 @@
+use std::ffi::OsString;
+use std::fmt::Write;
 use std::time::{Duration, SystemTime};
 
+use chrono::{DateTime, Datelike, Local, Timelike, Utc};
+
 use crate::concurrency::thread::MachineCallback;
 use crate::*;
 
@@ -107,6 +111,80 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         Ok(0)
     }
 
+    // The localtime() function shall convert the time in seconds since the Epoch pointed to by
+    // timer into a broken-down time, expressed as a local time.
+    // https://linux.die.net/man/3/localtime_r
+    fn localtime_r(
+        &mut self,
+        timep: &OpTy<'tcx, Provenance>,
+        result_op: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
+        let this = self.eval_context_mut();
+
+        this.assert_target_os_is_unix("localtime_r");
+        this.check_no_isolation("`localtime_r`")?;
+
+        let timep = this.deref_pointer(timep)?;
+        let result = this.deref_pointer_as(result_op, this.libc_ty_layout("tm"))?;
+
+        // The input "represents the number of seconds elapsed since the Epoch,
+        // 1970-01-01 00:00:00 +0000 (UTC)".
+        let sec_since_epoch: i64 = this
+            .read_scalar(&timep)?
+            .to_int(this.libc_ty_layout("time_t").size)?
+            .try_into()
+            .unwrap();
+        let dt_utc: DateTime<Utc> =
+            DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp");
+        // Convert that to local time, then return the broken-down time value.
+        let dt: DateTime<Local> = DateTime::from(dt_utc);
+
+        // This value is always set to -1, because there is no way to know if dst is in effect with
+        // chrono crate yet.
+        // This may not be consistent with libc::localtime_r's result.
+        let tm_isdst = -1;
+
+        // tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08.
+        // This may not be consistent with libc::localtime_r's result.
+        let offset_in_second = Local::now().offset().local_minus_utc();
+        let tm_gmtoff = offset_in_second;
+        let mut tm_zone = String::new();
+        if offset_in_second < 0 {
+            tm_zone.push('-');
+        } else {
+            tm_zone.push('+');
+        }
+        let offset_hour = offset_in_second.abs() / 3600;
+        write!(tm_zone, "{:02}", offset_hour).unwrap();
+        let offset_min = (offset_in_second.abs() % 3600) / 60;
+        if offset_min != 0 {
+            write!(tm_zone, "{:02}", offset_min).unwrap();
+        }
+
+        // FIXME: String de-duplication is needed so that we only allocate this string only once
+        // even when there are multiple calls to this function.
+        let tm_zone_ptr =
+            this.alloc_os_str_as_c_str(&OsString::from(tm_zone), MiriMemoryKind::Machine.into())?;
+
+        this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?;
+        this.write_int_fields_named(
+            &[
+                ("tm_sec", dt.second().into()),
+                ("tm_min", dt.minute().into()),
+                ("tm_hour", dt.hour().into()),
+                ("tm_mday", dt.day().into()),
+                ("tm_mon", dt.month0().into()),
+                ("tm_year", dt.year().checked_sub(1900).unwrap().into()),
+                ("tm_wday", dt.weekday().num_days_from_sunday().into()),
+                ("tm_yday", dt.ordinal0().into()),
+                ("tm_isdst", tm_isdst),
+                ("tm_gmtoff", tm_gmtoff.into()),
+            ],
+            &result,
+        )?;
+
+        Ok(result.ptr())
+    }
     #[allow(non_snake_case, clippy::arithmetic_side_effects)]
     fn GetSystemTimeAsFileTime(
         &mut self,
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index c72d3bb3df4..bd299aaa125 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -234,6 +234,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let result = this.gettimeofday(tv, tz)?;
                 this.write_scalar(Scalar::from_i32(result), dest)?;
             }
+            "localtime_r" => {
+                let [timep, result_op] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
+                let result = this.localtime_r(timep, result_op)?;
+                this.write_pointer(result, dest)?;
+            }
             "clock_gettime" => {
                 let [clk_id, tp] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
diff --git a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs b/src/tools/miri/tests/pass-dep/shims/libc-misc.rs
index abb384b0a85..f710daf5277 100644
--- a/src/tools/miri/tests/pass-dep/shims/libc-misc.rs
+++ b/src/tools/miri/tests/pass-dep/shims/libc-misc.rs
@@ -213,6 +213,50 @@ fn test_posix_gettimeofday() {
     assert_eq!(is_error, -1);
 }
 
+fn test_localtime_r() {
+    use std::ffi::CStr;
+    use std::{env, ptr};
+
+    // Set timezone to GMT.
+    let key = "TZ";
+    env::set_var(key, "GMT");
+
+    const TIME_SINCE_EPOCH: libc::time_t = 1712475836;
+    let custom_time_ptr = &TIME_SINCE_EPOCH;
+    let mut tm = libc::tm {
+        tm_sec: 0,
+        tm_min: 0,
+        tm_hour: 0,
+        tm_mday: 0,
+        tm_mon: 0,
+        tm_year: 0,
+        tm_wday: 0,
+        tm_yday: 0,
+        tm_isdst: 0,
+        tm_gmtoff: 0,
+        tm_zone: std::ptr::null_mut::<libc::c_char>(),
+    };
+    let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
+
+    assert_eq!(tm.tm_sec, 56);
+    assert_eq!(tm.tm_min, 43);
+    assert_eq!(tm.tm_hour, 7);
+    assert_eq!(tm.tm_mday, 7);
+    assert_eq!(tm.tm_mon, 3);
+    assert_eq!(tm.tm_year, 124);
+    assert_eq!(tm.tm_wday, 0);
+    assert_eq!(tm.tm_yday, 97);
+    assert_eq!(tm.tm_isdst, -1);
+    assert_eq!(tm.tm_gmtoff, 0);
+    unsafe { assert_eq!(CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00") };
+
+    // The returned value is the pointer passed in.
+    assert!(ptr::eq(res, &mut tm));
+
+    //Remove timezone setting.
+    env::remove_var(key);
+}
+
 fn test_isatty() {
     // Testing whether our isatty shim returns the right value would require controlling whether
     // these streams are actually TTYs, which is hard.
@@ -365,6 +409,7 @@ fn main() {
     test_posix_realpath_errors();
 
     test_thread_local_errno();
+    test_localtime_r();
 
     test_isatty();