I am currently reading "Modern CMake for C++" ([1]) and I am at page 193 where the concept of "position independent code" is explained. However it is only explained in one page and I didn't completely understand what its purpose is exactly and when I need to set it manually. I am still a noob when it comes to the details of how compiling/linking in C++ works, that's part of why I bought this book. It explains the basic concepts of how compilation/linking works in the previous chapters and I felt like I understood those concepts fairly well (how static libraries are basically archived object files and shared libraries being similar to final executables that can be loaded into runtime dynamically, for example). However, I am not sure how "position indenpendent code" fits in here, or, in other words, I am not sure if I understood it correctly.
From what I have read ([2], [3]) my current understanding is this:
- Static libraries produce position dependent (machine) code by default. Their variables are mapped to hardcoded virtual memory addresses which is more efficient.
- Shared libraries produce position independent machine code. Since they are dynamically loaded into the process runtime, it can't be known to which address their variables will be mapped to. For this purpose, they have a global offset table that acts as a placeholder address that will be resolved to some spot in the virtual memory when they are executed
- When a static library is linked to a shared library, it itself must also be position independent, otherwise... (I don't actually know what would happen otherwise. The book I read just says "you'll run into trouble with CMake" but I don't know what that means).
Please tell me if there is anything wrong in that explanation.
So, let's look at an example project that looks like this:
example/
└── src
├── CMakeLists.txt
├── hiprinter
│ ├── CMakeLists.txt
│ ├── hiprinter.cpp
│ ├── hiprinter.h
│ └── printer
│ ├── CMakeLists.txt
│ ├── printer.cpp
│ └── printer.h
├── prog
│ ├── CMakeLists.txt
│ └── main.cpp
└── warning_props.cmake
Content of the files (from top to bottom):
# src/CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(Prog LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
include(warning_props.cmake)
add_subdirectory(prog)
add_subdirectory(hiprinter)
# Create Graphviz Tree visualizing dependencies
add_custom_target(graphviz ALL
COMMAND ${CMAKE_COMMAND} "--graphviz=dtree.dot" .
COMMAND dot -Tpng dtree.dot -o dtree.png
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
# src/hiprinter/CMakeLists.txt
add_subdirectory(printer)
add_library(hiprinter STATIC hiprinter.cpp)
target_include_directories(hiprinter PRIVATE .)
target_link_libraries(hiprinter PRIVATE printer warning_props)
// src/hiprinter/hiprinter.cpp
#include "hiprinter.h"
#include "printer/printer.h"
#include <iostream>
void printHi()
{
print("hi");
}
// src/hiprinter/hiprinter.h
#pragma once
void printHi();
# src/hiprinter/printer/CMakeLists.txt
add_library(printer SHARED printer.cpp)
target_include_directories(printer PRIVATE .)
target_link_libraries(printer PRIVATE warning_props)
// src/hiprinter/printer/printer.cpp
#include "printer.h"
#include <iostream>
#include <string>
void print(std::string s)
{
std::cout << s << std::endl;
}
// src/hiprinter/printer/printer.h
#pragma once
#include <string>
void print(std::string s);
# src/prog/CMakeLists.txt
add_executable(Prog main.cpp)
target_link_libraries(Prog PRIVATE hiprinter warning_props)
target_include_directories(Prog PRIVATE ..)
// src/prog/main.cpp
#include "hiprinter/hiprinter.h"
int main()
{
printHi();
}
# src/warning_props.cmake
add_library(warning_props INTERFACE)
# Enable ALL warnings and treat them as errors
target_compile_options(warning_props INTERFACE
-Wall
-Wextra
-Wpedantic
-Werror
)
Prog depends on hiprinter, a static library. Since hiprinter itself is static and it depends on a shared library, printer, according to the reasons stated above, I would have to configure hiprinter as position-independent right? Because it is a static library and is position-dependent by default.
So, I'd have to edit src/hiprinter/CMakeLists.txt
like this:
# src/hiprinter/CMakeLists.txt
add_subdirectory(printer)
add_library(hiprinter STATIC hiprinter.cpp)
target_include_directories(hiprinter PRIVATE .)
target_link_libraries(hiprinter PRIVATE printer warning_props)
# Add this:
set_target_properties(hiprinter
PROPERTIES POSITION_INDEPENDENT_CODE
ON
)
Am I doing this right?
Also, are there any mistakes in my "explanation" above?
By the way, this project compiles fine with or without the addition for the PIC property. (I am on Ubuntu 20.04, using GCC 10.3.0)
PS: Another question: If a static library depends on another static library that is PIC, the first static library must also be configured as PIC, right?