Kenny McCormack <
gaz...@shell.xmission.com> wrote:
> I've used -pie, though I don't really understand how it is different from
> -fpic/-fPIC. Here's the recipe I use to compile a shared lib that also
> runs as an executable:
>
> gcc -s -W -Wall -Werror -fpic -pie -rdynamic -o libXXX.so XXX.c -ldl
>
> It works well, but I'm not quite sure why we need both -fpic and -pie.
The flags -fpic (-fPIC) and -fpie are for the compiler, and tell it to how
to generate code to load object addresses.
The flags -shared and -pie are for the compile-time linker, and tell it what
boilerplate code and data structures to generate for initializing the
position independent code at load time.
A position independent executable is more complex than a position
independent library because the executable has to bootstrap the stack,
relink _itself_, and do other complex stuff that historically was
accomplished at compile time using fixed addresses; stuff which even a
position independent shared library can take for granted. PIE is becoming
more popular because it's necessary for ASLR of the core executable code.
I don't think it's common to use -fpie, at least not on Linux and the BSDs.
AFAIU you can just build everything with -fPIC, use -pie when linking the
executable and -shared when linking the shared library. (Here's a good
summary:
https://lists.debian.org/debian-devel/2016/05/msg00309.html)
For my projects I almost always use -fPIC in CFLAGS and -shared in SOFLAGS,
which will work with GCC, clang, and Solaris Studio on all the Unix systems
I've tried (Linux, *BSD, AIX, Solaris). The exception is macOS, where -fPIC
is a noop, but -shared must with replaced with either -bundle or
-dynamiclib, plus other optional flags.
FWIW, IME -fPIC is more common than -fpic. -fPIC might generate slightly
slower code or more bloated code on platforms where it matters, but it's a
good default. A well-written Makefile can make it easy to replace without
hacking the build, anyhow.
Here's a portable Makefile snippet which should work with GNU Make, NetBSD
make (aka bmake), OpenBSD make, Solaris make, and possibly others. The trick
is that GNU Make supports $(shell $(CMD)), most everything else supports
$(CMD:sh), and none support both.
BOOL.true = true
BOOL. = false
OS.exec = uname -s | tr 'A-Z' 'a-z'
OS.guess = $(shell $(OS.exec))$(OS.exec:sh)
OS.is_darwin.darwin = true
OS.is_darwin = $(BOOL.$(OS.is_darwin.$(OS.guess)))
SOFLAGS.darwin.true = -dynamiclib
SOFLAGS.darwin.false = -shared
SOFLAGS = $(SOFLAGS.darwin.$(OS.is_darwin))
PIC_CFLAGS = -fPIC
PIE_CFLAGS = -fPIC
PIE_LDFLAGS = -pie
Symmetry would dictate something like DLL_LDFLAGS instead of SOFLAGS, but
SOFLAGS is an established convention for specifying the flags for linking a
shared library. I just threw PIE_CFLAGS in for good measure. Normally I'd
just include $(PIC_CFLAGS) in all object compile commands as I've never had
a situation where I wanted to build object files destined for an executable
differently than for a shared library. I always build everything with -fPIC.
But I typically code in C; C++ introduces a whole universe of linking
headaches.