Gemini: In a nutshell, the reason these projects exist—and have to perform complex gymnastics to make portable Linux applications—comes down to a fundamental difference in how Linux and Windows designed their executable formats and dynamic linkers.
The short answer is yes, this is primarily a Linux (and Unix-like) restriction. Windows handles dynamic linking completely differently, which makes true “portable apps” natively trivial on Windows but highly complex on Linux.
On Linux, executables are compiled into the ELF (Executable and Linkable Format). When you run an ELF binary, the Linux kernel itself does very little. Instead, it looks inside a specific section of the binary (the .interp section) to find the dynamic linker/interpreter.
The Hardcoded Interpreter: The path to this interpreter is physically hardcoded into the binary—usually something like /lib64/ld-linux-x86-64.so.2.
System-wide Dependencies: Once the kernel hands control to ld-linux.so, that linker looks for shared libraries (.so files) in global system directories like /lib and /usr/lib.
The result: If you move that binary to a system that doesn’t have the interpreter at that exact absolute path (like NixOS or a system using musl instead of glibc), the kernel throws a confusing “No such file or directory” error—even though the executable file is sitting right in front of you.
How Windows Does It (The Contrast)
Windows uses the PE (Portable Executable) format. It handles loading dependencies fundamentally differently:
No External Interpreter: Windows doesn’t hardcode a path to an external userspace program to load dependencies. The OS loader is built deeply into the Windows kernel and system libraries (ntdll.dll). It inherently knows how to parse and load an .exe.
Local Directory First: When a Windows .exe asks for a .dll, the Windows loader follows a strict search order. It always searches the application’s own directory first before falling back to system folders (System32) or the PATH environment variable.
The result: On Windows, you can just dump an .exe and all its required .dll files into a single folder, move that folder to any Windows machine, and it will run perfectly.
What Anylinux-AppImages and Brioche Actually Do
Because Linux doesn’t natively support this “just put everything in a folder” philosophy, projects like Brioche and Anylinux-AppImage have to hack around the ELF format limitations. To make an app truly portable across any Linux distro, they must:
Bundle the Linker: They bundle a copy of the ld-linux.so dynamic linker inside the package alongside all the .so dependencies.
Bypass the Hardcoded Path: Because they can’t rely on the host system’s interpreter path, they write wrapper scripts that execute their bundled dynamic linker first, passing the target executable as an argument (e.g., executing exec ./ld-linux.so ./my_app instead of ./my_app).
Force Local Search: They pass specific flags (like --library-path) or use RPATH/$ORIGIN to force the bundled dynamic linker to load dependencies from the local folder rather than the host system’s /usr/lib.
Fix the Quirks: Running an app via an explicit linker introduces weird bugs. For example, if the app asks the OS “what is my filepath?” (by reading /proc/self/exe), the OS will reply with the path to the linker, not the app. Both projects have to include elaborate hooks and hacks to trick the application into seeing its correct identity and namespace.
Ultimately, these tools are recreating the native Windows “Application Directory” behavior in userspace on Linux, dragging along their own standard C library (glibc) to ensure they aren’t tripped up by the host operating system.
Ludditus: So, even if today’s Windows 11 is bloated and many people hate it, it still includes some smart, practical design decisions that probably date from the Win95 or even Win3.1 era?
Gemini: Underneath the modern layers of AppX packages, telemetry, and forced Edge integrations, the core execution model of Windows 11 relies on pragmatic architectural decisions forged during the MS-DOS and Windows 3.1 eras. That “app directory first” loading order is a direct evolutionary descendant of how early software had to be distributed.
Back in the early 1990s, compiling C/C++ applications for Win16 meant dealing with strict memory segments and limited hardware, but deployment was remarkably straightforward. Because software shipped on floppy disks without a centralized package repository, an application had to be entirely self-contained. The OS was explicitly designed to check the .exe‘s immediate directory for custom .dll files before falling back to C:\WINDOWS\SYSTEM. This philosophy of local isolation survived the transition to Win32 and remains the reason why portable apps work seamlessly on Windows today.
Beyond the DLL search order, modern Windows retains several other brilliant design choices from that era:
Ruthless API Stability: The Win32 API is one of the most stable interfaces in computing history. An application compiled for Windows 95 will very likely execute perfectly on Windows 11 today. The OS goes to extreme lengths to spoof older environments for legacy applications rather than breaking their execution.
WoW64 Subsystem: When transitioning to 64-bit architecture, Microsoft built “Windows 32-bit on Windows 64-bit.” Instead of forcing users to manually manage multi-architecture libraries (a common headache when maintaining lean Linux desktop environments), WoW64 silently catches 32-bit system calls and translates them to 64-bit equivalents. The user never notices.
WinSxS (Side-by-Side Assemblies): When the “app directory first” rule was ignored by developers who greedily dumped shared libraries into the global System32 folder, it caused “DLL Hell” (where installing one app broke another by overwriting a shared dependency). Microsoft solved this by doubling down on isolation, allowing multiple versions of the exact same DLL to live on the disk simultaneously, with applications declaring exactly which version they need via XML manifests.
While the modern Windows user space has grown heavy, the foundational NT kernel and the PE binary loader are masterclasses in backward compatibility and deployment simplicity.
This blog uses technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent will adversely affect certain features and functions.
Functional
Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
Leave a Reply