Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we need to compile for different platforms (e.g. Windows/Linux)?

I've learned the basics about CPUs/ASM/C and don't understand why we need to compile C code differently for different OS targets. What the compiler does is create Assembler code that then gets assembled to binary machine code. The ASM code of course is different per CPU architecture (e.g. ARM) as the instruction set architecture is different.

But as Linux and Windows run on the same CPU, the machine operations like MOVE/ADD/... should be identical. While I do know that there are OS-specific functions like printing to a terminal, this functionality could be provided by different implementations of stdio.h, for example. And still, I could create a very basic program that just calculates a + b without printing anything, so that I do not need any OS-specific code. Why do I still need to compile for Linux and for Windows instead of just adding an .exe-Extension to my Linux executable?

like image 527
Maxbit Avatar asked Jan 12 '18 23:01

Maxbit


People also ask

Why do we need to compile the program?

Compiling allows the computer to run and understand the program without the need of the programming software used to create it. When a program is compiled it is often compiled for a specific platform (e.g., IBM platform) that works with IBM compatible computers, but not other platforms (e.g., Apple platform).

What is the use of compiler in Linux?

A compiler is a specialized computer program that converts source code written in one programming language into another language, usually machine language (also called machine code) so that it can be understood by processors (i.e., logic chips).

Why do we need to compile C++?

Because computer architecture is made up of electronic switches and cables that can only work with binary 1s and 0s, you need a compiler to translate your code from high level C++ to machine language that the CPU can understand.


2 Answers

Even though CPU is the same, there are still many differences:

  • Different executable formats.
  • Different calling conventions might be used. For example Windows x64 passes integer args in different registers than the x86-64 System V ABI and has several other significant differences, including call-preserved xmm6..15 in Windows, unlike other x86-64.
  • Different conventions regarding stack structure. Some systems have a concept of "red zone" to help compiler generate shorter code. Execution environment has to honor such concept to avoid stack corruption.
  • Programs are linked against different standard libraries with different ABIs - field order might differ, additional extension fields might be present.
  • In both C and C++ some data types have OS dependent sizes. For example on x86_64 long is 8 byte on Linux, but 4 bytes on Windows. (Type sizes and required alignments are another part of what makes an ABI, along with struct/class layout rules.)
  • Standard libraries can provide different set of functions. On Linux libc provide functions like snprintf directly, but on Windows snprintf might be implemented as static inline function in a header file that actually calls another function from C runtime. This is transparent for programmer, but generates different import list for executable.
  • Programs interact with OS in a different way: on Linux program might do system call directly as those are documented and are a part of provided interface, while on Windows they are not documented and programs should instead use provided functions.
  • Even if two OS rely on program doing system calls directly, each kernel has its own set of available system calls.

Even if a Linux program only calls the C library's wrapper functions, a Windows C library wouldn't have POSIX functions like read(), ioctl(), and mmap. Conversely, a Windows program might call VirtualAlloc which isn't available on Linux. (But programs that use OS-specific system calls, not just ISO C/C++ functions, aren't portable even at a source level; they need #ifdef to use Windows system calls only on Windows.)

  • Not OS related, but programs compiled by different compilers might not be interoperable: different standard libraries might be used, things like C++ name mangling might be different, making it impossible to link libraries against each other, C++ exception implementation might be non-interoperable.
  • Different filesystem structure. Not only there is a difference between "" on Windows and "/" on Unix-likes, but there are "special files" that might or might not be present like "/dev/null".

In theory everything listed here can be resolved: custom loaders can be written to support different executable formats, different conventions and interfaces do not cause problems if the whole program uses the same set of them. This is why projects like Wine can run Windows binaries on Linux. The problem is that Wine has to emulate functionality of Windows NT kernel on top of what other OSes provide, making implementation less efficient. Such program also have problems interacting with native programs as different non-interoperable interfaces are used.

Source-compatibility layers like Cygwin can be inefficient, too, when emulating POSIX system calls like fork() on top of the Windows model. But in general Cygwin has an easier job than WINE: programs need to be recompiled under Cygwin. It doesn't try to run native Linux binaries under Windows.

like image 183
StaceyGirl Avatar answered Nov 15 '22 22:11

StaceyGirl


In addition to everything else even with identical instructions even the calling conventions can differ, that is the placement of parameters on the stack or in registers, the order parameters are found, what registers must be preserved across a function call, how return values are passed from callee to caller.

like image 20
SoronelHaetir Avatar answered Nov 15 '22 23:11

SoronelHaetir