Bob Dylan
Bob Dylan

Reputation: 4463

How do you create a freestanding C++ program?

I'm just wondering how you create a freestanding program in C++?

Edit: By freestanding I mean a program that doesn't run in a hosted envrioment (eg. OS). I want my program to be the first program the computer loads, instead of the OS.

Upvotes: 17

Views: 10695

Answers (8)

Jerry Coffin
Jerry Coffin

Reputation: 490468

Legacy Systems

Even with your clarification, the answer is that it depends -- the exact boot sequence depends on the hardware -- though there's quite a bit of commonality. The boot loader is typically loaded at an absolute address, and the file it's contained in is frequently read into memory exactly as-is. This means instead of a normal linker, you typically use a "linking locator". Where a typical linker produces an executable file ready for relocation and loading, a locator produces an executable that's already set up to run at one exact address, with all relocations already applied. For those old enough to remember them, it's typically pretty much like an MS-DOS .COM file.

Along with that, it has to (of course) statically link the whole run-time that the program depends upon -- it can't depend on something like a DLL or shared object library, because the code to load either of those hasn't itself been loaded yet.

EFI/UEFI

Current PCs (and Macs) use EFI/UEFI. I'm going to just refer to UEFI throughout the remainder of this article, but most of it applies about equally to EFI as well (but UEFI is much more common).

These provide quite a bit more support for boot code. This includes drivers for most devices (it supports installing device drivers), so your boot code can use networking and such, which is much more difficult to support in legacy mode.

Bootable code under EFI uses the same PE format as Windows executables. Libraries are also available so quite a bit of boot code can be written much more like normal code that runs inside an OS. I won't try to get into a lot of detail, but here are links to some information.

https://www.intel.com/content/www/us/en/developer/articles/tool/unified-extensible-firmware-interface.html

https://www.intel.com/content/dam/doc/guide/uefi-driver-network-boot-devices-guide.pdf

https://www.intel.com/content/dam/www/public/us/en/documents/guides/bldk-v2-uefi-standard-based-guide.pdf

And perhaps the most important one--the development kit:

https://github.com/tianocore/edk2

Unikernel

To make this a bit easier and more practical, it's often easiest to start with what's called a "unikernel". This is basically a set of OS-like primitives implemented as a library, so when you link your program, the necessary OS primitives are linked directly into it, so it can boot on bare metal (or, often in a VM).

A couple of examples of this are IncludeOS and UniKraft. IncludeOS would/will generally require that you write new code specifically for it. UniKraft is much closer to a POSIX implementation, so it can run quite a bit of existing software (e.g., nginx, redis).

Upvotes: 6

NinjaDarth
NinjaDarth

Reputation: 393

Yes, of course. The ISO Standard for C and C++ support both hosted and free-standing environments, and the macro STDC_HOSTED is used to distinguish between the two. This has been the case since 2011.

Since most of the replies I see here are in the dark on the fact that this is all standard terminology (and is supposed to be common knowledge for C and C++ users), I'll recap, here, the distinction between the two, laid out by the ISO, and refer you to the following link for more details:

https://en.cppreference.com/w/cpp/freestanding

Hosted:
"main" is defined, and is started up in the main thread. The static objects may be constructed and destructed at the stand and end of the thread. Normally that means there is a host operating system (OS) that provides all the infrastructure required for an environment to run the program in. The POSIX standard, in particular, spells out a set of conditions expected for utilities that are called on a host system from the command-line; and the ISO C/C++ standards dovetail into each other and into the POSIX standard. (POSIX's support for C is at C99 at minimum.)

Free-Standing:
"main" may, but need not, be defined. Whether/how constructors are applied at start-up to static objects, and destructors to static objects on termination, depends on the implementation.

It's any environment where the routine made by the C or C++ program is not being called by some OS, but runs on its own. That's the typical case for embedded systems.

"Free-standing", of course, also includes OS's themselves, as a special case. An OS kernel is the epitome of a free-standing program. One of the respondents here actually went as far as to provide a link to a roll-your-own-OS kit for C++. In that vein, it would be an interesting exercise for you to test out the concept by rewriting the old 0.96 version of Linus' OS (which you can find in UNIX Archive) as a free-standing C++ program. His source code is actually filled with C++'isms (especially in the "fs" directory) that practically scream out "I'm a virtual function", "I'm a base class", "I'm a derived class" or "This is class inheritance"!

Libraries Required For Free-Standing Programs:
There are also minimum requirements on which of the standard libraries must be supported - and how much. Worthy of note: <new>, <exception> are mandatory, only partial support is required for <cstdlib> for start-up and termination, <atomic> (since 2011), <bit> and <coroutine> (both since 2020) are mandatory, as they had better be, if you're doing anything with embedded systems! <thread> is not mandatory.

A roll-your-own-OS kit would then provide templates for the mandatory libraries, and the required parts of the other semi-mandatory libraries. Any responsible embedded systems developer will be laying out their run-time system and environment, and compilers geared toward such users will be providing the under-the-box transparency needed to allow this to be done and to allow user-defined run-time systems to interface cleanly with the compiler.

Upvotes: 1

Evan Teran
Evan Teran

Reputation: 90503

See this page: http://wiki.osdev.org/C++

It has everything necessary to start writing an OS using c++ as the core language using the more popular toolchains.

In addition this page should prove to be very helpful: http://wiki.osdev.org/C++_Bare_Bones. It pretty much walks you through getting to the c++ entry point of an OS.

Upvotes: 7

PhilMY
PhilMY

Reputation: 2651

Have a look at this article:

http://www.codeproject.com/KB/tips/boot-loader.aspx

You would need a little assembly start-up code to get you as far as main() but then you could write the rest in C++. You'd have to write your own heap manager (new/delete) if you wanted to create objects at runtime and your own scheduler if you wanted more than one thread.

Upvotes: 13

gary
gary

Reputation: 584

If you were using BSD Unix, you would link with the standalone library. That included a basic IO system for disk and tty. Your source code looked the same as if it were to be run under Unix, but the binary could be loaded into a naked machine.

Upvotes: 1

MarkR
MarkR

Reputation: 63586

You will need an environment that provides:

  • A working C library, or enough of it to do what you want
  • The parts of the C++ runtime that you intend to use. This is compiler-specific

In addition to any other libraries. If you have no dynamic linker on your platform (if you have no OS, you probably have no linker) then you will have to static-link it all.

In practice this means linking some small C++ runtime, and a C library appropriate for your platform. Then you can simply write a standalone C++ program.

Upvotes: 2

Marsh Ray
Marsh Ray

Reputation: 2875

C++ is used in embedded systems programming, even to write OS kernels.

Usually you have at least a few assembler instructions early in the boot sequence. A few things are just easier to express that way, or there may be reference code from the CPU vendor you need to use.

For the initial boot process, you won't be able to use the standard library. No exceptions, RTII, new/delete. It's back to "C with classes". Most people just use C here.

Once you have enough supporting infrastructure loaded though, you can use whatever parts of the standard library you can port.

Upvotes: 2

pm100
pm100

Reputation: 50210

google 'embedded c++' for a start

Another idea is to start with the embedded systems emulators, for example the atmel AVR site has a nice IDE the emulates atmel AVR systems, and allows you to build raw code in C and load it into an emulated CPU, they use gcc as toolchain (I think)

Upvotes: 2

Related Questions