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 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
|
-----------------------------------------------------------------------------
--
-- Types for the linkers and the loader
--
-- (c) The University of Glasgow 2019
--
-----------------------------------------------------------------------------
{-# LANGUAGE TypeApplications #-}
module GHC.Linker.Types
( Loader (..)
, LoaderState (..)
, uninitializedLoader
, modifyClosureEnv
, LinkerEnv(..)
, filterLinkerEnv
, ClosureEnv
, emptyClosureEnv
, extendClosureEnv
, Linkable(..)
, LinkableSet
, mkLinkableSet
, unionLinkableSet
, ObjFile
, Unlinked(..)
, SptEntry(..)
, isObjectLinkable
, linkableObjs
, isObject
, nameOfObject
, nameOfObject_maybe
, isInterpretable
, byteCodeOfObject
, LibrarySpec(..)
, LoadedPkgInfo(..)
, PkgsLoaded
)
where
import GHC.Prelude
import GHC.Unit ( UnitId, Module )
import GHC.ByteCode.Types ( ItblEnv, AddrEnv, CompiledByteCode )
import GHC.Fingerprint.Type ( Fingerprint )
import GHCi.RemoteTypes ( ForeignHValue )
import GHC.Types.Var ( Id )
import GHC.Types.Name.Env ( NameEnv, emptyNameEnv, extendNameEnvList, filterNameEnv )
import GHC.Types.Name ( Name )
import GHC.Utils.Outputable
import GHC.Utils.Panic
import Control.Concurrent.MVar
import Data.Time ( UTCTime )
import Data.Maybe
import GHC.Unit.Module.Env
import GHC.Types.Unique.DSet
import GHC.Types.Unique.DFM
import GHC.Unit.Module.WholeCoreBindings
{- **********************************************************************
The Loader's state
********************************************************************* -}
{-
The loader state *must* match the actual state of the C dynamic linker at all
times.
The MVar used to hold the LoaderState contains a Maybe LoaderState. The MVar
serves to ensure mutual exclusion between multiple loaded copies of the GHC
library. The Maybe may be Nothing to indicate that the linker has not yet been
initialised.
The LinkerEnv maps Names to actual closures (for interpreted code only), for
use during linking.
-}
newtype Loader = Loader { loader_state :: MVar (Maybe LoaderState) }
data LoaderState = LoaderState
{ linker_env :: !LinkerEnv
-- ^ Current global mapping from Names to their true values
, bcos_loaded :: !LinkableSet
-- ^ The currently loaded interpreted modules (home package)
, objs_loaded :: !LinkableSet
-- ^ And the currently-loaded compiled modules (home package)
, pkgs_loaded :: !PkgsLoaded
-- ^ The currently-loaded packages; always object code
-- haskell libraries, system libraries, transitive dependencies
, temp_sos :: ![(FilePath, String)]
-- ^ We need to remember the name of previous temporary DLL/.so
-- libraries so we can link them (see #10322)
}
uninitializedLoader :: IO Loader
uninitializedLoader = Loader <$> newMVar Nothing
modifyClosureEnv :: LoaderState -> (ClosureEnv -> ClosureEnv) -> LoaderState
modifyClosureEnv pls f =
let le = linker_env pls
ce = closure_env le
in pls { linker_env = le { closure_env = f ce } }
data LinkerEnv = LinkerEnv
{ closure_env :: ClosureEnv
-- ^ Current global mapping from closure Names to their true values
, itbl_env :: !ItblEnv
-- ^ The current global mapping from RdrNames of DataCons to
-- info table addresses.
-- When a new Unlinked is linked into the running image, or an existing
-- module in the image is replaced, the itbl_env must be updated
-- appropriately.
, addr_env :: !AddrEnv
-- ^ Like 'closure_env' and 'itbl_env', but for top-level 'Addr#' literals,
-- see Note [Generating code for top-level string literal bindings] in GHC.StgToByteCode.
}
filterLinkerEnv :: (Name -> Bool) -> LinkerEnv -> LinkerEnv
filterLinkerEnv f le = LinkerEnv
{ closure_env = filterNameEnv (f . fst) (closure_env le)
, itbl_env = filterNameEnv (f . fst) (itbl_env le)
, addr_env = filterNameEnv (f . fst) (addr_env le)
}
type ClosureEnv = NameEnv (Name, ForeignHValue)
emptyClosureEnv :: ClosureEnv
emptyClosureEnv = emptyNameEnv
extendClosureEnv :: ClosureEnv -> [(Name,ForeignHValue)] -> ClosureEnv
extendClosureEnv cl_env pairs
= extendNameEnvList cl_env [ (n, (n,v)) | (n,v) <- pairs]
type PkgsLoaded = UniqDFM UnitId LoadedPkgInfo
data LoadedPkgInfo
= LoadedPkgInfo
{ loaded_pkg_uid :: !UnitId
, loaded_pkg_hs_objs :: ![LibrarySpec]
, loaded_pkg_non_hs_objs :: ![LibrarySpec]
, loaded_pkg_trans_deps :: UniqDSet UnitId
}
instance Outputable LoadedPkgInfo where
ppr (LoadedPkgInfo uid hs_objs non_hs_objs trans_deps) =
vcat [ppr uid
, ppr hs_objs
, ppr non_hs_objs
, ppr trans_deps ]
-- | Information we can use to dynamically link modules into the compiler
data Linkable = LM {
linkableTime :: !UTCTime, -- ^ Time at which this linkable was built
-- (i.e. when the bytecodes were produced,
-- or the mod date on the files)
linkableModule :: !Module, -- ^ The linkable module itself
linkableUnlinked :: [Unlinked]
-- ^ Those files and chunks of code we have yet to link.
--
-- INVARIANT: A valid linkable always has at least one 'Unlinked' item.
}
type LinkableSet = ModuleEnv Linkable
mkLinkableSet :: [Linkable] -> LinkableSet
mkLinkableSet ls = mkModuleEnv [(linkableModule l, l) | l <- ls]
unionLinkableSet :: LinkableSet -> LinkableSet -> LinkableSet
unionLinkableSet = plusModuleEnv_C go
where
go l1 l2
| linkableTime l1 > linkableTime l2 = l1
| otherwise = l2
instance Outputable Linkable where
ppr (LM when_made mod unlinkeds)
= (text "LinkableM" <+> parens (text (show when_made)) <+> ppr mod)
$$ nest 3 (ppr unlinkeds)
type ObjFile = FilePath
-- | Objects which have yet to be linked by the compiler
data Unlinked
= DotO ObjFile -- ^ An object file (.o)
| DotA FilePath -- ^ Static archive file (.a)
| DotDLL FilePath -- ^ Dynamically linked library file (.so, .dll, .dylib)
| CoreBindings WholeCoreBindings -- ^ Serialised core which we can turn into BCOs (or object files), or used by some other backend
-- See Note [Interface Files with Core Definitions]
| LoadedBCOs [Unlinked] -- ^ A list of BCOs, but hidden behind extra indirection to avoid
-- being too strict.
| BCOs CompiledByteCode
[SptEntry] -- ^ A byte-code object, lives only in memory. Also
-- carries some static pointer table entries which
-- should be loaded along with the BCOs.
-- See Note [Grand plan for static forms] in
-- "GHC.Iface.Tidy.StaticPtrTable".
instance Outputable Unlinked where
ppr (DotO path) = text "DotO" <+> text path
ppr (DotA path) = text "DotA" <+> text path
ppr (DotDLL path) = text "DotDLL" <+> text path
ppr (BCOs bcos spt) = text "BCOs" <+> ppr bcos <+> ppr spt
ppr (LoadedBCOs{}) = text "LoadedBCOs"
ppr (CoreBindings {}) = text "FI"
-- | An entry to be inserted into a module's static pointer table.
-- See Note [Grand plan for static forms] in "GHC.Iface.Tidy.StaticPtrTable".
data SptEntry = SptEntry Id Fingerprint
instance Outputable SptEntry where
ppr (SptEntry id fpr) = ppr id <> colon <+> ppr fpr
isObjectLinkable :: Linkable -> Bool
isObjectLinkable l = not (null unlinked) && all isObject unlinked
where unlinked = linkableUnlinked l
-- A linkable with no Unlinked's is treated as a BCO. We can
-- generate a linkable with no Unlinked's as a result of
-- compiling a module in NoBackend mode, and this choice
-- happens to work well with checkStability in module GHC.
linkableObjs :: Linkable -> [FilePath]
linkableObjs l = [ f | DotO f <- linkableUnlinked l ]
-------------------------------------------
-- | Is this an actual file on disk we can link in somehow?
isObject :: Unlinked -> Bool
isObject (DotO _) = True
isObject (DotA _) = True
isObject (DotDLL _) = True
isObject _ = False
-- | Is this a bytecode linkable with no file on disk?
isInterpretable :: Unlinked -> Bool
isInterpretable = not . isObject
nameOfObject_maybe :: Unlinked -> Maybe FilePath
nameOfObject_maybe (DotO fn) = Just fn
nameOfObject_maybe (DotA fn) = Just fn
nameOfObject_maybe (DotDLL fn) = Just fn
nameOfObject_maybe (CoreBindings {}) = Nothing
nameOfObject_maybe (LoadedBCOs{}) = Nothing
nameOfObject_maybe (BCOs {}) = Nothing
-- | Retrieve the filename of the linkable if possible. Panic if it is a byte-code object
nameOfObject :: Unlinked -> FilePath
nameOfObject o = fromMaybe (pprPanic "nameOfObject" (ppr o)) (nameOfObject_maybe o)
-- | Retrieve the compiled byte-code if possible. Panic if it is a file-based linkable
byteCodeOfObject :: Unlinked -> [CompiledByteCode]
byteCodeOfObject (BCOs bc _) = [bc]
byteCodeOfObject (LoadedBCOs ul) = concatMap byteCodeOfObject ul
byteCodeOfObject other = pprPanic "byteCodeOfObject" (ppr other)
{- **********************************************************************
Loading packages
********************************************************************* -}
data LibrarySpec
= Objects [FilePath] -- Full path names of set of .o files, including trailing .o
-- We allow batched loading to ensure that cyclic symbol
-- references can be resolved (see #13786).
-- For dynamic objects only, try to find the object
-- file in all the directories specified in
-- v_Library_paths before giving up.
| Archive FilePath -- Full path name of a .a file, including trailing .a
| DLL String -- "Unadorned" name of a .DLL/.so
-- e.g. On unix "qt" denotes "libqt.so"
-- On Windows "burble" denotes "burble.DLL" or "libburble.dll"
-- loadDLL is platform-specific and adds the lib/.so/.DLL
-- suffixes platform-dependently
| DLLPath FilePath -- Absolute or relative pathname to a dynamic library
-- (ends with .dll or .so).
| Framework String -- Only used for darwin, but does no harm
instance Outputable LibrarySpec where
ppr (Objects objs) = text "Objects" <+> ppr (map (text @SDoc) objs)
ppr (Archive a) = text "Archive" <+> text a
ppr (DLL s) = text "DLL" <+> text s
ppr (DLLPath f) = text "DLLPath" <+> text f
ppr (Framework s) = text "Framework" <+> text s
|