I came across a strange situation where pointer arithmetic was performed involving dynamically related characters leading to incorrect results. I'm not sure if some linker options are simply missing or if this is a linker error. Can someone explain what is wrong in the following example?
Consider the following code ( lib.c
) for a simple shared library:
#include <inttypes.h> #include <stdio.h> uintptr_t getmask() { return 0xffffffff; } int fn1() { return 42; } void fn2() { uintptr_t mask; uintptr_t p; mask = getmask(); p = (uintptr_t)fn1 & mask; printf("mask: %08x\n", mask); printf("fn1: %p\n", fn1); printf("p: %08x\n", p); }
This operation is a bitwise AND between the address fn1
and the variable mask
. The application ( app.c
) simply calls fn2
as follows:
extern int fn2(); int main() { fn2(); return 0; }
This leads to the following conclusion ...
mask: ffffffff fn1: 0x2aab43c0 p: 000003c0
... which is clearly wrong, because the same result is expected for fn1
and p
. The code runs on the AVR32 architecture and is compiled as follows:
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -c -o lib.o lib.c $ avr32-linux-uclibc-gcc -Os -Wextra -Wall -shared -o libfoo.so lib.o $ avr32-linux-uclibc-gcc -Os -Wextra -Wall -o app app.c -L. -lfoo
The compiler believes that this is the optimal solution for loading the mask
variable into 32-bit register 7 and splitting & -operations into two assemblers of operations with direct operands.
$ avr32-linux-uclibc-objdump -d libfoo.so 000003ce <fn1>: 3ce: 32 ac mov r12,42 3d0: 5e fc retal r12 000003d2 <fn2>: ... 3f0: e4 17 00 00 andh r7,0x0 3f4: e0 17 03 ce andl r7,0x3ce
I assume that the immediate operands of the and
commands do not move to the load address fn1
when the shared library is loaded into the application address space:
- Is this behavior intentional?
- How can I find out if there is a problem when linking a shared library or when downloading an executable file?
Reference Information. These are not academic questions. OpenSSL and LibreSSL use similar code, so changing the source of C is not an option. The code runs well on other architectures, and, of course, there is an inappropriate reason for performing bitwise operations on function pointers.