Tag: GCC

  • Master GCC Command Line Options: A Beginner-Friendly Guide (2026)

    GCC Command Line Options : Are you new to GCC and feeling overwhelmed by flags like -O2, .o files, or linker scripts? This beginner-friendly tutorial will guide you step-by-step through the world of GCC — the GNU Compiler Collection. Learn how your code transforms from .c to executable, understand every compilation stage (Preprocessing, Compilation, Assembly, Linking), and get hands-on with cross-compilation, object files, static vs shared libraries, and optimization techniques.

    You’ll also explore how to debug effectively using -g, what linker scripts do, and how to use essential toolchain components like as, ld, objdump, nm, and readelf.

    Perfect for embedded programmers, systems developers, or any curious C/C++ learner who wants to master the build process from the inside out!

    What You’ll Learnin this tutorial of GCC Command Line Options:

    • GCC Command-Line Options Explained
    • Compilation Stages (Preprocessing ➝ Linking)
    • Cross-Compilation Made Easy
    • Understanding .o, .a, .so Files
    • Optimization Flags (-O0 to -O3, -Os, -Ofast)
    • Debugging with -g and GDB
    • Basics of Linker Scripts
    • Practical Use of as, ld, objdump, nm, readelf

    What is GCC?

    GCC is a powerful compiler used to compile programs written in C, C++, and other languages. It’s used on Linux, embedded systems, and more.

    Basic Structure of GCC Command:

    gcc [options] source_files [object_files] [-o output_file]
    

    ✅ Commonly Used GCC Options:

    OptionDescription
    -o outputSpecify the name of the output file.
    -cCompile source to object file (.o) without linking.
    -WallEnable most compiler warnings.
    -WerrorTreat all warnings as errors.
    -gInclude debug information.
    -O0 to -O3Control optimization level.
    -EStop after preprocessing.
    -SStop after compilation (output assembly).
    -vVerbose output showing stages and toolchain paths.
    -I<dir>Add directory to header file search path.
    -L<dir>Add directory to library search path.
    -l<lib>Link with specified library (e.g., -lm for math).

    Example 1: Compile a C file

    gcc hello.c -o hello

    Example 2: Compile with warnings and debug info

    gcc -Wall -g hello.c -o hello

    Example 3: Stop after creating object file

    gcc -c hello.c

    This creates hello.o which can be linked later.

    When to Use What:

    GoalFlags to Use
    Debugging-g -O0
    Performance-O2 or -O3
    Library building-c, -fPIC, -shared
    Troubleshooting warnings-Wall -Werror

    Perfect! Let’s move on to:

    GCC Compilation Stages (Preprocessing, Compilation, Assembly, Linking)

    GCC doesn’t just “compile” your code in one step—it goes through 4 main stages:

    Overview of Stages

    StageCommand FlagDescription
    1. Preprocessing-EHandles macros, #include, #define, removes comments
    2. Compilation-SConverts preprocessed code to Assembly
    3. Assembly-cConverts Assembly to Object code (.o)
    4. Linking(default)Combines object code and libraries into final binary

    1. Preprocessing (-E)

    What Happens:

    • Expands #include headers
    • Replaces #define macros
    • Removes comments

    Example:

    gcc -E hello.c -o hello.i
    • Output: hello.i (pure C code with macros expanded)

    2. Compilation (-S)

    What Happens:

    • Converts .i to assembly (.s)
    • Handles syntax checking and optimizations

    Example:

    gcc -S hello.i -o hello.s
    
    • Output: hello.s (human-readable assembly code)

    3. Assembly (-c)

    What Happens:

    • Converts .s to machine code (.o)
    • Done by as (assembler)

    Example:

    gcc -c hello.s -o hello.o
    • Output: hello.o (object file, not runnable yet)

    4. Linking (Default GCC behavior)

    What Happens:

    • Links .o files and libraries to create final executable
    • Done by ld (linker)

    Example:

    gcc hello.o -o hello
    • Output: hello (final executable)

    Full Manual Process:

    gcc -E hello.c -o hello.i
    gcc -S hello.i -o hello.s
    gcc -c hello.s -o hello.o
    gcc hello.o -o hello

    Quick Tip:

    To see all stages and tools used by GCC:

    gcc -v hello.c -o hello

    Cross Compilation

    What is Cross Compilation?

    Cross compilation is when you compile code on one system (host) but the output is meant to run on a different system (target), often with a different CPU architecture (e.g., x86 host → ARM target).

    Why Use Cross Compilation?

    • Building for embedded systems (e.g., ARM Cortex on Raspberry Pi, ESP32)
    • Developing for devices with limited resources
    • Compiling code for different operating systems (Linux → QNX, Windows → Linux)

    Common Cross Compilation Toolchain

    ToolPurpose
    arm-none-eabi-gccGCC for bare-metal ARM targets
    arm-linux-gnueabihf-gccGCC for Linux ARM hard-float ABI targets
    aarch64-linux-gnu-gccGCC for 64-bit ARM Linux targets
    x86_64-w64-mingw32-gccGCC for compiling Windows binaries from Linux

    Structure of a Cross-Compiler:

    <target>-<tool>

    Example:

    • arm-linux-gnueabihf-gcc = cross-compiler for ARM Linux hard-float

    Cross Compilation Example:

    Let’s say you have this simple C file: main.c

    #include <stdio.h>
    int main() {
        printf("Hello from ARM!\n");
        return 0;
    }
    

    Compile for ARM Linux:

    arm-linux-gnueabihf-gcc main.c -o main_arm
    

    Result:

    • Output binary main_arm runs on ARM target (e.g., Raspberry Pi)
    • It will not run on your x86 machine

    💛 Support Embedded Prep

    If you find our tutorials helpful and want to support our mission of sharing high-quality embedded system knowledge, you can contribute by buying us a coffee. Every small contribution helps us keep creating valuable content for learners like you. ☕

    Thank you for your support — it truly keeps Embedded Prep growing. 💻✨

    How to Check Binary Architecture:

    file main_arm
    

    Example Output:

    main_arm: ELF 32-bit LSB executable, ARM, EABI5, dynamically linked...

    Installing Cross Compilers:

    On Ubuntu/Debian:

    sudo apt-get install gcc-arm-linux-gnueabihf

    For bare-metal (no OS):

    sudo apt-get install gcc-arm-none-eabi

    Checklist for Successful Cross Compilation:

    • Correct cross-compiler installed
    • Use proper --sysroot or -I, -L paths if targeting a full OS
    • Link against target-specific libraries
    • Test the binary on the target device

    GCC Optimization Flags: -O0, -O1, -O2, -O3, -Os, -Ofast

    GCC offers optimization flags to control how much it tweaks your code to make it faster, smaller, or more efficient.

    🔧 These optimizations are applied during the compilation stage (.c → .s).

    Why Use Optimization?

    • Improve performance (speed)
    • Reduce code size
    • Enable inlining, loop unrolling, dead code removal, etc.

    Optimization Levels

    FlagDescription
    -O0🔴 No optimization (default in debug mode). Easier to debug.
    -O1🟡 Basic optimization, removes unused code.
    -O2🟢 Aggressive optimization, safe for most applications.
    -O3🔵 Maximum performance. Includes -O2 + inlining & loop unrolling.
    -Os⚪ Optimize for size, not speed.
    -Ofast🚨 Fastest possible, but unsafe: ignores standards (e.g., IEEE/strict aliasing rules)

    Example:

    gcc -O0 main.c -o main_O0
    gcc -O2 main.c -o main_O2
    gcc -O3 main.c -o main_O3
    gcc -Os main.c -o main_Os
    

    Use time ./main_Ox or size main_Ox to compare runtime and binary size.

    Inspect Optimized Code:

    gcc -S -O3 main.c -o main_O3.s
    

    This outputs assembly code so you can see how optimization changes instructions.

    Pro Tip:

    Use optimization with debugging (covered next):

    gcc -g -O2 main.c -o main
    

    This allows debugging even with optimized code, although some variables might be removed or reordered.

    Great! Let’s now cover:

    Debug Flags & Symbols: -g, -O0 to -O3

    When developing software, debugging is crucial. GCC provides flags to include debug symbols and control optimization levels that affect debugging behavior.

    -g: Add Debug Info

    • Embeds debugging symbols (like variable names, function names, source lines) into the binary.
    • These symbols are used by GDB or other debuggers.
    • Doesn’t affect performance or behavior at runtime.

    Example:

    gcc -g main.c -o main
    

    Now you can debug with:

    gdb ./main
    

    Optimization & Debugging

    Flag comboUse case
    -g -O0 (default)Easiest debugging: variables, breakpoints work as expected
    -g -O2 or -g -O3Debug optimized code: might skip or inline functions, remove variables
    -O2 onlyNo debug info, faster execution
    -g onlyDebug symbols, but no optimization

    Test Code:

    #include <stdio.h>
    int main() {
        int x = 42;
        printf("x = %d\n", x);
        return 0;
    }
    

    Compile and debug:

    gcc -g -O0 main.c -o debuggable
    gdb ./debuggable
    

    Inside GDB:

    (gdb) break main
    (gdb) run
    (gdb) print x
    

    If you compile with -O2 or -O3, x may be optimized away:

    gcc -g -O2 main.c -o optimized_debug
    

    Inside GDB:

    (gdb) print x
    

    May return “optimized out”.

    Common Debug Tools:

    ToolPurpose
    gdbDebugger for stepping through code
    valgrindMemory leak checker
    straceTrace system calls
    ltraceTrace library calls

    Combine Flags Smartly

    ✅ Development:

    gcc -g -O0 -Wall -o app app.c
    

    ✅ Release (optional debug):

    gcc -g -O2 -o app app.c
    

    You can also strip symbols before release:

    strip app

    GCC Toolchain Components

    What is a Toolchain?

    A toolchain is a set of programming tools used together to build (compile), link, and analyze software — especially in systems programming like C, C++, and embedded development.

    Think of it like a production line in a factory: each tool does a specific job to turn your source code into a working executable program.

    Basic Steps in a Toolchain:

    1. Preprocessing – handles #include, #define, etc.
    2. Compiling – converts C code into assembly.
    3. Assembling – converts assembly to object code.
    4. Linking – combines object files and libraries into a final executable.

    Each of these steps is done by a specific tool.

    Common Components in a GCC Toolchain:

    StepToolPurpose
    PreprocessingcppProcesses #include, macros
    Compilinggcc or cc1Turns C/C++ into assembly code
    AssemblingasConverts .s to .o
    LinkingldCombines .o files into binary
    DebugginggdbDebugs your program
    Analyzingobjdump, nm, readelfAnalyze and inspect binaries

    Why Use a Toolchain?

    • Automates the process of building software.
    • Helps you go from source code to executable step by step.
    • Gives you fine control over what happens at each stage.
    • Essential in cross-compilation (e.g., compiling code on a PC to run on an embedded board like ARM or ESP32).

    Real-World Analogy:

    Imagine you’re baking a cake:

    • Recipe (source code) → You follow steps (preprocess, compile, etc.)
    • Ingredients → Preprocessed and compiled pieces
    • Oven → The assembler
    • Icing and decoration → Linker adds libraries and finishes the executable

    At the end, you get a ready-to-eat cake, or in our case, a working program!

    In this tutorial, we’ll understand 5 key tools:

    • as — the assembler
    • ld — the linker
    • objdump — the binary disassembler
    • nm — lists symbols
    • readelf — reads ELF binary structure

    1. as — The Assembler

    What it does:

    as converts assembly language (.s) into machine code object files (.o).

    Concept:

    After the compiler generates assembly code from C/C++ (gcc -S), the assembler takes this .s file and converts it into binary instructions that the CPU can understand.

    Example:

    gcc -S hello.c     # Step 1: Get assembly code (hello.s)
    as hello.s -o hello.o  # Step 2: Assemble to object file
    

    You now have an object file hello.o.

    2. ld — The Linker

    What it does:

    ld combines multiple .o files and libraries into a single executable.

    Concept:

    It resolves symbol references (like function calls), adds startup code, and organizes the layout of memory sections (text, data, etc.).

    Example:

    ld -o hello hello.o -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2 -e main
    

    Note: Usually, gcc does this linking automatically. The above is a manual link.

    3. objdump — The Disassembler

    What it does:

    objdump shows you what’s inside an object or binary file. You can view assembly code, symbol tables, and more.

    Concept:

    Useful for debugging or reverse engineering. You can see what machine code was generated from your C/C++ code.

    Example:

    objdump -d hello.o
    

    This disassembles the object file — shows actual assembly instructions.

    You can also inspect all:

    objdump -x hello.o

    4. nm — Lists Symbols

    What it does:

    nm lists symbols (like functions and global variables) from object files or executables.

    Concept:

    Symbols can be functions, variables, or labels. It shows whether they’re defined, undefined, or global/static.

    Example:

    nm hello.o
    

    Sample output:

    00000000 T main
             U printf
    
    • T main: symbol main is defined in text (code) section.
    • U printf: symbol printf is undefined (from libc).

    5. readelf — Reads ELF Binaries

    What it does:

    readelf displays information from ELF (Executable and Linkable Format) files — used by Linux.

    Concept:

    ELF files are used for executables, object files, shared libs, etc. You can view headers, sections, symbol tables, etc.

    Example:

    readelf -h hello.o    # ELF header
    readelf -S hello.o    # Section headers
    readelf -s hello.o    # Symbol table
    

    Summary Table

    ToolPurposeExample
    asAssembles .s to .oas file.s -o file.o
    ldLinks .o to executableld file.o -o file
    objdumpShows machine code, symbolsobjdump -d file.o
    nmLists functions/variablesnm file.o
    readelfDisplays ELF internalsreadelf -h file.o

    Try it Yourself: Mini Demo

    1. Create a file:
    // hello.c
    #include <stdio.h>
    void greet() { printf("Hello!\n"); }
    int main() { greet(); return 0; }
    
    1. Compile step by step:
    gcc -S hello.c            # Creates hello.s
    as hello.s -o hello.o     # Assemble to object file
    ld hello.o -o hello -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2 -e main
    ./hello                   # Run the program
    
    1. Inspect:
    nm hello.o
    objdump -d hello.o
    readelf -h hello

    You can also Visit other tutorials of Embedded Prep 

    Special thanks to @mr-raj for contributing to this article on EmbeddedPr

  • Master Build Process in C from Source Code to Executable (2026)

    Master the Build Process in C from source code to executable. Learn preprocessing, compilation, assembly, and linking for 2025 interviews.

    Master the Build Process in C from source code to executable in this complete 2025 guide. Learn each stage of the Build Process including preprocessing, compilation, assembly, and linking with clear explanations and real interview-oriented insights. Understand how source files are transformed into object files and finally into an executable, along with the role of compilers, assemblers, linkers, and build tools like GCC and Make. This guide is perfect for beginners, embedded engineers, and interview preparation, helping you troubleshoot build errors, optimize builds, and gain a strong foundation in C programming internals. Boost your understanding of the Build Process and confidently answer C interview questions in 2025.

    Build Process in C from ource Code to Executable : Understanding the build process in C is crucial for developers, especially those working with embedded systems, operating systems, or performance-critical applications. The process of transforming human-readable C source code into an executable involves multiple stages. Let’s dive deep into these stages and understand their significance.

    Stages of the Build Process

    The C build process consists of the following major stages:

    • Preprocessing
    • Compilation
    • Assembly
    • Linking

    Each of these stages plays a vital role in generating the final executable file.

    1. Preprocessing (Expanding Macros and Includes)

    The first stage of the build process is preprocessing, where the C preprocessor (cpp) expands macros, processes #include files, and handles conditional compilation directives like #ifdef.

    Key Tasks in Preprocessing:

    • Expands macros (#define)
    • Replaces header file includes (#include)
    • Handles conditional compilation (#ifdef, #ifndef, #endif)

    Example:
    Consider the following program.c:

    #include <stdio.h>  
    #define PI 3.14  
    int main() {  
        printf(\"Value of PI: %f\\n\", PI);  
        return 0;  
    }
    

    After preprocessing, the code will look like this:

    // Expanded version after preprocessing  
    #include <stdio.h>  
    int main() {  
        printf(\"Value of PI: %f\\n\", 3.14);  
        return 0;  
    }
    

    To see the preprocessed output, use:

    gcc -E program.c -o program.i  
    

    2. Compilation (Converting C Code to Assembly)

    In this stage, the compiler (gcc, clang) translates the preprocessed source code into assembly language, which is a low-level representation of the code.

    Example:

    gcc -S program.i -o program.s  
    

    This generates an assembly file (program.s) containing processor-specific instructions.

    Example assembly output (simplified):

    .section .text  
    .globl main  
    main:  
        pushq   %rbp  
        movq    %rsp, %rbp  
        movl    $0, %eax  
        popq    %rbp  
        ret  
    

    3. Assembly (Converting Assembly to Machine Code)

    The assembler (as) takes the assembly code and converts it into machine code, producing an object file (.o file).

    Command to generate object file:

    gcc -c program.s -o program.o  
    

    The .o file contains binary instructions but is not yet a complete executable because it still needs linking.

    4. Linking (Combining Object Files to Create an Executable)

    The linker (ld) combines multiple object files and links necessary system libraries to produce the final executable.

    • Resolves function calls (e.g., linking printf() to libc).
    • Merges object files (.o) into a single executable.
    • Allocates memory for variables and functions.

    Command to link:

    gcc program.o -o program  
    

    After linking, the final executable (program) is created and ready to run:

    ./program  
    

    Complete Build Process in One Command

    Instead of running all steps separately, we can compile and link in one step using:

    gcc program.c -o program  
    

    This internally performs preprocessing → compilation → assembly → linking automatically.

    Understanding Static and Dynamic Linking

    Static Linking:

    • Includes all required libraries in the executable.
    • Larger file size but runs independently.
    • Example: gcc -static program.c -o program

    Dynamic Linking:

    • Links external libraries at runtime (e.g., glibc).
    • Smaller executable but requires shared libraries (.so files).
    • Example: gcc program.c -o program -lm # Links math library dynamically

    Build Automation with Makefiles

    For large projects with multiple files, Makefiles automate the build process efficiently.

    Example Makefile:

    program: main.o helper.o  
        gcc main.o helper.o -o program  
    main.o: main.c  
        gcc -c main.c  
    helper.o: helper.c  
        gcc -c helper.c  
    clean:  
        rm -f *.o program  
    

    To build the program, simply run:

    make  
    

    To clean object files:

    make clean  

    💛 Support Embedded Prep

    If you find our tutorials helpful and want to support our mission of sharing high-quality embedded system knowledge, you can contribute by buying us a coffee. Every small contribution helps us keep creating valuable content for learners like you. ☕

    Thank you for your support — it truly keeps Embedded Prep growing. 💻✨

    Interview Questions of build process

    Build Process Basics

    1. What are the steps involved in the build process of a C/C++ program?
    2. What is the difference between compilation and linking?
    3. What happens during preprocessing? Can you give examples of preprocessing directives?
    4. What is an object file?
    5. Why do we need a linker in the build process?

    Makefile and Build Automation

    1. What is a Makefile and why is it used?
    2. Explain the structure of a Makefile. What are targets, prerequisites, and commands?
    3. How does make know when to rebuild a file?
    4. What is the difference between make and cmake?
    5. What is a phony target in Makefile and why do we use .PHONY?

    Toolchain and Cross-Compilation

    1. What is a cross-compiler and when do you use it?
    2. What’s the difference between gcc and g++?
    3. What are the different stages involved when using a cross-toolchain for embedded development?
    4. How do you build an image for an embedded board using a build system like Yocto or Buildroot?

    Build Configuration and Optimization

    1. What are common compiler optimization flags you use?
    2. What’s the purpose of flags like -Wall, -O2, -g, -std=c99, etc.?
    3. What’s the difference between static and dynamic linking?
    4. How do you debug a build error due to a missing symbol or header file?

    Advanced / Real-World Scenarios

    1. Have you written a custom Makefile or modified a Yocto recipe? Explain.
    2. How do you include third-party libraries in your build process?
    3. Have you used build systems like Yocto, CMake, or Bazel? Compare them.
    4. What’s the role of the linker script in embedded systems?

    Conclusion

    Understanding the build process helps in optimizing code, debugging errors, and improving performance. Whether you\’re debugging linking errors, reducing compilation time, or managing dependencies in large projects, mastering these stages is essential for any C developer.

    Thank you for exploring Build Process in C ! Stay ahead in embedded systems with expert insights, hands-on projects, and in-depth guides. Follow Embedded Prep for the latest trends, best practices, and step-by-step tutorials to enhance your expertise. Keep learning, keep innovating!

    You can also Visit other tutorials of Embedded Prep