diff --git a/src/dist/component/package.rs b/src/dist/component/package.rs
index dc545d4b62..4dd8ade86e 100644
--- a/src/dist/component/package.rs
+++ b/src/dist/component/package.rs
@@ -130,6 +130,7 @@
                     } else {
                         builder.move_file(path.clone(), &src_path)?
                     }
+                    nix_patchelf_if_needed(&target.prefix().path().join(path.clone()))
                 }
                 ComponentPartKind::Dir => {
                     if self.copy {
@@ -152,6 +153,176 @@
     }
 }
 
+fn nix_wrap_lld(dest_lld_path: &Path) -> Result<()> {
+    use std::fs;
+    use std::io::Write;
+    use std::os::unix::fs::PermissionsExt;
+
+    let path = dest_lld_path.parent().unwrap();
+    let mut unwrapped_name = path.file_name().unwrap().to_string_lossy().to_string();
+    unwrapped_name.push_str("-unwrapped");
+    let unwrapped_dir = path.with_file_name(unwrapped_name);
+    fs::create_dir_all(&unwrapped_dir).context("failed to create unwrapped directory")?;
+    let mut unwrapped_lld = unwrapped_dir;
+    unwrapped_lld.push(dest_lld_path.file_name().unwrap());
+    fs::rename(dest_lld_path, &unwrapped_lld).context("failed to move file")?;
+    let mut ld_wrapper_path = std::env::current_exe()?
+        .parent()
+        .ok_or(anyhow!("failed to get parent directory"))?
+        .with_file_name("nix-support");
+    let mut file = fs::File::create(dest_lld_path)?;
+    ld_wrapper_path.push("ld-wrapper.sh");
+
+    let wrapped_script = format!(
+        "#!/usr/bin/env bash
+set -eu -o pipefail +o posix
+shopt -s nullglob
+export PROG=\"{}\"
+\"{}\" \"$@\"",
+        unwrapped_lld.to_string_lossy().to_string(),
+        ld_wrapper_path.to_string_lossy().to_string(),
+    );
+    file.write_all(wrapped_script.as_bytes())?;
+    let mut permissions = file.metadata()?.permissions();
+    permissions.set_mode(0o755);
+    file.set_permissions(permissions)?;
+    Ok(())
+}
+
+fn nix_patchelf_if_needed(dest_path: &Path) {
+    use std::fs::File;
+    use std::os::unix::fs::FileExt;
+    use tracing::{debug, warn};
+
+    struct ELFReader<'a> {
+        file: &'a mut File,
+        is_32bit: bool,
+        is_little_end: bool,
+    }
+
+    impl<'a> ELFReader<'a> {
+        const MAGIC_NUMBER: &'static [u8] = &[0x7F, 0x45, 0x4c, 0x46];
+        const ET_EXEC: u16 = 0x2;
+        const ET_DYN: u16 = 0x3;
+        const PT_INTERP: u32 = 0x3;
+
+        fn new(file: &'a mut File) -> Option<Self> {
+            let mut magic_number = [0; 4];
+            file.read_exact(&mut magic_number).ok()?;
+            if Self::MAGIC_NUMBER != magic_number {
+                return None;
+            }
+            let mut ei_class = [0; 1];
+            file.read_exact_at(&mut ei_class, 0x4).ok()?;
+            let is_32bit = ei_class[0] == 1;
+            let mut ei_data = [0; 1];
+            file.read_exact_at(&mut ei_data, 0x5).ok()?;
+            let is_little_end = ei_data[0] == 1;
+            Some(Self {
+                file,
+                is_32bit,
+                is_little_end,
+            })
+        }
+
+        fn is_exec_or_dyn(&self) -> bool {
+            let e_type = self.read_u16_at(0x10);
+            e_type == Self::ET_EXEC || e_type == Self::ET_DYN
+        }
+
+        fn e_phoff(&self) -> u64 {
+            if self.is_32bit {
+                self.read_u32_at(0x1C) as u64
+            } else {
+                self.read_u64_at(0x20)
+            }
+        }
+
+        fn e_phentsize(&self) -> u64 {
+            let offset = if self.is_32bit { 0x2A } else { 0x36 };
+            self.read_u16_at(offset) as u64
+        }
+
+        fn e_phnum(&self) -> u64 {
+            let offset = if self.is_32bit { 0x2C } else { 0x38 };
+            self.read_u16_at(offset) as u64
+        }
+
+        fn has_interp(&self) -> bool {
+            let e_phoff = self.e_phoff();
+            let e_phentsize = self.e_phentsize();
+            let e_phnum = self.e_phnum();
+            for i in 0..e_phnum {
+                let p_type = self.read_u32_at(e_phoff + i * e_phentsize);
+                if p_type == Self::PT_INTERP {
+                    return true;
+                }
+            }
+            false
+        }
+
+        fn read_u16_at(&self, offset: u64) -> u16 {
+            let mut data = [0; 2];
+            self.file.read_exact_at(&mut data, offset).unwrap();
+            if self.is_little_end {
+                u16::from_le_bytes(data)
+            } else {
+                u16::from_be_bytes(data)
+            }
+        }
+
+        fn read_u32_at(&self, offset: u64) -> u32 {
+            let mut data = [0; 4];
+            self.file.read_exact_at(&mut data, offset).unwrap();
+            if self.is_little_end {
+                u32::from_le_bytes(data)
+            } else {
+                u32::from_be_bytes(data)
+            }
+        }
+
+        fn read_u64_at(&self, offset: u64) -> u64 {
+            let mut data = [0; 8];
+            self.file.read_exact_at(&mut data, offset).unwrap();
+            if self.is_little_end {
+                u64::from_le_bytes(data)
+            } else {
+                u64::from_be_bytes(data)
+            }
+        }
+    }
+
+    let Some(mut dest_file) = File::open(dest_path).ok() else {
+        return;
+    };
+    let Some(elf) = ELFReader::new(&mut dest_file) else {
+        return;
+    };
+    if !elf.is_exec_or_dyn() {
+        return;
+    }
+    let mut patch_command = std::process::Command::new("@patchelf@/bin/patchelf");
+    if elf.has_interp() {
+        patch_command
+            .arg("--set-interpreter")
+            .arg("@dynamicLinker@");
+    }
+    if Some(std::ffi::OsStr::new("rust-lld")) == dest_path.file_name() || !elf.has_interp() {
+        patch_command.arg("--add-rpath").arg("@libPath@");
+    }
+
+    debug!("patching {dest_path:?} using patchelf");
+    if let Err(err) = patch_command.arg(dest_path).output() {
+        warn!("failed to execute patchelf: {err:?}");
+    }
+
+    if Some(std::ffi::OsStr::new("ld.lld")) == dest_path.file_name() {
+        if let Err(err) = nix_wrap_lld(dest_path) {
+            warn!("failed to wrap `ld.lld`: {err:?}");
+        }
+    }
+}
+
 /// Handle the async result of io operations
 /// Replaces op.result with Ok(())
 fn filter_result(op: &mut CompletedIo) -> io::Result<()> {
