Fundamentals 10 min read

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.

JavaEdge
JavaEdge
JavaEdge
Why a Program Runs on Linux but Fails on Windows: Compilation, Linking, and ELF Explained

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”.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CompilationLinuxELFlinkingloaderExecutable
JavaEdge
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.