The code for R_SPARC_WDISP30 relocations at about line 326 of sysdeps/sparc/sparc64/dl-machine.h doesn't cope with backwards calls: if "value - reloc_addr" comes out as a negative 64-bit quantity, then bits 31 and 30 of the result get set, incorrectly. This causes SIGILL when the resulting code is executed. Modern binutils lays down such WDISP30 relocations for calls to other routines in the same translation unit. In particular, this happens when I build zlib and then someone (e.g. a PNG decoder in the GTK+2 build system) calls zlib's inflateInit, which has a backwards call to inflateInit2 (just before it in memory) that is laid down as a R_SPARC_WDISP30 relocation. Applying the patch below fixes the SIGILL problem. Sparc64 Linux (64-bit userland), GCC 3.3.5, binutils 2.15.94.0.2.2, problem observed using glibc 2.3.2 but identical code appears in 2.3.5. Code before dynamic linking (with stock glibc): --- glibc-2.3.2/sysdeps/sparc/sparc64/dl-machine.h~ 2002-09-28 04:35:31 +0100 +++ glibc-2.3.2/sysdeps/sparc/sparc64/dl-machine.h 2005-06-12 01:03:48 +0100 @@ -322,7 +322,7 @@ elf_machine_rela (struct link_map *map, case R_SPARC_WDISP30: *(unsigned int *) reloc_addr = ((*(unsigned int *)reloc_addr & 0xc0000000) | - ((value - (Elf64_Addr) reloc_addr) >> 2)); + (((value - (Elf64_Addr) reloc_addr) >> 2) & 0x3fffffff)); break; /* MEDLOW code model relocs */
On looking again, this was sort-of user error. I still reckon the glibc code is strictly speaking incorrect, but the reason I encountered the bug is that I'd accidentally not compiled the shared zlib with -fPIC. Adding -fPIC and recompiling results in no WDISP30 relocations (and so perhaps -fPIC *never* generates them) and thus the infelicitous code is never reached.
Fixed in cvs.