From 64e6719ee870f88db445feac0a1d5f597a7549e3 Mon Sep 17 00:00:00 2001 From: Rafael Espindola Date: Wed, 20 Oct 2010 16:46:08 +0000 Subject: [PATCH] Handle _GLOBAL_OFFSET_TABLE_ correctly. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@116932 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/X86/X86MCCodeEmitter.cpp | 27 +++++++++++++++++++++++++++ test/MC/ELF/global-offset.s | 18 ++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 test/MC/ELF/global-offset.s diff --git a/lib/Target/X86/X86MCCodeEmitter.cpp b/lib/Target/X86/X86MCCodeEmitter.cpp index 62f434d0fd1..47e91a8159c 100644 --- a/lib/Target/X86/X86MCCodeEmitter.cpp +++ b/lib/Target/X86/X86MCCodeEmitter.cpp @@ -18,6 +18,7 @@ #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCSymbol.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -193,6 +194,25 @@ static bool Is32BitMemOperand(const MCInst &MI, unsigned Op) { return false; } +/// StartsWithGlobalOffsetTable - Return true for the simple cases where this +/// expression starts with _GLOBAL_OFFSET_TABLE_. This is a needed to support +/// PIC on ELF i386 as that symbol is magic. We check only simple case that +/// are know to be used: _GLOBAL_OFFSET_TABLE_ by itself or at the start +/// of a binary expression. +static bool StartsWithGlobalOffsetTable(const MCExpr *Expr) { + if (Expr->getKind() == MCExpr::Binary) { + const MCBinaryExpr *BE = static_cast(Expr); + Expr = BE->getLHS(); + } + + if (Expr->getKind() != MCExpr::SymbolRef) + return false; + + const MCSymbolRefExpr *Ref = static_cast(Expr); + const MCSymbol &S = Ref->getSymbol(); + return S.getName() == "_GLOBAL_OFFSET_TABLE_"; +} + void X86MCCodeEmitter:: EmitImmediate(const MCOperand &DispOp, unsigned Size, MCFixupKind FixupKind, unsigned &CurByte, raw_ostream &OS, @@ -209,6 +229,13 @@ EmitImmediate(const MCOperand &DispOp, unsigned Size, MCFixupKind FixupKind, // If we have an immoffset, add it to the expression. const MCExpr *Expr = DispOp.getExpr(); + if (StartsWithGlobalOffsetTable(Expr)) { + // FIXME: We should probably change the FixupKind to a special one so that + // other parts of MC don't have to check the symbol name. + assert(ImmOffset == 0); + ImmOffset = CurByte; + } + // If the fixup is pc-relative, we need to bias the value to be relative to // the start of the field, not the end of the field. if (FixupKind == MCFixupKind(X86::reloc_pcrel_4byte) || diff --git a/test/MC/ELF/global-offset.s b/test/MC/ELF/global-offset.s new file mode 100644 index 00000000000..aa6328760d4 --- /dev/null +++ b/test/MC/ELF/global-offset.s @@ -0,0 +1,18 @@ +// RUN: llvm-mc -filetype=obj -triple i386-pc-linux-gnu %s -o - | elf-dump --dump-section-data | FileCheck %s + +// We test that _GLOBAL_OFFSET_TABLE_ will account for the two bytes at the +// start of the addl. + + addl $_GLOBAL_OFFSET_TABLE_, %ebx + +// CHECK: ('sh_name', 0x00000001) # '.text' +// CHECK-NEXT: ('sh_type', +// CHECK-NEXT: ('sh_flags', +// CHECK-NEXT: ('sh_addr', +// CHECK-NEXT: ('sh_offset', +// CHECK-NEXT: ('sh_size', +// CHECK-NEXT: ('sh_link', +// CHECK-NEXT: ('sh_info', +// CHECK-NEXT: ('sh_addralign', +// CHECK-NEXT: ('sh_entsize', +// CHECK-NEXT: ('_section_data', '81c30200 0000')