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
--------------------

Reply via email to