Skip to content

Commit 3d269e9

Browse files
committed
Auto merge of rust-lang#3474 - tyilo:wcslen, r=RalfJung
Implement wcslen
2 parents e477895 + 43c9916 commit 3d269e9

File tree

3 files changed

+64
-9
lines changed

3 files changed

+64
-9
lines changed

src/tools/miri/src/helpers.rs

+34-9
Original file line numberDiff line numberDiff line change
@@ -982,29 +982,46 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
982982
Ok((true, string_length))
983983
}
984984

985-
/// Read a sequence of u16 until the first null terminator.
986-
fn read_wide_str(&self, mut ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
985+
/// Helper function to read a sequence of unsigned integers of the given size and alignment
986+
/// until the first null terminator.
987+
fn read_c_str_with_char_size<T>(
988+
&self,
989+
mut ptr: Pointer<Option<Provenance>>,
990+
size: Size,
991+
align: Align,
992+
) -> InterpResult<'tcx, Vec<T>>
993+
where
994+
T: TryFrom<u128>,
995+
<T as TryFrom<u128>>::Error: std::fmt::Debug,
996+
{
997+
assert_ne!(size, Size::ZERO);
998+
987999
let this = self.eval_context_ref();
988-
let size2 = Size::from_bytes(2);
989-
this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
1000+
1001+
this.check_ptr_align(ptr, align)?;
9901002

9911003
let mut wchars = Vec::new();
9921004
loop {
9931005
// FIXME: We are re-getting the allocation each time around the loop.
9941006
// Would be nice if we could somehow "extend" an existing AllocRange.
995-
let alloc = this.get_ptr_alloc(ptr, size2)?.unwrap(); // not a ZST, so we will get a result
996-
let wchar = alloc.read_integer(alloc_range(Size::ZERO, size2))?.to_u16()?;
997-
if wchar == 0 {
1007+
let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); // not a ZST, so we will get a result
1008+
let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
1009+
if wchar_int == 0 {
9981010
break;
9991011
} else {
1000-
wchars.push(wchar);
1001-
ptr = ptr.offset(size2, this)?;
1012+
wchars.push(wchar_int.try_into().unwrap());
1013+
ptr = ptr.offset(size, this)?;
10021014
}
10031015
}
10041016

10051017
Ok(wchars)
10061018
}
10071019

1020+
/// Read a sequence of u16 until the first null terminator.
1021+
fn read_wide_str(&self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u16>> {
1022+
self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
1023+
}
1024+
10081025
/// Helper function to write a sequence of u16 with an added 0x0000-terminator, which is what
10091026
/// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
10101027
/// to write if `size` is not large enough to fit the contents of `os_string` plus a null
@@ -1037,6 +1054,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10371054
Ok((true, string_length))
10381055
}
10391056

1057+
/// Read a sequence of wchar_t until the first null terminator.
1058+
/// Always returns a `Vec<u32>` no matter the size of `wchar_t`.
1059+
fn read_wchar_t_str(&self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx, Vec<u32>> {
1060+
let this = self.eval_context_ref();
1061+
let wchar_t = this.libc_ty_layout("wchar_t");
1062+
self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
1063+
}
1064+
10401065
/// Check that the ABI is what we expect.
10411066
fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> {
10421067
if abi != exp_abi {

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

+10
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,16 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
657657
dest,
658658
)?;
659659
}
660+
"wcslen" => {
661+
let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
662+
let ptr = this.read_pointer(ptr)?;
663+
// This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
664+
let n = this.read_wchar_t_str(ptr)?.len();
665+
this.write_scalar(
666+
Scalar::from_target_usize(u64::try_from(n).unwrap(), this),
667+
dest,
668+
)?;
669+
}
660670
"memcpy" => {
661671
let [ptr_dest, ptr_src, n] =
662672
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
fn to_c_wchar_t_str(s: &str) -> Vec<libc::wchar_t> {
2+
let mut r = Vec::<libc::wchar_t>::new();
3+
for c in s.bytes() {
4+
if c == 0 {
5+
panic!("can't contain a null character");
6+
}
7+
if c >= 128 {
8+
panic!("only ASCII supported");
9+
}
10+
r.push(c.into());
11+
}
12+
r.push(0);
13+
r
14+
}
15+
16+
pub fn main() {
17+
let s = to_c_wchar_t_str("Rust");
18+
let len = unsafe { libc::wcslen(s.as_ptr()) };
19+
assert_eq!(len, 4);
20+
}

0 commit comments

Comments
 (0)