This is an archive of the discontinued LLVM Phabricator instance.

[MC] [X86] Teach leaq _GLOBAL_OFFSET_TABLE(%rip), %r15 to use R_X86_64_GOTPC32 instead of R_X86_64_PC32
ClosedPublic

Authored by MaskRay on May 29 2018, 5:44 PM.

Details

Summary

This is similar to D46319 (ARM). x86-64 psABI p40 gives an example:

leaq _GLOBAL_OFFSET_TABLE(%rip), %r15 # GOTPC32 reloc

GNU as creates such R_X86_64_GOTPC32. However, MC currently emits R_X86_64_PC32.

Diff Detail

Repository
rL LLVM

Event Timeline

MaskRay created this revision.May 29 2018, 5:44 PM
MaskRay edited reviewers, added: craig.topper; removed: espindola.May 29 2018, 7:04 PM

Friendly ping :)

craig.topper edited reviewers, added: ruiu; removed: espindola.May 31 2018, 2:15 PM
ruiu added a comment.May 31 2018, 2:21 PM

I'm not very familiar with this code, so please take it with a grain of salt.

lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp
355–356 ↗(On Diff #149011)

Is this the right place to add this code? There's code in this function that also set the same value to FixupKind. Can't you merge them?

craig.topper added inline comments.May 31 2018, 2:51 PM
test/MC/ELF/relocation.s
43 ↗(On Diff #149011)

If I just add this line to the test, I get the exact output shown in the check lines without any other changes.

Apologies I don't know x86 well enough to be authoritative here. It looks like at least the test case needs to use %rip to see a meaningful difference in the output of mc.

test/MC/ELF/relocation.s
43 ↗(On Diff #149011)

Going back to the original example from the LLD review D47098 it seems that llvm-mc will emit a R_X86_64_PC32 relocation when the register is %rip but will use R_X86_64_GOTPC32 if the register is something like %rax. I'm guessing that this is because use of %rip means that the fixups are pc relative, when %rax is chosen the existing use of StartsWithGlobalOffsetTable for FK_DATA4, FK_DATA8 catches it.

For the somewhat arbitrary test:

leaq _GLOBAL_OFFSET_TABLE_(%rip), %rax
leaq _GLOBAL_OFFSET_TABLE_(%rax), %r15

mc gives me:

   0:	48 8d 05 00 00 00 00 	lea    0x0(%rip),%rax        # 0x7
			3: R_X86_64_PC32	_GLOBAL_OFFSET_TABLE_-0x4
   7:	4c 8d b8 00 00 00 00 	lea    0x0(%rax),%r15
			a: R_X86_64_GOTPC32	_GLOBAL_OFFSET_TABLE_+0x3

whereas gas gives me:

   0:	48 8d 05 00 00 00 00 	lea    0x0(%rip),%rax        # 0x7
			3: R_X86_64_GOTPC32	_GLOBAL_OFFSET_TABLE_-0x4
   7:	4c 8d b8 00 00 00 00 	lea    0x0(%rax),%r15
			a: R_X86_64_GOTPC32	_GLOBAL_OFFSET_TABLE_

Apologies I don't know enough about x86 to know which of mc or gas is correct in this case. The code in the ARM gas backend that chooses the got relative relocation did seem a bit loose to me so I'd get an x86 expert to check before assuming that gas is right.

MaskRay updated this revision to Diff 149508.Jun 1 2018, 10:46 AM

Update test

MaskRay added a comment.EditedJun 1 2018, 10:50 AM

The r_addend offsets do not change before/after the patch.

Apologies I don't know x86 well enough to be authoritative here. It looks like at least the test case needs to use %rip to see a meaningful difference in the output of mc.

Yes, with _GLOBAL_OFFSET_TABLE_(%rax) (is this a malform?), r_addend is different in GNU as (RIP is relative to the location) and llvm-mc (before or after the patch: relative to the currect instruction):

leaq  _GLOBAL_OFFSET_TABLE_(%rax), %r15

But for:

leaq  _GLOBAL_OFFSET_TABLE_(%rip), %r15

Both GNU as and llvm-mc think it should be relative to the RIP of the next instruction.

Added the test`leaq _GLOBAL_OFFSET_TABLE_(%rip), %r15` along with leaq _GLOBAL_OFFSET_TABLE_(%rax), %r15

The behavior of GNU as is weird (it is relative neither the current instruction nor the next, but the relocation position).
https://gmplib.org/repo/gmp/file/tip/mpn/x86/README#l361 notes that instructions like leal _GLOBAL_OFFSET_TABLE_(%edi), %ebx can be assembled incorrectly.

I think the current llvm-mc behavior is fine and it is consistent with another test test/MC/ELF/global-offset.s.

MaskRay marked 2 inline comments as done.Jun 1 2018, 11:15 AM
MaskRay added inline comments.Jun 1 2018, 11:23 AM
lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp
355–356 ↗(On Diff #149011)

When %rip is mentioned, r_addend should be relative to the PC of the next instruction thus the ImmOffset -= 4 here (accounting for a 4-byte PC offset). This is different from the cases above (no PC is involved unless the magic _GLOBAL_OFFSET_TABLE_ appears, where r_addend should be relative to the PC of the currenct instruction ).

I think keeping them separate as is is not bad.

enthusiastic ping :) 🐱

echristo added inline comments.
lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp
355–356 ↗(On Diff #149011)

Perhaps not right above, but I think the code might be clearer if we just add the change in Fixup right before this block and add the condition to this block.

Thoughts?

MaskRay added inline comments.Jun 8 2018, 12:47 PM
lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp
355–356 ↗(On Diff #149011)

This block deals with 4-byte pc-relative FixupKind, while the 8-byte branch has been handled above.

_GLOBAL_OFFSET_TABLE_ can only be used in 4-byte or 8-byte FixupKind, the 1-byte and 2-byte branches below do not need this fix. Thus I think the current way organizes the logic well. Did I miss something?

echristo accepted this revision.Jun 11 2018, 11:10 PM

One inline comment then OK.

lib/Target/X86/MCTargetDesc/X86MCCodeEmitter.cpp
355–356 ↗(On Diff #149011)

Could you add a comment sorta like this:

// If this is a rip relative load off of the global offset table symbol:
// leaq _GLOBAL_OFFSET_TABLE(%rip), %r15
// this needs to be a GOTPC32 relocation.

Around the if statement?

Otherwise I played around with this and agree, without hoisting a bunch of the if conditionals into a separate function.

This revision is now accepted and ready to land.Jun 11 2018, 11:10 PM
MaskRay updated this revision to Diff 150966.Jun 12 2018, 9:21 AM

Add comment suggested by echristo@

This revision was automatically updated to reflect the committed changes.