forked from ghc/ghc
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathLinker.hs
More file actions
274 lines (236 loc) · 10.1 KB
/
Copy pathLinker.hs
File metadata and controls
274 lines (236 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RecordWildCards #-}
{-# OPTIONS_GHC -optc-DNON_POSIX_SOURCE #-}
--
-- (c) The University of Glasgow 2002-2006
--
-- NOTE: This module is only compiled when flag(interpreter) is enabled
-- (see ghc.cabal.in). No CPP guards needed.
-- | Bytecode assembler and linker
module GHC.ByteCode.Linker
( linkBCO
, lookupStaticPtr
, lookupIE
, linkFail
)
where
import GHC.Prelude
import GHC.Runtime.Interpreter
import GHC.ByteCode.Types
import GHCi.RemoteTypes
import GHCi.ResolvedBCO
import GHC.Builtin.PrimOps
import GHC.Builtin.PrimOps.Ids
import GHC.Unit.Module.Env
import GHC.Unit.Types
import GHC.Data.FastString
import GHC.Data.Maybe
import GHC.Data.SizedSeq
import GHC.Linker.Types
import GHC.Utils.Panic
import GHC.Utils.Outputable
import GHC.Types.Name
import GHC.Types.Name.Env
import qualified GHC.Types.Id as Id
import GHC.Types.Unique.DFM
-- Standard libraries
import Data.Array.Unboxed
import Foreign.Ptr
import GHC.Exts
{-
Linking interpretables into something we can run
-}
linkBCO
:: Interp
-> PkgsLoaded
-> LinkerEnv
-> LinkedBreaks
-> NameEnv Int
-> UnlinkedBCO
-> IO ResolvedBCO
linkBCO interp pkgs_loaded le lb bco_ix
(UnlinkedBCO _ arity insns bitmap lits0 ptrs0) = do
-- fromIntegral Word -> Word64 should be a no op if Word is Word64
-- otherwise it will result in a cast to longlong on 32bit systems.
(lits :: [Word]) <- mapM (fmap fromIntegral . lookupLiteral interp pkgs_loaded le lb) (elemsFlatBag lits0)
ptrs <- mapM (resolvePtr interp pkgs_loaded le lb bco_ix) (elemsFlatBag ptrs0)
let lits' = listArray (0 :: Int, fromIntegral (sizeFlatBag lits0)-1) lits
return $ ResolvedBCO { resolvedBCOIsLE = isLittleEndian
, resolvedBCOArity = arity
, resolvedBCOInstrs = insns
, resolvedBCOBitmap = bitmap
, resolvedBCOLits = mkBCOByteArray lits'
, resolvedBCOPtrs = addListToSS emptySS ptrs
}
lookupLiteral :: Interp -> PkgsLoaded -> LinkerEnv -> LinkedBreaks -> BCONPtr -> IO Word
lookupLiteral interp pkgs_loaded le lb ptr = case ptr of
BCONPtrWord lit -> return lit
BCONPtrLbl sym -> do
Ptr a# <- lookupStaticPtr interp sym
return (W# (int2Word# (addr2Int# a#)))
BCONPtrItbl nm -> do
Ptr a# <- lookupIE interp pkgs_loaded (itbl_env le) nm
return (W# (int2Word# (addr2Int# a#)))
BCONPtrAddr nm -> do
Ptr a# <- lookupAddr interp pkgs_loaded (addr_env le) nm
return (W# (int2Word# (addr2Int# a#)))
BCONPtrStr bs -> do
RemotePtr p <- fmap head $ interpCmd interp $ MallocStrings [bs]
pure $ fromIntegral p
BCONPtrFS fs -> do
RemotePtr p <- fmap head $ interpCmd interp $ MallocStrings [bytesFS fs]
pure $ fromIntegral p
BCONPtrFFIInfo (FFIInfo {..}) -> do
RemotePtr p <- interpCmd interp $ PrepFFI ffiInfoArgs ffiInfoRet
pure $ fromIntegral p
BCONPtrCostCentre InternalBreakpointId{..}
| interpreterProfiled interp -> do
case expectJust (lookupModuleEnv (ccs_env lb) ibi_info_mod) ! ibi_info_index of
RemotePtr p -> pure $ fromIntegral p
| otherwise ->
case toRemotePtr nullPtr of
RemotePtr p -> pure $ fromIntegral p
lookupStaticPtr :: Interp -> FastString -> IO (Ptr ())
lookupStaticPtr interp addr_of_label_string = do
m <- lookupSymbol interp (IFaststringSymbol addr_of_label_string)
case m of
Just ptr -> return ptr
Nothing -> linkFail "GHC.ByteCode.Linker: can't find label"
(ppr addr_of_label_string)
lookupIE :: Interp -> PkgsLoaded -> ItblEnv -> Name -> IO (Ptr ())
lookupIE interp pkgs_loaded ie con_nm =
case lookupNameEnv ie con_nm of
Just (_, ItblPtr a) -> return (fromRemotePtr (castRemotePtr a))
Nothing -> do -- try looking up in the object files.
let sym_to_find1 = IConInfoSymbol con_nm
m <- lookupHsSymbol interp pkgs_loaded sym_to_find1
case m of
Just addr -> return addr
Nothing
-> do -- perhaps a nullary constructor?
let sym_to_find2 = IStaticInfoSymbol con_nm
n <- lookupHsSymbol interp pkgs_loaded sym_to_find2
case n of
Just addr -> return addr
Nothing -> linkFail "GHC.ByteCode.Linker.lookupIE"
(ppr sym_to_find1 <> " or " <>
ppr sym_to_find2)
-- see Note [Generating code for top-level string literal bindings] in GHC.StgToByteCode
lookupAddr :: Interp -> PkgsLoaded -> AddrEnv -> Name -> IO (Ptr ())
lookupAddr interp pkgs_loaded ae addr_nm = do
case lookupNameEnv ae addr_nm of
Just (_, AddrPtr ptr) -> return (fromRemotePtr ptr)
Nothing -> do -- try looking up in the object files.
let sym_to_find = IBytesSymbol addr_nm
-- see Note [Bytes label] in GHC.Cmm.CLabel
m <- lookupHsSymbol interp pkgs_loaded sym_to_find
case m of
Just ptr -> return ptr
Nothing -> linkFail "GHC.ByteCode.Linker.lookupAddr"
(ppr sym_to_find)
lookupPrimOp :: Interp -> PkgsLoaded -> PrimOp -> IO (RemotePtr ())
lookupPrimOp interp pkgs_loaded primop = do
let sym_to_find = primopToCLabel primop "closure"
m <- lookupHsSymbol interp pkgs_loaded (IClosureSymbol (Id.idName $ primOpId primop))
case m of
Just p -> return (toRemotePtr p)
Nothing -> linkFail "GHC.ByteCode.Linker.lookupCE(primop)" (text sym_to_find)
resolvePtr
:: Interp
-> PkgsLoaded
-> LinkerEnv
-> LinkedBreaks
-> NameEnv Int
-> BCOPtr
-> IO ResolvedBCOPtr
resolvePtr interp pkgs_loaded le lb bco_ix ptr = case ptr of
BCOPtrName nm
| Just ix <- lookupNameEnv bco_ix nm
-> return (ResolvedBCORef ix) -- ref to another BCO in this group
| Just (_, rhv) <- lookupNameEnv (closure_env le) nm
-> return (ResolvedBCOPtr (unsafeForeignRefToRemoteRef rhv))
| otherwise
-> assertPpr (isExternalName nm) (ppr nm) $
do
let sym_to_find = IClosureSymbol nm
m <- lookupHsSymbol interp pkgs_loaded sym_to_find
case m of
Just p -> return (ResolvedBCOStaticPtr (toRemotePtr p))
Nothing -> linkFail "GHC.ByteCode.Linker.lookupCE" (ppr sym_to_find)
BCOPtrPrimOp op
-> ResolvedBCOStaticPtr <$> lookupPrimOp interp pkgs_loaded op
BCOPtrBCO bco
-> ResolvedBCOPtrBCO <$> linkBCO interp pkgs_loaded le lb bco_ix bco
BCOPtrBreakArray tick_mod ->
withForeignRef (expectJust (lookupModuleEnv (breakarray_env lb) tick_mod)) $
\ba -> pure $ ResolvedBCOPtrBreakArray ba
{-
Note [Symbol lookup order for boot libraries]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When looking up symbols for bytecode linking, we must check the main program's
symbol table BEFORE looking in dynamically loaded libraries. This is critical
for boot library symbols like 'stdout' from ghc-internal.
The issue: When GHC compiles Template Haskell code, it loads boot libraries
(like ghc-internal) as dynamic libraries for bytecode execution. These
dynamically loaded libraries contain their OWN copies of CAFs (Constant
Applicative Forms) like 'stdout'. If bytecode uses the DLL's stdout instead
of GHC's native stdout, output buffering becomes inconsistent:
- GHC's native code flushes GHC's stdout
- Bytecode writes to DLL's stdout (a different buffer!)
- Output is lost because the wrong buffer is flushed
The fix: Look up symbols in the main program first (via lookupSymbol, which
uses dlsym(RTLD_DEFAULT) and checks the main executable before loaded
libraries). Only if not found there do we search the loaded DLLs.
This aligns with the RTS's internal_dlsym() which also checks the main
program first. See Note [RTLD_LOCAL] in rts/Linker.c.
-}
-- | Look up the address of a Haskell symbol in the currently
-- loaded units.
--
-- See Note [Looking up symbols in the relevant objects].
-- See Note [Symbol lookup order for boot libraries].
lookupHsSymbol :: Interp -> PkgsLoaded -> InterpSymbol (Suffix s) -> IO (Maybe (Ptr ()))
lookupHsSymbol interp pkgs_loaded sym_to_find = do
massertPpr (isExternalName (interpSymbolName sym_to_find)) (ppr sym_to_find)
let pkg_id = moduleUnitId $ nameModule (interpSymbolName sym_to_find)
loaded_dlls = maybe [] loaded_pkg_hs_dlls $ lookupUDFM pkgs_loaded pkg_id
-- First try the main program / global symbol table.
-- This is important for boot library symbols (like stdout from ghc-internal)
-- to ensure bytecode uses the same CAFs as GHC's native code.
-- See Note [Symbol lookup order for boot libraries].
mb_main <- lookupSymbol interp sym_to_find
case mb_main of
Just ptr -> pure (Just ptr)
Nothing -> go loaded_dlls
where
go (dll:dlls) = do
mb_ptr <- lookupSymbolInDLL interp dll sym_to_find
case mb_ptr of
Just ptr -> pure (Just ptr)
Nothing -> go dlls
go [] = pure Nothing
linkFail :: String -> SDoc -> IO a
linkFail who what
= throwGhcExceptionIO (ProgramError $
unlines [ "",who
, "During interactive linking, GHCi couldn't find the following symbol:"
, ' ' : ' ' : showSDocUnsafe what
, "This may be due to you not asking GHCi to load extra object files,"
, "archives or DLLs needed by your current session. Restart GHCi, specifying"
, "the missing library using the -L/path/to/object/dir and -lmissinglibname"
, "flags, or simply by naming the relevant files on the GHCi command line."
, "Alternatively, this link failure might indicate a bug in GHCi."
, "If you suspect the latter, please report this as a GHC bug:"
, " https://github.com/stable-haskell/ghc/issues"
])
-- See Note [Primop wrappers] in GHC.Builtin.PrimOps
primopToCLabel :: PrimOp -> String -> String
primopToCLabel primop suffix = concat
[ "ghczminternal_GHCziInternalziPrimopWrappers_"
, zString (zEncodeFS (occNameFS (primOpOcc primop)))
, '_':suffix
]