Skip to content

Commit 75d6b28

Browse files
committed
checkpoint regression test demonstrating failure of rust issue 101913.
1 parent 3344386 commit 75d6b28

File tree

4 files changed

+156
-9
lines changed

4 files changed

+156
-9
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,8 @@ edition = '2018'
130130
name = "concurrent-panics"
131131
required-features = ["std"]
132132
harness = false
133+
134+
[[test]]
135+
name = "current-exe-mismatch"
136+
required-features = ["std"]
137+
harness = false

tests/common/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// Some tests only make sense in contexts where they can re-exec the test
2+
/// itself. Not all contexts support this, so you can call this method to find
3+
/// out which case you are in.
4+
pub fn cannot_reexec_the_test() -> bool {
5+
// These run in docker containers on CI where they can't re-exec the test,
6+
// so just skip these for CI. No other reason this can't run on those
7+
// platforms though.
8+
// Miri does not have support for re-execing a file
9+
cfg!(unix)
10+
&& (cfg!(target_arch = "arm")
11+
|| cfg!(target_arch = "aarch64")
12+
|| cfg!(target_arch = "s390x"))
13+
|| cfg!(miri)
14+
}

tests/concurrent-panics.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,11 @@ const PANICS: usize = 100;
99
const THREADS: usize = 8;
1010
const VAR: &str = "__THE_TEST_YOU_ARE_LUKE";
1111

12+
mod common;
13+
1214
fn main() {
13-
// These run in docker containers on CI where they can't re-exec the test,
14-
// so just skip these for CI. No other reason this can't run on those
15-
// platforms though.
16-
// Miri does not have support for re-execing a file
17-
if cfg!(unix)
18-
&& (cfg!(target_arch = "arm")
19-
|| cfg!(target_arch = "aarch64")
20-
|| cfg!(target_arch = "s390x"))
21-
|| cfg!(miri)
15+
// If we cannot re-exec this test, there's no point in trying to do it.
16+
if common::cannot_reexec_the_test()
2217
{
2318
println!("test result: ok");
2419
return;

tests/current-exe-mismatch.rs

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// rust-lang/rust#101913: when you run your program explicitly via `ld.so`,
2+
// `std::env::current_exe` will return the path of *that* program, and not
3+
// the Rust program itself.
4+
5+
use std::process::Command;
6+
use std::path::{Path, PathBuf};
7+
use std::io::{BufRead, BufReader};
8+
9+
mod common;
10+
11+
fn main() {
12+
if std::env::var(VAR).is_err() {
13+
// the parent waits for the child; then we then handle either printing
14+
// "test result: ok", "test result: ignored", or panicking.
15+
match parent() {
16+
Ok(()) => {
17+
println!("test result: ok");
18+
}
19+
Err(EarlyExit::IgnoreTest(_)) => {
20+
println!("test result: ignored");
21+
}
22+
Err(EarlyExit::IoError(e)) => {
23+
println!("{} parent encoutered IoError: {:?}", file!(), e);
24+
panic!();
25+
}
26+
}
27+
} else {
28+
// println!("{} running child", file!());
29+
child().unwrap();
30+
}
31+
}
32+
33+
const VAR: &str = "__THE_TEST_YOU_ARE_LUKE";
34+
35+
#[derive(Debug)]
36+
enum EarlyExit {
37+
IgnoreTest(String),
38+
IoError(std::io::Error),
39+
}
40+
41+
impl From<std::io::Error> for EarlyExit {
42+
fn from(e: std::io::Error) -> Self {
43+
EarlyExit::IoError(e)
44+
}
45+
}
46+
47+
fn parent() -> Result<(), EarlyExit> {
48+
// If we cannot re-exec this test, there's no point in trying to do it.
49+
if common::cannot_reexec_the_test()
50+
{
51+
return Err(EarlyExit::IgnoreTest("(cannot reexec)".into()));
52+
}
53+
54+
let me = std::env::current_exe().unwrap();
55+
let ld_so = find_interpreter(&me)?;
56+
57+
// use interp to invoke current exe, yielding child test.
58+
//
59+
// (if you're curious what you might compare this against, you can try
60+
// swapping in the below definition for `result`, which is the easy case of
61+
// not using the ld.so interpreter directly that Rust handled fine even
62+
// prior to resolution of rust-lang/rust#101913.)
63+
//
64+
// let result = Command::new(me).env(VAR, "1").output()?;
65+
let result = Command::new(ld_so).env(VAR, "1").arg(&me).output().unwrap();
66+
67+
if result.status.success() {
68+
return Ok(());
69+
}
70+
println!("stdout:\n{}", String::from_utf8_lossy(&result.stdout));
71+
println!("stderr:\n{}", String::from_utf8_lossy(&result.stderr));
72+
println!("code: {}", result.status);
73+
panic!();
74+
}
75+
76+
fn child() -> Result<(), EarlyExit> {
77+
let bt = backtrace::Backtrace::new();
78+
println!("{:?}", bt);
79+
80+
let mut found_my_name = false;
81+
82+
let my_filename = file!();
83+
'frames: for frame in bt.frames() {
84+
let symbols = frame.symbols();
85+
if symbols.is_empty() {
86+
continue;
87+
}
88+
89+
for sym in symbols {
90+
if let Some(filename) = sym.filename() {
91+
if filename.ends_with(my_filename) {
92+
// huzzah!
93+
found_my_name = true;
94+
break 'frames;
95+
}
96+
}
97+
}
98+
}
99+
100+
assert!(found_my_name);
101+
102+
Ok(())
103+
}
104+
105+
// we use the `readelf` command to extract the path to the interpreter requested
106+
// by our binary.
107+
//
108+
// if we cannot `readelf` for some reason, or if we fail to parse its output,
109+
// then we will just give up on this test (and not treat it as a test failure).
110+
fn find_interpreter(me: &Path) -> Result<PathBuf, EarlyExit> {
111+
let result = Command::new("readelf")
112+
.arg("-l")
113+
.arg(me)
114+
.output()
115+
.unwrap();
116+
if result.status.success() {
117+
let r = BufReader::new(&result.stdout[..]);
118+
for line in r.lines() {
119+
let line = line?;
120+
let line = line.trim();
121+
let prefix = "[Requesting program interpreter: ";
122+
if let Some((_, suffix)) = line.split_once(prefix) {
123+
if let Some((found_path, _ignore_suffix)) = suffix.rsplit_once("]") {
124+
return Ok(found_path.into());
125+
}
126+
}
127+
}
128+
129+
Err(EarlyExit::IgnoreTest("could not find interpreter from readelf output".into()))
130+
} else {
131+
Err(EarlyExit::IgnoreTest("readelf invocation failed".into()))
132+
}
133+
}

0 commit comments

Comments
 (0)