I am trying to compile a dynamic loader using LLVM. Part of the IR
for this loader looks like this:
@_rtld_local = hidden alias %struct.rtld_global* @_rtld_global @_rtld_global = unnamed_addr global %struct.rtld_global { ... }
The purpose of _rtld_local is to allow _rtld_global to be referenced
without using the GOT, as this global is accessed before the dynamic
loader initialises the GOT. However, LLVM sees through the alias
and resolves references to _rtld_local to _rtld_global, resulting in
segfaults when the dynamic loader is used.
I'm trying to figure out the best way to fix this. The
GlobalValue::mayBeOverridden() function controls whether LLVM resolves
aliases, and hacking this function to return true if a global is hidden
gets around this particular problem, but converts the function into a
misnomer (a hidden alias may _not_ be overridden, e.g. by LD_PRELOAD,
although I'm pretty sure mayBeOverridden is not intended to care about
LD_PRELOAD) and results in a lot of deoptimisation. I'm thinking
of introducing a new function GlobalAlias::mayBeResolved() which
returns true if mayBeOverridden() returns false and the visibility
of the alias is the same as that of the aliasee, and converting over
the relevant clients of mayBeOverridden to use this new function.
Oops, missed this message. I think there was some reason why I thought
protected visibility would not work, but I just tried it and it turns
out that it does. I'll see what upstream thinks and if they're ok
with it I'll drop the patch.
Upstream didn't go for it [1] (for understandable performance reasons
[2], given that the dynamic loader will be linked into every executable
on the system). Therefore, I'd like to ping my patch [3,4].
I think this is a bug in global opt. We should probably not consider
this a correctness issue in general, but
* It is fine for something low level like a dynamic linker to depend
on some implementation details.
* This is not profitable. Global opt should figure out that accesses
via a hidden alias are faster than via a non-hidden symbol and avoid
the transformation.
Note that it is not just efficiency. You actually get wrong results.
Without a marker like dllimport/dllexport used on windows, the
compiler can only guess if a symbol is on this module or not.
* One option is to be conservative and say it can be in another
module. In that case it will use the GOT to get the address of a
function, which is inefficient if the function is on this module. The
static linker could optimize this a bit. This is what OS X does
(symbols are effectively protected in there).
* The other option is to guess it is on the same module. The produced
code now has a relocation for a nearby entity (it is a 32 bit field),
and the best the dynamic linker can do is fill it with the GOT
address. This is what linux does.
You can observe the different strategies with the attached IL file:
I sometimes day dream of getting something like dllimport/dllexport on
ELF systems, but realistically I think the best we will get to is a
default like OS X with the push for position independent executables.