I'm using x86 RedHat 6.1, both egcs-2.91.66 and gcc-2.95.2,
and the binutils-2.9.1.0.23-6 RPM.
I'm using C as the back end for a compiler. One of the things this
compiler generates is a jump table consisting of an array of x86 `jmp'
instructions. Machine-generated C code calls through this table like
this:
extern const char jump_table[];
typedef void (*func_ptr)(void);
....
((func_ptr)&jump_table[256])();
This works fine unless I both optimize and compile with -fPIC. When I
compile that way (with both the call and the jump table are in the
same file, FWIW), the above code compiles to this:
call 256+jump_table@PLT
That looks plausible, although I don't know what @PLT does. But when
I load this library with dlopen and try to run it, the code crashes.
gdb shows the above call got mis-linked as this:
call jump_table
In other words, it dropped the 256 byte offset!
Deleting the @PLT from the assembly by hand fixes the problem.
FWIW the code works without optimization because gcc generates
different (slower) code for the call that invokes a different linking
strategy.
I've appended a small test case that shows the bug. The "normal"
binary it compiles works. The one it compiles as a shared library
loses the 3 byte jump table offset and therefore calls 0 bytes into
the table, resulting in a call to `bad'.
Any thoughts? Thanks!
-Mat
------ linker.c ------
#include <stdio.h>
extern const char jump_table[];
int good() { printf("Good\n"); return 0; }
int bad() { printf("Bad\n"); return 1; }
asm("jump_table:
jmp bad
jmp good");
typedef int (*func_ptr)(void);
int
call()
{
/* Call the `jmp good' instruction. When compiled -fPIC
* it loses the offset and calls the beginning of
* the array, invoking `jmp bad'.
*/
return ((func_ptr)&jump_table[2])();
}
----------------------
------ main.c ------
extern int call(void);
int
main()
{
return call();
}
--------------------
------ Makefile ------
CC = egcs
CFLAGS = -O2 -Wall
all: linker pic-linker
./linker
LD_LIBRARY_PATH=. ./pic-linker
linker: main.o linker.o
$(CC) $(CFLAGS) -o $@ $^
liblinker.so: linker.c
$(CC) $(CFLAGS) -shared -fPIC $^ -o $@
pic-linker: main.o liblinker.so
$(CC) $(CFLAGS) -o $@ $^
clean:
rm -f *.o *.so linker pic-linker
--------------------