summaryrefslogtreecommitdiff
path: root/compiler/GHC/CmmToAsm/PIC.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/CmmToAsm/PIC.hs')
-rw-r--r--compiler/GHC/CmmToAsm/PIC.hs298
1 files changed, 154 insertions, 144 deletions
diff --git a/compiler/GHC/CmmToAsm/PIC.hs b/compiler/GHC/CmmToAsm/PIC.hs
index 323d93d173..d4d8b55e7e 100644
--- a/compiler/GHC/CmmToAsm/PIC.hs
+++ b/compiler/GHC/CmmToAsm/PIC.hs
@@ -57,6 +57,7 @@ import GHC.Platform
import GHC.CmmToAsm.Instr
import GHC.Platform.Reg
import GHC.CmmToAsm.Monad
+import GHC.CmmToAsm.Config
import GHC.Cmm.Dataflow.Collections
@@ -163,7 +164,7 @@ cmmMakePicReference dflags lbl
| OSAIX <- platformOS $ targetPlatform dflags
= CmmMachOp (MO_Add W32)
[ CmmReg (CmmGlobal PicBaseReg)
- , CmmLit $ picRelative dflags
+ , CmmLit $ picRelative (wordWidth dflags)
(platformArch $ targetPlatform dflags)
(platformOS $ targetPlatform dflags)
lbl ]
@@ -172,7 +173,7 @@ cmmMakePicReference dflags lbl
| ArchPPC_64 _ <- platformArch $ targetPlatform dflags
= CmmMachOp (MO_Add W32) -- code model medium
[ CmmReg (CmmGlobal PicBaseReg)
- , CmmLit $ picRelative dflags
+ , CmmLit $ picRelative (wordWidth dflags)
(platformArch $ targetPlatform dflags)
(platformOS $ targetPlatform dflags)
lbl ]
@@ -181,7 +182,7 @@ cmmMakePicReference dflags lbl
&& absoluteLabel lbl
= CmmMachOp (MO_Add (wordWidth dflags))
[ CmmReg (CmmGlobal PicBaseReg)
- , CmmLit $ picRelative dflags
+ , CmmLit $ picRelative (wordWidth dflags)
(platformArch $ targetPlatform dflags)
(platformOS $ targetPlatform dflags)
lbl ]
@@ -404,7 +405,7 @@ howToAccessLabel dflags _ _ _ _ _
-- | Says what we have to add to our 'PIC base register' in order to
-- get the address of a label.
-picRelative :: DynFlags -> Arch -> OS -> CLabel -> CmmLit
+picRelative :: Width -> Arch -> OS -> CLabel -> CmmLit
-- Darwin, but not x86_64:
-- The PIC base register points to the PIC base label at the beginning
@@ -413,15 +414,15 @@ picRelative :: DynFlags -> Arch -> OS -> CLabel -> CmmLit
-- We have already made sure that all labels that are not from the current
-- module are accessed indirectly ('as' can't calculate differences between
-- undefined labels).
-picRelative dflags arch OSDarwin lbl
+picRelative width arch OSDarwin lbl
| arch /= ArchX86_64
- = CmmLabelDiffOff lbl mkPicBaseLabel 0 (wordWidth dflags)
+ = CmmLabelDiffOff lbl mkPicBaseLabel 0 width
-- On AIX we use an indirect local TOC anchored by 'gotLabel'.
-- This way we use up only one global TOC entry per compilation-unit
-- (this is quite similar to GCC's @-mminimal-toc@ compilation mode)
-picRelative dflags _ OSAIX lbl
- = CmmLabelDiffOff lbl gotLabel 0 (wordWidth dflags)
+picRelative width _ OSAIX lbl
+ = CmmLabelDiffOff lbl gotLabel 0 width
-- PowerPC Linux:
-- The PIC base register points to our fake GOT. Use a label difference
@@ -429,9 +430,9 @@ picRelative dflags _ OSAIX lbl
-- We have made sure that *everything* is accessed indirectly, so this
-- is only used for offsets from the GOT to symbol pointers inside the
-- GOT.
-picRelative dflags ArchPPC os lbl
+picRelative width ArchPPC os lbl
| osElfTarget os
- = CmmLabelDiffOff lbl gotLabel 0 (wordWidth dflags)
+ = CmmLabelDiffOff lbl gotLabel 0 width
-- Most Linux versions:
@@ -453,14 +454,14 @@ picRelative _ arch os lbl
in result
picRelative _ _ _ _
- = panic "PositionIndependentCode.picRelative undefined for this platform"
+ = panic "GHC.CmmToAsm.PIC.picRelative undefined for this platform"
--------------------------------------------------------------------------------
-needImportedSymbols :: DynFlags -> Arch -> OS -> Bool
-needImportedSymbols dflags arch os
+needImportedSymbols :: NCGConfig -> Bool
+needImportedSymbols config
| os == OSDarwin
, arch /= ArchX86_64
= True
@@ -471,7 +472,7 @@ needImportedSymbols dflags arch os
-- PowerPC Linux: -fPIC or -dynamic
| osElfTarget os
, arch == ArchPPC
- = positionIndependent dflags || gopt Opt_ExternalDynamicRefs dflags
+ = ncgPIC config || ncgExternalDynamicRefs config
-- PowerPC 64 Linux: always
| osElfTarget os
@@ -481,11 +482,15 @@ needImportedSymbols dflags arch os
-- i386 (and others?): -dynamic but not -fPIC
| osElfTarget os
, arch /= ArchPPC_64 ELF_V1 && arch /= ArchPPC_64 ELF_V2
- = gopt Opt_ExternalDynamicRefs dflags &&
- not (positionIndependent dflags)
+ = ncgExternalDynamicRefs config &&
+ not (ncgPIC config)
| otherwise
= False
+ where
+ platform = ncgPlatform config
+ arch = platformArch platform
+ os = platformOS platform
-- gotLabel
-- The label used to refer to our "fake GOT" from
@@ -499,13 +504,16 @@ gotLabel
---------------------------------------------------------------------------------
+-- Emit GOT declaration
+-- Output whatever needs to be output once per .s file.
+--
-- We don't need to declare any offset tables.
-- However, for PIC on x86, we need a small helper function.
-pprGotDeclaration :: DynFlags -> Arch -> OS -> SDoc
-pprGotDeclaration dflags ArchX86 OSDarwin
- | positionIndependent dflags
- = vcat [
+pprGotDeclaration :: NCGConfig -> SDoc
+pprGotDeclaration config = case (arch,os) of
+ (ArchX86, OSDarwin)
+ | ncgPIC config
+ -> vcat [
text ".section __TEXT,__textcoal_nt,coalesced,no_toc",
text ".weak_definition ___i686.get_pc_thunk.ax",
text ".private_extern ___i686.get_pc_thunk.ax",
@@ -513,48 +521,49 @@ pprGotDeclaration dflags ArchX86 OSDarwin
text "\tmovl (%esp), %eax",
text "\tret" ]
-pprGotDeclaration _ _ OSDarwin
- = empty
-
--- Emit XCOFF TOC section
-pprGotDeclaration _ _ OSAIX
- = vcat $ [ text ".toc"
- , text ".tc ghc_toc_table[TC],.LCTOC1"
- , text ".csect ghc_toc_table[RW]"
- -- See Note [.LCTOC1 in PPC PIC code]
- , text ".set .LCTOC1,$+0x8000"
- ]
-
-
--- PPC 64 ELF v1 needs a Table Of Contents (TOC)
-pprGotDeclaration _ (ArchPPC_64 ELF_V1) _
- = text ".section \".toc\",\"aw\""
--- In ELF v2 we also need to tell the assembler that we want ABI
--- version 2. This would normally be done at the top of the file
--- right after a file directive, but I could not figure out how
--- to do that.
-pprGotDeclaration _ (ArchPPC_64 ELF_V2) _
- = vcat [ text ".abiversion 2",
- text ".section \".toc\",\"aw\""
- ]
+ (_, OSDarwin) -> empty
--- Emit GOT declaration
--- Output whatever needs to be output once per .s file.
-pprGotDeclaration dflags arch os
+ -- Emit XCOFF TOC section
+ (_, OSAIX)
+ -> vcat $ [ text ".toc"
+ , text ".tc ghc_toc_table[TC],.LCTOC1"
+ , text ".csect ghc_toc_table[RW]"
+ -- See Note [.LCTOC1 in PPC PIC code]
+ , text ".set .LCTOC1,$+0x8000"
+ ]
+
+
+ -- PPC 64 ELF v1 needs a Table Of Contents (TOC)
+ (ArchPPC_64 ELF_V1, _)
+ -> text ".section \".toc\",\"aw\""
+
+ -- In ELF v2 we also need to tell the assembler that we want ABI
+ -- version 2. This would normally be done at the top of the file
+ -- right after a file directive, but I could not figure out how
+ -- to do that.
+ (ArchPPC_64 ELF_V2, _)
+ -> vcat [ text ".abiversion 2",
+ text ".section \".toc\",\"aw\""
+ ]
+
+ (arch, os)
| osElfTarget os
, arch /= ArchPPC_64 ELF_V1 && arch /= ArchPPC_64 ELF_V2
- , not (positionIndependent dflags)
- = empty
+ , not (ncgPIC config)
+ -> empty
| osElfTarget os
, arch /= ArchPPC_64 ELF_V1 && arch /= ArchPPC_64 ELF_V2
- = vcat [
+ -> vcat [
-- See Note [.LCTOC1 in PPC PIC code]
text ".section \".got2\",\"aw\"",
text ".LCTOC1 = .+32768" ]
-pprGotDeclaration _ _ _
- = panic "pprGotDeclaration: no match"
+ _ -> panic "pprGotDeclaration: no match"
+ where
+ platform = ncgPlatform config
+ arch = platformArch platform
+ os = platformOS platform
--------------------------------------------------------------------------------
@@ -563,43 +572,44 @@ pprGotDeclaration _ _ _
-- and one for non-PIC.
--
-pprImportedSymbol :: DynFlags -> Platform -> CLabel -> SDoc
-pprImportedSymbol dflags (Platform { platformMini = PlatformMini { platformMini_arch = ArchX86, platformMini_os = OSDarwin } }) importedLbl
+pprImportedSymbol :: DynFlags -> NCGConfig -> CLabel -> SDoc
+pprImportedSymbol dflags config importedLbl = case (arch,os) of
+ (ArchX86, OSDarwin)
| Just (CodeStub, lbl) <- dynamicLinkerLabelInfo importedLbl
- = case positionIndependent dflags of
- False ->
- vcat [
- text ".symbol_stub",
- text "L" <> pprCLabel dflags lbl <> ptext (sLit "$stub:"),
- text "\t.indirect_symbol" <+> pprCLabel dflags lbl,
- text "\tjmp *L" <> pprCLabel dflags lbl
- <> text "$lazy_ptr",
- text "L" <> pprCLabel dflags lbl
- <> text "$stub_binder:",
- text "\tpushl $L" <> pprCLabel dflags lbl
- <> text "$lazy_ptr",
- text "\tjmp dyld_stub_binding_helper"
- ]
- True ->
- vcat [
- text ".section __TEXT,__picsymbolstub2,"
- <> text "symbol_stubs,pure_instructions,25",
- text "L" <> pprCLabel dflags lbl <> ptext (sLit "$stub:"),
- text "\t.indirect_symbol" <+> pprCLabel dflags lbl,
- text "\tcall ___i686.get_pc_thunk.ax",
- text "1:",
- text "\tmovl L" <> pprCLabel dflags lbl
- <> text "$lazy_ptr-1b(%eax),%edx",
- text "\tjmp *%edx",
- text "L" <> pprCLabel dflags lbl
- <> text "$stub_binder:",
- text "\tlea L" <> pprCLabel dflags lbl
- <> text "$lazy_ptr-1b(%eax),%eax",
- text "\tpushl %eax",
- text "\tjmp dyld_stub_binding_helper"
- ]
- $+$ vcat [ text ".section __DATA, __la_sym_ptr"
- <> (if positionIndependent dflags then int 2 else int 3)
+ -> if not pic
+ then
+ vcat [
+ text ".symbol_stub",
+ text "L" <> pprCLabel dflags lbl <> ptext (sLit "$stub:"),
+ text "\t.indirect_symbol" <+> pprCLabel dflags lbl,
+ text "\tjmp *L" <> pprCLabel dflags lbl
+ <> text "$lazy_ptr",
+ text "L" <> pprCLabel dflags lbl
+ <> text "$stub_binder:",
+ text "\tpushl $L" <> pprCLabel dflags lbl
+ <> text "$lazy_ptr",
+ text "\tjmp dyld_stub_binding_helper"
+ ]
+ else
+ vcat [
+ text ".section __TEXT,__picsymbolstub2,"
+ <> text "symbol_stubs,pure_instructions,25",
+ text "L" <> pprCLabel dflags lbl <> ptext (sLit "$stub:"),
+ text "\t.indirect_symbol" <+> pprCLabel dflags lbl,
+ text "\tcall ___i686.get_pc_thunk.ax",
+ text "1:",
+ text "\tmovl L" <> pprCLabel dflags lbl
+ <> text "$lazy_ptr-1b(%eax),%edx",
+ text "\tjmp *%edx",
+ text "L" <> pprCLabel dflags lbl
+ <> text "$stub_binder:",
+ text "\tlea L" <> pprCLabel dflags lbl
+ <> text "$lazy_ptr-1b(%eax),%eax",
+ text "\tpushl %eax",
+ text "\tjmp dyld_stub_binding_helper"
+ ]
+ $+$ vcat [ text ".section __DATA, __la_sym_ptr"
+ <> (if pic then int 2 else int 3)
<> text ",lazy_symbol_pointers",
text "L" <> pprCLabel dflags lbl <> ptext (sLit "$lazy_ptr:"),
text "\t.indirect_symbol" <+> pprCLabel dflags lbl,
@@ -607,71 +617,68 @@ pprImportedSymbol dflags (Platform { platformMini = PlatformMini { platformMini_
<> text "$stub_binder"]
| Just (SymbolPtr, lbl) <- dynamicLinkerLabelInfo importedLbl
- = vcat [
+ -> vcat [
text ".non_lazy_symbol_pointer",
char 'L' <> pprCLabel dflags lbl <> text "$non_lazy_ptr:",
text "\t.indirect_symbol" <+> pprCLabel dflags lbl,
text "\t.long\t0"]
| otherwise
- = empty
+ -> empty
+ (_, OSDarwin) -> empty
-pprImportedSymbol _ (Platform { platformMini = PlatformMini { platformMini_os = OSDarwin } }) _
- = empty
--- XCOFF / AIX
---
--- Similar to PPC64 ELF v1, there's dedicated TOC register (r2). To
--- workaround the limitation of a global TOC we use an indirect TOC
--- with the label `ghc_toc_table`.
---
--- See also GCC's `-mminimal-toc` compilation mode or
--- http://www.ibm.com/developerworks/rational/library/overview-toc-aix/
---
--- NB: No DSO-support yet
+ -- XCOFF / AIX
+ --
+ -- Similar to PPC64 ELF v1, there's dedicated TOC register (r2). To
+ -- workaround the limitation of a global TOC we use an indirect TOC
+ -- with the label `ghc_toc_table`.
+ --
+ -- See also GCC's `-mminimal-toc` compilation mode or
+ -- http://www.ibm.com/developerworks/rational/library/overview-toc-aix/
+ --
+ -- NB: No DSO-support yet
-pprImportedSymbol dflags (Platform { platformMini = PlatformMini { platformMini_os = OSAIX } }) importedLbl
- = case dynamicLinkerLabelInfo importedLbl of
+ (_, OSAIX) -> case dynamicLinkerLabelInfo importedLbl of
Just (SymbolPtr, lbl)
-> vcat [
text "LC.." <> pprCLabel dflags lbl <> char ':',
text "\t.long" <+> pprCLabel dflags lbl ]
_ -> empty
--- ELF / Linux
---
--- In theory, we don't need to generate any stubs or symbol pointers
--- by hand for Linux.
---
--- Reality differs from this in two areas.
---
--- 1) If we just use a dynamically imported symbol directly in a read-only
--- section of the main executable (as GCC does), ld generates R_*_COPY
--- relocations, which are fundamentally incompatible with reversed info
--- tables. Therefore, we need a table of imported addresses in a writable
--- section.
--- The "official" GOT mechanism (label@got) isn't intended to be used
--- in position dependent code, so we have to create our own "fake GOT"
--- when not Opt_PIC && WayDyn `elem` ways dflags.
---
--- 2) PowerPC Linux is just plain broken.
--- While it's theoretically possible to use GOT offsets larger
--- than 16 bit, the standard crt*.o files don't, which leads to
--- linker errors as soon as the GOT size exceeds 16 bit.
--- Also, the assembler doesn't support @gotoff labels.
--- In order to be able to use a larger GOT, we have to circumvent the
--- entire GOT mechanism and do it ourselves (this is also what GCC does).
-
-
--- When needImportedSymbols is defined,
--- the NCG will keep track of all DynamicLinkerLabels it uses
--- and output each of them using pprImportedSymbol.
-
-pprImportedSymbol dflags platform@(Platform { platformMini = PlatformMini { platformMini_arch = ArchPPC_64 _ } })
- importedLbl
- | osElfTarget (platformOS platform)
- = case dynamicLinkerLabelInfo importedLbl of
+ -- ELF / Linux
+ --
+ -- In theory, we don't need to generate any stubs or symbol pointers
+ -- by hand for Linux.
+ --
+ -- Reality differs from this in two areas.
+ --
+ -- 1) If we just use a dynamically imported symbol directly in a read-only
+ -- section of the main executable (as GCC does), ld generates R_*_COPY
+ -- relocations, which are fundamentally incompatible with reversed info
+ -- tables. Therefore, we need a table of imported addresses in a writable
+ -- section.
+ -- The "official" GOT mechanism (label@got) isn't intended to be used
+ -- in position dependent code, so we have to create our own "fake GOT"
+ -- when not Opt_PIC && WayDyn `elem` ways dflags.
+ --
+ -- 2) PowerPC Linux is just plain broken.
+ -- While it's theoretically possible to use GOT offsets larger
+ -- than 16 bit, the standard crt*.o files don't, which leads to
+ -- linker errors as soon as the GOT size exceeds 16 bit.
+ -- Also, the assembler doesn't support @gotoff labels.
+ -- In order to be able to use a larger GOT, we have to circumvent the
+ -- entire GOT mechanism and do it ourselves (this is also what GCC does).
+
+
+ -- When needImportedSymbols is defined,
+ -- the NCG will keep track of all DynamicLinkerLabels it uses
+ -- and output each of them using pprImportedSymbol.
+
+ (ArchPPC_64 _, _)
+ | osElfTarget os
+ -> case dynamicLinkerLabelInfo importedLbl of
Just (SymbolPtr, lbl)
-> vcat [
text ".section \".toc\", \"aw\"",
@@ -679,11 +686,10 @@ pprImportedSymbol dflags platform@(Platform { platformMini = PlatformMini { plat
text "\t.quad" <+> pprCLabel dflags lbl ]
_ -> empty
-pprImportedSymbol dflags platform importedLbl
- | osElfTarget (platformOS platform)
- = case dynamicLinkerLabelInfo importedLbl of
+ _ | osElfTarget os
+ -> case dynamicLinkerLabelInfo importedLbl of
Just (SymbolPtr, lbl)
- -> let symbolSize = case wordWidth dflags of
+ -> let symbolSize = case ncgWordWidth config of
W32 -> sLit "\t.long"
W64 -> sLit "\t.quad"
_ -> panic "Unknown wordRep in pprImportedSymbol"
@@ -696,8 +702,12 @@ pprImportedSymbol dflags platform importedLbl
-- PLT code stubs are generated automatically by the dynamic linker.
_ -> empty
-pprImportedSymbol _ _ _
- = panic "PIC.pprImportedSymbol: no match"
+ _ -> panic "PIC.pprImportedSymbol: no match"
+ where
+ platform = ncgPlatform config
+ arch = platformArch platform
+ os = platformOS platform
+ pic = ncgPIC config
--------------------------------------------------------------------------------
-- Generate code to calculate the address that should be put in the