    -----------------------------------------------------------------------------
    --
    -- 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, RemotePtr )
    import GHCi.Message            ( LoadedDLL )

    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.

    Note [Looking up symbols in the relevant objects]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    In #23415, we determined that a lot of time (>10s, or even up to >35s!) was
    being spent on dynamically loading symbols before actually interpreting code
    when `:main` was run in GHCi. The root cause was that for each symbol we wanted
    to lookup, we would traverse the list of loaded objects and try find the symbol
    in each of them with dlsym (i.e. looking up a symbol was, worst case, linear in
    the amount of loaded objects).

    To drastically improve load time (from +-38 seconds down to +-2s), we now:

    1. For every of the native objects loaded for a given unit, store the handles returned by `dlopen`.
      - In `pkgs_loaded` of the `LoaderState`, which maps `UnitId`s to
        `LoadedPkgInfo`s, where the handles live in its field `loaded_pkg_hs_dlls`.

    2. When looking up a Name (e.g. `lookupHsSymbol`), find that name's `UnitId` in
        the `pkgs_loaded` mapping,

    3. And only look for the symbol (with `dlsym`) on the /handles relevant to that
        unit/, rather than in every loaded object.

    Note [Symbols may not be found in pkgs_loaded]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Currently the `pkgs_loaded` mapping only contains the dynamic objects
    associated with loaded units. Symbols defined in a static object (e.g. from a
    statically-linked Haskell library) are found via the generic `lookupSymbol`
    function call by `lookupHsSymbol` when the symbol is not found in any of the
    dynamic objects of `pkgs_loaded`.

    The rationale here is two-fold:

     * we have only observed major link-time issues in dynamic linking; lookups in
     the RTS linker's static symbol table seem to be fast enough

     * allowing symbol lookups restricted to a single ObjectCode would require the
     maintenance of a symbol table per `ObjectCode`, which would introduce time and
     space overhead

    This fallback is further needed because we don't look in the haskell objects
    loaded for the home units (see the call to `loadModuleLinkables` in
    `loadDependencies`, as opposed to the call to `loadPackages'` in the same
    function which updates `pkgs_loaded`). We should ultimately keep track of the
    objects loaded (probably in `objs_loaded`, for which `LinkableSet` is a bit
    unsatisfactory, see a suggestion in 51c5c4eb1f2a33e4dc88e6a37b7b7c135234ce9b)
    and be able to lookup symbols specifically in them too (similarly to
    `lookupSymbolInDLL`).
    -}

    newtype Loader = Loader { Loader -> MVar (Maybe LoaderState)
    loader_state :: MVar (Maybe LoaderState) }

    data LoaderState = LoaderState
        { LoaderState -> LinkerEnv
    linker_env :: !LinkerEnv
            -- ^ Current global mapping from Names to their true values

        , LoaderState -> LinkableSet
    bcos_loaded :: !LinkableSet
            -- ^ The currently loaded interpreted modules (home package)

        , LoaderState -> LinkableSet
    objs_loaded :: !LinkableSet
            -- ^ And the currently-loaded compiled modules (home package)

        , LoaderState -> PkgsLoaded
    pkgs_loaded :: !PkgsLoaded
            -- ^ The currently-loaded packages; always object code
            -- haskell libraries, system libraries, transitive dependencies

        , LoaderState -> [(FilePath, FilePath)]
    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 :: IO Loader
    uninitializedLoader = MVar (Maybe LoaderState) -> Loader
    Loader (MVar (Maybe LoaderState) -> Loader)
    -> IO (MVar (Maybe LoaderState)) -> IO Loader
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe LoaderState -> IO (MVar (Maybe LoaderState))
    forall a. a -> IO (MVar a)
    newMVar Maybe LoaderState
    forall a. Maybe a
    Nothing

    modifyClosureEnv :: LoaderState -> (ClosureEnv -> ClosureEnv) -> LoaderState
    modifyClosureEnv :: LoaderState -> (ClosureEnv -> ClosureEnv) -> LoaderState
    modifyClosureEnv LoaderState
    pls ClosureEnv -> ClosureEnv
    f =
        let le :: LinkerEnv
    le = LoaderState -> LinkerEnv
    linker_env LoaderState
    pls
            ce :: ClosureEnv
    ce = LinkerEnv -> ClosureEnv
    closure_env LinkerEnv
    le
        in LoaderState
    pls { linker_env = le { closure_env = f ce } }

    data LinkerEnv = LinkerEnv
      { LinkerEnv -> ClosureEnv
    closure_env :: !ClosureEnv
          -- ^ Current global mapping from closure Names to their true values

      , LinkerEnv -> ItblEnv
    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.

      , LinkerEnv -> AddrEnv
    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 :: (Name -> Bool) -> LinkerEnv -> LinkerEnv
    filterLinkerEnv Name -> Bool
    f LinkerEnv
    le = LinkerEnv
      { closure_env :: ClosureEnv
    closure_env = ((Name, ForeignHValue) -> Bool) -> ClosureEnv -> ClosureEnv
    forall elt. (elt -> Bool) -> NameEnv elt -> NameEnv elt
    filterNameEnv (Name -> Bool
    f (Name -> Bool)
    -> ((Name, ForeignHValue) -> Name) -> (Name, ForeignHValue) -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Name, ForeignHValue) -> Name
    forall a b. (a, b) -> a
    fst) (LinkerEnv -> ClosureEnv
    closure_env LinkerEnv
    le)
      , itbl_env :: ItblEnv
    itbl_env    = ((Name, ItblPtr) -> Bool) -> ItblEnv -> ItblEnv
    forall elt. (elt -> Bool) -> NameEnv elt -> NameEnv elt
    filterNameEnv (Name -> Bool
    f (Name -> Bool)
    -> ((Name, ItblPtr) -> Name) -> (Name, ItblPtr) -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Name, ItblPtr) -> Name
    forall a b. (a, b) -> a
    fst) (LinkerEnv -> ItblEnv
    itbl_env LinkerEnv
    le)
      , addr_env :: AddrEnv
    addr_env    = ((Name, AddrPtr) -> Bool) -> AddrEnv -> AddrEnv
    forall elt. (elt -> Bool) -> NameEnv elt -> NameEnv elt
    filterNameEnv (Name -> Bool
    f (Name -> Bool)
    -> ((Name, AddrPtr) -> Name) -> (Name, AddrPtr) -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Name, AddrPtr) -> Name
    forall a b. (a, b) -> a
    fst) (LinkerEnv -> AddrEnv
    addr_env LinkerEnv
    le)
      }

    type ClosureEnv = NameEnv (Name, ForeignHValue)

    emptyClosureEnv :: ClosureEnv
    emptyClosureEnv :: ClosureEnv
    emptyClosureEnv = ClosureEnv
    forall a. NameEnv a
    emptyNameEnv

    extendClosureEnv :: ClosureEnv -> [(Name,ForeignHValue)] -> ClosureEnv
    extendClosureEnv :: ClosureEnv -> [(Name, ForeignHValue)] -> ClosureEnv
    extendClosureEnv ClosureEnv
    cl_env [(Name, ForeignHValue)]
    pairs
      = ClosureEnv -> [(Name, (Name, ForeignHValue))] -> ClosureEnv
    forall a. NameEnv a -> [(Name, a)] -> NameEnv a
    extendNameEnvList ClosureEnv
    cl_env [ (Name
    n, (Name
    n,ForeignHValue
    v)) | (Name
    n,ForeignHValue
    v) <- [(Name, ForeignHValue)]
    pairs]

    type PkgsLoaded = UniqDFM UnitId LoadedPkgInfo

    data LoadedPkgInfo
      = LoadedPkgInfo
      { LoadedPkgInfo -> UnitId
    loaded_pkg_uid         :: !UnitId
      , LoadedPkgInfo -> [LibrarySpec]
    loaded_pkg_hs_objs     :: ![LibrarySpec]
      , LoadedPkgInfo -> [LibrarySpec]
    loaded_pkg_non_hs_objs :: ![LibrarySpec]
      , LoadedPkgInfo -> [RemotePtr LoadedDLL]
    loaded_pkg_hs_dlls     :: ![RemotePtr LoadedDLL]
        -- ^ See Note [Looking up symbols in the relevant objects]
      , LoadedPkgInfo -> UniqDSet UnitId
    loaded_pkg_trans_deps  :: UniqDSet UnitId
      }

    instance Outputable LoadedPkgInfo where
      ppr :: LoadedPkgInfo -> SDoc
    ppr (LoadedPkgInfo UnitId
    uid [LibrarySpec]
    hs_objs [LibrarySpec]
    non_hs_objs [RemotePtr LoadedDLL]
    _ UniqDSet UnitId
    trans_deps) =
        [SDoc] -> SDoc
    forall doc. IsDoc doc => [doc] -> doc
    vcat [UnitId -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr UnitId
    uid
             , [LibrarySpec] -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr [LibrarySpec]
    hs_objs
             , [LibrarySpec] -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr [LibrarySpec]
    non_hs_objs
             , UniqDSet UnitId -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr UniqDSet UnitId
    trans_deps ]

    -- | Information we can use to dynamically link modules into the compiler
    data Linkable = LM {
      Linkable -> UTCTime
    linkableTime     :: !UTCTime,         -- ^ Time at which this linkable was built
                                            -- (i.e. when the bytecodes were produced,
                                            --       or the mod date on the files)
      Linkable -> Module
    linkableModule   :: !Module,          -- ^ The linkable module itself
      Linkable -> [Unlinked]
    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 :: [Linkable] -> LinkableSet
    mkLinkableSet [Linkable]
    ls = [(Module, Linkable)] -> LinkableSet
    forall a. [(Module, a)] -> ModuleEnv a
    mkModuleEnv [(Linkable -> Module
    linkableModule Linkable
    l, Linkable
    l) | Linkable
    l <- [Linkable]
    ls]

    unionLinkableSet :: LinkableSet -> LinkableSet -> LinkableSet
    unionLinkableSet :: LinkableSet -> LinkableSet -> LinkableSet
    unionLinkableSet = (Linkable -> Linkable -> Linkable)
    -> LinkableSet -> LinkableSet -> LinkableSet
    forall a.
    (a -> a -> a) -> ModuleEnv a -> ModuleEnv a -> ModuleEnv a
    plusModuleEnv_C Linkable -> Linkable -> Linkable
    go
      where
        go :: Linkable -> Linkable -> Linkable
    go Linkable
    l1 Linkable
    l2
          | Linkable -> UTCTime
    linkableTime Linkable
    l1 UTCTime -> UTCTime -> Bool
    forall a. Ord a => a -> a -> Bool
    > Linkable -> UTCTime
    linkableTime Linkable
    l2 = Linkable
    l1
          | Bool
    otherwise = Linkable
    l2

    instance Outputable Linkable where
      ppr :: Linkable -> SDoc
    ppr (LM UTCTime
    when_made Module
    mod [Unlinked]
    unlinkeds)
         = (FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "LinkableM" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc
    parens (FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text (UTCTime -> FilePath
    forall a. Show a => a -> FilePath
    show UTCTime
    when_made)) SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> Module -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Module
    mod)
           SDoc -> SDoc -> SDoc
    forall doc. IsDoc doc => doc -> doc -> doc
    $$ Int -> SDoc -> SDoc
    nest Int
    3 ([Unlinked] -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr [Unlinked]
    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 :: Unlinked -> SDoc
    ppr (DotO FilePath
    path)   = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "DotO" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    path
      ppr (DotA FilePath
    path)   = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "DotA" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    path
      ppr (DotDLL FilePath
    path) = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "DotDLL" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    path
      ppr (BCOs CompiledByteCode
    bcos [SptEntry]
    spt) = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "BCOs" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> CompiledByteCode -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr CompiledByteCode
    bcos SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> [SptEntry] -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr [SptEntry]
    spt
      ppr (LoadedBCOs{})  = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "LoadedBCOs"
      ppr (CoreBindings {})       = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "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 -> SDoc
    ppr (SptEntry Id
    id Fingerprint
    fpr) = Id -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Id
    id SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <> SDoc
    forall doc. IsLine doc => doc
    colon SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> Fingerprint -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Fingerprint
    fpr

    isObjectLinkable :: Linkable -> Bool
    isObjectLinkable :: Linkable -> Bool
    isObjectLinkable Linkable
    l = Bool -> Bool
    not ([Unlinked] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Unlinked]
    unlinked) Bool -> Bool -> Bool
    && (Unlinked -> Bool) -> [Unlinked] -> Bool
    forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
    all Unlinked -> Bool
    isObject [Unlinked]
    unlinked
      where unlinked :: [Unlinked]
    unlinked = Linkable -> [Unlinked]
    linkableUnlinked Linkable
    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 :: Linkable -> [FilePath]
    linkableObjs Linkable
    l = [ FilePath
    f | DotO FilePath
    f <- Linkable -> [Unlinked]
    linkableUnlinked Linkable
    l ]

    -------------------------------------------

    -- | Is this an actual file on disk we can link in somehow?
    isObject :: Unlinked -> Bool
    isObject :: Unlinked -> Bool
    isObject (DotO FilePath
    _)   = Bool
    True
    isObject (DotA FilePath
    _)   = Bool
    True
    isObject (DotDLL FilePath
    _) = Bool
    True
    isObject Unlinked
    _          = Bool
    False

    -- | Is this a bytecode linkable with no file on disk?
    isInterpretable :: Unlinked -> Bool
    isInterpretable :: Unlinked -> Bool
    isInterpretable = Bool -> Bool
    not (Bool -> Bool) -> (Unlinked -> Bool) -> Unlinked -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Unlinked -> Bool
    isObject

    nameOfObject_maybe :: Unlinked -> Maybe FilePath
    nameOfObject_maybe :: Unlinked -> Maybe FilePath
    nameOfObject_maybe (DotO FilePath
    fn)   = FilePath -> Maybe FilePath
    forall a. a -> Maybe a
    Just FilePath
    fn
    nameOfObject_maybe (DotA FilePath
    fn)   = FilePath -> Maybe FilePath
    forall a. a -> Maybe a
    Just FilePath
    fn
    nameOfObject_maybe (DotDLL FilePath
    fn) = FilePath -> Maybe FilePath
    forall a. a -> Maybe a
    Just FilePath
    fn
    nameOfObject_maybe (CoreBindings {}) = Maybe FilePath
    forall a. Maybe a
    Nothing
    nameOfObject_maybe (LoadedBCOs{}) = Maybe FilePath
    forall a. Maybe a
    Nothing
    nameOfObject_maybe (BCOs {})   = Maybe FilePath
    forall a. Maybe a
    Nothing

    -- | Retrieve the filename of the linkable if possible. Panic if it is a byte-code object
    nameOfObject :: Unlinked -> FilePath
    nameOfObject :: Unlinked -> FilePath
    nameOfObject Unlinked
    o = FilePath -> Maybe FilePath -> FilePath
    forall a. a -> Maybe a -> a
    fromMaybe (FilePath -> SDoc -> FilePath
    forall a. HasCallStack => FilePath -> SDoc -> a
    pprPanic FilePath
    "nameOfObject" (Unlinked -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Unlinked
    o)) (Unlinked -> Maybe FilePath
    nameOfObject_maybe Unlinked
    o)

    -- | Retrieve the compiled byte-code if possible. Panic if it is a file-based linkable
    byteCodeOfObject :: Unlinked -> [CompiledByteCode]
    byteCodeOfObject :: Unlinked -> [CompiledByteCode]
    byteCodeOfObject (BCOs CompiledByteCode
    bc [SptEntry]
    _) = [CompiledByteCode
    bc]
    byteCodeOfObject (LoadedBCOs [Unlinked]
    ul) = (Unlinked -> [CompiledByteCode])
    -> [Unlinked] -> [CompiledByteCode]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap Unlinked -> [CompiledByteCode]
    byteCodeOfObject [Unlinked]
    ul
    byteCodeOfObject Unlinked
    other       = FilePath -> SDoc -> [CompiledByteCode]
    forall a. HasCallStack => FilePath -> SDoc -> a
    pprPanic FilePath
    "byteCodeOfObject" (Unlinked -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr Unlinked
    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 :: LibrarySpec -> SDoc
    ppr (Objects [FilePath]
    objs) = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "Objects" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> [SDoc] -> SDoc
    forall a. Outputable a => a -> SDoc
    ppr ((FilePath -> SDoc) -> [FilePath] -> [SDoc]
    forall a b. (a -> b) -> [a] -> [b]
    map (forall doc. IsLine doc => FilePath -> doc
    text @SDoc) [FilePath]
    objs)
      ppr (Archive FilePath
    a) = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "Archive" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    a
      ppr (DLL FilePath
    s) = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "DLL" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    s
      ppr (DLLPath FilePath
    f) = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "DLLPath" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    f
      ppr (Framework FilePath
    s) = FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    "Framework" SDoc -> SDoc -> SDoc
    forall doc. IsLine doc => doc -> doc -> doc
    <+> FilePath -> SDoc
    forall doc. IsLine doc => FilePath -> doc
    text FilePath
    s