Lecture 25

Compilation of a C Program

Static & Shared Libraries

Unit VI: Preprocessor, Macro, Static and Shared Library

Mohsin F. Dar

Assistant Professor, Cloud & Software Operations Cluster

UPES

Lecture Agenda

  • Compilation Process: Understanding the 4 stages of C compilation
  • Preprocessor Phase: How directives are processed
  • Compilation & Assembly: Converting code to machine language
  • Linking: Creating executable programs
  • Static Libraries: Creation and usage
  • Shared Libraries: Dynamic linking concepts
  • Practical Examples: Creating and using both library types

C Program Compilation Process

Four Stages of Transformation

Source Code
(.c file)
Preprocessor
(.i file)
Compiler
(.s file)




Executable
(a.out / .exe)
Linker
Assembler
(.o file)
Key Point: Each stage transforms the code into a form closer to machine-executable format. Understanding this process is crucial for debugging and optimization!

Stage 1: Preprocessing

Handling Directives and Text Manipulation

What Preprocessor Does:

  • Removes all comments from source code
  • Expands all #include directives (includes header files)
  • Expands all #define macros
  • Processes conditional compilation directives (#ifdef, #ifndef, #if)
  • Generates expanded source code (.i file)
// Original code
#include <stdio.h>
#define MAX 100

int main() {
    int x = MAX;  // Macro will be replaced
    return 0;
}
                    
gcc -E program.c -o program.i

Stage 2: Compilation

Converting to Assembly Language

Compiler Tasks:

  • Takes preprocessed code (.i file) as input
  • Performs syntax analysis and checks for errors
  • Performs semantic analysis (type checking)
  • Optimizes the code based on optimization flags
  • Generates assembly language code (.s file)

C Code

int sum = a + b;

Assembly Code

movl a, %eax addl b, %eax movl %eax, sum
gcc -S program.c -o program.s

Stage 3: Assembly

Creating Machine Code

Assembler Functions:

  • Converts assembly language (.s) to machine code
  • Creates object files (.o or .obj)
  • Object files contain binary machine instructions
  • Not yet executable - requires linking
  • Each source file generates one object file
program.s
(Assembly)
ASSEMBLER
program.o
(Object File)
gcc -c program.c -o program.o
Object files contain relocatable machine code with unresolved external references that will be resolved during linking.

Stage 4: Linking

Creating the Final Executable

Linker Responsibilities:

  • Combines multiple object files into one executable
  • Resolves external references (function calls, global variables)
  • Links with standard libraries (printf, scanf, etc.)
  • Links with user-defined libraries
  • Produces final executable file (a.out or .exe)
main.o
utils.o
libc.a
(Standard Library)




LINKER


a.out
(Executable)
Assembler
(.o file)
gcc main.o utils.o -o program

GCC Compilation Commands

Controlling the Compilation Process

Command Description
gcc -E file.c -o file.i Preprocessing only
gcc -S file.c -o file.s Compile to assembly
gcc -c file.c -o file.o Compile to object file
gcc file.c -o program Complete compilation to executable
gcc -Wall file.c Enable all warnings
gcc -O2 file.c Optimization level 2

Understanding Libraries

Code Reusability and Modularity

What is a Library?

  • Collection of precompiled functions and code
  • Provides reusable functionality to programs
  • Reduces code duplication and development time
  • Enables modular programming approach

Two Types of Libraries:

Static Libraries

  • Extension: .a (Linux/Unix)
  • Extension: .lib (Windows)
  • Linked at compile time
  • Code copied into executable

Shared Libraries

  • Extension: .so (Linux/Unix)
  • Extension: .dll (Windows)
  • Linked at runtime
  • Shared among programs

Static Libraries (.a files)

Archive of Object Files

Characteristics:

  • Created using ar (archiver) command
  • Library code is copied into executable at link time
  • Executable becomes larger but self-contained
  • No external dependencies at runtime
  • Faster execution (no dynamic linking overhead)
Advantages: Portable, no runtime dependencies, faster startup

Disadvantages: Larger executable size, updates require recompilation
Object Files
(.o)
ar command
Static Library
(.a)

Creating a Static Library

Step-by-Step Process

Step 1: Create source files

// mathops.h
#ifndef MATHOPS_H
#define MATHOPS_H

int add(int a, int b);
int multiply(int a, int b);

#endif

// mathops.c
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
                    

Step 2: Compile to object file

gcc -c mathops.c -o mathops.o

Step 3: Create static library

ar rcs libmathops.a mathops.o

r = insert/replace, c = create, s = create index

Using a Static Library

Linking with Your Program

Main Program:

// main.c
#include <stdio.h>
#include "mathops.h"

int main() {
    int result1 = add(10, 20);
    int result2 = multiply(5, 6);
    
    printf("Addition: %d\n", result1);
    printf("Multiplication: %d\n", result2);
    
    return 0;
}
                    

Compilation with Library:

gcc main.c -L. -lmathops -o program

-L. = Look for libraries in current directory
-lmathops = Link with libmathops.a (lib prefix and .a suffix are automatic)

Shared Libraries (.so files)

Dynamic Linking at Runtime

Characteristics:

  • Also called Dynamic Shared Objects (DSO)
  • Loaded into memory at runtime
  • Multiple programs can share the same library in memory
  • Smaller executable size
  • Easy to update library without recompiling programs
  • Requires library to be present at runtime
Advantages: Smaller executables, shared memory, easy updates, reduced disk space

Disadvantages: Runtime dependencies, slightly slower startup, version conflicts possible

Creating a Shared Library

Position Independent Code (PIC)

Step 1: Create source files (same as static)

// mathops.h and mathops.c (same as before)
int add(int a, int b) { return a + b; }
int multiply(int a, int b) { return a * b; }
                    

Step 2: Compile with -fPIC flag

gcc -c -fPIC mathops.c -o mathops.o

-fPIC = Position Independent Code (required for shared libraries)

Step 3: Create shared library

gcc -shared -o libmathops.so mathops.o

-shared = Create a shared library

Using a Shared Library

Dynamic Linking Process

Compilation:

gcc main.c -L. -lmathops -o program

-L. = Look in current directory for libraries
-lmathops = Link with libmathops.so

Runtime Linking:

export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./program

The dynamic linker needs to know where to find the shared library at runtime.

Note: For system-wide installation, place the .so file in /usr/local/lib/ and run ldconfig

Static vs Shared Libraries

Choosing the Right Approach

Feature Static Libraries (.a) Shared Libraries (.so)
Linking Time At compile time At runtime
Library Size Larger executable (includes library code) Smaller executable (shared code)
Updates Requires recompilation to update library Can update library without recompiling
Memory Usage Each program has its own copy Shared among programs
Performance Faster (no runtime linking overhead) Slight overhead at runtime
When to use static: When you want to avoid dependency issues or need a standalone executable.
When to use shared: When you need to save disk/memory space or want to update libraries without recompiling.

Summary

Key Points from Lecture 25

  • Compilation process involves multiple stages: preprocessing, compiling, assembling, and linking.
  • GCC commands provide fine control over each compilation stage.
  • Static libraries archive object files and are linked at compile time.
  • Shared libraries use position independent code and dynamic linking at runtime.
  • Choosing between static and shared libraries depends on use case and trade-offs.

End of Lecture 25

Thank you for your attention!