Bug 106834 - GCC creates R_X86_64_GOTOFF64 for 4-bytes immediate
Summary: GCC creates R_X86_64_GOTOFF64 for 4-bytes immediate
Status: RESOLVED MOVED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 12.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2022-09-05 08:15 UTC by Rui Ueyama
Modified: 2022-10-21 05:17 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Rui Ueyama 2022-09-05 08:15:43 UTC
I think I found a GCC bug. Here is how to reproduce the issue:

```
$ cat foo.c
#include <stdio.h>

extern char _GLOBAL_OFFSET_TABLE_[];

int main() {
  printf("%lx", (unsigned long)_GLOBAL_OFFSET_TABLE_);
}

$ gcc-12 -c -fPIC foo.c
$ gcc -o foo foo.o
$ ./foo
Illegal instruction (core dumped)
```

The resulting executable crashes with an illegal instruction because the linker overwrites instructions with an immediate. Take a look at the following objdump output:

```
 $ objdump -dr foo.o

foo.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:   f3 0f 1e fa             endbr64
   4:   55                      push   %rbp
   5:   48 89 e5                mov    %rsp,%rbp
   8:   48 8b 05 00 00 00 00    mov    0x0(%rip),%rax        # f <main+0xf>
                        b: R_X86_64_GOTOFF64    _GLOBAL_OFFSET_TABLE_-0x4
   f:   48 89 c6                mov    %rax,%rsi
  12:   48 8d 05 00 00 00 00    lea    0x0(%rip),%rax        # 19 <main+0x19>
                        15: R_X86_64_PC32       .rodata-0x4
  19:   48 89 c7                mov    %rax,%rdi
  1c:   b8 00 00 00 00          mov    $0x0,%eax
  21:   e8 00 00 00 00          call   26 <main+0x26>
                        22: R_X86_64_PLT32      printf-0x4
  26:   b8 00 00 00 00          mov    $0x0,%eax
  2b:   5d                      pop    %rbp
  2c:   c3                      ret
```

At offset 0xb, there's a relocation of type R_X86_64_GOTOFF64. GOTOFF64 makes the linker to write a 8-bytes offset to a given symbol. However, the instruction for that relocation is just `mov` and not `movabs`, so the subsequent 4-bytes are accidentally overwrote byt eh linker.
Comment 1 Rui Ueyama 2022-09-05 08:17:20 UTC
It was originally reported to the mold linker. For the record, here is the link to the original issue: https://github.com/rui314/mold/issues/693
Comment 2 Alexander Monakov 2022-09-05 08:49:29 UTC
Can you show how gcc -S output looks for you on this testcase? For me the problematic instruction is just

        movl    $_GLOBAL_OFFSET_TABLE_, %eax

or

        leaq    _GLOBAL_OFFSET_TABLE_(%rip), %rax

with -fpie, so it's the assembler who chooses the relocation type (which would make that a Binutils bug).
Comment 3 Rui Ueyama 2022-09-05 08:54:48 UTC
Here is my gcc -S output:

$ gcc-12 -S -o- foo.c
        .file   "foo.c"
        .text
        .section        .rodata
.LC0:
        .string "%lx"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        leaq    _GLOBAL_OFFSET_TABLE_(%rip), %rax
        movq    %rax, %rsi
        leaq    .LC0(%rip), %rax
        movq    %rax, %rdi
        movl    $0, %eax
        call    printf@PLT
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
Comment 4 Alexander Monakov 2022-09-05 09:38:50 UTC
Probably a Binutils bug then, with binutils-2.37 I get the correct

   4:   48 8d 05 00 00 00 00    lea    0x0(%rip),%rax        # b <main+0xb>
                        7: R_X86_64_GOTPC32     _GLOBAL_OFFSET_TABLE_-0x4

Can you please report it against binutils at https://sourceware.org/bugzilla/ and mention the link here?
Comment 5 Martin Liška 2022-09-05 10:24:50 UTC
(In reply to Alexander Monakov from comment #4)
> Probably a Binutils bug then, with binutils-2.37 I get the correct

Do you mean gas or ld?

> 
>    4:   48 8d 05 00 00 00 00    lea    0x0(%rip),%rax        # b <main+0xb>
>                         7: R_X86_64_GOTPC32     _GLOBAL_OFFSET_TABLE_-0x4

How did you get this output, please (from foo.o or final executable)?
Comment 6 Alexander Monakov 2022-09-05 11:20:19 UTC
(In reply to Martin Liška from comment #5)
> Do you mean gas or ld?

gas

> How did you get this output, please (from foo.o or final executable)?

From foo.o like in comment #0.
Comment 7 Martin Liška 2022-09-05 11:39:54 UTC
Note I get

			7: R_X86_64_GOTOFF64	_GLOBAL_OFFSET_TABLE_-0x4

when I use both as 2.37 and 2.39 with the output provided by gcc-12 -fPIC:

$ cat foo.s
	.file	"pr106834.c"
	.text
	.section	.rodata
.LC0:
	.string	"%lx"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movq	_GLOBAL_OFFSET_TABLE_@GOTPCREL(%rip), %rax
	movq	%rax, %rsi
	leaq	.LC0(%rip), %rax
	movq	%rax, %rdi
	movl	$0, %eax
	call	printf@PLT
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (SUSE Linux) 12.2.1 20220830 [revision e927d1cf141f221c5a32574bde0913307e140984]"
	.section	.note.GNU-stack,"",@progbits

while using the assembly from Comment 3 I get:
			b: R_X86_64_GOTPC32	_GLOBAL_OFFSET_TABLE_-0x4

for both as versions.
Comment 8 Alexander Monakov 2022-09-05 11:53:16 UTC
Right, sorry, due to presence of 'main' I overlooked -fPIC in comment #0, and then after my prompt it got dropped in comment #3.

If you modify the testcase as follows and compile it with -fPIC, it's evident that GCC is treating both external symbols the same, but gas does not. Similar to PR 106835, it seems Binutils is special-casing by symbol name. But here the situation is worse, because GCC output is mentioning the intended relocation kind:

        movq    _GLOBAL_OFFSET_TABLE_@GOTPCREL(%rip), %rax

so silently using R_X86_64_GOTOFF64 instead doesn't look right.

#include <stdio.h>

extern char _GLOBAL_OFFSET_TABLE_[];
extern char xGLOBAL_OFFSET_TABLE_[];

int main() {
  printf("%lx", (unsigned long)_GLOBAL_OFFSET_TABLE_);
  printf("%lx", (unsigned long)xGLOBAL_OFFSET_TABLE_);
}
Comment 9 H.J. Lu 2022-09-05 16:35:56 UTC
_GLOBAL_OFFSET_TABLE_ is a special symbol and can't be accessed like regular
symbols.  To workaround it:

[hjl@gnu-tgl-3 tmp]$ cat x.c
#include <stdio.h>

extern char GLOBAL_OFFSET_TABLE[];

int main() {
  printf("%lx\n", (unsigned long)GLOBAL_OFFSET_TABLE);
}
[hjl@gnu-tgl-3 tmp]$ gcc -fPIC x.c
/usr/local/bin/ld: /tmp/cc4Lekj4.o: in function `main':
x.c:(.text+0x7): undefined reference to `GLOBAL_OFFSET_TABLE'
collect2: error: ld returned 1 exit status
[hjl@gnu-tgl-3 tmp]$ gcc -fPIC x.c -Wl,--defsym,GLOBAL_OFFSET_TABLE=_GLOBAL_OFFSET_TABLE_
[hjl@gnu-tgl-3 tmp]$ ./a.out 
403fe8
[hjl@gnu-tgl-3 tmp]$
Comment 10 Alexander Monakov 2022-09-05 21:17:09 UTC
Okay, so this should have been reported against Binutils, but since we are having the conversation here: the current behavior is not good, gas is silently selecting a different relocation kind for no clear reason. Why is it not a warning or an error? Note that if you assemble such GOT reference via NASM:

extern _GLOBAL_OFFSET_TABLE_
default rel
f:
        mov     rax, [_GLOBAL_OFFSET_TABLE_ wrt ..gotpc]
        ret

then t.o has

0000000000000000 <f>:
   0:   48 8b 05 00 00 00 00    mov    0x0(%rip),%rax        # 7 <f+0x7>
                        3: R_X86_64_GOTPCREL    _GLOBAL_OFFSET_TABLE_-0x4
   7:   c3                      ret

and ld -shared --no-relax -o t.so t.o does not reject it and t.so has

0000000000001000 <f>:
    1000:       48 8b 05 f1 1f 00 00    mov    0x1ff1(%rip),%rax        # 2ff8 <_DYNAMIC+0xe0>
    1007:       c3                      ret

and without --no-relax:

0000000000001000 <f>:
    1000:       48 8d 05 f9 1f 00 00    lea    0x1ff9(%rip),%rax        # 3000 <_GLOBAL_OFFSET_TABLE_>
    1007:       c3                      ret

So I don't see the reason why it's special-cased in gas.
Comment 11 H.J. Lu 2022-09-06 15:54:57 UTC
I opened:

https://sourceware.org/bugzilla/show_bug.cgi?id=29551
Comment 12 H.J. Lu 2022-09-06 17:53:18 UTC
We can't use

movq    _GLOBAL_OFFSET_TABLE_@GOTPCREL(%rip), %rax

to get the address of _GLOBAL_OFFSET_TABLE_ since there is no entry for
_GLOBAL_OFFSET_TABLE_ in GOT.  We can't use

movl $_GLOBAL_OFFSET_TABLE_, %eax

either since it generates R_X86_64_GOTPC32 relocation.  The reliable way to get
the address of _GLOBAL_OFFSET_TABLE_ is to use

leaq	_GLOBAL_OFFSET_TABLE_(%rip), %rsi
Comment 13 Andrew Pinski 2022-10-21 05:17:57 UTC
.