This bug is almost identical to #66609 for [sh], except the conditions needed to trigger it are slightly different. Minimal test case (compile with -fPIC; -O level does not matter): __attribute__((__weak__,__visibility__("hidden"))) void foo() { } void *bar() { return (void *)foo; } The expected output should have a relocation for foo, since the weak definition is replaceable by a strong definition from another TU. The actual output has a PC-relative constant pointing to the weak definition. This bug seriously broke musl libc's pthread_cancel.c, where the weak version of the symbol used for updating PC upon cancellation has the wrong contract for stack state, and the strong version from arm-specific asm needs to be used. I still question whether this issue should be fixed on the binutils side for all targets; see: https://sourceware.org/bugzilla/show_bug.cgi?id=18561
Confirmed.
FYI a workaround for this and similar bugs, for users who are unable to upgrade once it's fixed, is to always use -ffunction-sections -fdata-sections. This inhibits the assembler's "optimization" differences between symbols simply because cross-section differences are never constant.
This is an assembler bug and it only affects ARM state (thumb2 is OK). In Thumb2 code we get: 00000000 <foo>: 0: 4770 bx lr 2: bf00 nop 00000004 <bar>: 4: 4801 ldr r0, [pc, #4] ; (c <bar+0x8>) 6: 4478 add r0, pc 8: 4770 bx lr a: bf00 nop c: 00000002 .word 0x00000002 c: R_ARM_REL32 foo While in ARM code we get: 00000000 <foo>: 0: e12fff1e bx lr 00000004 <bar>: 4: e59f0004 ldr r0, [pc, #4] ; 10 <bar+0xc> 8: e08f0000 add r0, pc, r0 c: e12fff1e bx lr 10: fffffff0 .word 0xfffffff0 (no relocation generated). even though the assembly output is equivalent. (resolving as invalid because this isn't a GCC problem but a GAS problem).
Well the binutils side seems to think it's a GCC bug to generate relative address expressions like this; at least that's the response I got when I reported it for sh. See the binutils bug linked in the original report above: https://sourceware.org/bugzilla/show_bug.cgi?id=18561
This particular case is a very specific situation. A definition of foo is guaranteed to exist (you've provided one); but it can be overridden. The definition (due to the use of hidden) has to exist in this share library. Given the above, we can never get to the situation where the symbol could resolve to null, nor could we ever get to the situation where the offset to the definition can't be calculated when linking the shared library. That makes it perfectly reasonable to use a pc-relative relocation from within the literal pool to hold the offset. It's quite possible that with a subtle change to the conditions you could end up requiring the compiler to generate another code sequence, but not in this case.
Oh, and another point; since this is a function symbol, not a data symbol, it can't be subject to a copy relocation at run time, so even protected symbols should be acceptable here.
I agree that the PC-relative relocation in the literal pool is acceptable and what the compiler should be doing. However, the form of the expression the compiler puts in the assembly output does not actually generate a relocation; instead it generates a 'fixup' that the assembler resolves before generating the output object file. I claim it's wrong for the assembler to do this, but Alan Modra claims it's right in comment 8 on binutils bug 18561. I believe a variant of Nick Clifton's patch (from comment 6) that applies to all targets should be applied, eliminating consideration of the 'strict' option to S_FORCE_RELOC for weak definitions since this 'optimization' is always wrong for weak defs. Possibly the whole 'strict' argument should be removed; the other cases where it's used may be wrong too.
Based on the follow-up discussions, I'm re-opening this. While I don't think it's really a GCC bug, we can work around the assembler ambiguity problems in the compiler. To do this, legitimize_pic_address needs to not call arm_pic_static_addr() when a symbol definition is weak, even if it does bind locally.
I'm testing a patch.
Was this ever fixed? I've been using -ffunction-sections -fdata-sections by default for a long time now so it dropped off my radar.
Dup of bug 78253 in the end. It was fixed in GCC 6.4.0 and GCC 5.5.0. *** This bug has been marked as a duplicate of bug 78253 ***