- Published on
Linking in C Language
Linking in C programming language refers to the process of combining multiple object files and libraries to create an executable program. It helps in modularizing the source code to modify and compile them separately. Linking can be done at compile time or at run time. Linking at compile time is called static linking, and the linking done at run time is called dynamic linking.
The below files are used to explain both processes in detail.
#include<iostream>
using namespace std;
int action(int a, int b);
int main() {
cout << action(3, 4) << endl;
return 0;
}
int action(int a, int b) {
return a * b;
}
int action(int a, int b) {
return a + b;
}
Static linking
Static linking copies the dependent code into the final executable during compile time. The final executable will not depend on the library anymore and it makes the distribution of the software easier and runs faster. But, on the other hand, if something changes in the library, it would require relinking and recompilation.
The final executable main
has the action_one.cpp/action_two.cpp linked and can run as a standalone program.
# Static linking example
g++ main.cpp action_one.cpp -o main && ./main -0-
# 12
g++ main.cpp action_two.cpp -o main && ./main -0-
# 7
# The object files (.o files generated when -c flag is passed) also can be linked together.
g++ main.cpp -c -0-
g++ action_two.cpp -c -0-
ls -0-
# action_two.o main.o
g++ main.o action_two.o -o main && ./main -0-
# 7
Dynamic linking
When the linking happens at run time (when the program is executed) then the process is called dynamic linking. The executable only has the info on the dependent library like its name and version. When the program is run, it searches for the shared library locally to link and execute the code. Dynamic linking makes the software upgrades easier and makes the executable less in size, but sometimes the program runs slower because it has to figure out the dependencies and resolve them during runtime.
To create a shared lib from a source or object, -shared
flag is passed to the compiler. The shared library must have lib prefix and .so as suffix - this is by convention.
Compile action_one.cpp
into libaction.so file.
g++ -shared action_one.cpp -o libaction.so
Compile the source code with dynamic linking. The -l
flag suggests the compiler to use libaction.so library (it auto appends lib and .so) and -L tells the compiler to add the current directory path to find the libaction.so library.
# This creates an executable but the libaction.so is not copied to it. We get the main executable but it cannot execute on its own.
g++ main.cpp -laction -L. -o main
# When you try to run it, it searches in default locations such as /usr/local/lib, /usr/lib and current directory.
./main -0-
# dyld[8172]: Library not loaded: 'libaction.so'
# Referenced from: '<current working directory>'
# Reason: tried: 'libaction.so' (no such file), '/usr/local/lib/libaction.so' (no such file), '/usr/lib/libaction.so' (no such file), '<current working directory>/libaction.so' (no such file), '/usr/local/lib/libaction.so' (no such file), '/usr/lib/libaction.so' (no such file)
# [1] 8172 abort ./main
Either use -L
flag to specify the path or set the env variable LD_LIBRARY_PATH
to the location of the shared library.
./main -L.
# 12
export LD_LIBRARY_PATH=$HOME/<wherever your library is>
./main
The library implementation can be changed without having to recompile main.cpp
. Here, action_two.cpp is used to create libaction.so and main program resolves it without having to recompile.
g++ -shared action_two.cpp -o libaction.so -130-
./main -0-
# 7
ldd
(in linux) or otool
(in mac) are some of the utilities that can be used to list the dependencies of a program.
otool -L main -0-
# main:
# libaction.so (compatibility version 0.0.0, current version 0.0.0)
# /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1300.36.0)
# /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
ldd main
# libaction.so => ./libaction.so (0x0000ffff80320000)
Dynamic linking also allows us to maintain multiple versions of the same library and use one whenever needed using symbolic links. For example, we can maintain libaction.so.1 (from action_one.cpp) and libaction.so.2 (from action_two.cpp) and use libaction.so
as a link to point to one of the two.