9

What is the difference between two empty programs main {} when compiled using a c/c++ compiler for target with OS (say linux) and without an OS (say for an embedded DSP target)? I am specifically interested to know what the compiler does differently when there is an OS and otherwise. How is the compiler/language runtime differ in two cases?

Shan
  • 5,054
  • 12
  • 44
  • 58

4 Answers4

12

It is actually the linker that does a different job when packaging a program to run on an operating system versus building a program that runs on the bare hardware alone. The compiler merely produces object files consisting of instructions targeted for the host architecture, and these chunks are later combined and packaged by the linker.

A program that is meant to run on an operating system has to have a certain binary structure -- this is where executable formats come into play. Such a format may dictate that the program should have a few header sections in the beginning and then the code should follow, for example. It is the job of the OS loader to interpret this structure and then feed the CPU with the stream of instructions that the code section contains.

In contrast, a program that is meant to run on the bare hardware usually doesn't have a special structure and can be fed directly to the CPU.

netcoder
  • 66,435
  • 19
  • 125
  • 142
Blagovest Buyukliev
  • 42,498
  • 14
  • 94
  • 130
  • 1
    Great fundamental question. This answer shifted the discussion focus (correctly IMO) away from compiler. Great references for more info 1) Balau's articles on Bare Metal (http://balau82.wordpress.com/2010/02/14/simplest-bare-metal-program-for-arm/). 2) "executable format" see Teensy ELF Executables for Linux (http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html) – Joe Kul Dec 30 '12 at 18:07
6

It is actually the linker that does a different job when packaging a program to run on an operating system versus building a program that runs on the bare hardware alone. The compiler merely produces object files consisting of instructions targeted for the host architecture, and these chunks are later combined and packaged by the linker.

A program that is meant to run on an operating system has to have a certain binary structure -- this is where executable formats come into play. Such a format may dictate that the program should have a few header sections in the beginning and then the code should follow, for example. It is the job of the OS loader to interpret this structure and then feed the CPU with stream of instructions that the code section contains.

In contrast, a program that is meant to run on the bare hardware usually doesn't have a special structure and can be feeded directly to the CPU.

I'd like to build on this very well written answer by Blagovest. Indeed as he suggests there's a difference of executable container formats and binary interfaces and whatnot. However, possibly the biggest difference is the actual main entry point to the execution of the application code as well as the presence of a startup code along with a runtime library; although, if you know what you're doing you can avoid linking against the latter on a full-fledged OS as well.

Oftentimes with the presence of startup routines, a runtime library, such as crt0, the actual entry point of your application is not main but something else (usually _start). Before this actual entry point hands the control down to your main it might perform a bunch of very specific tasks, usually related to initialisation.

There's always Wikipedia for more information on crt0.

However, on a bare-metal platform there might not be such routines that come bundled with your compiler. As a result the control might be handed down straight to your main and the first code that's to execute on the platform would be yours.

There you go, this is the most fundamental difference between the two kinds of mains. However, I have to say your question is a bit vague as you can do away without a startup script if you initialise the stack etc. yourself and you can also do with a runtime library that does all of these on some (most?) bare-metal platforms. In fact this all depends on your compiler suite, the platform you're targeting, etc.

3

I'm tempted to say that there is no difference. What a compiler (or the translation system) does is largely implementation dependent; a translation system is likely to do different things for Windows than for Linux, even though both qualify as OSs.

The main difference is whether the implementation is hosted or not. If it is not hosted, then it might not even support main, or if it does, it might not support main with arguments. What a non-hosted implementation does or requires for startup is implementation defined. While there is also a lot of "implementation defined" with regards to start-up in a hosted environment, the implementation is required to support main, returning int and with at least two signatures, and the application is required to provide such a function.

Note that both in the past and today, many implementations which you would think of as hosted really aren't for various reasons. In the past, for example, g++ documented itself as non-hosted (at least gcc did) because they had no control over and couldn't guarantee the library parts of the implementation. And even today, Microsoft's C++ can only be considered hosted when generating console applications; Windows applications don't have a main.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
0

@BlagovestBuyukliev has provided a well-written answer.

I'd like to expand it a bit - No OS actually means no software-implemented OS. Piece of HW capable of executing a binary code also has a protocol that handles the loading of the program and feeding it into the CPU and all the rest of low-level details. In this case the "OS" is actually present as a HW implemented one. From this point on the differences between it and standard OS are not principal but technical, as far as the binary code execution considered.

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85