Question

What Are .a and .so Files in C? Static vs Shared Libraries Explained

cunixcompilationshared-libraries.a

Question

I am porting a C application to AIX and am confused about the roles of .a and .so files. What are these file types, and how are they used when building and running a C application?

Short Answer

By the end of this page, you will understand what .a and .so files are, the difference between static and shared libraries, how the compiler and linker use them, and what happens at runtime when your program depends on one of these libraries.

Concept

In C and C-like systems programming, .a and .so files are both library files, but they are used differently.

  • A .a file is usually a static library archive.
  • A .so file is usually a shared object, also called a shared library.

.a files: static libraries

A static library is a bundle of compiled object files collected into one archive file.

For example, if you compile several source files:

math_utils.c
string_utils.c
file_utils.c

You might create a static library like this:

ar rcs libhelpers.a math_utils.o string_utils.o file_utils.o

When you link your program against libhelpers.a, the linker copies the needed machine code from the library into your final executable.

That means:

  • the executable becomes larger
  • the code is included at build time
  • the program usually does not need that .a file at runtime

.so files: shared libraries

A shared library is compiled code that stays outside the executable. The executable contains references to it, and the operating system loads it when the program starts, or sometimes on demand.

For example:

gcc -shared -o libhelpers.so math_utils.o string_utils.o file_utils.o

When you link against libhelpers.so:

  • your executable is usually smaller
  • multiple programs can share the same library in memory
  • the library must usually be available on the target system at runtime

Why this matters

This matters because building and running are two different stages:

  • Build time: the compiler and linker need library files to resolve function references
  • Runtime: the operating system loader may need shared libraries to actually start the program

If you link statically, most code is embedded into the executable. If you link dynamically, the executable depends on external shared libraries being present.

About AIX

On AIX, library naming and behavior can be a little different from Linux. In many Unix-like systems:

  • .a means static archive
  • .so means shared library

But on AIX, shared library content is often stored in archive files too, sometimes inside a .a file rather than a standalone .so file. That is one reason porting to AIX can be confusing: the file extension alone does not always tell the whole story there.

So the core idea is:

  • .a commonly represents an archive library
  • .so commonly represents a shared object
  • the platform decides exactly how those are packaged and loaded

Mental Model

Think of libraries like tool storage for your program.

  • A static library (.a) is like copying the exact tools you need into your own toolbox before leaving the workshop.
  • A shared library (.so) is like leaving the tools in a shared cabinet that many workers can use when needed.

Static library

You pack the tools into your own box:

  • self-contained
  • heavier to carry
  • you do not depend on the cabinet later

Shared library

You borrow tools from the shared cabinet:

  • lighter personal toolbox
  • many people can use the same cabinet
  • you must know where the cabinet is when work begins

This is the key difference:

  • static linking copies code into the program
  • dynamic linking keeps code external and loads it later
Take Quiz

Syntax and Examples

Basic naming convention

On many Unix-like systems, libraries are named like this:

  • libname.a for a static library
  • libname.so for a shared library

When linking with -lname, the linker looks for libname.a or libname.so.

Example: building a static library

Source file

/* greet.c */
#include <stdio.h>

void greet(void) {
    printf("Hello from the library!\n");
}

Main program

/* main.c */
void greet(void);

int main(void) {
    greet();
    return 0;
}

Build steps

gcc -c greet.c -o greet.o
ar rcs libgreet.a greet.o
gcc main.c -L. -lgreet -o app

What happens

  • gcc -c compiles source into an object file
  • ar rcs creates the archive libgreet.a
  • gcc main.c -L. -lgreet -o app links main.c with the library in the current directory

The needed code from libgreet.a gets copied into app.

Example: building a shared library

gcc -fPIC -c greet.c -o greet.o
gcc -shared -o libgreet.so greet.o
gcc main.c -L. -lgreet -o app

What changes here

  • -fPIC creates position-independent code, commonly used for shared libraries
  • -shared builds a shared library
  • the executable links to the shared library instead of embedding all code

At runtime, the system must be able to find libgreet.so.

Running a program with a shared library

On many systems, you may need to set a library search path:

export LD_LIBRARY_PATH=.
./app

This tells the loader where to look for shared libraries.

Common linker style

gcc main.c -L/path/to/libs -lmylib -o app
  • -L/path/to/libs adds a directory to the library search path
  • -lmylib means look for libmylib.a or libmylib.so

The exact choice depends on platform defaults, linker options, and what files are available.

Step by Step Execution

Consider this program:

/* main.c */
void greet(void);

int main(void) {
    greet();
    return 0;
}

And this library code:

/* greet.c */
#include <stdio.h>

void greet(void) {
    printf("Hello from the library!\n");
}

Static linking walkthrough

gcc -c greet.c -o greet.o
ar rcs libgreet.a greet.o
gcc main.c -L. -lgreet -o app

Step by step

  1. greet.c is compiled into greet.o

    • This contains machine code for greet().
  2. greet.o is placed into libgreet.a

    • The .a file is just an archive of object files.
  3. main.c is compiled and linked

    • main() calls greet().
    • The linker sees that greet() is missing.
    • It searches the libraries provided by -lgreet.
    • It finds greet() inside libgreet.a.
    • It copies the required code into app.
  4. You run ./app

    • The executable already contains the library code it needs.
    • The .a file is usually not needed now.

Shared linking walkthrough

gcc -fPIC -c greet.c -o greet.o
gcc -shared -o libgreet.so greet.o
gcc main.c -L. -lgreet -o app

Step by step

  1. greet.c is compiled into position-independent object code.

  2. libgreet.so is built from that object code.

  3. main.c is compiled and linked.

    • The linker records that app needs libgreet.so.
    • It does not copy all the library code into the executable the same way a static link does.
  4. You run ./app

    • The loader checks which shared libraries are required.
    • It searches known library paths.
    • If it finds libgreet.so, the program starts.
    • If not, startup fails with a shared library error.

Key build-vs-run idea

  • Static library: resolve now, include code now
  • Shared library: record dependency now, load code later

Real World Use Cases

Static libraries (.a) are often used when

  • building small self-contained command-line tools
  • deploying software to systems where installing dependencies is difficult
  • embedding a specific library version into a binary
  • reducing runtime dependency problems
  • building for restricted or embedded environments

Example:

  • A maintenance utility is shipped as one executable with no external library setup.

Shared libraries (.so) are often used when

  • many programs use the same library
  • disk space or memory sharing matters
  • libraries need to be updated independently of applications
  • plugin-style systems load code dynamically
  • operating system packages manage common dependencies

Example:

  • Multiple applications use the same SSL or database client library.

In porting work

When moving a C program to another Unix platform such as AIX, you often need to check:

  • whether the target platform prefers static or shared linking for a library
  • where the runtime loader searches for libraries
  • whether a .a file contains static objects, shared objects, or both
  • whether the build scripts assume Linux-style .so behavior
Take Quiz

Real Codebase Usage

In real projects, developers usually do not think only in terms of file extensions. They think in terms of linking strategy, deployment, and runtime loading.

Common patterns

Using system libraries

A project may link with standard libraries like:

gcc app.c -lm -lpthread -o app

The exact files chosen by the linker depend on what is installed and how the system is configured.

Using build tools

In Makefiles, CMake, or Autotools, developers usually specify library names and search paths rather than hardcoding file names.

Example Makefile style:

CFLAGS += -I./include
LDFLAGS += -L./lib
LDLIBS += -lgreet

This keeps the build more portable.

Guarding against runtime failures

When using shared libraries, teams often:

  • install libraries in standard system paths
  • configure runtime search paths
  • package the correct library versions
  • verify dependencies in CI or deployment scripts

Versioned shared libraries

Real systems often use versioned shared libraries such as:

libssl.so
libssl.so.3

This helps compatibility and upgrades.

Plugin loading

Some applications do not link a shared library at build time. Instead, they load it at runtime with platform APIs. This is common for:

  • database drivers
  • image codecs
  • optional features
  • modular server extensions

That is still part of the shared-library idea: code lives outside the executable and is loaded when needed.

Common Mistakes

1. Thinking .a and .so are source files

They are not source code files. They are already compiled binary libraries.

  • .a = archive of object files
  • .so = shared library object

2. Confusing compile time with link time

Beginners often think #include is enough to use a library.

#include only gives declarations. You still need to link the compiled library.

Broken example:

#include "greet.h"

int main(void) {
    greet();
    return 0;
}

If you compile without linking the library:

gcc main.c -o app

You may get an error like:

undefined reference to `greet`

3. Linking succeeds, but the program fails at runtime

This often happens with shared libraries.

You linked successfully, but the loader cannot find the .so file later.

Typical cause:

  • library path not configured
  • library not installed on target machine
  • wrong library version

4. Assuming .a always means static and .so always means shared on every Unix system

This is a useful general rule, but AIX can package shared-library content differently. Always check the platform's conventions.

5. Forgetting position-independent code for shared libraries

On many systems, shared libraries should be built with -fPIC.

Broken example:

gcc -c greet.c -o greet.o
gcc -shared -o libgreet.so greet.o

Better:

gcc -fPIC -c greet.c -o greet.o
gcc -shared -o libgreet.so greet.o

6. Hardcoding exact file names in portable builds

Less portable:

gcc main.c ./lib/libgreet.so -o app

More portable:

gcc main.c -L./lib -lgreet -o app

This lets the linker choose the appropriate file for the platform.

Comparisons

Feature.a static library.so shared library
Full nameArchive libraryShared object
Linked whenBuild timeBuild time and loaded at runtime
Code copied into executableUsually yesUsually no
Executable sizeLargerSmaller
Runtime dependencyUsually noUsually yes
Memory sharing across processesNoYes
Easier deploymentOften yesSometimes harder
Easier independent library updatesNoYes

Static linking vs dynamic linking

  • Static linking means the program gets the needed code during the build.
  • Dynamic linking means the program keeps a reference to external code that is loaded later.

.o vs .a vs .so

File typeMeaning
.oOne compiled object file
.aArchive containing one or more object files
.soShared library loaded dynamically

Which should you choose?

  • Choose static libraries when you want a more self-contained executable.
  • Choose shared libraries when many programs use the same code or when you want easier library updates.
  • On AIX, confirm how the platform expects shared libraries to be packaged before deciding.
Take Quiz

Cheat Sheet

Quick reference

  • .o = object file
  • .a = static library archive
  • .so = shared library

Build a static library

gcc -c file.c -o file.o
ar rcs libname.a file.o
gcc main.c -L. -lname -o app

Build a shared library

gcc -fPIC -c file.c -o file.o
gcc -shared -o libname.so file.o
gcc main.c -L. -lname -o app

Linker naming rule

-lname

usually means:

  • search for libname.a
  • or search for libname.so

Key idea

  • static library: code is copied into executable
  • shared library: code stays external and must be found at runtime

Common flags

  • -c compile only
  • -Ldir add library search directory
  • -lname link library named libname
  • -shared create shared library
  • -fPIC create position-independent code

Watch out for

  • undefined reference means the linker could not find function definitions
  • runtime loader errors usually mean a shared library was not found
  • AIX may use .a files in ways that differ from Linux

FAQ

What is the difference between .a and .so files?

A .a file is usually a static library archive, while a .so file is usually a shared library. Static libraries are copied into the executable during linking; shared libraries are loaded separately at runtime.

Do I need a .a file at runtime?

Usually no. If your program was statically linked with code from a .a file, that code is already inside the executable.

Do I need a .so file at runtime?

Usually yes. The program depends on the shared library being available when it starts, unless it was linked differently or bundled specially.

Why does my program build but not run?

A common reason is that the program links against a shared library successfully, but the system cannot find that library at runtime.

What does -lfoo mean in a C build command?

It tells the linker to search for a library named libfoo, typically something like libfoo.a or libfoo.so.

Are .a files always static on AIX?

Not always in the way Linux developers expect. AIX can package shared-library-related content inside archive files too, so you should check AIX-specific library conventions.

Which is better: static or shared libraries?

Neither is always better. Static libraries help create self-contained binaries. Shared libraries reduce duplication and support easier updates.

Take Quiz

Mini Project

Description

Create a tiny C project that demonstrates both a static library and a shared library using the same function. This project helps you see the difference between build-time linking and runtime loading in a concrete, hands-on way.

Goal

Build one C program that can be linked against a custom library, then observe how behavior differs when using a static library versus a shared library.

Requirements

  1. Create a library function that prints a message.
  2. Build that function into both a .a library and a .so library.
  3. Write a main program that calls the library function.
  4. Link and run the program using the library.
  5. Confirm what file is needed at runtime in each case.
Take Quiz

Keep learning

Related questions

View more