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.
π Course Contents
Understanding Programming Languages
Low-Level Languages: The Foundation
Why C for Embedded Development?
π§ 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 β
ββββββββββββββββββββββββββββββββββββββββ
π― 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
- Compile to Bytecode: Java compiler (javac) converts source code to platform-independent bytecode (.class files)
- JVM Execution: Java Virtual Machine (JVM) reads the bytecode and converts it to native machine code for the specific platform
- 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
#includedirectives (inserts header file contents) - Replaces all
#definemacros 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.,
printffrom 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
_startas 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
