Can an x86 executable run on any x86 platform given the right runtime libraries? - c++

While I did find similar-ish questions, they did not really answer this specific question.
Can a compiled x86 executable run on any x86 platform given the right runtime libraries?
Say I make a C++17 program without dependencies, could I run this program on Windows 95 or is there some sort of support required by the OS?
I also heard that RTTI (in the case of C++) may not be supported everywhere, is this only due to the processor having to support this feature or does the OS play a role in that? This would imply that new features would maybe not be supported by, e.g., Windows 95.
Edit
What I'm after is whether an executable (e.g., x86) can run on any platform supporting that instruction set or wether certain features, like RTTI, need specific OS support and thus are not available on all platforms supporting that instruction set.

In general you cannot, even if you restricted your universe to x86 hardware - at least not without some conversion of the binary or some platform-specific "loader" for each target platform.
For exmaple a typical binary emitted by a C or C++ compiler1 will have some minimal dependency on the OS and runtime, for example to load and do runtime linking on the executable. Different platforms have different binary formats (such as PE/COFF on Windows or ELF across various UNIX flavors and Linux) and there isn't any common "x86 format" that would work directly on any platform.
Furthermore, any non-trivial program and in many cases any program, trivial or not, is going to have platform-specific dependencies on the the langauge runtime. For example, even an empty main() function often requires runtime support to get from the OS-defined "start" method to the main method, and without unusual build options there are often calls at startup to initialize parts of the standard library.
Finally, as you alluded to with your comment about RTTI, various language or platform features may essentially be compiled into the binary and require OS support. RTTI probably doesn't obviously fall into this category, but things like position-independent code, thread-local storage and stack-unwinding support for exception handling often do. The compiled x86 code that uses such features may be quite different on different platforms since it needs to build in assumptions of how those work.
In principle, however, you could imagine this working, at least for some limited subset of programs. For example, while the various executable formats are in practice incompatible, they aren't that different and tools exist to convert between them. So you could certainly implement a minimal runtime on your platform of interest that takes an x86 executable compiled to whatever fixed format you choose and converts at runtime to the local format and runs it.
Beyond that actually trying to map even standard library calls would be quite difficult since different operating systems using different calling conventions, but it could be possible for "C" functions using some thunks to put things in the right place. C++ is pretty much right out because the ABI there is much more complex, compiler-and-platform specific and much of the implementation detail is already compiled-in for stuff implemented in headers.
In fact, the idea that (a subset of) x86 might provide a interesting intermediate language for cross-platform execution is exactly the idea behind exploited in Google's [NaCl project]. Essentially, the NaCl runtime provides platform agnostic "loading" capabilities which allow x86 code to run more-or-less natively on various platforms. Subsequently other native formats such as ARM were added, but it started as an x86 sandbox. A large part of the project deals with running code that provably safe (i.e., sandboxed) - but it shows that with some infrastructure you can write "portable" x86. A standard C or C++ compiler isn't going to emit NaCl compatible code directly, however.
1 Really, any compiler that compiles to a native format. I just call out C and C++ since they seem like the ones you are interested in and are widely familiar.

This question misses the point. C++ is, first and foremost, a language to describe the behaviour of a computer program.
Using a compiler to create a native binary executable file to produce that behaviour on an actual computer is the typical way of using the language.
Once you have the binary file, all traces of the source code used to produce it are gone (unless you have built a special version for debugging purposes). The compatibility of the binary file with specific hardware or operating systems is beyond the scope of C++ itself.
The same is true for C, or any other programming language which typically gets compiled to native binary code.
Or, to answer the question more briefly:
Can compiled C++/C code (i.e. an executable) run anywhere given the right runtime libraries?
No.

Can a compiled x86 executable run anywhere given the right runtime libraries?
No, it will only work on x86 hardware, or other hardware (or software, such as a virtual machine) that emulates the x86 instruction set (such as a x64 CPU). In practice, that's very likely to be a far cry from "anywhere."
And even if the hardware matches, an x86 executable will have operating system dependencies. A Windows binary won't run on Linux, even if the hardware is the same. There are various strategies that can make things like this "work" in some situations, Microsoft's Linux Subsystem for Windows is one recent example which allows Linux binaries to run unchanged on Windows. Again, a fry cry from "anywhere."

Related

Are C++ applications cross-platform?

One of the first things I learned as a student was that C++ applications don't run on different operating systems. Recently, I read that Qt based C++ applications run everywhere. So, what is going on? Are C++ applications cross-platform or not?
Source code compatible. If I compile the source code, will it run everywhere?
API/ABI compatibility. Does the OS provide the interface to its components in a way that the code will understand?
Binary compatibility. Is the code capable of running on the target host?
Source code compatible
C++ is a standard which defines how structures, memory, files can be read and written.
#include <iostream>
int main( int argc, char ** argv )
{
std::cout << "Hello World" << std::endl;
}
Code written to process data (e.g. grep, awk, sed) is generally cross-platform.
When you want to interact with the user, modern operating systems have a GUI, these are not cross-platform, and cause code to be written for a specific platform.
Libraries such as qt or wxWidgets have implementations for multiple platforms and allow you to program for qt instead of Windows or iOS, with the result being compatible with both.
The problem with these anonymizing libraries, is they take some of the specific benefits of platform X away in the interest of uniformity across platforms.
Examples of this would be on Windows using the WaitForMultipleObjects function, which allows you to wait for different types of events to occur, or the fork function on UNIX, which allows two copies of your process to be running with significant shared state. In the UI, the forms look and behave slightly different (e.g. color-picker, maximize, minimize, the ability to track mouse outside of your window, the behaviour of gestures).
When the work you need to be done is important to you, then you may end up wanting to write platform specific code to leverage the advantages of the specific application.
The C library sqlite is broadly cross-platform code, but its low-level IO is platform specific, so it can make guarantees for database integrity (that the data is really written to disk).
So libraries such as Qt do work, they may produce results which are unsatisfactory, and you end up having to write native code.
API/ABI compatibility
Different releases of UNIX and Windows have some form of compatibility with each other. These allow a binary built for one version of the OS to run on other versions of the OS.
In UNIX the choice of your build machine defines the compatibility. The lowest OS revision you wish to support should be your build machine, and it will produce binaries compatible with subsequent minor versions until they make a breaking change (deprecate a library).
On Windows and Mac OS X, you choose an SDK which allows you to target a set of OS's with the same issues with breaking changes.
On Linux, each kernel revision is ABI incompatible with any other, and kernel modules need to be re-compiled for each kernel revision.
Binary compatibility
This is the ability of the CPU to understand the code. This is more complex than you might think, as the x64 chips, can be capable (depending on OS support) of running x86 code.
Typically a C++ program is packaged inside a container (PE executable, ELF format) which is used by the operating system to unpack the sections of code and data and to load libraries. This makes the final program have both binary (type of code) and API (format of the container) forms of incompatibilities.
Also today if you compile a x86 Windows Application (targeting Windows 7 on Visual Studio 2015), then the code may fail to execute if the processor does not have SSE2 instructions (about 10 years old CPU).
Finally when Apple changed from PowerPC to x86, they provided an emulation layer which allowed the old PowerPC code to run in an emulator on the x86 platform.
So in general binary incompatibility is a murky area. It would be possible to produce an OS which identified invalid instructions (e.g. SSE2) and in the fault, emulated the behaviour, this could be updated as new features come out, and keeps your code running, even though it is binary incompatible.
Even if your platform is incapable of running a form of instruction set, it could be emulated and behave compatibly.
Standard C++ is cross platform in the "write once, compile anywhere" sense, but not in the "compile once, run anywhere" sense.
That means that if you write a program in standard C++, you can compile and then run it on any target environment that has a standard conforming implementation of C++.
You can however not compile your program on your machine, ship the binary and then expect it to work on other targets. (At least not in general. One can of course distribute binaries from C++ code under certain conditions, but those depend on the actual target. This is a broad field.)
Of course, if you use extra, non-standard features like gcc's variable length arrays or third party libraries, you can only compile on systems that provide those extensions and libraries.
Some libraries like Qt and Boost are available on many systems (those two on Linux, Mac and Windows at least I believe), so your code will stay cross platform if you use those.
You can achieve that your source compiles on various platforms, giving you various binaries from the same source base.
This is not "compile once, run anywhere with an appropriate VM" as Java or C# do it, but "write once, compile anywhere with an appropriate environment" the way C has done it all the time.
Since the standard library does not provide everything you might need, you have to look for third-party libraries to provide that functionality. Certain frameworks -- like Boost, Qt, GTK+, wxWidgets etc. -- can provide that. Since these frameworks are written in a way that they compile on different platforms, you can achieve cross-platform functionality in the aforementioned sense.
There are various things to be aware of if you want your C++ code to be cross-platform.
The obvious thing is source that makes assumption on data types. Your long might be 32bit here and 64bit there. Data type alignment and struct padding might differ. There are ways to "play it safe" here, like size_t / size_type / uint16_t typedefs etc., and ways to get it wrong, like wchar_t and std::wstring. It takes discipline and some experience to "get it right".
Not all compilers are created equal. You cannot use all the latest C++ language features, or use libraries that rely on those features, if you require your source to compile on other C++ compilers. Check the compatibility chart first.
Another thing is endianess. Just one example, when you're writing a stream of integers to file on one platform (say, x86 or x86_64), and then read it back again on a different platform (say, POWER), you can run into problems. Why would you write integers to file? Well, UTF-16 is integers... again, discipline and some experience go a long way toward making this rather painless.
Once you've checked all those boxes, you need to make sure of the availability of the libraries you base your code on. While std:: is safe (but see "not all compilers are created equal" above), something as innocent as boost:: can become a problem if you're looking beyond the mainstream. (I helped the Boost guys to fix one or two showstoppers regarding AIX / Visual Age in past years simply because they didn't have access to that platform for testing new releases...)
Oh, and watch out for the various licensing schemes out there. Some frameworks that improve your cross-platform capabilities -- like Qt or Cygwin -- have their strings attached. That is not to say they are not a big help in the right circumstances, just that you need to be aware of copyleft / proprietary licensing requirements.
All that being said, there is Wine ("Wine is not emulation"), which makes executables compiled for Windows run on a variety of Unix-alike systems (Linux, OS X, *BSD, Solaris). There are certain limits to its capabilities, but it's getting better all the time.
Yes. No. Maybe. What is cross-platform C++ code? Cross-platform C++ code is such a code that can be compiled under different operation systems without the need to be modified.
That means, if you explicitly use any platform-dependant headers, your code is no longer cross-platform. Qt solves this problem in the following way: they provide wrappers for everything that is platform-specific. For example, imagine that you are using QFile to open/read/write a file. Your code looks like
QFile file(filename);
file.open(QFile::ReadOnly);
//other stuff
You can compile this code under any OS as long as you have a suitable compiler and Qt libraries for that OS. The code hidden under QFile will use the OS-appropriate file-handling functions, but that shouldn't concern you.
Also, if you only use the standard library, your code can be compiled anywhere where a C++ compiler is present.
The already-compiled applications, however, are not cross-platform in a way that, say, Java applications are - for instance, you can't compile an app for Windows and then run in in Linux, you will have to recompile your code under Linux instead.
C++ is cross-platform. You can use it to build applications that will run on many different operating systems.
What is not cross-platform is the compilers that translate C++ into object code. No single compiler, to my knowledge, has all the necessary features so that when you use it to compile a C++ program, it will automatically run on Windows, Linux and Mac OS.
Qt Creator is integrated with multiple compilers and has build automation. It makes it easy to switch between different setups and target platforms. It provides support for building, running and deploying C++ applications not only for desktop environments but also for mobile devices.
C++ is a programming language. Text. As such, it doesn't run anywhere.
Conforming Standard C++ code is expected to behave equally on any platform; "cross-platform" if you want. Writing (strictly) conforming C++ code requires pedantry because some assumptions often made have dependencies on details that are final to the actual implementation and this is inherited from the targets C++ itself aims to.
Notice we're still talking about C++ code, not C++ programs. Indeed, when we pass to term "program", we've no more guarantees because we aren't talking about C++ anymore; rather, the output of the compiler. This is where portability begins to fade away: executable format, ISA, ABI, low-level routines and so on.
Can you rely on that? If you can't, then you need to integrate your C++ program in the environment it will run on, by recompiling it or using platform-specific elements.

C++ What determines which version of C++ can run on a specific architecture (like Arduino)

I know there are slight changes to C++ like C++11 or C++14. If I have a microcontroller or other computer device, what is it that determines if the code can be run on that computer. I.e. what determines if the Arduino can run C++14 code or not?
Is it the compiler on my machine, the interpreter on the other system's processor or what?
It's the compiler's version. If the compiler supports the syntax/C++ version and if the compiler is suitable for the platform - then a valid code will be produced.
#Kiril Kirov's answer is correct, it depends mostly on compiler availability, but some other elements are at stake.
The compiler is responsible for transforming C++ code to machine code in the native instruction set.
It also relies on the c++ standard library which obviously needs to be compilable for your system using said compiler.
Note that after this operation the produced code is not essentially different from other native code produced by other means (using a C compiler or written by hand), so there's no reason it wouldn't be executed by your microprocessor.
You also need a linker which knows the memory layout of the target microcontroller (processor+RAM+flash memory or ROM).
You also need a way to flash the code to your system, such as USB link and drivers.
In arduino's case, you can find all these elements readily available because it is a known platform (Arduino runs on AVR or ARM depending on the version, so possible compilers would be respectively avr-gcc or arm-none-eabi-gcc), but in more exotic cases it isn't a given (chances are that you cannot flash your Mastercard).
Any computer platform can in principle support any arbitrary programming language as long as someone has written a compiler for it.
The processor itself is agnostic to which programming language is used, but even if it weren't that wouldn't necessarily rule out support for other languages or dialects in the compiler via source code translation.
Whether you get new C++ standards support on your Arduino is completely at the whim of the people who are providing the compiler toolchain and standard library you are using.

How can I make a portable executable?

It's there a way to compile a c/c++ source file to output a .exe file that can be run on other processors on different computers ?
I am asking this for windows platform.
I know it can be done with java or c# , but it uses virtual machine.
PS: For those who said that it can be done just with virtual machines or the source cod must be compiled on every machine , i am asking if all viruses are written in java or c# and you need a vm machine to be infected or you need to compile source cod of worm on your machine to be infected ? (i am not trying to make a virus, but is a good example :) )
Different computers use different instruction sets, OS system calls, etc., which is why machine code is platform specific. This is why various technologies like byte code, virtual machines, etc., have been developed to allow portable executables. However, generally C/C++ compiles directly to platform-specific machine code.
So a Windows exe simply won't run on another platform without some kind of emulation layer.
However, you can make your C/C++ source code portable. This means all you need to do to make your application run on another platform is to compile it for that platform.
Yes, you can, but it's not necessarily a good idea.
Apple introduced the idea of a fat binary when they were migrating from the Motorola 68000 to the PowerPC chips back in the early 90s (I'm not saying they invented it, that's just the earliest incarnation I know of). That link also describes the FatELF Linux universal binaries but, given how little we hear about them, they don't seem to have taken off.
This was a single file which basically contained both the 68000 and PowerPc executables bundled into one single file and required some smarts from the operating system so it could load and execute the relevant one.
You could, if you were so inclined, write a compiler which produced a fat binary that would run on a great many platforms but:
it would be hideously large; and
it would almost certainly require special loaders on each target system.
Since gcc has such a huge amount of support for different platforms and cross-compiling, it would be where I would concentrate the effort, were I mad enough to try :-)
The short answer is - you can't. Longer answer is write in portable (standard) C/C++ and compile on the platforms you need.
You can, however, do it in a different language. If you need something to run on multiple platforms, I suggest you investigate Java. Similar language to c/c++, and you can "compile" (sort of) programs to run on pretty much any computer.
Do not confuse processor platforms with OS platforms.
for different OS platforms, machine binaries are altogether different. even not possible to make one-to-one instruction mapper. it is beacause whole instruction set architecture may be different, and different instruction groups may have totally different instruction format, and even some instructions may be missing or target platform.
Only an emulator or virtual machine can do this.
Actually, some operating systems support this; it is usually called a "fat binary".
In particular, Mac OS uses (used) it to support PowerPC and x86 in one binary.
On MS Windows however, this is not possible as the OS does not support it.
Windows can run 32bit executables in 64bit mode with no problem. So your exe will be portable if you compile it in 32bit mode. Or else you must release 2 versions of your executable. If you use Java or C# (or any bytecode-compiled language), the JIT/interpreter can optimize your code for the current OS's mode, so it's fully portable. But on C++, since it produces native code, I'm afraid this can't be done except using 2 versions of your binary.
The trick to do this, is to create a binary which has machine instructions which will be emulated in a virtual machine on the operating systems and processors you want to support.
The most widely spread such virtual machine are variants of the Java virtual machine. So my suggestion would be to look at a compiler which compiles C code to Java byte code.
Also, Windows once upon a time treated x86 as a virtual machine on other (Alpha) architectures.
To summarize the other answers, if you want to create a single executable file that can be loaded and run on multiple platforms, you basically have two options:
Create a "fat binary", which contains the machine code for multiple platforms. This is not normally supported by most development tools and may require special loaders on the target platform;
Compile to a byte code for the JVM or for .Net. I've heard of one C compiler that generates Java byte code (can't remember the name offhand), but never used it, nor do I have any idea what the quality of the implementation would be.
Normally, the procedure for supporting multiple platforms in C is to generate different executables for each target, either by using a cross compiler or running a compiler on each platform. That requires you to put some thought into how you write and organize your source code so that the platform-specific bits can be easily swapped out without affecting the overall program logic, for varying degrees of "easily".
The short answer is you can't, The long answer is there are several options.
Fat Binary. Downside is that this requires OS support. The only user level OS I know of that supports it is OS X for their power pc to Intel migration.
On the fly cross translation. As used by Transmeta and Apple. Again no general solution provider that I know of.
a C\C++ interpreter. There is at least one I am aware of Ch. It runs on Windows, Linux, OS X. Note Ch is not fully C++ compatible.
This question likes to that ask "Is there a way can travel from Canada to another city in the world?"
And answer is: "yes there is."
for compiling a C/C++ Source code to an Executable file on Windows Platform without any virtual machine you can use Windows Legacy API or MFC (specially with use MFC in a Static Library instead in Dll). This executable file approximately runs on all PCs that have windows, because windows runs on only 3 platforms (x86, x64, IA64; except windows 8 & 8.1 that supports ARMs).Of course you should compile your source to x86 codes to run on 32 bit and x86-64 platforms and Itaniums can run your exe in emulation. But about all Processors that runs windows as their OS (Like ARMS in mobiles) you should compile that for the Windows Phone or Windows CE.
You can write a mid-library~ such as:
[ Library ]
[ mid-library]
[linux part] [windows part]
then, you can use library, your app will be portable~~

Why is Application Binary Interface important for programming

I don't understand why the ABI is important context of developing user-space applications. Is the set of system calls for an operating system considered an ABI? But if so then aren't all the complexities regarding system calls encapsulated within standard libraries?
So then is ABI compatibility only relevant for running statically linked applications on different platforms, since the system calls would be embedded into the binary?
An ABI defines a set of alignment, calling convention, and data types that are common to a system. This makes an ABI awfully important if you're doing any sort of dynamic linking; as without it code from one application has no way of calling code provided by another.
So, no. ABI compatibility is relevant for all dynamic linking (less so for static).
Its worth emphasizing again that a system's ABI affects inter-application work as well as application-to-operating-system work.
The ABI is more than what system calls are available. It also usually describes the actual way arguments are passed to functions and how structures and objects are laid-out in memory. Without a consistent ABI, code built by different compilers might not be able to call each other -- if you call foo(a,b) and one compiler pushes a and b on the stack while another passes those in registers, you've got an ABI clash.
"ABI" (see Wikipedia) is an umbrella term for all the assumptions an operating system makes about data formats. This includes the layout of executable files and that of any data structure in memory given its C definition.
The term also generally covers formatting requirements between programs written in the same language. Each language has particular features that might result in different conventions within executable formats and memory structures, but all must ultimately generate executables compatible with the OS and data structures compatible with the processor's instruction set.
ABI doesn't matter much if you only care about compiling standard-conformant code. It matters a little when you violate the standard and do unportable things like casting a char * to a long *. It's yet more important when writing a large body of assembly code. Writing something like a linker or a debugger, it can come to embody the bulk of work to be done.
Incompatible ABI is why even though OSX, Linux, Solaris, Windows and *BSD all run on Intel x86 CPUs, a simple POSIX-only hello world program compiled on one OS that does not use any vendor specific or proprietary system calls and/or libraries generally cannot run on one OS when compiled for another OS*.
ABI is not really important to programmers as such because we already instinctively know that you cannot run a Windows app on Macs. Even non-programmers (except Hollywood screenwriters) know this. It is important to compiler writers when they need to target a particular environment.
* note: Some OSes like Linux and BSD support foreign ABI so that a simple Linux command line program can sometimes be executed on BSD without modification. And there are of course emulation layers like wine.
Don't forget in C++ the way name mangling is implemented forms part of the ABI
Only when you want your binary to be run on other environment without recompilation, then there are some places you may need take ABI into account:
you may call a third library in your program, and the third library may varies on different environment. (so only the ABI you can trust)
the syscall to os. (if you static linked the syscall to your binary instead dynamically link to libc)
Actually, most developer need not take ABI into account, only the binary loader/tool developer need know more about it.
System calls also follow an ABI - the syscall interface differs from operating system to operating system.
Statically linking your application and the standard library into it will tie it into one syscall ABI. For instance, FreeBSD allows using the Linux syscall ABI only through an emulation module.

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