Jia*_*Liu 3 unsafe libc execvp rust
我一直在尝试在Rust中编写一个直接链接到libc库的shell .我用a Vec<String>来保存要传递的参数execvp(),但似乎我的转换char **没有成功.执行时,所有参数都变为空字符串.
这是涉及的代码片段.
fn safe_execvp(path: String, argv: Vec<String>) -> Result<(), i32> {
unsafe {
let c_path = CString::new(path.as_str()).unwrap();
let mut c_argv_vec = Vec::new();
for arg in &argv {
let c_arg = CString::new(arg.as_str()).unwrap().as_ptr();
c_argv_vec.push(c_arg);
}
c_argv_vec.push(std::ptr::null());
match execvp(c_file.as_ptr(), c_argv_vec.as_ptr()) {
num => Err(num),
}
}
}
Run Code Online (Sandbox Code Playgroud)
execvp是C库函数定义为fn execvp(file: *const i8, argv: *const*const i8) -> i32;.
我不确定我做错了什么.是因为参数的内存在调用之前被释放了execvp()吗?
您正在创建CString实例并立即获取指针.因此,正如您所猜测的那样,此字符串的所有权过早被删除.这类似于返回对本地实例的引用,除了因为指针不保留生命周期信息,这种情况不会触发编译错误.
问题的解决方案是在函数范围内保留拥有的C风格字符串,并分别生成指向相同内容的指针的指针.
let cstr_argv: Vec<_> = argv.iter()
.map(|arg| CString::new(arg.as_str()).unwrap())
.collect();
let mut p_argv: Vec<_> = cstr_argv.iter() // do NOT into_iter()
.map(|arg| arg.as_ptr())
.collect();
p_argv.push(std::ptr::null());
let p: *const *const c_char = p_argv.as_ptr();
Run Code Online (Sandbox Code Playgroud)
游乐场.
我建议您再次阅读该文档CString::as_ptr:
警告
您有责任确保底层内存不会过早释放。例如,
ptr在unsafe块内使用以下代码将导致未定义的行为:Run Code Online (Sandbox Code Playgroud)# #![allow(unused_must_use)] use std::ffi::{CString}; let ptr = CString::new("Hello").expect("CString::new failed").as_ptr(); unsafe { // `ptr` is dangling *ptr; }发生这种情况是因为所返回的指针
as_ptr不包含任何生存期信息,并且CString在对CString::new("Hello").expect("CString::new failed").as_ptr()表达式求值后立即将其释放。要解决此问题,请将绑定CString到本地变量:Run Code Online (Sandbox Code Playgroud)# #![allow(unused_must_use)] use std::ffi::{CString}; let hello = CString::new("Hello").expect("CString::new failed"); let ptr = hello.as_ptr(); unsafe { // `ptr` is valid because `hello` is in scope *ptr; }这样,
CStringin 的生存期就hello包含了ptr和unsafe块的生存期。
您正在完全按照文档所说做。