Skip to content

Commit 2ca5524

Browse files
committed
Auto merge of rust-lang#3461 - tiif:add_localtime_r_shim, r=RalfJung
Add localtime_r shim - Implement ``localtime_r`` shim as mentioned in rust-lang#2057 Note: - ``tm_zone``, ``tm_gmtoff`` might not be consistent with ``libc::localtime_r`` as custom implementation is provided through ``chrono``. Due to the lack of daylight saving information in ``chrono``, ``is_dst`` value will always be ``-1``.
2 parents bc1538d + fde24ed commit 2ca5524

File tree

5 files changed

+273
-0
lines changed

5 files changed

+273
-0
lines changed

src/tools/miri/Cargo.lock

+144
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ dependencies = [
3737
"memchr",
3838
]
3939

40+
[[package]]
41+
name = "android-tzdata"
42+
version = "0.1.1"
43+
source = "registry+https://github.com/rust-lang/crates.io-index"
44+
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
45+
46+
[[package]]
47+
name = "android_system_properties"
48+
version = "0.1.5"
49+
source = "registry+https://github.com/rust-lang/crates.io-index"
50+
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
51+
dependencies = [
52+
"libc",
53+
]
54+
4055
[[package]]
4156
name = "annotate-snippets"
4257
version = "0.9.2"
@@ -106,6 +121,12 @@ dependencies = [
106121
"serde",
107122
]
108123

124+
[[package]]
125+
name = "bumpalo"
126+
version = "3.16.0"
127+
source = "registry+https://github.com/rust-lang/crates.io-index"
128+
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
129+
109130
[[package]]
110131
name = "camino"
111132
version = "1.1.6"
@@ -150,6 +171,18 @@ version = "1.0.0"
150171
source = "registry+https://github.com/rust-lang/crates.io-index"
151172
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
152173

174+
[[package]]
175+
name = "chrono"
176+
version = "0.4.38"
177+
source = "registry+https://github.com/rust-lang/crates.io-index"
178+
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
179+
dependencies = [
180+
"android-tzdata",
181+
"iana-time-zone",
182+
"num-traits",
183+
"windows-targets 0.52.3",
184+
]
185+
153186
[[package]]
154187
name = "cipher"
155188
version = "0.4.4"
@@ -216,6 +249,12 @@ dependencies = [
216249
"windows-sys 0.52.0",
217250
]
218251

252+
[[package]]
253+
name = "core-foundation-sys"
254+
version = "0.8.6"
255+
source = "registry+https://github.com/rust-lang/crates.io-index"
256+
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
257+
219258
[[package]]
220259
name = "cpufeatures"
221260
version = "0.2.12"
@@ -319,6 +358,29 @@ version = "0.28.1"
319358
source = "registry+https://github.com/rust-lang/crates.io-index"
320359
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
321360

361+
[[package]]
362+
name = "iana-time-zone"
363+
version = "0.1.60"
364+
source = "registry+https://github.com/rust-lang/crates.io-index"
365+
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
366+
dependencies = [
367+
"android_system_properties",
368+
"core-foundation-sys",
369+
"iana-time-zone-haiku",
370+
"js-sys",
371+
"wasm-bindgen",
372+
"windows-core",
373+
]
374+
375+
[[package]]
376+
name = "iana-time-zone-haiku"
377+
version = "0.1.2"
378+
source = "registry+https://github.com/rust-lang/crates.io-index"
379+
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
380+
dependencies = [
381+
"cc",
382+
]
383+
322384
[[package]]
323385
name = "indenter"
324386
version = "0.3.3"
@@ -372,6 +434,15 @@ dependencies = [
372434
"libc",
373435
]
374436

437+
[[package]]
438+
name = "js-sys"
439+
version = "0.3.69"
440+
source = "registry+https://github.com/rust-lang/crates.io-index"
441+
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
442+
dependencies = [
443+
"wasm-bindgen",
444+
]
445+
375446
[[package]]
376447
name = "lazy_static"
377448
version = "1.4.0"
@@ -484,6 +555,7 @@ name = "miri"
484555
version = "0.1.0"
485556
dependencies = [
486557
"aes",
558+
"chrono",
487559
"colored",
488560
"ctrlc",
489561
"getrandom",
@@ -512,6 +584,15 @@ dependencies = [
512584
"libc",
513585
]
514586

587+
[[package]]
588+
name = "num-traits"
589+
version = "0.2.18"
590+
source = "registry+https://github.com/rust-lang/crates.io-index"
591+
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
592+
dependencies = [
593+
"autocfg",
594+
]
595+
515596
[[package]]
516597
name = "number_prefix"
517598
version = "0.4.0"
@@ -964,6 +1045,60 @@ version = "0.11.0+wasi-snapshot-preview1"
9641045
source = "registry+https://github.com/rust-lang/crates.io-index"
9651046
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
9661047

1048+
[[package]]
1049+
name = "wasm-bindgen"
1050+
version = "0.2.92"
1051+
source = "registry+https://github.com/rust-lang/crates.io-index"
1052+
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
1053+
dependencies = [
1054+
"cfg-if",
1055+
"wasm-bindgen-macro",
1056+
]
1057+
1058+
[[package]]
1059+
name = "wasm-bindgen-backend"
1060+
version = "0.2.92"
1061+
source = "registry+https://github.com/rust-lang/crates.io-index"
1062+
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
1063+
dependencies = [
1064+
"bumpalo",
1065+
"log",
1066+
"once_cell",
1067+
"proc-macro2",
1068+
"quote",
1069+
"syn",
1070+
"wasm-bindgen-shared",
1071+
]
1072+
1073+
[[package]]
1074+
name = "wasm-bindgen-macro"
1075+
version = "0.2.92"
1076+
source = "registry+https://github.com/rust-lang/crates.io-index"
1077+
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
1078+
dependencies = [
1079+
"quote",
1080+
"wasm-bindgen-macro-support",
1081+
]
1082+
1083+
[[package]]
1084+
name = "wasm-bindgen-macro-support"
1085+
version = "0.2.92"
1086+
source = "registry+https://github.com/rust-lang/crates.io-index"
1087+
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
1088+
dependencies = [
1089+
"proc-macro2",
1090+
"quote",
1091+
"syn",
1092+
"wasm-bindgen-backend",
1093+
"wasm-bindgen-shared",
1094+
]
1095+
1096+
[[package]]
1097+
name = "wasm-bindgen-shared"
1098+
version = "0.2.92"
1099+
source = "registry+https://github.com/rust-lang/crates.io-index"
1100+
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
1101+
9671102
[[package]]
9681103
name = "winapi"
9691104
version = "0.3.9"
@@ -986,6 +1121,15 @@ version = "0.4.0"
9861121
source = "registry+https://github.com/rust-lang/crates.io-index"
9871122
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
9881123

1124+
[[package]]
1125+
name = "windows-core"
1126+
version = "0.52.0"
1127+
source = "registry+https://github.com/rust-lang/crates.io-index"
1128+
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
1129+
dependencies = [
1130+
"windows-targets 0.52.3",
1131+
]
1132+
9891133
[[package]]
9901134
name = "windows-sys"
9911135
version = "0.48.0"

src/tools/miri/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ smallvec = "1.7"
2424
aes = { version = "0.8.3", features = ["hazmat"] }
2525
measureme = "11"
2626
ctrlc = "3.2.5"
27+
chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
2728

2829
# Copied from `compiler/rustc/Cargo.toml`.
2930
# But only for some targets, it fails for others. Rustc configures this in its CI, but we can't

src/tools/miri/src/shims/time.rs

+78
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
use std::ffi::OsString;
2+
use std::fmt::Write;
13
use std::time::{Duration, SystemTime};
24

5+
use chrono::{DateTime, Datelike, Local, Timelike, Utc};
6+
37
use crate::concurrency::thread::MachineCallback;
48
use crate::*;
59

@@ -107,6 +111,80 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
107111
Ok(0)
108112
}
109113

114+
// The localtime() function shall convert the time in seconds since the Epoch pointed to by
115+
// timer into a broken-down time, expressed as a local time.
116+
// https://linux.die.net/man/3/localtime_r
117+
fn localtime_r(
118+
&mut self,
119+
timep: &OpTy<'tcx, Provenance>,
120+
result_op: &OpTy<'tcx, Provenance>,
121+
) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
122+
let this = self.eval_context_mut();
123+
124+
this.assert_target_os_is_unix("localtime_r");
125+
this.check_no_isolation("`localtime_r`")?;
126+
127+
let timep = this.deref_pointer(timep)?;
128+
let result = this.deref_pointer_as(result_op, this.libc_ty_layout("tm"))?;
129+
130+
// The input "represents the number of seconds elapsed since the Epoch,
131+
// 1970-01-01 00:00:00 +0000 (UTC)".
132+
let sec_since_epoch: i64 = this
133+
.read_scalar(&timep)?
134+
.to_int(this.libc_ty_layout("time_t").size)?
135+
.try_into()
136+
.unwrap();
137+
let dt_utc: DateTime<Utc> =
138+
DateTime::from_timestamp(sec_since_epoch, 0).expect("Invalid timestamp");
139+
// Convert that to local time, then return the broken-down time value.
140+
let dt: DateTime<Local> = DateTime::from(dt_utc);
141+
142+
// This value is always set to -1, because there is no way to know if dst is in effect with
143+
// chrono crate yet.
144+
// This may not be consistent with libc::localtime_r's result.
145+
let tm_isdst = -1;
146+
147+
// tm_zone represents the timezone value in the form of: +0730, +08, -0730 or -08.
148+
// This may not be consistent with libc::localtime_r's result.
149+
let offset_in_second = Local::now().offset().local_minus_utc();
150+
let tm_gmtoff = offset_in_second;
151+
let mut tm_zone = String::new();
152+
if offset_in_second < 0 {
153+
tm_zone.push('-');
154+
} else {
155+
tm_zone.push('+');
156+
}
157+
let offset_hour = offset_in_second.abs() / 3600;
158+
write!(tm_zone, "{:02}", offset_hour).unwrap();
159+
let offset_min = (offset_in_second.abs() % 3600) / 60;
160+
if offset_min != 0 {
161+
write!(tm_zone, "{:02}", offset_min).unwrap();
162+
}
163+
164+
// FIXME: String de-duplication is needed so that we only allocate this string only once
165+
// even when there are multiple calls to this function.
166+
let tm_zone_ptr =
167+
this.alloc_os_str_as_c_str(&OsString::from(tm_zone), MiriMemoryKind::Machine.into())?;
168+
169+
this.write_pointer(tm_zone_ptr, &this.project_field_named(&result, "tm_zone")?)?;
170+
this.write_int_fields_named(
171+
&[
172+
("tm_sec", dt.second().into()),
173+
("tm_min", dt.minute().into()),
174+
("tm_hour", dt.hour().into()),
175+
("tm_mday", dt.day().into()),
176+
("tm_mon", dt.month0().into()),
177+
("tm_year", dt.year().checked_sub(1900).unwrap().into()),
178+
("tm_wday", dt.weekday().num_days_from_sunday().into()),
179+
("tm_yday", dt.ordinal0().into()),
180+
("tm_isdst", tm_isdst),
181+
("tm_gmtoff", tm_gmtoff.into()),
182+
],
183+
&result,
184+
)?;
185+
186+
Ok(result.ptr())
187+
}
110188
#[allow(non_snake_case, clippy::arithmetic_side_effects)]
111189
fn GetSystemTimeAsFileTime(
112190
&mut self,

src/tools/miri/src/shims/unix/foreign_items.rs

+5
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
234234
let result = this.gettimeofday(tv, tz)?;
235235
this.write_scalar(Scalar::from_i32(result), dest)?;
236236
}
237+
"localtime_r" => {
238+
let [timep, result_op] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
239+
let result = this.localtime_r(timep, result_op)?;
240+
this.write_pointer(result, dest)?;
241+
}
237242
"clock_gettime" => {
238243
let [clk_id, tp] =
239244
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

src/tools/miri/tests/pass-dep/shims/libc-misc.rs

+45
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,50 @@ fn test_posix_gettimeofday() {
213213
assert_eq!(is_error, -1);
214214
}
215215

216+
fn test_localtime_r() {
217+
use std::ffi::CStr;
218+
use std::{env, ptr};
219+
220+
// Set timezone to GMT.
221+
let key = "TZ";
222+
env::set_var(key, "GMT");
223+
224+
const TIME_SINCE_EPOCH: libc::time_t = 1712475836;
225+
let custom_time_ptr = &TIME_SINCE_EPOCH;
226+
let mut tm = libc::tm {
227+
tm_sec: 0,
228+
tm_min: 0,
229+
tm_hour: 0,
230+
tm_mday: 0,
231+
tm_mon: 0,
232+
tm_year: 0,
233+
tm_wday: 0,
234+
tm_yday: 0,
235+
tm_isdst: 0,
236+
tm_gmtoff: 0,
237+
tm_zone: std::ptr::null_mut::<libc::c_char>(),
238+
};
239+
let res = unsafe { libc::localtime_r(custom_time_ptr, &mut tm) };
240+
241+
assert_eq!(tm.tm_sec, 56);
242+
assert_eq!(tm.tm_min, 43);
243+
assert_eq!(tm.tm_hour, 7);
244+
assert_eq!(tm.tm_mday, 7);
245+
assert_eq!(tm.tm_mon, 3);
246+
assert_eq!(tm.tm_year, 124);
247+
assert_eq!(tm.tm_wday, 0);
248+
assert_eq!(tm.tm_yday, 97);
249+
assert_eq!(tm.tm_isdst, -1);
250+
assert_eq!(tm.tm_gmtoff, 0);
251+
unsafe { assert_eq!(CStr::from_ptr(tm.tm_zone).to_str().unwrap(), "+00") };
252+
253+
// The returned value is the pointer passed in.
254+
assert!(ptr::eq(res, &mut tm));
255+
256+
//Remove timezone setting.
257+
env::remove_var(key);
258+
}
259+
216260
fn test_isatty() {
217261
// Testing whether our isatty shim returns the right value would require controlling whether
218262
// these streams are actually TTYs, which is hard.
@@ -365,6 +409,7 @@ fn main() {
365409
test_posix_realpath_errors();
366410

367411
test_thread_local_errno();
412+
test_localtime_r();
368413

369414
test_isatty();
370415

0 commit comments

Comments
 (0)