Skip to content

Commit 328c0c9

Browse files
committed
implemented getenvironmentvariablew/setenvironmentvariablew for Windows
1 parent 8dcfc38 commit 328c0c9

File tree

3 files changed

+81
-45
lines changed

3 files changed

+81
-45
lines changed

src/helpers.rs

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -508,21 +508,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
508508
u16vec_to_osstring(u16_vec)
509509
}
510510

511-
/// Dispatches to appropriate implementations for writing an OsString to Memory,
512-
/// depending on the interpretation target.
513-
fn write_os_str_to_target_str(
514-
&mut self,
515-
os_str: &OsStr,
516-
mplace: MPlaceTy<'tcx, Tag>,
517-
size: u64,
518-
) -> InterpResult<'tcx, (bool, u64)> {
519-
let target_os = self.eval_context_ref().tcx.sess.target.target.target_os.as_str();
520-
match target_os {
521-
"linux" | "macos" => self.write_os_str_to_c_str(os_str, mplace.ptr, size),
522-
"windows" => self.write_os_str_to_wide_str(os_str, mplace, size),
523-
_ => throw_unsup_format!("OsString support for target OS not yet available"),
524-
}
525-
}
526511

527512
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
528513
/// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
@@ -614,7 +599,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
614599
&mut self,
615600
os_str: &OsStr,
616601
memkind: MemoryKind<MiriMemoryKind>,
617-
) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> {
602+
) -> InterpResult<'tcx, Pointer<Tag>> {
618603
let target_os = self.eval_context_ref().tcx.sess.target.target.target_os.as_str();
619604
match target_os {
620605
"linux" | "macos" => self.alloc_os_str_as_c_str(os_str, memkind),
@@ -627,28 +612,28 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
627612
&mut self,
628613
os_str: &OsStr,
629614
memkind: MemoryKind<MiriMemoryKind>,
630-
) -> Pointer<Tag> {
615+
) -> InterpResult<'tcx, Pointer<Tag>> {
631616
let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
632617
let this = self.eval_context_mut();
633618

634619
let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
635620
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
636621
self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap();
637-
Ok(arg_place)
622+
Ok(arg_place.ptr.assert_ptr())
638623
}
639624

640625
fn alloc_os_str_as_wide_str(
641626
&mut self,
642627
os_str: &OsStr,
643628
memkind: MemoryKind<MiriMemoryKind>,
644-
) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> {
629+
) -> InterpResult<'tcx, Pointer<Tag>> {
645630
let size = os_str.len() as u64 + 1; // Make space for `0x0000` terminator.
646631
let this = self.eval_context_mut();
647632

648633
let arg_type = this.tcx.mk_array(this.tcx.types.u16, size);
649634
let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind);
650635
self.write_os_str_to_wide_str(os_str, arg_place, size).unwrap();
651-
Ok(arg_place)
636+
Ok(arg_place.ptr.assert_ptr())
652637
}
653638
}
654639

src/shims/env.rs

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_mir::interpret::Pointer;
1313
#[derive(Default)]
1414
pub struct EnvVars<'tcx> {
1515
/// Stores pointers to the environment variables. These variables must be stored as
16-
/// null-terminated C strings with the `"{name}={value}"` format.
16+
/// null-terminated target strings(c_str or wide_str) with the `"{name}={value}"` format.
1717
map: FxHashMap<OsString, Pointer<Tag>>,
1818

1919
/// Place where the `environ` static is stored. Lazily initialized, but then never changes.
@@ -46,21 +46,17 @@ fn alloc_env_var_as_target_str<'mir, 'tcx>(
4646
let mut name_osstring = name.to_os_string();
4747
name_osstring.push("=");
4848
name_osstring.push(value);
49-
Ok(ecx
50-
.alloc_os_str_as_target_str(name_osstring.as_os_str(), MiriMemoryKind::Machine.into())?
51-
.ptr
52-
.assert_ptr())
49+
Ok(ecx.alloc_os_str_as_target_str(name_osstring.as_os_str(), MiriMemoryKind::Machine.into())?)
5350
}
5451

5552
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
5653
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
57-
fn getenv(&mut self, name_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar<Tag>> {
58-
let this = self.eval_context_mut();
54+
fn getenv(&self, name_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar<Tag>> {
55+
let this = self.eval_context_ref();
5956

6057
let name_ptr = this.read_scalar(name_op)?.not_undef()?;
6158
let name = this.read_os_str_from_target_str(name_ptr)?;
6259
Ok(match this.machine.env_vars.map.get(&name) {
63-
// The offset is used to strip the "{name}=" part of the string.
6460
Some(var_ptr) => {
6561
Scalar::from(var_ptr.offset(Size::from_bytes(u64::try_from(name.len()).unwrap().checked_add(1).unwrap()), this)?)
6662
}
@@ -69,8 +65,57 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
6965
}
7066

7167

72-
fn getenvironmentvariablew() {
68+
fn getenvironmentvariablew(
69+
&mut self,
70+
name_op: OpTy<'tcx, Tag>, // LPCWSTR lpName
71+
buf_op: OpTy<'tcx, Tag>, // LPWSTR lpBuffer
72+
size_op: OpTy<'tcx, Tag>, // DWORD nSize
73+
) -> InterpResult<'tcx, u32> {
74+
let this = self.eval_context_mut();
75+
76+
let name_ptr = this.read_scalar(name_op)?.not_undef()?;
77+
let name = this.read_os_str_from_target_str(name_ptr)?;
78+
Ok(match this.machine.env_vars.map.get(&name) {
79+
Some(var_ptr) => {
80+
// The offset is used to strip the "{name}=" part of the string.
81+
let var_ptr = Scalar::from(var_ptr.offset(Size::from_bytes((name.len() as u64 + 1) * 2), this)?);
82+
let buf_size = this.read_scalar(size_op)?.to_i32()? as u64;
83+
let buf_ptr = this.read_scalar(buf_op)?.not_undef()?;
84+
let size_u16 = Size::from_bytes(2);
85+
86+
// The following loop attempts to figure out the length of env_var (`var_size`)
87+
let mut var_size = 0u64;
88+
loop {
89+
let temp_var_ptr = var_ptr.ptr_offset(Size::from_bytes(var_size * 2), this)?;
90+
let bytes = this.memory.read_bytes(temp_var_ptr, size_u16)?;
91+
var_size += 1;
92+
// encountered 0x0000 terminator
93+
if bytes[0] == 0 && bytes[1] == 0 { break; }
94+
}
7395

96+
let return_val = if var_size > buf_size {
97+
// If lpBuffer is not large enough to hold the data, the return value is the buffer size, in characters,
98+
// required to hold the string and its terminating null character and the contents of lpBuffer are undefined.
99+
var_size
100+
} else {
101+
for i in 0..var_size {
102+
this.memory.copy(
103+
this.force_ptr(var_ptr.ptr_offset(Size::from_bytes(i * 2), this)?)?,
104+
this.force_ptr(buf_ptr.ptr_offset(Size::from_bytes(i * 2), this)?)?,
105+
size_u16,
106+
true,
107+
)?;
108+
}
109+
// If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer,
110+
// not including the terminating null character.
111+
var_size - 1
112+
};
113+
assert_eq!(return_val as u32 as u64, return_val);
114+
return_val as u32
115+
}
116+
// return zero upon failure
117+
None => 0u32
118+
})
74119
}
75120

76121
fn setenv(
@@ -97,14 +142,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
97142
.deallocate(var, None, MiriMemoryKind::Machine.into())?;
98143
}
99144
this.update_environ()?;
100-
Ok(0)
145+
Ok(0) // return zero on success
101146
} else {
102147
Ok(-1)
103148
}
104149
}
105150

106-
fn setenvironmentvariablew() {
107-
151+
fn setenvironmentvariablew(
152+
&mut self,
153+
name_op: OpTy<'tcx, Tag>, // LPCWSTR lpName,
154+
value_op: OpTy<'tcx, Tag>, // LPCWSTR lpValue,
155+
) -> InterpResult<'tcx, i32> {
156+
// return non-zero on success
157+
self.setenv(name_op, value_op).map(|x| x + 1)
108158
}
109159

110160
fn unsetenv(&mut self, name_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, i32> {

src/shims/foreign_items/windows.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,25 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
2121
// DWORD = ULONG = u32
2222
// BOOL = i32
2323

24+
"GetEnvironmentStringsW" => {
25+
// this.write_scalar(this.machine.env_vars.environ.unwrap().vtable(), dest)?;
26+
}
27+
2428
// Environment related shims
2529
"GetEnvironmentVariableW" => {
26-
// args[0] : LPCWSTR lpName (32-bit ptr to a const string of 16-bit Unicode chars)
27-
// args[1] : LPWSTR lpBuffer (32-bit pointer to a string of 16-bit Unicode chars)
28-
// lpBuffer : ptr to buffer that receives contents of the env_var as a null-terminated string.
29-
// Return `# of chars` stored in the buffer pointed to by lpBuffer, excluding null-terminator.
30-
// Return 0 upon failure.
31-
32-
// This is not the env var you are looking for.
33-
this.set_last_error(Scalar::from_u32(203))?; // ERROR_ENVVAR_NOT_FOUND
34-
this.write_null(dest)?;
30+
let result = this.getenvironmentvariablew(args[0], args[1], args[2])?;
31+
if result == 0 {
32+
this.set_last_error(Scalar::from_u32(203))?; // ERROR_ENVVAR_NOT_FOUND
33+
// this.write_null(dest)?;
34+
} else {
35+
// this.write_scalar(Scalar::from_uint(result, dest.layout.size), dest)?;
36+
}
37+
this.write_scalar(Scalar::from_uint(result, dest.layout.size), dest)?;
3538
}
3639

3740
"SetEnvironmentVariableW" => {
38-
// args[0] : LPCWSTR lpName (32-bit ptr to a const string of 16-bit Unicode chars)
39-
// args[1] : LPCWSTR lpValue (32-bit ptr to a const string of 16-bit Unicode chars)
40-
// Return nonzero if success, else return 0.
41-
throw_unsup_format!("can't set environment variable on Windows");
41+
let result = this.setenvironmentvariablew(args[0], args[1])?;
42+
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
4243
}
4344

4445
// File related shims

0 commit comments

Comments
 (0)