Lesson 21 of 29 ↓
- 1.Rhel 9 Administration Guide
- 2.Ubuntu Boot Process Guide
- 3.Linux Basic Commands Guide
- 4.Comprehensive Unix Shells Guide
- 5.Mastering Vim Deep & Practical Guide
- 6.Magic Numbers in Linux Deep Systems Guide
- 7.Wildcards & Globbing in Linux Deep Cli Guide
- 8.Linux Users & Groups Deep Administration Guide
- 9.Journaling in Linux Filesystems Deep Systems Guide
- 10.Getopt & Getoptlong Deep System-level Option Parsing Guide
- 11.Su Command in Linux Complete Guide with Environment Variables
- 12.Linux File Permissions and Ownership Complete Practical Guide
- 13.Ubuntu Package Management Apt Complete Guide
- 14.Bash Scripting Basics
- 15.Linux File System Essentials
- 16.Process and Thread Management
- 17.Session 10 : Introduction to Networking & the Application Layer
- 18.Linux Networking Cli & Introduction to Yocto for Machine Routing
- 19.Comprehensive Guide to Ssh (secure Shell)
- 20.Logical Volume Management (lvm) Guide
- 21.C Build Process & Libraries Guide
- 22.Archiving and Compression in Linux
- 23.Linux Package Managers: a Detailed Guide
- 24.Misra-c Guidelines Detailed Cheat Sheet
- 25.Mqtt Server Setup (pc as Server)
- 26.Detailed Explanation of Tty and Pts in Linux
- 27.Vim Cheat Sheet
- 28.Difference Between apt and apt-get
- 29.Difference Between apt Upgrade and apt Full-upgrade
7 min read
C Build Process & Libraries Guide
This guide explains the basic steps to convert source code from a .c file into an executable program, as well as how to create and use Static and Shared Libraries.
First: the 4 Stages of the Build Process
The build process goes through four main stages, and we can use gcc (GNU Compiler Collection) to control each stage.
Preprocessing
What happens? The Preprocessor reads the .c source code and executes commands that start with a #.
-
#include: This line is replaced with the content of the included file (e.g.,stdio.h). -
#define: Every "macro" is replaced with its defined value. -
Removing Comments: All comments (
/* ... */and// ...) are deleted.
Output File: A "Pure C Code" file with a .i extension.
Command:
# -E: Means "Stop after the preprocessing step only"
# -o source.i: Specify the output filename
gcc -E source.c -o source.iCompilation
What happens? The Compiler takes the .i file (raw code) and converts it to Assembly Language specific to the target architecture (e.g., x86). This step also includes checking for syntax errors and performing optimizations.
Output File: An Assembly Language file with a .s extension.
Command:
# -S: Means "Stop after the compilation step only"
# (Uppercase S)
gcc -S source.i -o source.sAssembly
What happens? The Assembler converts the assembly code (the .s file) into Machine Code, turning human-readable instructions (like MOV, ADD) into binary numbers that the processor understands.
Output File: An Object File with a .o extension. This file contains binary code but is not yet executable because it needs linking.
Command:
# -c: Means "Compile and assemble, but do not link"
# (Lowercase c)
gcc -c source.s -o source.oLinking
What happens? This is the final stage. The Linker gathers your object file(s) (like source.o) along with any other libraries the program needs (like the printf function from the C standard library) to produce a complete executable file.
Output File: An Executable File. (e.g., .exe on Windows, or typically no extension on Linux/Mac).
Command:
# This is the default step if you don't specify -E, -S, or -c
# gcc calls the linker (ld) automatically
gcc source.o -o program.exeCreating a Map File
A map file is a text file that shows how the Linker organized functions and variables in memory within the executable. This is very useful for advanced debugging.
# -Wl,...: This flag passes the commands that follow it directly to the Linker (ld)
# -Map=main.map: We ask the linker to create a map file named main.map
gcc test.c -o test -Wl,-Map=main.mapSecond: Static Libraries
A static library is an "archive" (a file with a .a extension) that contains a collection of object files (.o). When you link your program against it, the code your program needs from the library is copied into your final executable file. This makes the executable larger but self-contained.
Creating the Static Library
Step A: Create Object Files First, we need to convert all the .c files we want in the library into .o files.
# Use -c to create .o files only
gcc -c file1.c -o file1.o
gcc -c file2.c -o file2.o
# ... and so on
# Or compile all .c files in the current directory at once
gcc -c *.cStep B: Create the Archive (Library) We use the ar (archiver) tool to create the library from the .o files.
# ar: The archiver tool
# r: Add files to the archive (or replace them if they already exist)
# c: Create the archive if it doesn't exist
# s: Create an index of the files inside the archive (important for linking speed)
# libfile.a: The library name. (It is conventional to start the name with "lib")
ar rcs libfile.a file1.o file2.o *.oUseful command: To list the contents of the library (which .o files are inside):
ar t libfile.aUsing the Static Library (linking with It)
Let's assume you have a program main.c that uses functions from your library libfile.a, which is located in a folder named mylib.
Step A: Compile the Main Program First, we create the main.o object file.
# -I./mylib: This tells the compiler to look in the "mylib" folder
# for header files (.h) that you #included in main.c
gcc -c main.c -o main.o -I./mylibStep B: Link the Program with the Library to Create the Executable
# main.o: Your program's object file
# -L./mylib: This tells the Linker to look in the "mylib" folder
# for library files
# -lfile: This tells the Linker to look for a library named "libfile.a"
# (The linker automatically adds "lib" to the beginning and ".a" to the end of "file")
gcc main.o -o main_program -L./mylib -lfileThird: Shared (dynamic) Libraries
A shared library (or "Shared Object" - .so file on Linux) is different. The code is not copied into your executable. Instead, the executable only contains a reference to the library. The operating system loads the shared library into memory at runtime when you run the program.
Advantages:
-
Smaller Executables: Your program file is much smaller.
-
Efficiency: If 10 programs use the same library, the OS only loads one copy of the library into memory.
-
Updatability: You can update the shared library (e.g., with bug fixes) without recompiling your main program.
Creating the Shared Library
Step A: Create Position-Independent Object Files When creating a shared library, you must compile your source files into Position-Independent Code (PIC). This allows the library to be loaded at any memory address by the OS.
# -fPIC: Generate Position-Independent Code. This is mandatory for shared libraries.
# -c: Compile only, don't link.
gcc -c -fPIC file1.c -o file1.o
gcc -c -fPIC file2.c -o file2.o
# Or for all files:
gcc -c -fPIC *.cStep B: Create the Shared Library Now, link these .o files together using the -shared flag.
# -shared: Tells gcc to create a shared library (.so file)
# *.o: All the PIC object files
gcc -shared -o libfile.so *.oAlternative (One-Step Method): You can also combine these two steps into one:
gcc -fPIC -shared *.c -o libfile.soCompiling with the Shared Library (linking)
This step looks very similar to static linking. You tell the linker where to find the library and which one to use.
# -I./mylib: Tell the compiler where to find header files (.h)
# -L./mylib: Tell the linker where to find the library file (libfile.so)
# -lfile: Link against the library named "libfile.so"
# (The linker will prefer libfile.so over libfile.a if both exist)
gcc main.c -o main_program -I./mylib -L./mylib -lfileAt this point, main_program is created, but it only knows the name libfile.so, not where to find it at runtime.
Running the Program (runtime Linking)
If you try to run ./main_program directly, you will likely get an error: ./main_program: error while loading shared libraries: libfile.so: cannot open shared object file: No such file or directory
This is because the OS loader (which runs your program) doesn't know to look in the ./mylib folder. The -L flag was only for the compiler/linker at build time.
You must tell the OS loader where to find your .so file using the LD_LIBRARY_PATH environment variable.
Method 1: Set for a single command Prefix the command with the variable. This is great for testing.
LD_LIBRARY_PATH=./mylib ./main_programMethod 2: Set for the current terminal session Use export to set the variable for your current shell.
export LD_LIBRARY_PATH=./mylib
./main_program
# You can now run ./main_program freely until you close this terminalNote on "Real" Installation: For permanent installation, developers typically copy the .so file to a standard system directory (like /usr/local/lib) and then run sudo ldconfig to update the system's cache of known libraries.
Comments
Loading the discussion…