free linux development course

Advanced Linux Development Course – free linux development course

Master the fundamentals of system programming, compilation process, and cross-platform development, this is free linux development course by embedded pathashala.

πŸ”§ Understanding Programming Languages

Hello students in this linux development course first let us understnad what are programming lanaguages, Programming languages are the tools we use to communicate with computers. They can be classified into three broad categories based on their level of abstraction from the hardware:

Programming Language Hierarchy

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         PROGRAMMING LANGUAGES CLASSIFICATION             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  HIGH-LEVEL LANGUAGES                          β”‚    β”‚
β”‚  β”‚  β€’ Platform Independent                        β”‚    β”‚
β”‚  β”‚  β€’ Easy to Learn & Use                         β”‚    β”‚
β”‚  β”‚  β€’ Examples: Java, Python, .NET, JavaScript    β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                          β–²                              β”‚
β”‚                          β”‚                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  MID-LEVEL LANGUAGES                           β”‚    β”‚
β”‚  β”‚  β€’ Hardware Access + Abstraction               β”‚    β”‚
β”‚  β”‚  β€’ Pointers & Memory Control                   β”‚    β”‚
β”‚  β”‚  β€’ Examples: C, C++                            β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                          β–²                              β”‚
β”‚                          β”‚                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  LOW-LEVEL LANGUAGES                           β”‚    β”‚
β”‚  β”‚  β”œβ”€ Assembly Language                          β”‚    β”‚
β”‚  β”‚  β”‚  β€’ Human-readable mnemonics                 β”‚    β”‚
β”‚  β”‚  β”‚  β€’ Architecture-specific                    β”‚    β”‚
β”‚  β”‚  └─ Machine Language                           β”‚    β”‚
β”‚  β”‚     β€’ Binary (0s and 1s)                       β”‚    β”‚
β”‚  β”‚     β€’ Direct hardware execution                β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚                                                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

βš™οΈ Low-Level Languages: The Foundation

Why Do We Need Low-Level Languages?

You might wonder: “If we have high-level languages like Java and Python, why bother with assembly or machine code?” The answer lies in what high-level languages cannot do.

🎯 Critical Use Cases for Assembly Language

1️⃣ Stack Setup (Bootstrap Code)

When your C program starts, it assumes a stack already exists. But who creates the stack? Assembly language does!

  • C programs need a stack for local variables and function calls
  • The compiler assumes the stack is ready
  • Assembly language doesn’t need a stack to execute
  • Startup code (written in assembly) sets up the stack before C code runs
2️⃣ Direct Hardware Access

Some processor features cannot be accessed through C:

  • Coprocessor configuration
  • Special CPU registers
  • Memory management unit (MMU) setup
  • Cache control operations

Example: Bootstrap Code Flow


POWER ON
   β”‚
   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ASSEMBLY LANGUAGE STARTUP CODE      β”‚
β”‚  β”œβ”€ Initialize stack pointer (SP)   β”‚
β”‚  β”œβ”€ Setup memory regions             β”‚
β”‚  β”œβ”€ Configure CPU registers          β”‚
β”‚  └─ Jump to C main() function        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
   β”‚
   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  C PROGRAM EXECUTION                 β”‚
β”‚  β”œβ”€ main() function starts           β”‚
β”‚  β”œβ”€ Local variables use stack        β”‚
β”‚  └─ Function calls use stack         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    
πŸ’‘ Key Insight: Without assembly language to set up the initial environment, your C program cannot even begin execution. This is why bootloaders, firmware, and operating system kernels always contain assembly code.

🎯 Why C for Embedded Development?

C occupies a sweet spot between low-level control and high-level productivity. Here’s why it dominates embedded systems and operating system development:

🎯 Direct Memory Access

// Access hardware registers directly
volatile uint32_t *gpio_base = (uint32_t *)0x40020000;

// Set bit 5 in GPIO output register
*gpio_base |= (1 << 5);

// Read from specific memory address
uint32_t value = *(volatile uint32_t *)0x20000000;
        

Using pointers, we can read/write to any physical memory address – essential for hardware control.

πŸ”§ Bitwise Operations

// GPIO register programming example
#define GPIO_PIN_5  (1 << 5)
#define GPIO_PIN_7  (1 << 7)

// Set pin 5 high
GPIOA_ODR |= GPIO_PIN_5;

// Clear pin 7
GPIOA_ODR &= ~GPIO_PIN_7;

// Toggle pin 5
GPIOA_ODR ^= GPIO_PIN_5;

// Check if pin 7 is set
if (GPIOA_IDR & GPIO_PIN_7) {
    // Pin 7 is high
}
        

Bitwise operators allow precise control of individual bits in hardware registers.

C vs High-Level Languages for Embedded Systems

Feature C Language High-Level (Java/Python)
Memory Access βœ… Direct pointer access to any address ❌ No direct memory access
Hardware Control βœ… Can manipulate registers directly ❌ No hardware access (requires JNI/native code)
Binary Size βœ… Compact executables (few KB) ❌ Large runtime required (MBs)
Execution Speed βœ… Fast (compiled to machine code) ⚠️ Slower (interpreted or JIT compiled)
Memory Safety ❌ No automatic bounds checking βœ… Runtime memory protection

πŸš€ The Future: Rust for EmbeddedModern embedded development is gradually shifting toward Rust, which offers:

  • Memory safety without garbage collection
  • Zero-cost abstractions
  • Prevention of buffer overflows and use-after-free bugs at compile time
  • Same level of hardware control as C

However, C remains dominant due to its vast ecosystem and decades of tooling.

☁️ High-Level Languages & Platform Independence

High-level languages prioritize developer productivity and platform independence over direct hardware access. Let’s understand how they achieve portability:

Platform Independence: Java vs C

C COMPILATION (Platform Dependent)
═══════════════════════════════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   hello.c   β”‚  gcc for x86           β”‚ x86 binary  β”‚
β”‚             │──────────────────────▢ β”‚ (can't run  β”‚
β”‚  C Source   β”‚                        β”‚  on ARM)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                              β”‚
                                              βœ— Won't execute on ARM
                                              β–Ό
                                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                       β”‚ ARM Machine β”‚
                                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

JAVA COMPILATION (Platform Independent)
════════════════════════════════════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Hello.java β”‚  javac                 β”‚ Hello.class  β”‚
β”‚             │──────────────────────▢ β”‚ (bytecode)   β”‚
β”‚ Java Source β”‚                        β”‚              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                              β”‚
                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β–Ό                    β–Ό                    β–Ό
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  β”‚   x86 JVM   β”‚      β”‚   ARM JVM   β”‚     β”‚ RISC-V JVM  β”‚
                  β”‚             β”‚      β”‚             β”‚     β”‚             β”‚
                  β”‚ Converts to β”‚      β”‚ Converts to β”‚     β”‚ Converts to β”‚
                  β”‚ x86 machine β”‚      β”‚ ARM machine β”‚     β”‚ RISC-V code β”‚
                  β”‚    code     β”‚      β”‚    code     β”‚     β”‚             β”‚
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                         β–Ό                    β–Ό                    β–Ό
                  βœ… Runs on x86       βœ… Runs on ARM        βœ… Runs on RISC-V

How Java Achieves Platform Independence

  1. Compile to Bytecode: Java compiler (javac) converts source code to platform-independent bytecode (.class files)
  2. JVM Execution: Java Virtual Machine (JVM) reads the bytecode and converts it to native machine code for the specific platform
  3. Write Once, Run Anywhere: The same .class file runs on Windows (x86), Linux (ARM), macOS (x86_64), etc.

⚠️ Trade-off: Platform independence comes at a cost:

  • Larger binaries (JVM/runtime must be installed)
  • Slower execution (JIT compilation overhead)
  • Higher memory usage
  • No direct hardware access

⚑ Compilation vs Interpretation

There are two fundamental approaches to executing programs: compilation and interpretation. Understanding the difference is crucial for system programming.

Compilation Process

COMPILATION
═══════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Source Code  β”‚
β”‚  (hello.c)   β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  COMPILER    β”‚ ◄─── One-time process
β”‚   (gcc)      β”‚      Happens before execution
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Target Code  β”‚
β”‚ (executable) β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚
       β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β–Ό              β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Input1 β”‚    β”‚ Input2 β”‚
  β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
      β”‚             β”‚
      β–Ό             β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚Output1 β”‚    β”‚Output2 β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Multiple executions, no recompilation needed

Interpretation Process

INTERPRETATION
══════════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Source Code  β”‚      β”‚    Input     β”‚
β”‚ (script.py)  β”‚      β”‚              β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                     β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β–Ό
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚  INTERPRETER  β”‚ ◄─── Executes line by line
          β”‚   (python3)   β”‚      Every time you run
          β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
                  β–Ό
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚    Output     β”‚
          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Source code interpreted every time

Compiler vs Interpreter

Aspect Compiler (C, C++, Rust) Interpreter (Python, Bash)
Translation Entire program translated once Translates line-by-line during execution
Execution Speed βœ… Very fast (native code) ❌ Slower (overhead per line)
Error Detection βœ… All errors found at compile time ❌ Errors found at runtime
Portability ❌ Platform-specific binaries βœ… Source runs on any platform
Development Cycle ⚠️ Edit β†’ Compile β†’ Test βœ… Edit β†’ Test (faster iteration)

Examples of Interpreters


# Python Interpreter
$ python3
Python 3.10.12 (main, Mar 3 2026, 11:56:32)
>>> 3 + 5
8
>>> print("Hello World")
Hello World
>>> quit()

# Bash Interpreter
$ bash
$ echo "The current date is $(date)"
The current date is Wed Apr 15 22:45:00 IST 2026
$ exit
    

πŸ”¨ The Compilation Process

When you run gcc -o hello hello.c, a lot happens behind the scenes! Let’s break down each stage of compilation:

Complete Compilation Pipeline

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    GCC COMPILATION STAGES                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚  β”‚  hello.c    β”‚  ────▢ β”‚ PREPROCESSOR β”‚  gcc -E                   β”‚
β”‚  β”‚             β”‚        β”‚   (cpp)      β”‚                           β”‚
β”‚  β”‚ β€’ #include  β”‚        β”‚              β”‚                           β”‚
β”‚  β”‚ β€’ #define   β”‚        β”‚ β€’ Expands    β”‚                           β”‚
β”‚  β”‚ β€’ macros    β”‚        β”‚   macros     β”‚                           β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚ β€’ Includes   β”‚                           β”‚
β”‚                         β”‚   headers    β”‚                           β”‚
β”‚                         β”‚ β€’ Removes    β”‚                           β”‚
β”‚                         β”‚   comments   β”‚                           β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                β”‚                                    β”‚
β”‚                                β–Ό                                    β”‚
β”‚                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚                         β”‚  hello.i     β”‚  (Expanded source)         β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                β”‚                                    β”‚
β”‚                                β–Ό                                    β”‚
β”‚                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚                         β”‚  COMPILER    β”‚  gcc -S                   β”‚
β”‚                         β”‚   (cc1)      β”‚                           β”‚
β”‚                         β”‚              β”‚                           β”‚
β”‚                         β”‚ β€’ Syntax     β”‚                           β”‚
β”‚                         β”‚   checking   β”‚                           β”‚
β”‚                         β”‚ β€’ Type       β”‚                           β”‚
β”‚                         β”‚   checking   β”‚                           β”‚
β”‚                         β”‚ β€’ Generates  β”‚                           β”‚
β”‚                         β”‚   assembly   β”‚                           β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                β”‚                                    β”‚
β”‚                                β–Ό                                    β”‚
β”‚                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚                         β”‚  hello.s     β”‚  (Assembly code)           β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                β”‚                                    β”‚
β”‚                                β–Ό                                    β”‚
β”‚                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚                         β”‚  ASSEMBLER   β”‚  gcc -c                   β”‚
β”‚                         β”‚   (as)       β”‚                           β”‚
β”‚                         β”‚              β”‚                           β”‚
β”‚                         β”‚ β€’ Converts   β”‚                           β”‚
β”‚                         β”‚   mnemonics  β”‚                           β”‚
β”‚                         β”‚   to binary  β”‚                           β”‚
β”‚                         β”‚ β€’ Creates    β”‚                           β”‚
β”‚                         β”‚   object     β”‚                           β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                β”‚                                    β”‚
β”‚                                β–Ό                                    β”‚
β”‚                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚                         β”‚  hello.o     β”‚  (Object file)             β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                β”‚                                    β”‚
β”‚                                β–Ό                                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚  β”‚ Libraries   β”‚  ────▢ β”‚   LINKER     β”‚  gcc (default)            β”‚
β”‚  β”‚ (libc.so)   β”‚        β”‚   (ld)       β”‚                           β”‚
β”‚  β”‚             β”‚        β”‚              β”‚                           β”‚
β”‚  β”‚ β€’ printf()  β”‚        β”‚ β€’ Resolves   β”‚                           β”‚
β”‚  β”‚ β€’ scanf()   β”‚        β”‚   symbols    β”‚                           β”‚
β”‚  β”‚ β€’ malloc()  β”‚        β”‚ β€’ Relocates  β”‚                           β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚   addresses  β”‚                           β”‚
β”‚                         β”‚ β€’ Links      β”‚                           β”‚
β”‚                         β”‚   libraries  β”‚                           β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                β”‚                                    β”‚
β”‚                                β–Ό                                    β”‚
β”‚                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚                         β”‚    hello     β”‚  (Executable - ELF format) β”‚
β”‚                         β”‚              β”‚                           β”‚
β”‚                         β”‚  ./hello     β”‚  βœ… Ready to run!          β”‚
β”‚                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚                                                                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Stage 1: Preprocessing


# Generate preprocessed output
$ gcc -E -o hello.i hello.c

# View the expanded code (includes all header files)
$ vi hello.i
      

What the preprocessor does:

  • Expands all #include directives (inserts header file contents)
  • Replaces all #define macros with their values
  • Removes all comments
  • Handles conditional compilation (#ifdef, #ifndef)

Stage 2: Compilation


# Generate assembly code
$ gcc -S -o hello.s hello.c

# View assembly instructions
$ vi hello.s
      

Sample Assembly Output (x86-64)


.file   "hello.c"
.text
.section    .rodata
.LC0:
    .string "hello world"
.text
.globl  main
.type   main, @function
main:
    pushq   %rbp
    movq    %rsp, %rbp
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $0, %eax
    popq    %rbp
    ret
      

What the compiler does:

  • Performs syntax and semantic analysis
  • Type checking and optimization
  • Generates human-readable assembly code
  • Reports compilation errors and warnings

Stage 3: Assembly


# Generate object file
$ gcc -c -o hello.o hello.s

# Or directly from source
$ gcc -c -o hello.o hello.c

# View object file (binary format)
$ file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1
      

What the assembler does:

  • Converts assembly mnemonics (mov, push, call) to binary machine code
  • Creates an object file (.o) in ELF format
  • Generates symbol tables and relocation information

Stage 4: Linking


# Link object file to create executable
$ gcc -o hello hello.o

# Or do everything in one command
$ gcc -o hello hello.c

# See verbose linker output
$ gcc -o hello hello.c -Wl,--verbose

# Run the executable
$ ./hello
hello world
      

What the linker does:

  • Symbol Resolution: Finds definitions for all function calls (e.g., printf from libc)
  • Relocation: Assigns final memory addresses (all .o files start at address 0x0)
  • Library Linking: Includes code from static (.a) or dynamic (.so) libraries
  • Entry Point: Sets _start as the program entry point

Hands-On: Viewing All Compilation Stages


# Create a simple C program
$ cat > hello.c <<'EOF'
#include <stdio.h>

int main() {
    printf("hello world");
    return 0;
}
EOF

# Generate all intermediate files using -save-temps
$ gcc -o hello -save-temps hello.c

# List all generated files
$ ls
hello       # Final executable
hello.c     # Original source
hello.i     # Preprocessed source
hello.s     # Assembly code
hello.o     # Object file

# Check file sizes
$ ls -lh
-rwxrwxr-x 1 user user  16K hello      # Executable (ELF)
-rw-rw-r-- 1 user user  60B hello.c    # Source
-rw-rw-r-- 1 user user  17K hello.i    # Preprocessed (huge due to stdio.h)
-rw-rw-r-- 1 user user 488B hello.s    # Assembly
-rw-rw-r-- 1 user user 1.5K hello.o    # Object file
    

πŸ” Understanding ELF (Executable and Linkable Format)

Linux executables use the ELF format, which contains:

  • ELF Header: Magic number, architecture, entry point
  • Program Headers: How to load segments into memory
  • Section Headers: Code (.text), data (.data), symbols, etc.

# View ELF header
$ readelf -h hello

# View section headers
$ readelf -S hello

# View symbols
$ readelf -s hello
      

πŸ“š Static vs Dynamic Libraries

Libraries allow code reuse without sharing source code. There are two types:

Aspect Static Libraries (.a) Dynamic Libraries (.so)
Linking Time Compile time (linked into executable) Runtime (loaded when program runs)
Binary Size ❌ Larger (library code included) βœ… Smaller (just references)
Dependencies βœ… None (self-contained) ❌ Requires .so files at runtime
Updates ❌ Must recompile to update βœ… Update .so without recompiling
Memory Usage ❌ Each program has own copy βœ… Shared across processes

Static vs Dynamic Linking

STATIC LINKING
══════════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ main.o   β”‚     β”‚ libmath.aβ”‚
β”‚          β”‚     β”‚ (static) β”‚
β”‚ calls    β”‚     β”‚          β”‚
β”‚ add()    β”‚     β”‚ add()    β”‚
β”‚ sub()    β”‚     β”‚ sub()    β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
     β”‚                β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
              β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚ LINKER      β”‚
       β”‚ Copies code β”‚
       β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
              β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚ executable  β”‚ ◄── Larger file (1 MB)
       β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚     Contains all library code
       β”‚ β”‚ main    β”‚ β”‚
       β”‚ β”‚ add()   β”‚ │◄── Library functions embedded
       β”‚ β”‚ sub()   β”‚ β”‚
       β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


DYNAMIC LINKING
═══════════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ main.o   β”‚     β”‚libmath.soβ”‚
β”‚          β”‚     β”‚(dynamic) β”‚
β”‚ calls    β”‚     β”‚          β”‚
β”‚ add()    β”‚     β”‚ add()    β”‚
β”‚ sub()    β”‚     β”‚ sub()    β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
     β”‚                β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
              β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚ LINKER      β”‚
       β”‚ Creates ref β”‚
       β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
              β–Ό
       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         At Runtime
       β”‚ executable  β”‚ ◄── Smaller file (100 KB)    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
       β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚                              β”‚ libmath.so   β”‚
       β”‚ β”‚ main    β”‚ β”‚                              β”‚              β”‚
       β”‚ β”‚ @add    β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ add() { }    β”‚
       β”‚ β”‚ @sub    β”‚ β”‚  Dynamic loader maps library β”‚ sub() { }    β”‚
       β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚                              β”‚              β”‚
       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
              β–²
              β”‚ Shared by multiple processes
              β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β–Ό                   β–Ό             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚Process1β”‚         β”‚Process2β”‚    β”‚Process3β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜

🌍 Cross Compilation

Cross compilation means compiling code on one architecture (e.g., x86 PC) to run on a different architecture (e.g., ARM embedded device).

Native vs Cross Compilation

NATIVE COMPILATION
══════════════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   x86-64 Development Machine        β”‚
β”‚   (Ubuntu Linux)                    β”‚
β”‚                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚   β”‚  hello.c   β”‚                   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚         β”‚                           β”‚
β”‚         β–Ό                           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚   β”‚    gcc     β”‚ ◄── Native compilerβ”‚
β”‚   β”‚            β”‚     for x86-64     β”‚
β”‚   β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚         β”‚                           β”‚
β”‚         β–Ό                           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚   β”‚   hello    β”‚ ◄── x86-64 binary β”‚
β”‚   β”‚  (x86-64)  β”‚     Runs HERE βœ…   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


CROSS COMPILATION
═════════════════
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   x86-64 Development Machine        β”‚
β”‚   (Ubuntu Linux)                    β”‚
β”‚                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚   β”‚  hello.c   β”‚                   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚         β”‚                           β”‚
β”‚         β–Ό                           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚   β”‚ arm-linux-      β”‚ ◄── Cross    β”‚
β”‚   β”‚ gnueabihf-gcc   β”‚   compiler   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   for ARM    β”‚
β”‚         β”‚                           β”‚
β”‚         β–Ό                           β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚   β”‚   hello    β”‚ ◄── ARM binary    β”‚
β”‚   β”‚   (ARM)    β”‚     Won't run hereβŒβ”‚
β”‚   β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚ Transfer binary
          β”‚ (USB, SCP, TFTP)
          β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   ARM Target Device                 β”‚
β”‚   (Raspberry Pi / BeagleBone)       β”‚
β”‚                                     β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚   β”‚   hello    β”‚                   β”‚
β”‚   β”‚   (ARM)    β”‚ ◄── Runs here βœ…   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ”§ Cross Compiler Toolchains

A complete cross-compilation toolchain includes:

  • Compiler: arm-linux-gnueabihf-gcc, aarch64-linux-gnu-gcc
  • Binutils: Assembler, linker, objdump, etc.
  • C Library: glibc or musl compiled for target architecture
  • Headers: System headers for target platform

Installing ARM Cross Compiler


# Install ARM toolchain on Ubuntu
$ sudo apt-get install gcc-arm-linux-gnueabihf

# Compile for ARM (hard-float ABI)
$ arm-linux-gnueabihf-gcc -o hello_arm hello.c

# Check the binary architecture
$ file hello_arm
hello_arm: ELF 32-bit LSB executable, ARM, EABI5 version 1

# Try to run on x86 (won't work!)
$ ./hello_arm
bash: ./hello_arm: cannot execute binary file: Exec format error

# Transfer to Raspberry Pi and run
$ scp hello_arm pi@192.168.1.100:/home/pi/
$ ssh pi@192.168.1.100
pi@raspberrypi:~ $ ./hello_arm
hello world  βœ… Works on ARM!
    

Understanding Toolchain Naming

Cross compiler names follow a pattern: <arch>-<vendor>-<os>-<abi>-gcc

  • arm-linux-gnueabihf-gcc
    • arm: Target architecture (ARMv7)
    • linux: Operating system
    • gnueabi: GNU EABI (Embedded Application Binary Interface)
    • hf: Hard Float (hardware floating point support)
  • aarch64-linux-gnu-gcc
    • aarch64: 64-bit ARM architecture
  • arm-none-eabi-gcc
    • none: No operating system (bare metal)

πŸ”Œ ABI and System Calls

The Application Binary Interface (ABI) defines how programs interact with the operating system and how functions pass parameters at the binary level.

What is an ABI?

The ABI specifies:

  • Calling Convention: How arguments are passed to functions (registers vs stack)
  • Register Usage: Which registers are caller-saved vs callee-saved
  • System Call Interface: How to invoke kernel services
  • Data Types: Size and alignment of int, long, pointers, etc.
  • Binary Format: ELF structure, relocation types

System Call Conventions: ARM vs x86-64

Architecture Syscall Number Arg 1 Arg 2 Arg 3 Arg 4 Arg 5 Arg 6 Trigger Return
ARM EABI r7 r0 r1 r2 r3 r4 r5 swi 0x0 r0
x86-64 rax rdi rsi rdx r10 r8 r9 syscall rax
x86 (32-bit) eax ebx ecx edx esi edi ebp int 0x80 eax

Example: write() System Call in Assembly (x86-64)


section .data
    msg db "Hello World", 0xA    ; Message with newline
    len equ $ - msg               ; Length = 12 bytes

section .text
    global _start

_start:
    ; write(1, msg, len)
    mov rax, 1          ; syscall number: write = 1
    mov rdi, 1          ; arg1: fd = 1 (stdout)
    mov rsi, msg        ; arg2: buffer = msg
    mov rdx, len        ; arg3: count = len
    syscall             ; Invoke kernel

    ; exit(0)
    mov rax, 60         ; syscall number: exit = 60
    xor rdi, rdi        ; arg1: status = 0
    syscall             ; Invoke kernel
    

Viewing System Calls


# List all system calls
$ man syscalls

# View specific syscall details
$ man 2 write
$ man 2 open
$ man 2 fork

# Trace system calls made by a program
$ strace ./hello
execve("./hello", ["./hello"], 0x7ffc... = 0
write(1, "hello world", 11)         = 11
exit_group(0)                       = ?

# Count syscalls
$ strace -c ls
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.00    0.000020           2        10           mmap
 20.00    0.000010           2         5           read
 ...
    

Function Calling Convention (x86-64 System V ABI)

int calculate(int a, int b, int c, int d, int e, int f, int g, int h);

Arguments 1-6: Passed in REGISTERS
═══════════════════════════════════
β”Œβ”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”
β”‚ rdi β”‚ rsi β”‚ rdx β”‚ rcx β”‚ r8  β”‚ r9  β”‚
β”œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€
β”‚  a  β”‚  b  β”‚  c  β”‚  d  β”‚  e  β”‚  f  β”‚
β””β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”˜

Arguments 7+: Pushed to STACK
══════════════════════════════
        Stack (grows down β–Ό)
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚     h       β”‚ ◄── [rsp+8]
        β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
        β”‚     g       β”‚ ◄── [rsp]
        β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
        β”‚ Return Addr β”‚
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Return Value: rax register
═════════════════════════════
β”Œβ”€β”€β”€β”€β”€β”
β”‚ rax β”‚ ◄── Function return value
β””β”€β”€β”€β”€β”€β”˜

πŸ’‘ Why Understanding ABI Matters

  • Cross-platform development: Same C code generates different assembly on different architectures
  • Calling assembly from C: You need to follow the ABI to pass arguments correctly
  • Debugging: Understanding register conventions helps when debugging with GDB
  • Optimization: Knowing register usage helps write efficient inline assembly
  • Linker errors: ABI mismatches cause hard-to-debug linking failures

πŸ” Finding Linker Scripts

Linker scripts control how sections are arranged in the final executable:


# Show default linker script
$ ld --verbose

# Show linker script used for specific target
$ arm-linux-gnueabihf-ld --verbose

# GCC shows linker script with verbose flag
$ gcc -o hello hello.c -Wl,--verbose 2>&1 | grep "using internal linker"

# Common linker script locations
/usr/lib/ldscripts/
/usr/arm-linux-gnueabihf/lib/ldscripts/
      

πŸ§ͺ Hands-On Lab Exercise

Lab 1: Explore All Compilation Stages

Step 1: Write a Simple C Program


#include <stdio.h>
#define MAX_SIZE 100

int main() {
    int numbers[MAX_SIZE];
    printf("Array size: %d\n", MAX_SIZE);
    return 0;
}
        

Step 2: Generate Preprocessed Output


$ gcc -E -o program.i program.c
$ wc -l program.i    # See how many lines after preprocessing
        

Notice how #include <stdio.h> expands to thousands of lines!

Step 3: Generate Assembly Code


$ gcc -S -o program.s program.c
$ cat program.s      # View human-readable assembly
        

Step 4: Create Object File


$ gcc -c -o program.o program.c
$ file program.o
$ readelf -h program.o    # View ELF header
        

Step 5: Link and Create Executable


$ gcc -o program program.o
$ ./program
Array size: 100
        

Step 6: All in One Command


$ gcc -o program -save-temps program.c
$ ls
program  program.c  program.i  program.s  program.o
        

Lab 2: Cross Compilation for ARM

Step 1: Install ARM Toolchain


$ sudo apt-get install gcc-arm-linux-gnueabihf qemu-user
        

Step 2: Cross Compile


$ arm-linux-gnueabihf-gcc -o hello_arm hello.c
$ file hello_arm
hello_arm: ELF 32-bit LSB executable, ARM
        

Step 3: Run with QEMU (ARM Emulator)


$ qemu-arm -L /usr/arm-linux-gnueabihf ./hello_arm
hello world
        

πŸ“ Chapter Summary

Key Takeaways

  • βœ… Programming languages are classified into low-level (assembly, machine), mid-level (C, C++), and high-level (Java, Python)
  • βœ… Assembly language is essential for startup code, direct hardware access, and processor-specific features
  • βœ… C language provides pointers for memory access and bitwise operators for hardware programming
  • βœ… Compilation (C, C++) generates native code; interpretation (Python, Bash) executes line-by-line
  • βœ… Compilation stages: Preprocessing β†’ Compilation β†’ Assembly β†’ Linking
  • βœ… Cross compilation allows building binaries for different architectures
  • βœ… ABI defines calling conventions, register usage, and system call interfaces
  • βœ… Linux executables use ELF format with sections for code, data, and symbols

πŸš€ What’s Next?

In the next session, we’ll dive into:

  • Writing custom linker scripts for bare-metal programming
  • QEMU-based ARM development and debugging
  • Raspberry Pi bare-metal programming
  • Advanced GCC optimization flags and their impact

Leave a Reply

Your email address will not be published. Required fields are marked *