[Prev][Next][Index][Thread]

Re: Overriding stuff from OSKit libs, some random remarks, and patches



Michael Hohmuth wrote:
> Unfortunately, this doesn't work as well as I thought 
> at first.  Even if all symbols in the OSKit libraries 
> are weak, they are still preferred over non-weak 
> symbols in my own library as described above.
> :-(

Looking in my Big Binder Of CS Articles,
I find this on page 1-18 of the TIS ELF specs:

"When the link editor combines several relocatable
object files, it does not allow multiple definitions
of STB_GLOBAL symbols with the same name.  On the
other hand, if a defined global symbol exists, the
appearance of a weak symbol with the same name will
not cause an error.  The link editor honors the
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
global definition and ignores the weak ones."
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So, the problem isn't there (I checked it,
GNU LD is okay).

What might be the problem, is this.
say you have a function A() in a file that calls another function B() in
the same file.  The symbol
B is weak. Now there's a non-weak function B() in
a different file, which should replace the weak
one.  However, because A() and B(weak) are in the
same file, could it be that GCC when it emits the
object file directly has A call the weak B, because
they're in the same file so it can directly emit
the correct code ?  I always thought that this
wasn't the case, and that the linker is supposed
to relocate all symbols irregardless of where they
are defined. Turns out this is not the case...
Here are some test files:

----- foo.c
#include <stdio.h>

void foo(void) __attribute__((weak));
void foo(void)
{
        printf("Foo! Bar!\n");
}

void bar(void)
{
        foo();
}
----- foo2.c
#include <stdio.h>

void foo(void)
{
        printf("Hah, you didn't think you'd get"
               "that stupid text did you ?\n");
}
----- main.c
#include <stdio.h>

extern void bar(void);
extern void foo(void);

int main(void)
{
        foo();
        bar();
        return 0;
}
-----

I get the following output:

[root@ramon temp]# ./test 
Hah, you didn't think you'd get that stupid text did you ?
Foo! Bar!
[root@ramon temp]# 

Blah, yuck, etc., this is not what we want.
Inspection of foo.o yields:

[root@ramon temp]# objdump --reloc ./foo.o 
./foo.o:     file format elf32-i386
RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE 
00000004 R_386_32          .rodata
00000009 R_386_PC32        printf
[root@ramon temp]# 

So GCC didn't actually emit a relocation entry
for the invocation of foo() in bar()... in which
case the linker cannot link it, of course.  GCC
thinks the weak version of foo() is the right one
and produces code to reference it:

[root@ramon temp]# objdump --disassemble foo.o     
foo.o:     file format elf32-i386

Disassembly of section .text:

00000000 <foo>:
   0:   55                      pushl  %ebp
   1:   89 e5                   movl   %esp,%ebp
   3:   68 00 00 00 00          pushl  $0x0
   8:   e8 fc ff ff ff          call   9 <foo+0x9>
   d:   83 c4 04                addl   $0x4,%esp
  10:   c9                      leave  
  11:   c3                      ret    
  12:   89 f6                   movl   %esi,%esi

00000014 <bar>:
  14:   55                      pushl  %ebp
  15:   89 e5                   movl   %esp,%ebp
  17:   e8 e4 ff ff ff          call   0 <foo>
  1c:   c9                      leave  
  1d:   c3                      ret    
[root@ramon temp]# 

As I see it, the way to cleanly fix the problem
is to hack GCC adding another command line option,
which instructs it to emit relocation entries for
*all* symbols, even internally referenced ones.
This would slow down linking but then, if you need
it it's nice that you can use it.  However, hacking
GCC doesn't sound like a trivial job (but then,
I have no experience with GCC's internals, it might
not be too hard).

Ramon

References: