Resolve symlinks continuously until we find the actual binary name.

When a guest executable is invoked through two or more levels of symlink,
FEX expands $ORIGIN in its DT_RPATH/DT_RUNPATH to the directory of the
intermediate symlink rather than the directory of the fully-resolved binary.
Libraries referenced relative to $ORIGIN then fail to load.

This PR fixes the issue by continuously chasing the symlinks until we find
the actual executable name.
This commit is contained in:
Ben Niu committed 2026-08-28 14:20:25 -07:00
1 parent 063af711af
commit 9742a560f5
1 file changed
+8 -7
+8 -7
View File
@@ -416,16 +416,17 @@ static fextl::string RecoverGuestProgramFilename(fextl::string Program, bool Exe
// Only in the case that FEX is executing an FD will the program argument potentially be a symlink.
// This symlink will be in the style of `/dev/fd/<FD>`.
//
// If the argument /is/ a symlink then resolve its path to get the original application name.
if (FHU::Symlinks::IsSymlink(Program)) {
// If the argument /is/ a symlink then resolve its path continuously until the original application name.
while (FHU::Symlinks::IsSymlink(Program)) {
char Filename[PATH_MAX];
auto SymlinkPath = FHU::Symlinks::ResolveSymlink(Program, Filename);
if (SymlinkPath.starts_with('/')) {
// This file was executed through an FD.
// Remove the ` (deleted)` text if the file was deleted after the fact.
// Otherwise just get the symlink without the deleted text.
return fextl::string {SymlinkPath.substr(0, SymlinkPath.rfind(" (deleted)"))};
if (!SymlinkPath.starts_with('/')) {
break;
}
// This file was executed through an FD.
// Remove the ` (deleted)` text if the file was deleted after the fact.
// Otherwise just get the symlink without the deleted text.
Program = fextl::string {SymlinkPath.substr(0, SymlinkPath.rfind(" (deleted)"))};
}
}
#endif