Issue 97937
Summary [BOLT] Optimizing immediate relocation with addends produces incorrect assembly
Labels BOLT
Assignees
Reporter Lawqup
    I ran into an issue running BOLT on a program that accesses a global struct field provided by the linker. When accessing a field of that struct, the optimized binary wipes the addend and instead replaces reads of that field with reads of the struct+0.

## Versions of things

I tested with both x86 and aarch64 and managed to reproduce.

```sh
$ llvm-bolt --version
LLVM (http://llvm.org/):
  LLVM version 19.0.0git
 Optimized build with assertions.
BOLT revision bb6a4850553dd4140a5bd63187ec1b14d0b731f9
```

```sh
$ gcc --version 
gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-17)
```

## Repro
This is a minimal repro on x86.

The following is the C, `print_global.c`
```c
#include <stdio.h>
#include <stdint.h>

struct build_id_note {
  char pad[16];
  uint8_t hash[20];
};

extern const struct build_id_note build_id_note;

void print_build_id()
{
	int x;

	for (x = 0; x < 20; x++) {
		printf("%02hhx", build_id_note.hash[x]);
	}
        printf("\n");
}

int main() {
        print_build_id();
        return 0;
}
```

I used the default linker script outputted by the `-Wl,-verbose` gcc flag, then inserted the `build_id_note` at an absolute location right under that first `PROVIDE` in `build_id.ld`, like so:

```ld
/* first bit of default linker script removed */
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
  .note.gnu.build-id (0x400200):
   {
 build_id_note = ABSOLUTE(.);
    *(.note.gnu.build-id)
   }
  /* rest of default linker script removed */
}
```

This is compiled with
```sh
gcc -o print_global print_global.c -Wl,-T,build_id.ld -Wl,--emit-relocs -Wl,--build-id=sha1
```

Then bolting:
```sh
$ llvm-bolt ./print_global -o print_global.bolted --funcs print_build_id         
BOLT-INFO: Target architecture: x86_64
BOLT-INFO: BOLT version: bb6a4850553dd4140a5bd63187ec1b14d0b731f9
BOLT-INFO: first alloc address is 0x200000
BOLT-INFO: creating new program header table at address 0x800000, offset 0x600000
BOLT-INFO: enabling relocation mode
BOLT-INFO: enabling -align-macro-fusion=all since no profile was specified
BOLT-INFO: enabling lite mode
BOLT-INFO: 0 out of 12 functions in the binary (0.0%) have non-empty execution profile
BOLT-INFO: setting _end to 0xa001cc
BOLT-INFO: patched build-id (flipped last bit)
```

The output of the non-bolted binary is:
```sh
$ ./print_global
74007820799e8dc7eb6ff742fe419c5866842b5b
```

Yet, the bolted binary produces the wrong result
```
$ ./print_global.bolted 
040000001400000003000000474e550074007820
```

Of course, the build id is still as expected for the bolted binary:
```sh
$ readelf -n print_global.bolted 
Displaying notes found in: .note.gnu.build-id
 Owner                 Data size       Description
  GNU 0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
    Build ID: 74007820799e8dc7eb6ff742fe419c5866842b5a
```

So this is a case where BOLT leads to incorrect runtime behavior. Inspecting the assembly, we see the root of the problem is that the immediate address for `build_id_note.hash` is missing the addend and thus we print starting from the `pad` field:

Non-bolted asm:
```sh
$ gdb -batch -ex "disassemble print_build_id" print_global                
Dump of assembler code for function print_build_id:
   0x00000000004004e7 <+0>: push   %rbp
   0x00000000004004e8 <+1>:     mov    %rsp,%rbp
 0x00000000004004eb <+4>:     sub    $0x10,%rsp
   0x00000000004004ef <+8>: movl   $0x0,-0x4(%rbp)
   0x00000000004004f6 <+15>:    jmp    0x40051c <print_build_id+53>
   0x00000000004004f8 <+17>:    mov -0x4(%rbp),%eax
   0x00000000004004fb <+20>:    cltq   
 0x00000000004004fd <+22>:    movzbl 0x400210(%rax),%eax <---------- Access of build_id_note.hash
   0x0000000000400504 <+29>:    movzbl %al,%eax
 0x0000000000400507 <+32>:    mov    %eax,%esi
   0x0000000000400509 <+34>: mov    $0x4005d0,%edi
   0x000000000040050e <+39>:    mov $0x0,%eax
   0x0000000000400513 <+44>:    callq  0x400410 <printf@plt>
 0x0000000000400518 <+49>:    addl   $0x1,-0x4(%rbp)
 0x000000000040051c <+53>:    cmpl   $0x13,-0x4(%rbp)
   0x0000000000400520 <+57>:    jle    0x4004f8 <print_build_id+17>
   0x0000000000400522 <+59>: mov    $0xa,%edi
   0x0000000000400527 <+64>:    callq  0x400400 <putchar@plt>
   0x000000000040052c <+69>:    nop
   0x000000000040052d <+70>:    leaveq 
   0x000000000040052e <+71>:    retq   
End of assembler dump.

```

Bolted asm:
```sh
$ gdb -batch -ex "disassemble print_build_id" print_global.bolted 
Dump of assembler code for function print_build_id:
 0x0000000000a00000 <+0>:     push   %rbp
   0x0000000000a00001 <+1>: mov    %rsp,%rbp
   0x0000000000a00004 <+4>:     sub    $0x10,%rsp
 0x0000000000a00008 <+8>:     movl   $0x0,-0x4(%rbp)
   0x0000000000a0000f <+15>:    jmp    0xa00035 <print_build_id+53>
   0x0000000000a00011 <+17>: mov    -0x4(%rbp),%eax
   0x0000000000a00014 <+20>:    cltq   
 0x0000000000a00016 <+22>:    movzbl 0x400200(%rax),%eax <---------- Uh oh, should be 0x400210
   0x0000000000a0001d <+29>:    movzbl %al,%eax
 0x0000000000a00020 <+32>:    mov    %eax,%esi
   0x0000000000a00022 <+34>: mov    $0x4005d0,%edi
   0x0000000000a00027 <+39>:    mov $0x0,%eax
   0x0000000000a0002c <+44>:    callq  0x400410 <printf@plt>
 0x0000000000a00031 <+49>:    addl   $0x1,-0x4(%rbp)
 0x0000000000a00035 <+53>:    cmpl   $0x13,-0x4(%rbp)
   0x0000000000a00039 <+57>:    jle    0xa00011 <print_build_id+17>
   0x0000000000a0003b <+59>: mov    $0xa,%edi
   0x0000000000a00040 <+64>:    callq  0x400400 <putchar@plt>
   0x0000000000a00045 <+69>:    leaveq 
 0x0000000000a00046 <+70>:    retq   
End of assembler dump.

```
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to