This Time Self-Hosted
dark mode light mode Search

Should only libraries be concerned about PIC?

Position Independent Code is a technique used to create executable code that, as the name implies, is independent from the starting address where it is loaded (the position). This means that the pointers to data and functions in the code, as well as in the default value of pointers cannot be assumed to be always the same as the ones set after the build process in the executable file (or the library).

What this means in practical terms is that, as you can’t be sure how many and which libraries a program might load at runtime, libraries are usually loaded at dynamically-assigned addresses, thus the code need not to statically use one value as base address. When a shared library is loaded with a static base address (thus not using PIC), it has to be relocated by the runtime loader, and that causes changes to the .text section, which breaks the assumption that sections should be either writable or executable, but not both at the same time.

When using PIC, instead, the access to symbols (data and functions) is maintained by a global offset table (GOT), so the code does not need to be relocated, only the GOT and the pointers stored in the data sections. As you can guess, this kind of indirect access takes more time than the direct access that non-PIC code uses, and this is why a lot of people hate the use of PIC in x86 systems (on the other hand, shared libraries not using PIC not only breaks the security assumption noted above, making it impossible to use mitigation technologies like NX – PaX in Linux, W^X in OpenBSD – it also increase the memory usage of software as all the .text sections containing code will need to be relocated and thus duplicated by the copy-on-write).

Using hidden visibility is possible to reduce the performance hit caused by GOT access, by using PC-relative addressing (relative to the position on the file), if the architecture supports them of course. It does not save much for what concerns pointers in the data sections, as they will still need relocations. This is what causes arrays of strings to be written in .data.rel.ro sections rather than .rodata sections: the former gets relocated, the latter doesn’t, so is always shared.

So this covers shared libraries, right? Copy-on-write on shared libraries are bad, shared-libraries use PIC, pointers in data section on PIC code cause copy-on-write. But does it stop with shared libraries?

One often useful security mitigation factor is random base address choice for executables: instead of loading the code always at the same address, it is randomised between different executions. This is useful because an attacker can’t just start guessing at which address the program will be loaded. But applying this technique to non-PIC code will cause relocations in the .text section, which in turn will break another security mitigation technique, so is not really a good idea.

Introducing PIE (Position Independent Executable).

PIE is not really anything new: it only means that even executables are built with PIC enabled. This means that while arrays of pointer to characters are often considered fine for executables, and are written to .rodata (if properly declared) for non-PIC code, the problem with them reappears when using PIE.

It’s not much of a concern usually because the people using PIE are usually concerned with security more than performance (after all it is slower than using non-PIC code), but I think it’s important for software correctness to actually start considering that an issue too.

Under this light, not only it is important to replace pointers to characters with characters array (and similar), but hiding the symbols for executables become even more important to reduce the hit caused by PIC.

I’m actually tempted to waste some performance in my next box and start using PIE all over just to find this kind of problems more easily… yeah I’m masochist.

Comments 9
  1. I use hardened gentoo on a regular basis, i have to say, once you’re used to it, there is no going back, try it out next time you get the chance, the only reason i’m not currently running it on my desktop is that nvidia binary x drivers send it nuts, otherwise though i run it on all computers in the house (5 others) and tell everyone who uses gentoo as a server to do it.

  2. Maybe you could try to lighten your workload by providing links to good documentation about the topic and a list of upstream projects to notify?Best would be a wiki were readers of your blog could mark projects where they filed bug reports with links to them. That way you don’t have to create tons of accounts and more upstream projects are informed. Many will simply reject it of course but in some we might encounter people happy to learn something new.

  3. A good post, albeit there is nothing new with this, given our longstanding subproject.Now I am not so certain that there is some real correlation between these things and the concept of “software correctness”. Replacing “pointers to characters with characters array (and similar)” or “declaring properly” only and only because of some UNIX semantics is vague at best, at least if we move a little towards more general views about “software correctness”, let alone quality. As we know, PIE was created by Red Hat[1], and while being a great invention, it involves some Linux/UNIX politics, and linking itself has very little to do with the actual quality and correctness of code.But personally I tend to also consider these things when actually writing code, albeit these things should not be an obstacle in what you are trying to achieve.We long-term hardened users salut you and hope that maybe you manage to devote some time also for the dark side of Gentoo.Btw, PaX is now also (optionally) part of NetBSD; I have built the whole system with PIE and SSP – it works nicely (excluding pkgsrc).[1] http://sources.redhat.com/m

  4. Well, Linux/Unix politics and the C language are pretty much going well together 🙂 The history of C and Unix goes pretty much along with them together, rather than alone.And while PIE in itself in Linux is an extension rather than a rule, it might as well be a rule under some circumstances.So to me, writing data that falls in .rodata is… writing proper, correct code 🙂

  5. I’m impressed. Your knowledge has come a long way in a short time Diego. Please let us know in the future how those pie+symbol visibility tests go. benchmarks and pretty pictures are always plus for the masses.

  6. Thanks man, you can’t imagine how important is that for me, coming from you :)As for the pretty pictures, I’ve never been much of a visual person, but I admit that does not really help the cause: being able to show the difference in visual terms is quite important indeed. I’ll see to start tomorrow by improving the script I use to analyse bindings to create a gnuplot-compatible output.

  7. What exactly is tecnical difference between -fPIC and -fPIE BTW ?

  8. “Hiding the symbols for executables becomes even more important” – symbols in executables are non-interposable so calls to them from within executable do not go through GOT and can be inlined by compiler (that’s sole difference of -fPIE vs -fPIC). So I’m not sure hidden visibilities will add much in this case, apart from redicing size of dynamic symtab…

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.