0

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
)

GraphViz dependency tree: enter image description here

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?

1 https://en.wikipedia.org/wiki/Position-independent_code

2 GCC -fPIC option

3 Combining CMake object libraries with shared libraries

RedBox
  • 133
  • 9
  • 1
    The explanation might be slightly outdated, current compilers [default to position independent for everything](https://askubuntu.com/questions/911538/disable-pie-and-pic-defaults-in-gcc-on-ubuntu-17-04) due to security advantages (address randomization ability). So things may be simpler now. If you get something wrong, the tools will let you know, so no need to worry too much in advance. Make sure to read the error messages and test things. – teapot418 Feb 08 '23 at 14:34
  • @teapot418 thank you for your comment. did you notice any other incorrect things in the explanation? – RedBox Feb 08 '23 at 14:36
  • @teapot Also, as I said, I don't get any error messages whether I set the property or not (it is a very small example project, admittedly). I just want to make sure I'm doing things right. – RedBox Feb 08 '23 at 14:37
  • 1
    "I don't actually know what would happen otherwise" ... the linker would fail with an error about something like invalid relocation. "Static libraries produce position dependent code"... technically it is [relocatable code](https://en.wikipedia.org/wiki/Relocation_(computing)). – teapot418 Feb 08 '23 at 15:02
  • "When a static library is linked to a shared library, it itself must also be position independent" - It is true. "Since hiprinter itself is static and it depends on a shared library, printer, according to the reasons stated above ..." - Following reasoning is **wrong** since your project exhibits **opposite situation**: a shared library is linked into the static one. There is no such thing in compiler/linker world, but it affects on a CMake project: when your executable is linked with a static library, it is automatically linked with the shared one. – Tsyvarev Feb 08 '23 at 17:09
  • Swap types of `hiprinter` and `printer` libraries - declare `hiprinter` as SHARED and `printer` as STATIC - and you will observe the error in case of absent setting `POSITION_INDEPENDENT_CODE`. – Tsyvarev Feb 08 '23 at 17:17

0 Answers0