Skip to content

--emit-relocs LLD does not rewrite relocations during target-specific relaxation #162610

@dominik-steenken

Description

@dominik-steenken

When performing target-specific relaxation, lld might change instructions in such a way that the original relocations associated with them are no longer correct. If those relocations are resolved during the linking and not emitted, this is not a problem. When combined with --emit-relocs though, the original, no longer applicable relocations are emitted, rather than the relocation that might apply to the transformed code. This is true at least for X86 and SystemZ targets. The ld linker does apply the relaxation transformation to the relocation as well, which should be the correct approach, I think, since --emit-relocs is meant to allow postprocessing tools to use the relocations for further optimizations / analysis, which might fail if the relocations are incorrect.

Example Code (X86):

        .hidden foo
        .comm foo,8,8
        .text
        .globl  bar
        .type   bar, @function
bar:
        movq    foo@GOTPCREL(%rip), %rax

When assembled and linked with --emit-relocs, once with --no-relax and once without --no-relax, this demonstrates that the relaxation applies to the instruction only (mov -> lea), and not the relocation:

$ as --64 -o relcheck_x86-64.o relcheck_x86-64.s
$ ld.lld -melf_x86_64 -shared --emit-relocs -o relcheck_x86-64_lld_relax relcheck_x86-64.o
$ objdump -dr relcheck_x86-64_lld_relax | grep -E '^\s+[0-9a-f]+:'
    126d:       48 8d 05 8c 20 00 00    lea    0x208c(%rip),%rax        # 3300 <foo>
                        1270: R_X86_64_REX_GOTPCRELX    foo-0x4
$ ld.lld -melf_x86_64 -shared --emit-relocs --no-relax -o relcheck_x86-64_lld_no-relax relcheck_x86-64.o
$ objdump -dr relcheck_x86-64_lld_no-relax | grep -E '^\s+[0-9a-f]+:'
    1288:       48 8b 05 b1 10 00 00    mov    0x10b1(%rip),%rax        # 2340 <_DYNAMIC+0xb0>
                        128b: R_X86_64_REX_GOTPCRELX    foo-0x4

When this same process is performed with ld, the relocation is transformed as well:

$ as --64 -o relcheck_x86-64.o relcheck_x86-64.s
$ ld -melf_x86_64 -shared --emit-relocs -o relcheck_x86-64_relax relcheck_x86-64.o
$ ld -melf_x86_64 -shared --emit-relocs --no-relax -o relcheck_x86-64_no-relax relcheck_x86-64.o
$ objdump -dr relcheck_x86-64_relax | grep -E '^\s+[0-9a-f]+:'
 1c8:   48 8d 05 31 2e 00 00    lea    0x2e31(%rip),%rax        # 3000 <foo>
                        1cb: R_X86_64_PC32      foo-0x4
$ objdump -dr relcheck_x86-64_no-relax | grep -E '^\s+[0-9a-f]+:'
 1c8:   48 8b 05 11 2e 00 00    mov    0x2e11(%rip),%rax        # 2fe0 <.got>
                        1cb: R_X86_64_REX_GOTPCRELX     foo-0x4

This same behavior seems to be present at least on s390x as well. This was tested with ld version 2.44-6.fc42, and ld.lld version 20.1.8.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions