module GHC.Linker.ExtraObj
( mkExtraObj
, mkExtraObjToLinkIntoBinary
, mkNoteObjsToLinkIntoBinary
, checkLinkInfo
, getLinkInfo
, getCompilerInfo
, ghcLinkInfoSectionName
, ghcLinkInfoNoteName
, platformSupportsSavingLinkOpts
, haveRtsOptsFlags
)
where
import GHC.Prelude
import GHC.Platform
import GHC.Unit
import GHC.Unit.Env
import GHC.Unit.State
import GHC.Utils.Asm
import GHC.Utils.Error
import GHC.Utils.Misc
import GHC.Utils.Outputable as Outputable
import GHC.Utils.Logger
import GHC.Utils.TmpFs
import GHC.Driver.Session
import GHC.Driver.Ppr
import qualified GHC.Data.ShortText as ST
import GHC.SysTools.Elf
import GHC.SysTools.Tasks
import GHC.SysTools.Info
import GHC.Linker.Unit
import Control.Monad.IO.Class
import Control.Monad
import Data.Maybe
mkExtraObj :: Logger -> TmpFs -> DynFlags -> UnitState -> Suffix -> String -> IO FilePath
mkExtraObj logger tmpfs dflags unit_state extn xs
= do cFile <- newTempName logger tmpfs dflags TFL_CurrentModule extn
oFile <- newTempName logger tmpfs dflags TFL_GhcSession "o"
writeFile cFile xs
ccInfo <- liftIO $ getCompilerInfo logger dflags
runCc Nothing logger tmpfs dflags
([Option "-c",
FileOption "" cFile,
Option "-o",
FileOption "" oFile]
++ if extn /= "s"
then cOpts
else asmOpts ccInfo)
return oFile
where
cOpts = map Option (picCCOpts dflags)
++ map (FileOption "-I" . ST.unpack)
(unitIncludeDirs $ unsafeLookupUnit unit_state rtsUnit)
asmOpts ccInfo =
if any (ccInfo ==) [Clang, AppleClang, AppleClang51]
then [Option "-Qunused-arguments"]
else []
mkExtraObjToLinkIntoBinary :: Logger -> TmpFs -> DynFlags -> UnitState -> IO (Maybe FilePath)
mkExtraObjToLinkIntoBinary logger tmpfs dflags unit_state = do
when (gopt Opt_NoHsMain dflags && haveRtsOptsFlags dflags) $
logInfo logger dflags $ withPprStyle defaultUserStyle
(text "Warning: -rtsopts and -with-rtsopts have no effect with -no-hs-main." $$
text " Call hs_init_ghc() from your main() function to set these options.")
case ghcLink dflags of
_ | gopt Opt_NoHsMain dflags
-> return Nothing
LinkDynLib
| OSMinGW32 <- platformOS (targetPlatform dflags)
-> mk_extra_obj dllMain
| otherwise
-> return Nothing
_ -> mk_extra_obj exeMain
where
mk_extra_obj = fmap Just . mkExtraObj logger tmpfs dflags unit_state "c" . showSDoc dflags
exeMain = vcat [
text "#include <Rts.h>",
text "extern StgClosure ZCMain_main_closure;",
text "int main(int argc, char *argv[])",
char '{',
text " RtsConfig __conf = defaultRtsConfig;",
text " __conf.rts_opts_enabled = "
<> text (show (rtsOptsEnabled dflags)) <> semi,
text " __conf.rts_opts_suggestions = "
<> text (if rtsOptsSuggestions dflags
then "true"
else "false") <> semi,
text "__conf.keep_cafs = "
<> text (if gopt Opt_KeepCAFs dflags
then "true"
else "false") <> semi,
case rtsOpts dflags of
Nothing -> Outputable.empty
Just opts -> text " __conf.rts_opts= " <>
text (show opts) <> semi,
text " __conf.rts_hs_main = true;",
text " return hs_main(argc,argv,&ZCMain_main_closure,__conf);",
char '}',
char '\n'
]
dllMain = vcat [
text "#include <Rts.h>",
text "#include <windows.h>",
text "#include <stdbool.h>",
char '\n',
text "bool",
text "WINAPI",
text "DllMain ( HINSTANCE hInstance STG_UNUSED",
text " , DWORD reason STG_UNUSED",
text " , LPVOID reserved STG_UNUSED",
text " )",
text "{",
text " return true;",
text "}",
char '\n'
]
mkNoteObjsToLinkIntoBinary :: Logger -> TmpFs -> DynFlags -> UnitEnv -> [UnitId] -> IO [FilePath]
mkNoteObjsToLinkIntoBinary logger tmpfs dflags unit_env dep_packages = do
link_info <- getLinkInfo dflags unit_env dep_packages
if (platformSupportsSavingLinkOpts (platformOS platform ))
then fmap (:[]) $ mkExtraObj logger tmpfs dflags unit_state "s" (showSDoc dflags (link_opts link_info))
else return []
where
unit_state = ue_units unit_env
platform = ue_platform unit_env
link_opts info = hcat
[
makeElfNote platform ghcLinkInfoSectionName ghcLinkInfoNoteName 0 info
, if platformHasGnuNonexecStack platform
then text ".section .note.GNU-stack,\"\","
<> sectionType platform "progbits" <> char '\n'
else Outputable.empty
]
getLinkInfo :: DynFlags -> UnitEnv -> [UnitId] -> IO String
getLinkInfo dflags unit_env dep_packages = do
package_link_opts <- getUnitLinkOpts dflags unit_env dep_packages
pkg_frameworks <- if not (platformUsesFrameworks (ue_platform unit_env))
then return []
else do
ps <- mayThrowUnitErr (preloadUnitsInfo' unit_env dep_packages)
return (collectFrameworks ps)
let link_info =
( package_link_opts
, pkg_frameworks
, rtsOpts dflags
, rtsOptsEnabled dflags
, gopt Opt_NoHsMain dflags
, map showOpt (ldInputs dflags)
, getOpts dflags opt_l
)
return (show link_info)
platformSupportsSavingLinkOpts :: OS -> Bool
platformSupportsSavingLinkOpts os
| os == OSSolaris2 = False
| otherwise = osElfTarget os
ghcLinkInfoSectionName :: String
ghcLinkInfoSectionName = ".debug-ghc-link-info"
ghcLinkInfoNoteName :: String
ghcLinkInfoNoteName = "GHC link info"
checkLinkInfo :: Logger -> DynFlags -> UnitEnv -> [UnitId] -> FilePath -> IO Bool
checkLinkInfo logger dflags unit_env pkg_deps exe_file
| not (platformSupportsSavingLinkOpts (platformOS (ue_platform unit_env)))
= return False
| otherwise
= do
link_info <- getLinkInfo dflags unit_env pkg_deps
debugTraceMsg logger dflags 3 $ text ("Link info: " ++ link_info)
m_exe_link_info <- readElfNoteAsString logger dflags exe_file
ghcLinkInfoSectionName ghcLinkInfoNoteName
let sameLinkInfo = (Just link_info == m_exe_link_info)
debugTraceMsg logger dflags 3 $ case m_exe_link_info of
Nothing -> text "Exe link info: Not found"
Just s
| sameLinkInfo -> text ("Exe link info is the same")
| otherwise -> text ("Exe link info is different: " ++ s)
return (not sameLinkInfo)
haveRtsOptsFlags :: DynFlags -> Bool
haveRtsOptsFlags dflags =
isJust (rtsOpts dflags) || case rtsOptsEnabled dflags of
RtsOptsSafeOnly -> False
_ -> True