rust: teach the stdlib about our ld.so trickery

We deliver cross-platform cross-compilers in a trimmed-down chroot-like
environment that doesn't need to ever be `chroot`ed into. chromite's
`lddtree.py` sets this up, but the gist is that all binaries inside of
this chroot are executed like `${chroot}/lib/ld-linux-blah.so --args
"${actual_executable}" "$@"`.

This invocation strategy makes `/proc/self/exe` point to
`ld-linux-blah.so` instead of the ELF executable we're "actually"
invoking.

`lddtree.py` generates scripts which already set a special env var to
point to the "original" `argv[0]`, but that's a path relative to the
directory in which the current program was _initially_ invoked, so
that's not very useful as a general approach.

The intent of this change is to detect if the current binary is being
executed using this ld.so trickery. If it is, we should have an env
var(*) that tells us where the 'original' binary is, relative to ld.so,
so we consult that to find the original binary.

(*) -- once the corresponding change for that lands:
https://chromium-review.googlesource.com/c/chromiumos/chromite/+/2424046

BUG=chromium:1003841, chromium:1114301
TEST=Ran `rustc` in this environment; sysroot was properly detected

Change-Id: I6f6c01f08ad99064614dc0219a0eed2e4f481042
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/overlays/chromiumos-overlay/+/2453076
Reviewed-by: LaMont Jones <lamontjones@chromium.org>
Commit-Queue: Mike Nichols <mikenichols@chromium.org>
Tested-by: Mike Nichols <mikenichols@chromium.org>
diff --git a/dev-lang/rust/files/rust-1.46.0-ld-argv0.patch b/dev-lang/rust/files/rust-1.46.0-ld-argv0.patch
new file mode 100644
index 0000000..d967dba
--- /dev/null
+++ b/dev-lang/rust/files/rust-1.46.0-ld-argv0.patch
@@ -0,0 +1,56 @@
+Our cross-compilers and related tooling are executed via ld.so trickery, which
+makes /proc/self/exe not point to the right place. If we detect that we're in
+that situation in `current_exe()`, we _should_ have `LD_ARGV0_REL` in an env
+var. This is the path to the _original_ binary, relative to ld.so.
+
+diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs
+index a9cd509..92dd6b9 100644
+--- a/src/libstd/sys/unix/os.rs
++++ b/src/libstd/sys/unix/os.rs
+@@ -327,12 +327,45 @@ pub fn current_exe() -> io::Result<PathBuf> {
+ 
+ #[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
+ pub fn current_exe() -> io::Result<PathBuf> {
++    let is_ld_so = |p: &crate::path::Path| -> Option<bool> {
++        let parent_dir_name = p.parent()?.file_name()?;
++        if parent_dir_name != OsStr::new("lib") {
++            return Some(false);
++        }
++        // We assume that the `ld.so` path is always valid unicode, since there's... no reason for
++        // it not to be. :)
++        let file_name = p.file_name()?.to_str()?;
++        Some(
++            file_name.starts_with("ld-linux-")
++                && (file_name.ends_with(".so") || file_name.contains(".so.")),
++        )
++    };
++
+     match crate::fs::read_link("/proc/self/exe") {
+         Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new(
+             io::ErrorKind::Other,
+             "no /proc/self/exe available. Is /proc mounted?",
+         )),
+-        other => other,
++        Err(x) => Err(x),
++        Ok(p) => {
++            // Chrome OS-specific: in some configurations, Rust binaries are invoked through
++            // `ld.so`. In these cases, we want to present the user with the path to the Rust
++            // binary that was invoked.
++            //
++            // Because the ld.so wrappers _generally_ don't want to invoke things with absolute
++            // paths, this is _generally_ a path relative to dirname(ld.so).
++            if is_ld_so(&p) == Some(true) {
++                if let Some(relative_to_ld) = crate::env::var_os("LD_ARGV0_REL") {
++                    let relative_to_ld = PathBuf::from(relative_to_ld);
++                    if relative_to_ld.is_absolute() {
++                        return Ok(relative_to_ld);
++                    }
++                    // safety: is_ld_so checks the parent directory of `p`.
++                    return Ok(p.parent().unwrap().join(relative_to_ld));
++                }
++            }
++            Ok(p)
++        }
+     }
+ }
+ 
diff --git a/dev-lang/rust/rust-1.46.0.ebuild b/dev-lang/rust/rust-1.46.0-r1.ebuild
similarity index 99%
rename from dev-lang/rust/rust-1.46.0.ebuild
rename to dev-lang/rust/rust-1.46.0-r1.ebuild
index d02e97e..d5048f2 100644
--- a/dev-lang/rust/rust-1.46.0.ebuild
+++ b/dev-lang/rust/rust-1.46.0-r1.ebuild
@@ -57,6 +57,7 @@
 	"${FILESDIR}/${P}-sanitizer-supported.patch"
 	"${FILESDIR}/${P}-cc.patch"
 	"${FILESDIR}/${P}-revert-libunwind-build.patch"
+	"${FILESDIR}/${P}-ld-argv0.patch"
 )
 
 S="${WORKDIR}/${MY_P}-src"