Package: binutils
Version: 2.16.1-2
Severity: normal

I've attached a C source file where ld from binutils >= 2.16.1-1
screws up, relocating a call in the wrong way. I had no problems
with 2.15.*.

I hope a little excerpt from the source code and disasm dumps from
compiled programs will help. It is a cutted down version of a
syscall benchmarking tool I coded up, and it tries to do a syscall
via the vsyscall DSO which is provided by 2.6 kernels on i386.

==== benchmark.c ==== 
static force_inline void DO_sysenter() {
        asm volatile(
                        // syscall 17 on Linux/i386 is ni_syscall ->
                        // not implemented
                        // Probably check this before trying this out
                        "movl $17,%%eax\n\t"
                        "call 0xffffe400\n\t"
                        ::: "eax", "ecx", "edx");
}
=====================

With binutils 2.15-6 installed, gcc generated that code, which is
what I want (that's the entry point of the virtual syscall code the
kernel provides).

==== objdump -d benchmark.2.15-6 ====
 80483a5:       b8 11 00 00 00          mov    $0x11,%eax
 80483aa:       e8 51 60 fb f7          call   ffffe400 <_end+0xf7fb4e14>
=====================================

With binutils 2.16.1-1 I get:

==== objdump -d benchmark.2.16.1-1 ====
 80483b6:       b8 11 00 00 00          mov    $0x11,%eax
 80483bb:       e8 fc e3 ff ff          call   80467bc <_init-0x1ac4>
=====================================

It seems clear that the new binutils relocated the symbol marked as
*ABS* in a wrong way (relative to the current location instead of
absolute):
(0x80483bb + 0xffffe400) % 2**32 == 0x80467bb

This results in a segfault for me.

Some more information I managed to dig up:
gcc -O2 -Wall -ggdb -S -o benchmark.S benchmark.c
as benchmark.S -o benchmark.o
objdump -d -r benchmark.o
=====================================
benchmark.o:     file format elf32-i386

Disassembly of section .text:

00000000 <main>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 08                sub    $0x8,%esp
   6:   83 e4 f0                and    $0xfffffff0,%esp
   9:   83 ec 1c                sub    $0x1c,%esp
   c:   68 00 00 00 00          push   $0x0
                        d: R_386_32     .rodata.str1.1
  11:   e8 fc ff ff ff          call   12 <main+0x12>
                        12: R_386_PC32  puts
  16:   b8 11 00 00 00          mov    $0x11,%eax
  1b:   e8 fc e3 ff ff          call   ffffe41c <main+0xffffe41c>
                        1c: R_386_PC32  *ABS*
  20:   31 c0                   xor    %eax,%eax
  22:   c9                      leave  
  23:   c3                      ret    
=====================================

So far this is the same, no matter what version of gcc/binutils I
use. Trying to link that together with ld, with a commandline stolen
from an strace of gcc (slightly modified library {,search }paths):

ld --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 \
-o benchmark /usr/lib/crt1.o /usr/lib/crti.o \
/usr/lib/gcc/i486-linux-gnu/4.0.2/crtbegin.o \
-L/usr/lib/gcc-lib/i486-linux-gnu/3.3.6/ \
-L/usr/lib/gcc-lib/i486-linux-gnu/3.3.6/../../.. benchmark.o -lgcc \
--as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s \
--no-as-needed /usr/lib/gcc-lib/i486-linux-gnu/3.3.6/crtend.o \
/usr/lib/crtn.o

Looking at the produced executable now shows this:

objdump -d benchmark.2.15-7 | grep '$0x11,' -A 5
=====================================
80483b6:       b8 11 00 00 00          mov    $0x11,%eax
80483bb:       e8 40 60 fb f7          call   ffffe400 <_end+0xf7fb4df0>
80483c0:       31 c0                   xor    %eax,%eax
80483c2:       c9                      leave  
80483c3:       c3                      ret    
80483c4:       90                      nop
=====================================

Looks quite reasonable (notice version 2.15-7)

objdump -d benchmark.2.16.1-1 | grep '$0x11,' -A 5
=====================================
80483b6:       b8 11 00 00 00          mov    $0x11,%eax
80483bb:       e8 fc e3 ff ff          call   80467bc <_init-0x1ac4>
80483c0:       31 c0                   xor    %eax,%eax
80483c2:       c9                      leave  
80483c3:       c3                      ret    
80483c4:       90                      nop
=====================================

Well, this sucks, ld relocated a symbol marked *ABS* as relative to
the current location. The call goes somewhere to nirvana, which
causes a segfault.

I'm not sure whether this was intended or not, but I don't really
think so, and it's not documented anywhere too.

I've had a quick look at the source, but I didn't see anything
obvious. Maybe I can have a deeper look tomorrow.

If you have more questions (probably hard after my lengthy
bugreport), just mail me.

Cheers,
Christian Aichinger

-- System Information:
Debian Release: testing/unstable
  APT prefers unstable
  APT policy: (500, 'unstable'), (500, 'testing'), (1, 'experimental')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.13-rc1r20050702
Locale: [EMAIL PROTECTED], [EMAIL PROTECTED] (charmap=UTF-8)

Versions of packages binutils depends on:
ii  libc6                         2.3.5-3    GNU C Library: Shared libraries an

binutils recommends no packages.

-- no debconf information
#include <stdio.h>

#define force_inline __attribute__((always_inline)) inline

static force_inline void DO_sysenter() {
	asm volatile(
			// syscall 17 on Linux/i386 is ni_syscall ->
			// not implemented
			// Used this to benchmark syscall overhead
			"movl $17,%%eax\n\t"
			"call 0xffffe400\n\t"
			::: "eax", "ecx", "edx");
}

int main(int argc, char *argv[]) {
	printf("I: Starting up\n");
	DO_sysenter();
	return 0;
}

Attachment: signature.asc
Description: Digital signature

Reply via email to