Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

-fPIC and -fpic (and building shared libraries)

853 views
Skip to first unread message

Kenny McCormack

unread,
May 15, 2017, 6:51:36 PM5/15/17
to
Basically, my question is "How do you know whether to use -fPIC or to use
-fpic or to use neither?" when compiling/building a shared library.

I know that you need one or the other on Sparc, but I think it is optional
on x86. It seems to work either with or without (whereas on Sparc, it
fails ugly if you don't use it). As an aside, on Cygwin, using -fPIC
generates an error: Something to the effect that the code is always
position independent and that you shouldn't use that option.
Interestingly, on Linux/x86 (which Cyginw is almost, but not entirely, the
same as), no such superfluous error message is generated.

--
I love the poorly educated.

Kaz Kylheku

unread,
May 15, 2017, 9:23:01 PM5/15/17
to
On 2017-05-15, Kenny McCormack <gaz...@shell.xmission.com> wrote:
> Basically, my question is "How do you know whether to use -fPIC or to use
> -fpic or to use neither?" when compiling/building a shared library.

Basically it's like one of two hammers, where you need a bigger one
if the little one doesn't do the job.

On some architectures, there is a way to do position-independent code
to access certain kinds of symbols through a GOT ("global offset
table"). The GOT is not exactly position-independent, but it's patched
up when loaded into memory. The position of the GOT itself is relative
to the code somehow, or via some context pointer (like register $28
on MIPS).

The GOT can be too small for the number of symbols in a shared library,
in which case some alternative approach is needed, for which we bring
out the bigger -fPIC hammer.

I have seen a GOT table overflow on MIPS (huge C++ shared libs with
countless classes, generating lots of symbols).

The worst thing was: it was *just a damned warning* from the GNU linker.
The binaries were generated anyway, with overflowed tables, which didn't
work, causing the code to fail at run-time due to incorrect (wrapping
or truncated) references into the table.

The table limit has to do with the addressing mode which accesses the
GOT: the limited offset field relative to the base register.
On some machines they use an instruction like

load 4(r28, offset), r3 ; r28 has the GOT base pointer

the offset is some limited width field in the instruction, like say,
16 bits, so that the table can only have 64K entries.

Why it's not an issue on x86: x86 has those "de luxe" addressing
modes, like MOV EAX, [EBX + ECX] and whatnot, thanks to which the
offsets into the GOT can be 32 bits wide.

> on x86. It seems to work either with or without (whereas on Sparc, it
> fails ugly if you don't use it). As an aside, on Cygwin, using -fPIC
> generates an error: Something to the effect that the code is always
> position independent and that you shouldn't use that option.

"This error message brought to you by Asperger, the god of not just
letting it slide that you dared request something that happens to be
default behavior in this situation---though not default in half a dozen
other situations in which you'd like to use exactly the same damned
command to keep things simple."

> Interestingly, on Linux/x86 (which Cyginw is almost, but not entirely, the
> same as), no such superfluous error message is generated.

Cygwin is very different in that its dynamic linking is based on
Windows DLL's. The linkage concepts like the global offset table and
whatnot don't apply.

There are no "-fPIC" or "GOT" concepts described in the MSDN library. :)

William Ahern

unread,
May 16, 2017, 1:30:09 AM5/16/17
to
Kaz Kylheku <686-67...@kylheku.com> wrote:
> On 2017-05-15, Kenny McCormack <gaz...@shell.xmission.com> wrote:
<snip>
>> Interestingly, on Linux/x86 (which Cyginw is almost, but not entirely, the
>> same as), no such superfluous error message is generated.
>
> Cygwin is very different in that its dynamic linking is based on
> Windows DLL's. The linkage concepts like the global offset table and
> whatnot don't apply.
>
> There are no "-fPIC" or "GOT" concepts described in the MSDN library. :)

Windows uses a relocation table

https://en.wikipedia.org/wiki/Relocation_(computing)

where the compiler hardcodes an address (like with non-PIC code), then
generates a table that tells the runtime linker which parts of the
executable code to rewrite. Arguably this makes for more performant code.[1]
However, when using ASLR it means DLLs can't be shared between processes,
nor shared between the file buffer cache and the page cache. On ELF-based
systems the bulk of shared libraries need only exist once, in the file
system buffer cache (typically unified with the page cache), which can
substantially reduce memory pressure.

A small irony is that, AFAIU, Windows uses strict memory accounting in
tandem with dynamically grown swap, while Linux only makes a half-hearted
attempt at memory accounting, if it does it at all. I think this is why
Windows software is more susceptible to memory bloat and swap thrashing.

[1] I suspect the real reason for this is simply because Microsoft never
cared to upgrade it's ancient binary format. They never even bothered to
maintain backwards compatibility of their compiler's C runtime until Visual
Studio 2015, from which DLL Hell ultimately derives.

Kenny McCormack

unread,
May 16, 2017, 4:36:04 AM5/16/17
to
In article <201705151...@kylheku.com>,
Kaz Kylheku <686-67...@kylheku.com> wrote:
>On 2017-05-15, Kenny McCormack <gaz...@shell.xmission.com> wrote:
>> Basically, my question is "How do you know whether to use -fPIC or to use
>> -fpic or to use neither?" when compiling/building a shared library.
>
>Basically it's like one of two hammers, where you need a bigger one
>if the little one doesn't do the job.

So, does it really boil down to:

1) Try it with neither option. If it works, done.
else
2) Try it with -fpic. If it works, done.
else
3) Try it with -fPIC. If it works, done.
else
You're screwed.

What I'm worried about is that it may work, say with neither option (case
1) above), but be suboptimal, either in terms of speed or code size. That
would be bad - since you might not know that you should be using one of the
options, even if it (seems to) work without it.

Incidentally, "man gcc" seems to imply that these options are only relevant
on these platforms: SPARC, m68k, RS/6000, and PowerPC. It looks like it
doesn't matter on any other platform.

--
Religion is what keeps the poor from murdering the rich.

- Napoleon Bonaparte -

Marcel Mueller

unread,
May 16, 2017, 11:36:25 AM5/16/17
to
On 16.05.17 00.51, Kenny McCormack wrote:
> Basically, my question is "How do you know whether to use -fPIC or to use
> -fpic or to use neither?" when compiling/building a shared library.

AFAIK -fpic is the usual way.

> I know that you need one or the other on Sparc, but I think it is optional
> on x86.

No. This applies only to 32 bit code.
With 64 bit code you have the same problem. You need -fpic. I recently
walked straight into it.


Marcel

sp...@potato.field

unread,
May 16, 2017, 11:51:07 AM5/16/17
to
One has to wonder why it was made compulsory on x64. If relocatable code is a
requirement to build a shared object then why not just make it implicit with
the -shared option?

--
Spud

Kenny McCormack

unread,
May 16, 2017, 11:54:14 AM5/16/17
to
In article <off69n$i71$1...@gwaiyur.mb-net.net>,
Interesting. I assume you're talking about 64 bit Linux on x86.

I say "interesting", because I've found that with Cygwin on 64 bit Windows,
you not only don't need it, but it is illegal to do so.

Of course, real Windows developers don't use gcc or create .so files.
They use Visual Studio and create .DLL files. I wonder how any or all of
this plays out in that world.

Also, when you say you "Walked straight into it", I assume you mean that
you left the option out and something bad happened. I don't quite
understand this; surely, it just generated either a compiletime or runtime
error and you were able to quickly diagnose and fix the problem. Right?

--
Mike Huckabee has yet to consciously uncouple from Josh Duggar.

Kenny McCormack

unread,
May 16, 2017, 11:57:01 AM5/16/17
to
On the surface, I totally agree with you. Unfortunately, the problem is
that you usually compile your .c's into .o's and then link them all
together using the -shared option. The hitch is that you need to include
-fpic on each of the "compile steps" (i.e., when you make the .o's).

--
"You can safely assume that you have created God in your own image when
it turns out that God hates all the same people you do." -- Anne Lamott

Kaz Kylheku

unread,
May 16, 2017, 1:14:33 PM5/16/17
to
Because -shared is used when linking .o files. It is absent when those
files are built, and the code generation for relocatability has to be
applied then.

sp...@potato.field

unread,
May 17, 2017, 4:19:57 AM5/17/17
to
On Tue, 16 May 2017 15:56:59 +0000 (UTC)
gaz...@shell.xmission.com (Kenny McCormack) wrote:
>In article <off756$pj3$1...@gioia.aioe.org>, <sp...@potato.field> wrote:
>>One has to wonder why it was made compulsory on x64. If relocatable code is a
>>requirement to build a shared object then why not just make it implicit with
>>the -shared option?
>
>On the surface, I totally agree with you. Unfortunately, the problem is
>that you usually compile your .c's into .o's and then link them all
>together using the -shared option. The hitch is that you need to include
>-fpic on each of the "compile steps" (i.e., when you make the .o's).

Ah ok. Its been a while since I've built any .so's.

--
Spud


Philip Guenther

unread,
May 18, 2017, 7:42:47 PM5/18/17
to
On Monday, May 15, 2017 at 3:51:36 PM UTC-7, Kenny McCormack wrote:
> Basically, my question is "How do you know whether to use -fPIC or to use
> -fpic or to use neither?" when compiling/building a shared library.

Unfortunately, as you've found, it depends on the requirements of the ABI/platform being compiled for.


> I know that you need one or the other on Sparc, but I think it is optional
> on x86.

Which x86 and which ABI? The original SysV ELF ABI for 32bit x86 didn't require it, though it was encouraged as without it your library would probably have text-relocations which prevent sharing of the memory pages of the executable code.

When the 64bit x86 ELF ABI ("amd64") came out, common implementations (GNU, Solaris, others?) made it a fatal error to try to build a shared library with text-relocations, making it a requirement to compile the objects with -fpic or -fPIC.

Further, some OSes now support "PIE", position independent executables, adding the -fpie and -fPIE options...and possibly even making -fpie the default. Compiling with -fpie/-fPIE is similar to -fpic/-fPIC, but preserves the knowledge that this code will be part of the executable and therefore subject to optimizations in symbol lookup and binding, thread-local-storage details, etc.


So to your original question "how can you know?": reading the platforms documentation is the most sure bet...if it actually has documentation that clearly says what is needed and not totally general documentation that requires you to already know the answer to find confirmation of it. The Solaris documentation was quite good back in the day, but many open source OSes limit themselves to packaging the general docs for their compiler which covers N cpus on M OSes with K ABIs. <sigh>

Lacking that, the accumulated wisdom of the net? Review of how the system itself is built?


Philip Guenther

Kenny McCormack

unread,
May 23, 2017, 1:24:43 PM5/23/17
to
In article <1f0c6c5d-b44b-4546...@googlegroups.com>,
Philip Guenther <guen...@gmail.com> wrote:
>On Monday, May 15, 2017 at 3:51:36 PM UTC-7, Kenny McCormack wrote:
>> Basically, my question is "How do you know whether to use -fPIC or to use
>> -fpic or to use neither?" when compiling/building a shared library.
>
>Unfortunately, as you've found, it depends on the requirements of the
>ABI/platform being compiled for.

Thanks for confirming what I suspected: That there isn't really a simple,
obvious answer - you sort of have to "just know". It wouldn't be too bad
if there was an algorithm for figuring it out: As I said in a previous
post, if you just try things until it works (*), then you're done. The
worst kind of bug is when something works (or "seems to work"), but is
working sub-optimally. You never find out that you're doing it wrong.

(*) And by "works", I mean "compiles/links". As we all know, programs can
fail to run properly even after they have compiled/linked/passed-minimal-QA.
...

>Further, some OSes now support "PIE", position independent executables,
>adding the -fpie and -fPIE options...and possibly even making -fpie the
>default. Compiling with -fpie/-fPIE is similar to -fpic/-fPIC, but
>preserves the knowledge that this code will be part of the executable and
>therefore subject to optimizations in symbol lookup and binding,
>thread-local-storage details, etc.

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.

>So to your original question "how can you know?": reading the platforms
>documentation is the most sure bet...if it actually has documentation that
>clearly says what is needed and not totally general documentation that
>requires you to already know the answer to find confirmation of it. The
>Solaris documentation was quite good back in the day, but many open source
>OSes limit themselves to packaging the general docs for their compiler
>which covers N cpus on M OSes with K ABIs. <sigh>

Again, as I feared, it is not exactly analytic.

>Lacking that, the accumulated wisdom of the net? Review of how the system
>itself is built?

Yup. There's always the wisdom of the net to fall back on.

--
BigBusiness types (aka, Republicans/Conservatives/Independents/Liberatarians/whatevers)
don't hate big government. They *love* big government as a means for them to get
rich, sucking off the public teat. What they don't like is *democracy* - you know,
like people actually having the right to vote and stuff like that.

Scott Lurndal

unread,
May 23, 2017, 1:52:29 PM5/23/17
to
gaz...@shell.xmission.com (Kenny McCormack) writes:
>In article <1f0c6c5d-b44b-4546...@googlegroups.com>,
>Philip Guenther <guen...@gmail.com> wrote:
>>On Monday, May 15, 2017 at 3:51:36 PM UTC-7, Kenny McCormack wrote:
>>> Basically, my question is "How do you know whether to use -fPIC or to use
>>> -fpic or to use neither?" when compiling/building a shared library.
>>
>>Unfortunately, as you've found, it depends on the requirements of the
>>ABI/platform being compiled for.
>
>Thanks for confirming what I suspected: That there isn't really a simple,
>obvious answer - you sort of have to "just know". It wouldn't be too bad
>if there was an algorithm for figuring it out:

There is software that deals with the platform dependencies.

https://www.gnu.org/software/libtool/manual/libtool.html

Kaz Kylheku

unread,
May 23, 2017, 3:48:51 PM5/23/17
to
The soundbite:

"Libtool hides the complexity of using shared libraries behind a
consistent, portable interface."

The reality:

Scheme Report R6RS PDF: 90 pages

GNU Libtool 2.4.6 Manual PDF: 127 pages

Libtool hides the complexity of making a shared lib so well that it
takes more pages to describe than a major programming language.

You're better off just figuring out what the elementary toolchain
steps are for the platforms you need to support.

William Ahern

unread,
May 23, 2017, 10:30:09 PM5/23/17
to
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.

William Ahern

unread,
May 23, 2017, 11:00:09 PM5/23/17
to
William Ahern <wil...@25thandclement.com> wrote:
> Kenny McCormack <gaz...@shell.xmission.com> wrote:
<snip>
> 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.

I was wrong. -fPIC doesn't work with Solaris Studio. My build code defaults
to -xcode=pic13, but it seems that's equivalent to -fpic. -xcode=pic32 would
be the same as -fPIC.

Noob

unread,
May 24, 2017, 3:24:40 AM5/24/17
to
On 23/05/2017 19:24, Kenny McCormack wrote:

> I've used -pie

Isn't it obvious that one should always use -pie on Mac OS?

... because Apple -pie
0 new messages