fPIC vs. fPIE

fPIC vs. fPIE

这两个都是编译选项,具体可以参考GCC的官方文档 。其作用就是

参考[4], 实际-fPIC和-fPIE的区别非常有限。

It works very much like what PIC does for dynamic libraries, the difference is that a Procedure Linkage Table (PLT) is not created, instead PC-relative relocation is used.

差别就是体现在导出的全局变量上。如果使用-fPIE编译的.o文件,则不会为这些全局变量创建PLT表项。但用-fPIC选项则会创建。则使用-fPIC编译的代码,性能会略差。参考[5],这一点性能损失也非常有限

: 根据编译器同学的推测,这一差别也有可能在链接二进制的时候被抹去。

LLVM和GCC针对这两个编译选项的处理也不尽相同。GCC的处理可以参考[1]。而LLVM的处理是采用互相覆盖的方法。

clang++ -fPIE -fPIC -o a.o -c a.cpp
如果最后一个参数是-fPIC,则fPIE会被忽略。

clang++ -fPIE -fPIC -fPIE -o a.o -c a.cpp
同理,如果最后一个是-fPIE,那么-fPIC就会被忽略。这也是我们编译报错的原因。

cmake编译时的默认选项

cmake在2.8以后加入了编译策略(policy),每个策略拥有一个CMP的编号,例如:CMP0018 — CMake 3.22.1 Documentation。编译策略可以通过cmake_policy命令进行设置。
在CMP0018中,就阐明了针对position independent相关编译选项的设置,即POSITION_INDEPENDENT_CODE。通过设置该选项,可以为构建目标加入-fPIE的编译选项,以及-pie的链接选项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cmake_minimum_required(VERSION 3.18.1)  
project(test
VERSION 2.0
LANGUAGES CXX
)

include(CheckPIESupported)
check_pie_supported() # <<< 加入这两行,-pie才能被成功添加,否则即便有POSITION_INDEPENDENT_CODE,也不会添加-pie的链接选项

set(CMAKE_VERBOSE_MAKEFILE on)
add_library(lib-static STATIC lib-static.cpp)
add_library(lib-shared SHARED lib-shared.cpp)
add_executable(test-exe main.cpp lib-static.cpp)

set_target_properties(test-exe
PROPERTIES
POSITION_INDEPENDENT_CODE ON # <<< 这一参数会为可执行二进制添加-fPIE和-pie
)

-fPIE的参数会通过该编译选项加入要链接到二进制的.o文件中,而动态库会默认加上-fPIC选项,即便设置了POSITION_INDEPENDENT_CODE,也不会加上-fPIE选项。当然如果通过CMAKE_C_FLAG之类的变量强行加上,还是可以的。
另外即便指定了POSITION_INDEPENDENT_CODE,-pie的链接选项仍然不会被加上。可以参考[3]。必须要指定这两行才可以:

1
2
include(CheckPIESupported)  
check_pie_supported()

参考文献

  1. PIC/PIE&ASLR分析

  2. 安全编译选项之 PIE和PIC的区别_weixin_43820063的博客-程序员宅基地 - 程序员宅基地

  3. POSITION_INDEPENDENT_CODE does not add -pie (#14983) · Issues · CMake / CMake · GitLab

  4. What is the -fPIE option for position-independent executables in gcc and ld? | Newbedev

  5. Re: PIE and static libraries