Support libc::getenv in mir interpreter

This commit is contained in:
hkalbasi 2023-08-06 01:46:29 +03:30
parent cc5664c5a2
commit 9b636e2326
4 changed files with 83 additions and 1 deletions

View file

@ -130,6 +130,7 @@ impl ChangeFixture {
let mut default_crate_root: Option<FileId> = None; let mut default_crate_root: Option<FileId> = None;
let mut default_target_data_layout: Option<String> = None; let mut default_target_data_layout: Option<String> = None;
let mut default_cfg = CfgOptions::default(); let mut default_cfg = CfgOptions::default();
let mut default_env = Env::new_for_test_fixture();
let mut file_set = FileSet::default(); let mut file_set = FileSet::default();
let mut current_source_root_kind = SourceRootKind::Local; let mut current_source_root_kind = SourceRootKind::Local;
@ -200,6 +201,7 @@ impl ChangeFixture {
assert!(default_crate_root.is_none()); assert!(default_crate_root.is_none());
default_crate_root = Some(file_id); default_crate_root = Some(file_id);
default_cfg = meta.cfg; default_cfg = meta.cfg;
default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
default_target_data_layout = meta.target_data_layout; default_target_data_layout = meta.target_data_layout;
} }
@ -220,7 +222,7 @@ impl ChangeFixture {
None, None,
default_cfg, default_cfg,
Default::default(), Default::default(),
Env::new_for_test_fixture(), default_env,
false, false,
CrateOrigin::Local { repo: None, name: None }, CrateOrigin::Local { repo: None, name: None },
default_target_data_layout default_target_data_layout

View file

@ -686,6 +686,12 @@ impl fmt::Display for Edition {
} }
} }
impl Extend<(String, String)> for Env {
fn extend<T: IntoIterator<Item = (String, String)>>(&mut self, iter: T) {
self.entries.extend(iter);
}
}
impl FromIterator<(String, String)> for Env { impl FromIterator<(String, String)> for Env {
fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self { fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
Env { entries: FromIterator::from_iter(iter) } Env { entries: FromIterator::from_iter(iter) }

View file

@ -473,6 +473,38 @@ impl Evaluator<'_> {
self.write_memory_using_ref(destination.addr, destination.size)?.fill(0); self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
Ok(()) Ok(())
} }
"getenv" => {
let [name] = args else {
return Err(MirEvalError::TypeError("libc::write args are not provided"));
};
let mut name_buf = vec![];
let name = {
let mut index = Address::from_bytes(name.get(self)?)?;
loop {
let byte = self.read_memory(index, 1)?[0];
index = index.offset(1);
if byte == 0 {
break;
}
name_buf.push(byte);
}
String::from_utf8_lossy(&name_buf)
};
let value = self.db.crate_graph()[self.crate_id].env.get(&name);
match value {
None => {
// Write null as fail
self.write_memory_using_ref(destination.addr, destination.size)?.fill(0);
}
Some(mut value) => {
value.push('\0');
let addr = self.heap_allocate(value.len(), 1)?;
self.write_memory(addr, value.as_bytes())?;
self.write_memory(destination.addr, &addr.to_bytes())?;
}
}
Ok(())
}
_ => not_supported!("unknown external function {as_str}"), _ => not_supported!("unknown external function {as_str}"),
} }
} }

View file

@ -729,6 +729,48 @@ fn main() {
) )
} }
#[test]
fn posix_getenv() {
check_pass(
r#"
//- /main.rs env:foo=bar
type c_char = u8;
extern "C" {
pub fn getenv(s: *const c_char) -> *mut c_char;
}
fn should_not_reach() {
_ // FIXME: replace this function with panic when that works
}
fn main() {
let result = getenv(b"foo\0" as *const _);
if *result != b'b' {
should_not_reach();
}
let result = (result as usize + 1) as *const c_char;
if *result != b'a' {
should_not_reach();
}
let result = (result as usize + 1) as *const c_char;
if *result != b'r' {
should_not_reach();
}
let result = (result as usize + 1) as *const c_char;
if *result != 0 {
should_not_reach();
}
let result = getenv(b"not found\0" as *const _);
if result as usize != 0 {
should_not_reach();
}
}
"#,
);
}
#[test] #[test]
fn posix_tls() { fn posix_tls() {
check_pass( check_pass(