Why a Program Runs on Linux but Fails on Windows: Compilation, Linking, and ELF Explained
This article explains why the same program can execute on Linux yet not run on Windows by breaking down the compilation, assembly, linking stages, the ELF executable format, and how loaders and symbol tables make cross‑platform execution possible.
Background
Even though a program ultimately becomes machine code, the same binary may run on Linux but not on Windows, and vice‑versa, because the executable formats and loading mechanisms differ.
Compilation, Assembly, and Linking
A C source file is first compiled into assembly, then assembled into object files containing machine code. These object files ( add_lib.o and link_example.o) are not executable by themselves; they lack the necessary address resolution.
Using gcc -c add_lib.c and gcc -c link_example.c produces the object files, and objdump -d -M intel -S link_example.o can display their disassembly.
Attempting to run an object file directly (e.g., ./link_example.o) results in a “Permission denied” or “cannot execute binary file: Exec format error” because the file lacks an executable header and proper address mapping.
Linker Role
The linker combines multiple object files and libraries, resolves symbols, and produces a single executable. The -o option of gcc creates the final binary, which can then be executed to obtain the expected result.
ELF File Format
On Linux, both executable and object files use the ELF (Executable and Linkable Format). ELF files contain sections such as:
.text : code segment
.data : initialized data
.rel.text : relocation entries for unresolved addresses
.symtab : symbol table mapping names to addresses
The linker builds a global symbol table from all input object files, applies relocations using the relocation table, and merges sections into a final executable where function calls point to correct addresses.
Loader
After linking, the loader reads the ELF header, loads the appropriate sections into memory, and the CPU begins execution. Because the loader understands ELF, it cannot load Windows PE (Portable Executable) binaries.
Cross‑Platform Execution
To run Windows programs on Linux, a loader that understands PE is required. The open‑source project Wine provides such a compatibility layer. Conversely, Windows Subsystem for Linux (WSL) adds an ELF loader to Windows, allowing Linux binaries to run.
Conclusion
The key reason a program runs on Linux but not on Windows is the differing executable formats (ELF vs. PE) and the corresponding loaders. Understanding the compilation, linking, and loading pipeline clarifies how static linking, symbol tables, and relocation enable correct execution.
Recommended Reading
For a deeper dive into linking and ELF, see chapters 1‑4 of “Programmer’s Self‑Cultivation: Linking, Loading, and Libraries”.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
JavaEdge
First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
