3 min read

How complex is Hello World really?

How complex is Hello World really?

Almost every beginner programmer's first program is “Write Hello World”, where you write the syntax that will print “Hello World” in the console. A straightforward program with a simple premise, but is a program like this complex? No, right? It turns out it isn’t.

At first glance, "Hello, World" appears simple and straightforward. You write the code, hit compile, and the “Hello World” output is presented on the screen. However, this program relies on a complex underlying infrastructure.

This blog is written by Akshat Virmani at KushoAI. We're building the fastest way to test your APIs. It's completely free and you can sign up here.

In this article, we will cover behind the scenes of the “Hello World” program in Linux.

Surface Level

On the surface level, this seems pretty straightforward. Let’s take a code as an example.

The above example has an input-output library “<stdio.h>”, a main function and a print statement. Now, let’s take a look at what is happening behind the scenes.

The Compilation Process

When you compile the program, several stages occur behind the scenes:

  1. Preprocessing: The preprocessor expands the code to include the necessary library functions and replaces the directive with actual content.
  2. Compilation: The expanded code is translated into assembly language and is a human-readable representation of machine instructions.
  3. Assembly: An assembler converts the assembly code into machine code, resulting in an object file 
  4. Linking: The linker combines the object file with the necessary system libraries to produce the final executable. 

These steps ensure that the high-level code written by the programmer is converted to machine-level code that the computer can execute.

Role of the ELF Format

On Linux/Unix systems, the final executable adheres to the ELF (Executable and Linkable Format) specification. It is a common format for programs on various systems. The programmer interacts with the system's standard libraries, which provide pre-written code for common tasks. These libraries handle low-level operations like memory management and input/output.

An ELF file is composed of several key sections like ELF Header, which contains metadata about the binary, Program Headers (defines segments used at runtime), Section Headers (provides additional details for linking and debugging); and Dynamic Linking Information.

These are basically designed to ensure compatibility, extensibility, and efficient runtime operations.

Runtime Execution

Once the ELF file is created, running the program involves several steps like:

  1. Loading: The operating system’s loader reads the ELF file and loads it into the memory.
  2. Dynamic Linking: The loader resolves symbols from shared libraries.
  3. Entry Point Execution: Execution begins at the entry point defined in the ELF header. Typically, this is a function like “_start”.
  4. System Call Interaction: The Input-Output (in this case - printf) function invokes the write system to output the message. 

This is how the backend looks like to print a string:

For the above, I took reference from this blog.

Conclusion: 

Well, this is how a simple program works behind the scenes. Now, I am thinking about how complex programs and infrastructure work underneath the surface.

This blog is written by Akshat Virmani at KushoAI. We're building the fastest way to test your APIs. It's completely free and you can sign up here.