Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
-
e650ec3e
by Ben Gamari at 2025-05-23T03:42:46-04:00
-
94cd9ca4
by Ben Gamari at 2025-05-23T03:42:46-04:00
-
7722232c
by Ben Gamari at 2025-05-23T03:42:46-04:00
-
3f4b823c
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
6e23fef2
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
ea74860c
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
74c4db46
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
972d81d6
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
8a1073a5
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
44f509f2
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
bfb12783
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
08469ff8
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
823d1ccf
by Ben Gamari at 2025-05-23T03:43:28-04:00
-
f9501cef
by Sylvain Henry at 2025-05-23T10:21:43-04:00
-
f422b3c7
by Ben Gamari at 2025-05-23T10:21:45-04:00
30 changed files:
- compiler/GHC.hs
- compiler/GHC/ByteCode/Types.hs
- compiler/GHC/HsToCore/Breakpoints.hs
- compiler/GHC/Runtime/Debugger/Breakpoints.hs
- compiler/GHC/Runtime/Eval.hs
- compiler/GHC/Runtime/Interpreter.hs
- compiler/GHC/StgToByteCode.hs
- ghc/GHCi/UI.hs
- libraries/base/changelog.md
- libraries/base/src/Control/Exception/Backtrace.hs
- libraries/ghc-internal/src/GHC/Internal/Exception/Backtrace.hs
- rts/Hash.c
- rts/Hash.h
- rts/Linker.c
- rts/LinkerInternals.h
- rts/PathUtils.c
- rts/PathUtils.h
- rts/linker/Elf.c
- rts/linker/MachO.c
- rts/linker/PEi386.c
- rts/linker/PEi386.h
- + rts/linker/ProddableBlocks.c
- + rts/linker/ProddableBlocks.h
- rts/rts.cabal
- testsuite/tests/interface-stability/base-exports.stdout
- testsuite/tests/interface-stability/base-exports.stdout-javascript-unknown-ghcjs
- testsuite/tests/interface-stability/base-exports.stdout-mingw32
- testsuite/tests/interface-stability/base-exports.stdout-ws-32
- + testsuite/tests/rts/TestProddableBlockSet.c
- testsuite/tests/rts/all.T
Changes:
| ... | ... | @@ -1307,7 +1307,7 @@ typecheckModule pmod = do |
| 1307 | 1307 | minf_instances = fixSafeInstances safe $ instEnvElts $ md_insts details,
|
| 1308 | 1308 | minf_iface = Nothing,
|
| 1309 | 1309 | minf_safe = safe,
|
| 1310 | - minf_modBreaks = emptyModBreaks
|
|
| 1310 | + minf_modBreaks = Nothing
|
|
| 1311 | 1311 | }}
|
| 1312 | 1312 | |
| 1313 | 1313 | -- | Desugar a typechecked module.
|
| ... | ... | @@ -1461,7 +1461,7 @@ data ModuleInfo = ModuleInfo { |
| 1461 | 1461 | minf_instances :: [ClsInst],
|
| 1462 | 1462 | minf_iface :: Maybe ModIface,
|
| 1463 | 1463 | minf_safe :: SafeHaskellMode,
|
| 1464 | - minf_modBreaks :: ModBreaks
|
|
| 1464 | + minf_modBreaks :: Maybe ModBreaks
|
|
| 1465 | 1465 | }
|
| 1466 | 1466 | -- We don't want HomeModInfo here, because a ModuleInfo applies
|
| 1467 | 1467 | -- to package modules too.
|
| ... | ... | @@ -1490,7 +1490,7 @@ getPackageModuleInfo hsc_env mdl |
| 1490 | 1490 | minf_instances = error "getModuleInfo: instances for package module unimplemented",
|
| 1491 | 1491 | minf_iface = Just iface,
|
| 1492 | 1492 | minf_safe = getSafeMode $ mi_trust iface,
|
| 1493 | - minf_modBreaks = emptyModBreaks
|
|
| 1493 | + minf_modBreaks = Nothing
|
|
| 1494 | 1494 | }))
|
| 1495 | 1495 | |
| 1496 | 1496 | availsToGlobalRdrEnv :: HasDebugCallStack => HscEnv -> Module -> [AvailInfo] -> IfGlobalRdrEnv
|
| ... | ... | @@ -1567,7 +1567,7 @@ modInfoIface = minf_iface |
| 1567 | 1567 | modInfoSafe :: ModuleInfo -> SafeHaskellMode
|
| 1568 | 1568 | modInfoSafe = minf_safe
|
| 1569 | 1569 | |
| 1570 | -modInfoModBreaks :: ModuleInfo -> ModBreaks
|
|
| 1570 | +modInfoModBreaks :: ModuleInfo -> Maybe ModBreaks
|
|
| 1571 | 1571 | modInfoModBreaks = minf_modBreaks
|
| 1572 | 1572 | |
| 1573 | 1573 | isDictonaryId :: Id -> Bool
|
| ... | ... | @@ -19,7 +19,7 @@ module GHC.ByteCode.Types |
| 19 | 19 | , ItblEnv, ItblPtr(..)
|
| 20 | 20 | , AddrEnv, AddrPtr(..)
|
| 21 | 21 | , CgBreakInfo(..)
|
| 22 | - , ModBreaks (..), BreakIndex, emptyModBreaks
|
|
| 22 | + , ModBreaks (..), BreakIndex
|
|
| 23 | 23 | , CCostCentre
|
| 24 | 24 | , FlatBag, sizeFlatBag, fromSmallArray, elemsFlatBag
|
| 25 | 25 | ) where
|
| ... | ... | @@ -45,12 +45,11 @@ import Foreign |
| 45 | 45 | import Data.Array
|
| 46 | 46 | import Data.ByteString (ByteString)
|
| 47 | 47 | import Data.IntMap (IntMap)
|
| 48 | -import qualified Data.IntMap as IntMap
|
|
| 49 | 48 | import qualified GHC.Exts.Heap as Heap
|
| 50 | 49 | import GHC.Stack.CCS
|
| 51 | 50 | import GHC.Cmm.Expr ( GlobalRegSet, emptyRegSet, regSetToList )
|
| 52 | 51 | import GHC.Iface.Syntax
|
| 53 | -import Language.Haskell.Syntax.Module.Name (ModuleName, mkModuleNameFS)
|
|
| 52 | +import Language.Haskell.Syntax.Module.Name (ModuleName)
|
|
| 54 | 53 | import GHC.Unit.Types (UnitId(..))
|
| 55 | 54 | |
| 56 | 55 | -- -----------------------------------------------------------------------------
|
| ... | ... | @@ -250,7 +249,7 @@ data CCostCentre |
| 250 | 249 | -- | All the information about the breakpoints for a module
|
| 251 | 250 | data ModBreaks
|
| 252 | 251 | = ModBreaks
|
| 253 | - { modBreaks_flags :: ForeignRef BreakArray
|
|
| 252 | + { modBreaks_flags :: !(ForeignRef BreakArray)
|
|
| 254 | 253 | -- ^ The array of flags, one per breakpoint,
|
| 255 | 254 | -- indicating which breakpoints are enabled.
|
| 256 | 255 | , modBreaks_locs :: !(Array BreakIndex SrcSpan)
|
| ... | ... | @@ -281,20 +280,6 @@ seqModBreaks ModBreaks{..} = |
| 281 | 280 | rnf modBreaks_module `seq`
|
| 282 | 281 | rnf modBreaks_module_unitid
|
| 283 | 282 | |
| 284 | --- | Construct an empty ModBreaks
|
|
| 285 | -emptyModBreaks :: ModBreaks
|
|
| 286 | -emptyModBreaks = ModBreaks
|
|
| 287 | - { modBreaks_flags = error "ModBreaks.modBreaks_array not initialised"
|
|
| 288 | - -- ToDo: can we avoid this?
|
|
| 289 | - , modBreaks_locs = array (0,-1) []
|
|
| 290 | - , modBreaks_vars = array (0,-1) []
|
|
| 291 | - , modBreaks_decls = array (0,-1) []
|
|
| 292 | - , modBreaks_ccs = array (0,-1) []
|
|
| 293 | - , modBreaks_breakInfo = IntMap.empty
|
|
| 294 | - , modBreaks_module = mkModuleNameFS nilFS
|
|
| 295 | - , modBreaks_module_unitid = UnitId nilFS
|
|
| 296 | - }
|
|
| 297 | - |
|
| 298 | 283 | {-
|
| 299 | 284 | Note [Field modBreaks_decls]
|
| 300 | 285 | ~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -18,6 +18,7 @@ import GHC.Utils.Outputable as Outputable |
| 18 | 18 | |
| 19 | 19 | import Data.List (intersperse)
|
| 20 | 20 | import Data.Array
|
| 21 | +import qualified Data.IntMap as IntMap
|
|
| 21 | 22 | |
| 22 | 23 | -- | Initialize memory for breakpoint data that is shared between the bytecode
|
| 23 | 24 | -- generator and the interpreter.
|
| ... | ... | @@ -38,15 +39,16 @@ mkModBreaks interp mod extendedMixEntries |
| 38 | 39 | locsTicks = listArray (0,count-1) [ tick_loc t | t <- entries ]
|
| 39 | 40 | varsTicks = listArray (0,count-1) [ tick_ids t | t <- entries ]
|
| 40 | 41 | declsTicks = listArray (0,count-1) [ tick_path t | t <- entries ]
|
| 41 | - return $ emptyModBreaks
|
|
| 42 | - { modBreaks_flags = breakArray
|
|
| 43 | - , modBreaks_locs = locsTicks
|
|
| 44 | - , modBreaks_vars = varsTicks
|
|
| 45 | - , modBreaks_decls = declsTicks
|
|
| 46 | - , modBreaks_ccs = ccs
|
|
| 47 | - , modBreaks_module = moduleName mod
|
|
| 48 | - , modBreaks_module_unitid = toUnitId $ moduleUnit mod
|
|
| 49 | - }
|
|
| 42 | + return $ ModBreaks
|
|
| 43 | + { modBreaks_flags = breakArray
|
|
| 44 | + , modBreaks_locs = locsTicks
|
|
| 45 | + , modBreaks_vars = varsTicks
|
|
| 46 | + , modBreaks_decls = declsTicks
|
|
| 47 | + , modBreaks_ccs = ccs
|
|
| 48 | + , modBreaks_breakInfo = IntMap.empty
|
|
| 49 | + , modBreaks_module = moduleName mod
|
|
| 50 | + , modBreaks_module_unitid = toUnitId $ moduleUnit mod
|
|
| 51 | + }
|
|
| 50 | 52 | |
| 51 | 53 | mkCCSArray
|
| 52 | 54 | :: Interp -> Module -> Int -> [Tick]
|
| ... | ... | @@ -145,15 +145,17 @@ resolveFunctionBreakpoint inp = do |
| 145 | 145 | validateBP _ "" (Just _) = pure $ Just $ text "Function name is missing"
|
| 146 | 146 | validateBP _ fun_str (Just modl) = do
|
| 147 | 147 | isInterpr <- GHC.moduleIsInterpreted modl
|
| 148 | - (_, decls) <- getModBreak modl
|
|
| 149 | 148 | mb_err_msg <- case isInterpr of
|
| 150 | - False -> pure $ Just $ text "Module" <+> quotes (ppr modl)
|
|
| 151 | - <+> text "is not interpreted"
|
|
| 152 | - True -> case fun_str `elem` (intercalate "." <$> elems decls) of
|
|
| 153 | - False -> pure $ Just $
|
|
| 154 | - text "No breakpoint found for" <+> quotes (text fun_str)
|
|
| 155 | - <+> text "in module" <+> quotes (ppr modl)
|
|
| 156 | - True -> pure Nothing
|
|
| 149 | + False -> pure $ Just $ text "Module" <+> quotes (ppr modl) <+> text "is not interpreted"
|
|
| 150 | + True -> do
|
|
| 151 | + mb_modbreaks <- getModBreak modl
|
|
| 152 | + let found = case mb_modbreaks of
|
|
| 153 | + Nothing -> False
|
|
| 154 | + Just mb -> fun_str `elem` (intercalate "." <$> elems (GHC.modBreaks_decls mb))
|
|
| 155 | + if found
|
|
| 156 | + then pure Nothing
|
|
| 157 | + else pure $ Just $ text "No breakpoint found for" <+> quotes (text fun_str)
|
|
| 158 | + <+> text "in module" <+> quotes (ppr modl)
|
|
| 157 | 159 | pure mb_err_msg
|
| 158 | 160 | |
| 159 | 161 | -- | The aim of this function is to find the breakpoints for all the RHSs of
|
| ... | ... | @@ -184,8 +186,7 @@ type TickArray = Array Int [(GHC.BreakIndex,RealSrcSpan)] |
| 184 | 186 | makeModuleLineMap :: GhcMonad m => Module -> m (Maybe TickArray)
|
| 185 | 187 | makeModuleLineMap m = do
|
| 186 | 188 | mi <- GHC.getModuleInfo m
|
| 187 | - return $
|
|
| 188 | - mkTickArray . assocs . GHC.modBreaks_locs . GHC.modInfoModBreaks <$> mi
|
|
| 189 | + return $ mkTickArray . assocs . GHC.modBreaks_locs <$> (GHC.modInfoModBreaks =<< mi)
|
|
| 189 | 190 | where
|
| 190 | 191 | mkTickArray :: [(BreakIndex, SrcSpan)] -> TickArray
|
| 191 | 192 | mkTickArray ticks
|
| ... | ... | @@ -195,15 +196,12 @@ makeModuleLineMap m = do |
| 195 | 196 | max_line = foldr max 0 [ GHC.srcSpanEndLine sp | (_, RealSrcSpan sp _) <- ticks ]
|
| 196 | 197 | srcSpanLines pan = [ GHC.srcSpanStartLine pan .. GHC.srcSpanEndLine pan ]
|
| 197 | 198 | |
| 198 | --- | Get the 'modBreaks_locs' and 'modBreaks_decls' of the given 'Module'
|
|
| 199 | +-- | Get the 'ModBreaks' of the given 'Module' when available
|
|
| 199 | 200 | getModBreak :: GHC.GhcMonad m
|
| 200 | - => Module -> m (Array Int SrcSpan, Array Int [String])
|
|
| 201 | + => Module -> m (Maybe ModBreaks)
|
|
| 201 | 202 | getModBreak m = do
|
| 202 | 203 | mod_info <- fromMaybe (panic "getModBreak") <$> GHC.getModuleInfo m
|
| 203 | - let modBreaks = GHC.modInfoModBreaks mod_info
|
|
| 204 | - let ticks = GHC.modBreaks_locs modBreaks
|
|
| 205 | - let decls = GHC.modBreaks_decls modBreaks
|
|
| 206 | - return (ticks, decls)
|
|
| 204 | + pure $ GHC.modInfoModBreaks mod_info
|
|
| 207 | 205 | |
| 208 | 206 | --------------------------------------------------------------------------------
|
| 209 | 207 | -- Getting current breakpoint information
|
| ... | ... | @@ -522,9 +522,8 @@ result_fs = fsLit "_result" |
| 522 | 522 | |
| 523 | 523 | -- | Read the 'ModBreaks' of the given home 'Module' from the 'HomeUnitGraph'.
|
| 524 | 524 | readModBreaks :: HscEnv -> Module -> IO ModBreaks
|
| 525 | -readModBreaks hsc_env mod =
|
|
| 526 | - getModBreaks . expectJust <$>
|
|
| 527 | - HUG.lookupHugByModule mod (hsc_HUG hsc_env)
|
|
| 525 | +readModBreaks hsc_env mod = expectJust . getModBreaks . expectJust <$> HUG.lookupHugByModule mod (hsc_HUG hsc_env)
|
|
| 526 | + |
|
| 528 | 527 | |
| 529 | 528 | bindLocalsAtBreakpoint
|
| 530 | 529 | :: HscEnv
|
| ... | ... | @@ -435,22 +435,24 @@ handleSeqHValueStatus interp unit_env eval_status = |
| 435 | 435 | resume_ctxt_fhv <- liftIO $ mkFinalizedHValue interp resume_ctxt
|
| 436 | 436 | |
| 437 | 437 | let put x = putStrLn ("*** Ignoring breakpoint " ++ (showSDocUnsafe x))
|
| 438 | + let nothing_case = put $ brackets . ppr $ mkGeneralSrcSpan (fsLit "<unknown>")
|
|
| 438 | 439 | case maybe_break of
|
| 439 | - Nothing ->
|
|
| 440 | + Nothing -> nothing_case
|
|
| 440 | 441 | -- Nothing case - should not occur!
|
| 441 | 442 | -- Reason: Setting of flags in libraries/ghci/GHCi/Run.hs:evalOptsSeq
|
| 442 | - put $ brackets . ppr $
|
|
| 443 | - mkGeneralSrcSpan (fsLit "<unknown>")
|
|
| 444 | 443 | |
| 445 | 444 | Just break -> do
|
| 446 | 445 | let bi = evalBreakpointToId break
|
| 447 | 446 | |
| 448 | 447 | -- Just case: Stopped at a breakpoint, extract SrcSpan information
|
| 449 | 448 | -- from the breakpoint.
|
| 450 | - breaks_tick <- getModBreaks . expectJust <$>
|
|
| 449 | + mb_modbreaks <- getModBreaks . expectJust <$>
|
|
| 451 | 450 | lookupHugByModule (ibi_tick_mod bi) (ue_home_unit_graph unit_env)
|
| 452 | - put $ brackets . ppr $
|
|
| 453 | - (modBreaks_locs breaks_tick) ! ibi_tick_index bi
|
|
| 451 | + case mb_modbreaks of
|
|
| 452 | + -- Nothing case - should not occur! We should have the appropriate
|
|
| 453 | + -- breakpoint information
|
|
| 454 | + Nothing -> nothing_case
|
|
| 455 | + Just modbreaks -> put $ brackets . ppr $ (modBreaks_locs modbreaks) ! ibi_tick_index bi
|
|
| 454 | 456 | |
| 455 | 457 | -- resume the seq (:force) processing in the iserv process
|
| 456 | 458 | withForeignRef resume_ctxt_fhv $ \hval -> do
|
| ... | ... | @@ -737,14 +739,14 @@ fromEvalResult :: EvalResult a -> IO a |
| 737 | 739 | fromEvalResult (EvalException e) = throwIO (fromSerializableException e)
|
| 738 | 740 | fromEvalResult (EvalSuccess a) = return a
|
| 739 | 741 | |
| 740 | -getModBreaks :: HomeModInfo -> ModBreaks
|
|
| 742 | +getModBreaks :: HomeModInfo -> Maybe ModBreaks
|
|
| 741 | 743 | getModBreaks hmi
|
| 742 | 744 | | Just linkable <- homeModInfoByteCode hmi,
|
| 743 | 745 | -- The linkable may have 'DotO's as well; only consider BCOs. See #20570.
|
| 744 | 746 | [cbc] <- linkableBCOs linkable
|
| 745 | - = fromMaybe emptyModBreaks (bc_breaks cbc)
|
|
| 747 | + = bc_breaks cbc
|
|
| 746 | 748 | | otherwise
|
| 747 | - = emptyModBreaks -- probably object code
|
|
| 749 | + = Nothing -- probably object code
|
|
| 748 | 750 | |
| 749 | 751 | -- | Interpreter uses Profiling way
|
| 750 | 752 | interpreterProfiled :: Interp -> Bool
|
| ... | ... | @@ -439,8 +439,8 @@ schemeER_wrk d p rhs = schemeE d 0 p rhs |
| 439 | 439 | --
|
| 440 | 440 | -- If the breakpoint is inlined from another module, look it up in the home
|
| 441 | 441 | -- package table.
|
| 442 | --- If the module doesn't exist there, or its module pointer is null (which means
|
|
| 443 | --- that the 'ModBreaks' value is uninitialized), skip the instruction.
|
|
| 442 | +-- If the module doesn't exist there, or if the 'ModBreaks' value is
|
|
| 443 | +-- uninitialized, skip the instruction (i.e. return Nothing).
|
|
| 444 | 444 | break_info ::
|
| 445 | 445 | HscEnv ->
|
| 446 | 446 | Module ->
|
| ... | ... | @@ -449,18 +449,11 @@ break_info :: |
| 449 | 449 | BcM (Maybe ModBreaks)
|
| 450 | 450 | break_info hsc_env mod current_mod current_mod_breaks
|
| 451 | 451 | | mod == current_mod
|
| 452 | - = pure $ check_mod_ptr =<< current_mod_breaks
|
|
| 452 | + = pure current_mod_breaks
|
|
| 453 | 453 | | otherwise
|
| 454 | 454 | = ioToBc (lookupHpt (hsc_HPT hsc_env) (moduleName mod)) >>= \case
|
| 455 | - Just hp -> pure $ check_mod_ptr (getModBreaks hp)
|
|
| 455 | + Just hp -> pure $ getModBreaks hp
|
|
| 456 | 456 | Nothing -> pure Nothing
|
| 457 | - where
|
|
| 458 | - check_mod_ptr mb
|
|
| 459 | - | mod_ptr <- modBreaks_module mb
|
|
| 460 | - , not $ nullFS $ moduleNameFS mod_ptr
|
|
| 461 | - = Just mb
|
|
| 462 | - | otherwise
|
|
| 463 | - = Nothing
|
|
| 464 | 457 | |
| 465 | 458 | getVarOffSets :: Platform -> StackDepth -> BCEnv -> [Id] -> [Maybe (Id, WordOff)]
|
| 466 | 459 | getVarOffSets platform depth env = map getOffSet
|
| ... | ... | @@ -3629,8 +3629,10 @@ completeBreakpoint = wrapCompleter spaces $ \w -> do -- #3000 |
| 3629 | 3629 | -- Return all possible bids for a given Module
|
| 3630 | 3630 | bidsByModule :: GhciMonad m => [ModuleName] -> Module -> m [String]
|
| 3631 | 3631 | bidsByModule nonquals mod = do
|
| 3632 | - (_, decls) <- getModBreak mod
|
|
| 3633 | - let bids = nub $ declPath <$> elems decls
|
|
| 3632 | + mb_decls <- fmap GHC.modBreaks_decls <$> getModBreak mod
|
|
| 3633 | + let bids = case mb_decls of
|
|
| 3634 | + Just decls -> nub $ declPath <$> elems decls
|
|
| 3635 | + Nothing -> []
|
|
| 3634 | 3636 | pure $ case (moduleName mod) `elem` nonquals of
|
| 3635 | 3637 | True -> bids
|
| 3636 | 3638 | False -> (combineModIdent (showModule mod)) <$> bids
|
| ... | ... | @@ -3656,11 +3658,14 @@ completeBreakpoint = wrapCompleter spaces $ \w -> do -- #3000 |
| 3656 | 3658 | -- declarations. See Note [Field modBreaks_decls] in GHC.ByteCode.Types
|
| 3657 | 3659 | addNestedDecls :: GhciMonad m => (String, Module) -> m [String]
|
| 3658 | 3660 | addNestedDecls (ident, mod) = do
|
| 3659 | - (_, decls) <- getModBreak mod
|
|
| 3660 | - let (mod_str, topLvl, _) = splitIdent ident
|
|
| 3661 | - ident_decls = [ elm | elm@(el : _) <- elems decls, el == topLvl ]
|
|
| 3662 | - bids = nub $ declPath <$> ident_decls
|
|
| 3663 | - pure $ map (combineModIdent mod_str) bids
|
|
| 3661 | + mb_decls <- fmap GHC.modBreaks_decls <$> getModBreak mod
|
|
| 3662 | + case mb_decls of
|
|
| 3663 | + Nothing -> pure []
|
|
| 3664 | + Just decls -> do
|
|
| 3665 | + let (mod_str, topLvl, _) = splitIdent ident
|
|
| 3666 | + ident_decls = [ elm | elm@(el : _) <- elems decls, el == topLvl ]
|
|
| 3667 | + bids = nub $ declPath <$> ident_decls
|
|
| 3668 | + pure $ map (combineModIdent mod_str) bids
|
|
| 3664 | 3669 | |
| 3665 | 3670 | completeModule = wrapIdentCompleterMod $ \w -> do
|
| 3666 | 3671 | hsc_env <- GHC.getSession
|
| ... | ... | @@ -4066,7 +4071,7 @@ breakById inp = do |
| 4066 | 4071 | case mb_error of
|
| 4067 | 4072 | Left sdoc -> printForUser sdoc
|
| 4068 | 4073 | Right (mod, mod_info, fun_str) -> do
|
| 4069 | - let modBreaks = GHC.modInfoModBreaks mod_info
|
|
| 4074 | + let modBreaks = expectJust (GHC.modInfoModBreaks mod_info)
|
|
| 4070 | 4075 | findBreakAndSet mod $ \_ -> findBreakForBind fun_str modBreaks
|
| 4071 | 4076 | |
| 4072 | 4077 | breakSyntax :: a
|
| ... | ... | @@ -22,11 +22,13 @@ |
| 22 | 22 | * `GHC.TypeNats.Internal`
|
| 23 | 23 | * `GHC.ExecutionStack.Internal`.
|
| 24 | 24 | * Deprecate `GHC.JS.Prim.Internal.Build`, as per [CLC #329](https://github.com/haskell/core-libraries-committee/issues/329)
|
| 25 | + * Expose constructor and field of `Backtraces` from `Control.Exception.Backtrace`, as per [CLC #199](https://github.com/haskell/core-libraries-committee/issues/199#issuecomment-1954662391)
|
|
| 25 | 26 | |
| 26 | 27 | * Fix incorrect results of `integerPowMod` when the base is 0 and the exponent is negative, and `integerRecipMod` when the modulus is zero ([#26017](https://gitlab.haskell.org/ghc/ghc/-/issues/26017)).
|
| 27 | 28 | |
| 28 | 29 | |
| 29 | -## 4.21.0.0 *TBA*
|
|
| 30 | +## 4.21.0.0 *December 2024*
|
|
| 31 | + * Shipped with GHC 9.12.1
|
|
| 30 | 32 | * Change `SrcLoc` to be a strict and unboxed (finishing [CLC proposal #55](https://github.com/haskell/core-libraries-committee/issues/55))
|
| 31 | 33 | * Introduce `Data.Bounded` module exporting the `Bounded` typeclass (finishing [CLC proposal #208](https://github.com/haskell/core-libraries-committee/issues/208))
|
| 32 | 34 | * Deprecate export of `Bounded` class from `Data.Enum` ([CLC proposal #208](https://github.com/haskell/core-libraries-committee/issues/208))
|
| ... | ... | @@ -311,29 +313,29 @@ |
| 311 | 313 | |
| 312 | 314 | * Re-export the `IsList` typeclass from the new `GHC.IsList` module.
|
| 313 | 315 | |
| 314 | - * There's a new special function ``withDict`` in ``GHC.Exts``: ::
|
|
| 316 | + * There's a new special function `withDict` in `GHC.Exts`: ::
|
|
| 315 | 317 | |
| 316 | 318 | withDict :: forall {rr :: RuntimeRep} cls meth (r :: TYPE rr). WithDict cls meth => meth -> (cls => r) -> r
|
| 317 | 319 | |
| 318 | - where ``cls`` must be a class containing exactly one method, whose type
|
|
| 319 | - must be ``meth``.
|
|
| 320 | + where `cls` must be a class containing exactly one method, whose type
|
|
| 321 | + must be `meth`.
|
|
| 320 | 322 | |
| 321 | - This function converts ``meth`` to a type class dictionary.
|
|
| 322 | - It removes the need for ``unsafeCoerce`` in implementation of reflection
|
|
| 323 | + This function converts `meth` to a type class dictionary.
|
|
| 324 | + It removes the need for `unsafeCoerce` in implementation of reflection
|
|
| 323 | 325 | libraries. It should be used with care, because it can introduce
|
| 324 | 326 | incoherent instances.
|
| 325 | 327 | |
| 326 | - For example, the ``withTypeable`` function from the
|
|
| 327 | - ``Type.Reflection`` module can now be defined as: ::
|
|
| 328 | + For example, the `withTypeable` function from the
|
|
| 329 | + `Type.Reflection` module can now be defined as: ::
|
|
| 328 | 330 | |
| 329 | 331 | withTypeable :: forall k (a :: k) rep (r :: TYPE rep). ()
|
| 330 | 332 | => TypeRep a -> (Typeable a => r) -> r
|
| 331 | 333 | withTypeable rep k = withDict @(Typeable a) rep k
|
| 332 | 334 | |
| 333 | 335 | Note that the explicit type application is required, as the call to
|
| 334 | - ``withDict`` would be ambiguous otherwise.
|
|
| 336 | + `withDict` would be ambiguous otherwise.
|
|
| 335 | 337 | |
| 336 | - This replaces the old ``GHC.Exts.magicDict``, which required
|
|
| 338 | + This replaces the old `GHC.Exts.magicDict`, which required
|
|
| 337 | 339 | an intermediate data type and was less reliable.
|
| 338 | 340 | |
| 339 | 341 | * `Data.Word.Word64` and `Data.Int.Int64` are now always represented by
|
| ... | ... | @@ -351,17 +353,17 @@ |
| 351 | 353 | |
| 352 | 354 | * Shipped with GHC 9.2.4
|
| 353 | 355 | |
| 354 | - * winio: make consoleReadNonBlocking not wait for any events at all.
|
|
| 356 | + * winio: make `consoleReadNonBlocking` not wait for any events at all.
|
|
| 355 | 357 | |
| 356 | - * winio: Add support to console handles to handleToHANDLE
|
|
| 358 | + * winio: Add support to console handles to `handleToHANDLE`
|
|
| 357 | 359 | |
| 358 | 360 | ## 4.16.2.0 *May 2022*
|
| 359 | 361 | |
| 360 | 362 | * Shipped with GHC 9.2.2
|
| 361 | 363 | |
| 362 | - * Export GHC.Event.Internal on Windows (#21245)
|
|
| 364 | + * Export `GHC.Event.Internal` on Windows (#21245)
|
|
| 363 | 365 | |
| 364 | - # Documentation Fixes
|
|
| 366 | + * Documentation Fixes
|
|
| 365 | 367 | |
| 366 | 368 | ## 4.16.1.0 *Feb 2022*
|
| 367 | 369 | |
| ... | ... | @@ -430,10 +432,17 @@ |
| 430 | 432 | |
| 431 | 433 | - Newtypes `And`, `Ior`, `Xor` and `Iff` which wrap their argument,
|
| 432 | 434 | and whose `Semigroup` instances are defined using `(.&.)`, `(.|.)`, `xor`
|
| 433 | - and ```\x y -> complement (x `xor` y)```, respectively.
|
|
| 435 | + and `\x y -> complement (x `xor` y)`, respectively.
|
|
| 434 | 436 | |
| 435 | 437 | - `oneBits :: FiniteBits a => a`, `oneBits = complement zeroBits`.
|
| 436 | 438 | |
| 439 | + * Various folding operations in `GHC.List` are now implemented via strict
|
|
| 440 | + folds:
|
|
| 441 | + - `sum`
|
|
| 442 | + - `product`
|
|
| 443 | + - `maximum`
|
|
| 444 | + - `minimum`
|
|
| 445 | + |
|
| 437 | 446 | ## 4.15.0.0 *Feb 2021*
|
| 438 | 447 | |
| 439 | 448 | * Shipped with GHC 9.0.1
|
| ... | ... | @@ -51,7 +51,7 @@ module Control.Exception.Backtrace |
| 51 | 51 | , getBacktraceMechanismState
|
| 52 | 52 | , setBacktraceMechanismState
|
| 53 | 53 | -- * Collecting backtraces
|
| 54 | - , Backtraces
|
|
| 54 | + , Backtraces(..)
|
|
| 55 | 55 | , displayBacktraces
|
| 56 | 56 | , collectBacktraces
|
| 57 | 57 | ) where
|
| ... | ... | @@ -9,7 +9,7 @@ module GHC.Internal.Exception.Backtrace |
| 9 | 9 | , getBacktraceMechanismState
|
| 10 | 10 | , setBacktraceMechanismState
|
| 11 | 11 | -- * Collecting backtraces
|
| 12 | - , Backtraces
|
|
| 12 | + , Backtraces(..)
|
|
| 13 | 13 | , displayBacktraces
|
| 14 | 14 | , collectBacktraces
|
| 15 | 15 | ) where
|
| ... | ... | @@ -94,13 +94,13 @@ hashWord(const HashTable *table, StgWord key) |
| 94 | 94 | }
|
| 95 | 95 | |
| 96 | 96 | int
|
| 97 | -hashStr(const HashTable *table, StgWord w)
|
|
| 97 | +hashBuffer(const HashTable *table, const void *buf, size_t len)
|
|
| 98 | 98 | {
|
| 99 | - const char *key = (char*) w;
|
|
| 99 | + const char *key = (char*) buf;
|
|
| 100 | 100 | #if WORD_SIZE_IN_BITS == 64
|
| 101 | - StgWord h = XXH3_64bits_withSeed (key, strlen(key), 1048583);
|
|
| 101 | + StgWord h = XXH3_64bits_withSeed (key, len, 1048583);
|
|
| 102 | 102 | #else
|
| 103 | - StgWord h = XXH32 (key, strlen(key), 1048583);
|
|
| 103 | + StgWord h = XXH32 (key, len, 1048583);
|
|
| 104 | 104 | #endif
|
| 105 | 105 | |
| 106 | 106 | /* Mod the size of the hash table (a power of 2) */
|
| ... | ... | @@ -114,6 +114,13 @@ hashStr(const HashTable *table, StgWord w) |
| 114 | 114 | return bucket;
|
| 115 | 115 | }
|
| 116 | 116 | |
| 117 | +int
|
|
| 118 | +hashStr(const HashTable *table, StgWord w)
|
|
| 119 | +{
|
|
| 120 | + const char *key = (char*) w;
|
|
| 121 | + return hashBuffer(table, key, strlen(key));
|
|
| 122 | +}
|
|
| 123 | + |
|
| 117 | 124 | STATIC_INLINE int
|
| 118 | 125 | compareWord(StgWord key1, StgWord key2)
|
| 119 | 126 | {
|
| ... | ... | @@ -69,6 +69,10 @@ void * removeStrHashTable ( StrHashTable *table, const char * key, |
| 69 | 69 | */
|
| 70 | 70 | typedef int HashFunction(const HashTable *table, StgWord key);
|
| 71 | 71 | typedef int CompareFunction(StgWord key1, StgWord key2);
|
| 72 | + |
|
| 73 | +// Helper for implementing hash functions
|
|
| 74 | +int hashBuffer(const HashTable *table, const void *buf, size_t len);
|
|
| 75 | + |
|
| 72 | 76 | int hashWord(const HashTable *table, StgWord key);
|
| 73 | 77 | int hashStr(const HashTable *table, StgWord w);
|
| 74 | 78 | void insertHashTable_ ( HashTable *table, StgWord key,
|
| ... | ... | @@ -79,6 +83,7 @@ void * removeHashTable_ ( HashTable *table, StgWord key, |
| 79 | 83 | const void *data, HashFunction f,
|
| 80 | 84 | CompareFunction cmp );
|
| 81 | 85 | |
| 86 | + |
|
| 82 | 87 | /* Freeing hash tables
|
| 83 | 88 | */
|
| 84 | 89 | void freeHashTable ( HashTable *table, void (*freeDataFun)(void *) );
|
| ... | ... | @@ -1194,7 +1194,7 @@ void freeObjectCode (ObjectCode *oc) |
| 1194 | 1194 | stgFree(oc->sections);
|
| 1195 | 1195 | }
|
| 1196 | 1196 | |
| 1197 | - freeProddableBlocks(oc);
|
|
| 1197 | + freeProddableBlocks(&oc->proddables);
|
|
| 1198 | 1198 | freeSegments(oc);
|
| 1199 | 1199 | |
| 1200 | 1200 | /* Free symbol_extras. On x86_64 Windows, symbol_extras are allocated
|
| ... | ... | @@ -1279,7 +1279,7 @@ mkOc( ObjectType type, pathchar *path, char *image, int imageSize, |
| 1279 | 1279 | oc->sections = NULL;
|
| 1280 | 1280 | oc->n_segments = 0;
|
| 1281 | 1281 | oc->segments = NULL;
|
| 1282 | - oc->proddables = NULL;
|
|
| 1282 | + initProddableBlockSet(&oc->proddables);
|
|
| 1283 | 1283 | oc->foreign_exports = NULL;
|
| 1284 | 1284 | #if defined(NEED_SYMBOL_EXTRAS)
|
| 1285 | 1285 | oc->symbol_extras = NULL;
|
| ... | ... | @@ -1834,50 +1834,6 @@ OStatus getObjectLoadStatus (pathchar *path) |
| 1834 | 1834 | return r;
|
| 1835 | 1835 | }
|
| 1836 | 1836 | |
| 1837 | -/* -----------------------------------------------------------------------------
|
|
| 1838 | - * Sanity checking. For each ObjectCode, maintain a list of address ranges
|
|
| 1839 | - * which may be prodded during relocation, and abort if we try and write
|
|
| 1840 | - * outside any of these.
|
|
| 1841 | - */
|
|
| 1842 | -void
|
|
| 1843 | -addProddableBlock ( ObjectCode* oc, void* start, int size )
|
|
| 1844 | -{
|
|
| 1845 | - ProddableBlock* pb
|
|
| 1846 | - = stgMallocBytes(sizeof(ProddableBlock), "addProddableBlock");
|
|
| 1847 | - |
|
| 1848 | - IF_DEBUG(linker, debugBelch("addProddableBlock: %p %p %d\n", oc, start, size));
|
|
| 1849 | - ASSERT(size > 0);
|
|
| 1850 | - pb->start = start;
|
|
| 1851 | - pb->size = size;
|
|
| 1852 | - pb->next = oc->proddables;
|
|
| 1853 | - oc->proddables = pb;
|
|
| 1854 | -}
|
|
| 1855 | - |
|
| 1856 | -void
|
|
| 1857 | -checkProddableBlock (ObjectCode *oc, void *addr, size_t size )
|
|
| 1858 | -{
|
|
| 1859 | - ProddableBlock* pb;
|
|
| 1860 | - |
|
| 1861 | - for (pb = oc->proddables; pb != NULL; pb = pb->next) {
|
|
| 1862 | - char* s = (char*)(pb->start);
|
|
| 1863 | - char* e = s + pb->size;
|
|
| 1864 | - char* a = (char*)addr;
|
|
| 1865 | - if (a >= s && (a+size) <= e) return;
|
|
| 1866 | - }
|
|
| 1867 | - barf("checkProddableBlock: invalid fixup in runtime linker: %p", addr);
|
|
| 1868 | -}
|
|
| 1869 | - |
|
| 1870 | -void freeProddableBlocks (ObjectCode *oc)
|
|
| 1871 | -{
|
|
| 1872 | - ProddableBlock *pb, *next;
|
|
| 1873 | - |
|
| 1874 | - for (pb = oc->proddables; pb != NULL; pb = next) {
|
|
| 1875 | - next = pb->next;
|
|
| 1876 | - stgFree(pb);
|
|
| 1877 | - }
|
|
| 1878 | - oc->proddables = NULL;
|
|
| 1879 | -}
|
|
| 1880 | - |
|
| 1881 | 1837 | /* -----------------------------------------------------------------------------
|
| 1882 | 1838 | * Section management.
|
| 1883 | 1839 | */
|
| ... | ... | @@ -12,6 +12,7 @@ |
| 12 | 12 | #include "RtsSymbols.h"
|
| 13 | 13 | #include "Hash.h"
|
| 14 | 14 | #include "linker/M32Alloc.h"
|
| 15 | +#include "linker/ProddableBlocks.h"
|
|
| 15 | 16 | |
| 16 | 17 | #if RTS_LINKER_USE_MMAP
|
| 17 | 18 | #include <sys/mman.h>
|
| ... | ... | @@ -175,14 +176,6 @@ struct _Section { |
| 175 | 176 | struct SectionFormatInfo* info;
|
| 176 | 177 | };
|
| 177 | 178 | |
| 178 | -typedef
|
|
| 179 | - struct _ProddableBlock {
|
|
| 180 | - void* start;
|
|
| 181 | - int size;
|
|
| 182 | - struct _ProddableBlock* next;
|
|
| 183 | - }
|
|
| 184 | - ProddableBlock;
|
|
| 185 | - |
|
| 186 | 179 | typedef struct _Segment {
|
| 187 | 180 | void *start; /* page aligned start address of a segment */
|
| 188 | 181 | size_t size; /* page rounded size of a segment */
|
| ... | ... | @@ -328,7 +321,7 @@ struct _ObjectCode { |
| 328 | 321 | /* SANITY CHECK ONLY: a list of the only memory regions which may
|
| 329 | 322 | safely be prodded during relocation. Any attempt to prod
|
| 330 | 323 | outside one of these is an error in the linker. */
|
| 331 | - ProddableBlock* proddables;
|
|
| 324 | + ProddableBlockSet proddables;
|
|
| 332 | 325 | |
| 333 | 326 | #if defined(NEED_SYMBOL_EXTRAS)
|
| 334 | 327 | SymbolExtra *symbol_extras;
|
| ... | ... | @@ -434,10 +427,6 @@ void exitLinker( void ); |
| 434 | 427 | void freeObjectCode (ObjectCode *oc);
|
| 435 | 428 | SymbolAddr* loadSymbol(SymbolName *lbl, RtsSymbolInfo *pinfo);
|
| 436 | 429 | |
| 437 | -void addProddableBlock ( ObjectCode* oc, void* start, int size );
|
|
| 438 | -void checkProddableBlock (ObjectCode *oc, void *addr, size_t size );
|
|
| 439 | -void freeProddableBlocks (ObjectCode *oc);
|
|
| 440 | - |
|
| 441 | 430 | void addSection (Section *s, SectionKind kind, SectionAlloc alloc,
|
| 442 | 431 | void* start, StgWord size, StgWord mapped_offset,
|
| 443 | 432 | void* mapped_start, StgWord mapped_size);
|
| ... | ... | @@ -13,7 +13,7 @@ |
| 13 | 13 | #include <wchar.h>
|
| 14 | 14 | #endif
|
| 15 | 15 | |
| 16 | -pathchar* pathdup(pathchar *path)
|
|
| 16 | +pathchar* pathdup(const pathchar *path)
|
|
| 17 | 17 | {
|
| 18 | 18 | pathchar *ret;
|
| 19 | 19 | #if defined(mingw32_HOST_OS)
|
| ... | ... | @@ -26,7 +26,7 @@ pathchar* pathdup(pathchar *path) |
| 26 | 26 | return ret;
|
| 27 | 27 | }
|
| 28 | 28 | |
| 29 | -pathchar* pathdir(pathchar *path)
|
|
| 29 | +pathchar* pathdir(const pathchar *path)
|
|
| 30 | 30 | {
|
| 31 | 31 | pathchar *ret;
|
| 32 | 32 | #if defined(mingw32_HOST_OS)
|
| ... | ... | @@ -40,7 +40,8 @@ pathchar* pathdir(pathchar *path) |
| 40 | 40 | stgFree(drive);
|
| 41 | 41 | stgFree(dirName);
|
| 42 | 42 | #else
|
| 43 | - pathchar* dirName = dirname(path);
|
|
| 43 | + // N.B. cast is safe as we do not modify dirName
|
|
| 44 | + const pathchar* dirName = dirname((pathchar *) path);
|
|
| 44 | 45 | size_t memberLen = pathlen(dirName);
|
| 45 | 46 | ret = stgMallocBytes(pathsize * (memberLen + 2), "pathdir(path)");
|
| 46 | 47 | strcpy(ret, dirName);
|
| ... | ... | @@ -50,7 +51,7 @@ pathchar* pathdir(pathchar *path) |
| 50 | 51 | return ret;
|
| 51 | 52 | }
|
| 52 | 53 | |
| 53 | -pathchar* mkPath(char* path)
|
|
| 54 | +pathchar* mkPath(const char* path)
|
|
| 54 | 55 | {
|
| 55 | 56 | #if defined(mingw32_HOST_OS)
|
| 56 | 57 | size_t required = mbstowcs(NULL, path, 0);
|
| ... | ... | @@ -66,7 +67,7 @@ pathchar* mkPath(char* path) |
| 66 | 67 | #endif
|
| 67 | 68 | }
|
| 68 | 69 | |
| 69 | -HsBool endsWithPath(pathchar* base, pathchar* str) {
|
|
| 70 | +HsBool endsWithPath(const pathchar* base, const pathchar* str) {
|
|
| 70 | 71 | int blen = pathlen(base);
|
| 71 | 72 | int slen = pathlen(str);
|
| 72 | 73 | return (blen >= slen) && (0 == pathcmp(base + blen - slen, str));
|
| ... | ... | @@ -37,9 +37,9 @@ |
| 37 | 37 | |
| 38 | 38 | #include "BeginPrivate.h"
|
| 39 | 39 | |
| 40 | -pathchar* pathdup(pathchar *path);
|
|
| 41 | -pathchar* pathdir(pathchar *path);
|
|
| 42 | -pathchar* mkPath(char* path);
|
|
| 43 | -HsBool endsWithPath(pathchar* base, pathchar* str);
|
|
| 40 | +pathchar* pathdup(const pathchar *path);
|
|
| 41 | +pathchar* pathdir(const pathchar *path);
|
|
| 42 | +pathchar* mkPath(const char* path);
|
|
| 43 | +HsBool endsWithPath(const pathchar* base, const pathchar* str);
|
|
| 44 | 44 | |
| 45 | 45 | #include "EndPrivate.h" |
| ... | ... | @@ -924,7 +924,7 @@ ocGetNames_ELF ( ObjectCode* oc ) |
| 924 | 924 | oc->sections[i].info->stubs = NULL;
|
| 925 | 925 | #endif
|
| 926 | 926 | |
| 927 | - addProddableBlock(oc, start, size);
|
|
| 927 | + addProddableBlock(&oc->proddables, start, size);
|
|
| 928 | 928 | } else {
|
| 929 | 929 | addSection(&oc->sections[i], kind, alloc, oc->image+offset, size,
|
| 930 | 930 | 0, 0, 0);
|
| ... | ... | @@ -1272,7 +1272,7 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC, |
| 1272 | 1272 | debugBelch("Reloc: P = %p S = %p A = %p type=%d\n",
|
| 1273 | 1273 | (void*)P, (void*)S, (void*)A, reloc_type ));
|
| 1274 | 1274 | #if defined(DEBUG)
|
| 1275 | - checkProddableBlock ( oc, pP, sizeof(Elf_Word) );
|
|
| 1275 | + checkProddableBlock ( &oc->proddables, pP, sizeof(Elf_Word) );
|
|
| 1276 | 1276 | #else
|
| 1277 | 1277 | (void) pP; /* suppress unused varialbe warning in non-debug build */
|
| 1278 | 1278 | #endif
|
| ... | ... | @@ -1684,7 +1684,7 @@ do_Elf_Rela_relocations ( ObjectCode* oc, char* ehdrC, |
| 1684 | 1684 | #if defined(DEBUG)
|
| 1685 | 1685 | IF_DEBUG(linker_verbose,debugBelch("Reloc: P = %p S = %p A = %p\n",
|
| 1686 | 1686 | (void*)P, (void*)S, (void*)A ));
|
| 1687 | - checkProddableBlock(oc, (void*)P, sizeof(Elf_Word));
|
|
| 1687 | + checkProddableBlock(&oc->proddables, (void*)P, sizeof(Elf_Word));
|
|
| 1688 | 1688 | #endif
|
| 1689 | 1689 | |
| 1690 | 1690 | #if defined(powerpc_HOST_ARCH) || defined(x86_64_HOST_ARCH)
|
| ... | ... | @@ -253,7 +253,7 @@ resolveImports( |
| 253 | 253 | return 0;
|
| 254 | 254 | }
|
| 255 | 255 | |
| 256 | - checkProddableBlock(oc,
|
|
| 256 | + checkProddableBlock(&oc->proddables,
|
|
| 257 | 257 | ((void**)(oc->image + sect->offset)) + i,
|
| 258 | 258 | sizeof(void *));
|
| 259 | 259 | ((void**)(oc->image + sect->offset))[i] = addr;
|
| ... | ... | @@ -287,7 +287,7 @@ decodeAddend(ObjectCode * oc, Section * section, MachORelocationInfo * ri) { |
| 287 | 287 | /* the instruction. It is 32bit wide */
|
| 288 | 288 | uint32_t * p = (uint32_t*)((uint8_t*)section->start + ri->r_address);
|
| 289 | 289 | |
| 290 | - checkProddableBlock(oc, (void*)p, 1 << ri->r_length);
|
|
| 290 | + checkProddableBlock(&oc->proddables, (void*)p, 1 << ri->r_length);
|
|
| 291 | 291 | |
| 292 | 292 | switch(ri->r_type) {
|
| 293 | 293 | case ARM64_RELOC_UNSIGNED: {
|
| ... | ... | @@ -364,7 +364,7 @@ encodeAddend(ObjectCode * oc, Section * section, |
| 364 | 364 | MachORelocationInfo * ri, int64_t addend) {
|
| 365 | 365 | uint32_t * p = (uint32_t*)((uint8_t*)section->start + ri->r_address);
|
| 366 | 366 | |
| 367 | - checkProddableBlock(oc, (void*)p, 1 << ri->r_length);
|
|
| 367 | + checkProddableBlock(&oc->proddables, (void*)p, 1 << ri->r_length);
|
|
| 368 | 368 | |
| 369 | 369 | switch (ri->r_type) {
|
| 370 | 370 | case ARM64_RELOC_UNSIGNED: {
|
| ... | ... | @@ -788,7 +788,7 @@ relocateSection(ObjectCode* oc, int curSection) |
| 788 | 788 | default:
|
| 789 | 789 | barf("Unknown size.");
|
| 790 | 790 | }
|
| 791 | - checkProddableBlock(oc,thingPtr,relocLenBytes);
|
|
| 791 | + checkProddableBlock(&oc->proddables,thingPtr,relocLenBytes);
|
|
| 792 | 792 | |
| 793 | 793 | /*
|
| 794 | 794 | * With SIGNED_N the relocation is not at the end of the
|
| ... | ... | @@ -1034,9 +1034,9 @@ relocateSection(ObjectCode* oc, int curSection) |
| 1034 | 1034 | */
|
| 1035 | 1035 | if (0 == reloc->r_extern) {
|
| 1036 | 1036 | if (reloc->r_pcrel) {
|
| 1037 | - checkProddableBlock(oc, (void *)((char *)thing + baseValue), 1);
|
|
| 1037 | + checkProddableBlock(&oc->proddables, (void *)((char *)thing + baseValue), 1);
|
|
| 1038 | 1038 | } else {
|
| 1039 | - checkProddableBlock(oc, (void *)thing, 1);
|
|
| 1039 | + checkProddableBlock(&oc->proddables, (void *)thing, 1);
|
|
| 1040 | 1040 | }
|
| 1041 | 1041 | }
|
| 1042 | 1042 | |
| ... | ... | @@ -1343,7 +1343,7 @@ ocGetNames_MachO(ObjectCode* oc) |
| 1343 | 1343 | secArray[sec_idx].info->stub_size = 0;
|
| 1344 | 1344 | secArray[sec_idx].info->stubs = NULL;
|
| 1345 | 1345 | #endif
|
| 1346 | - addProddableBlock(oc, start, section->size);
|
|
| 1346 | + addProddableBlock(&oc->proddables, start, section->size);
|
|
| 1347 | 1347 | }
|
| 1348 | 1348 | |
| 1349 | 1349 | curMem = (char*) secMem + section->size;
|
| ... | ... | @@ -378,7 +378,7 @@ static size_t makeSymbolExtra_PEi386( |
| 378 | 378 | #endif
|
| 379 | 379 | |
| 380 | 380 | static void addDLLHandle(
|
| 381 | - pathchar* dll_name,
|
|
| 381 | + const pathchar* dll_name,
|
|
| 382 | 382 | HINSTANCE instance);
|
| 383 | 383 | |
| 384 | 384 | static bool verifyCOFFHeader(
|
| ... | ... | @@ -427,8 +427,52 @@ const int default_alignment = 8; |
| 427 | 427 | the pointer as a redirect. Essentially it's a DATA DLL reference. */
|
| 428 | 428 | const void* __rts_iob_func = (void*)&__acrt_iob_func;
|
| 429 | 429 | |
| 430 | +/*
|
|
| 431 | + * Note [Avoiding repeated DLL loading]
|
|
| 432 | + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 433 | + * As LoadLibraryEx tends to be expensive and addDLL_PEi386 is called on every
|
|
| 434 | + * DLL-imported symbol, we use a hash-map to keep track of which DLLs have
|
|
| 435 | + * already been loaded. This hash-map is keyed on the dll_name passed to
|
|
| 436 | + * addDLL_PEi386 and is mapped to its HINSTANCE. This serves as a quick check
|
|
| 437 | + * to avoid repeated calls to LoadLibraryEx for the identical DLL. See #26009.
|
|
| 438 | + */
|
|
| 439 | + |
|
| 440 | +typedef struct {
|
|
| 441 | + HashTable *hash;
|
|
| 442 | +} LoadedDllCache;
|
|
| 443 | + |
|
| 444 | +LoadedDllCache loaded_dll_cache;
|
|
| 445 | + |
|
| 446 | +static void initLoadedDllCache(LoadedDllCache *cache) {
|
|
| 447 | + cache->hash = allocHashTable();
|
|
| 448 | +}
|
|
| 449 | + |
|
| 450 | +static int hash_path(const HashTable *table, StgWord w)
|
|
| 451 | +{
|
|
| 452 | + const pathchar *key = (pathchar*) w;
|
|
| 453 | + return hashBuffer(table, key, sizeof(pathchar) * wcslen(key));
|
|
| 454 | +}
|
|
| 455 | + |
|
| 456 | +static int compare_path(StgWord key1, StgWord key2)
|
|
| 457 | +{
|
|
| 458 | + return wcscmp((pathchar*) key1, (pathchar*) key2) == 0;
|
|
| 459 | +}
|
|
| 460 | + |
|
| 461 | +static void addLoadedDll(LoadedDllCache *cache, const pathchar *dll_name, HINSTANCE instance)
|
|
| 462 | +{
|
|
| 463 | + insertHashTable_(cache->hash, (StgWord) dll_name, instance, hash_path);
|
|
| 464 | +}
|
|
| 465 | + |
|
| 466 | +static HINSTANCE isDllLoaded(const LoadedDllCache *cache, const pathchar *dll_name)
|
|
| 467 | +{
|
|
| 468 | + void *result = lookupHashTable_(cache->hash, (StgWord) dll_name, hash_path, compare_path);
|
|
| 469 | + return (HINSTANCE) result;
|
|
| 470 | +}
|
|
| 471 | + |
|
| 430 | 472 | void initLinker_PEi386(void)
|
| 431 | 473 | {
|
| 474 | + initLoadedDllCache(&loaded_dll_cache);
|
|
| 475 | + |
|
| 432 | 476 | if (!ghciInsertSymbolTable(WSTR("(GHCi/Ld special symbols)"),
|
| 433 | 477 | symhash, "__image_base__",
|
| 434 | 478 | GetModuleHandleW (NULL), HS_BOOL_TRUE,
|
| ... | ... | @@ -440,10 +484,11 @@ void initLinker_PEi386(void) |
| 440 | 484 | addDLLHandle(WSTR("*.exe"), GetModuleHandle(NULL));
|
| 441 | 485 | #endif
|
| 442 | 486 | |
| 443 | - /* Register the cleanup routine as an exit handler, this gives other exit handlers
|
|
| 444 | - a chance to run which may need linker information. Exit handlers are ran in
|
|
| 445 | - reverse registration order so this needs to be before the linker loads anything. */
|
|
| 446 | - atexit (exitLinker_PEi386);
|
|
| 487 | + /* Register the cleanup routine as an exit handler, this gives other exit handlers
|
|
| 488 | + * a chance to run which may need linker information. Exit handlers are ran in
|
|
| 489 | + * reverse registration order so this needs to be before the linker loads anything.
|
|
| 490 | + */
|
|
| 491 | + atexit (exitLinker_PEi386);
|
|
| 447 | 492 | }
|
| 448 | 493 | |
| 449 | 494 | void exitLinker_PEi386(void)
|
| ... | ... | @@ -454,7 +499,7 @@ void exitLinker_PEi386(void) |
| 454 | 499 | static OpenedDLL* opened_dlls = NULL;
|
| 455 | 500 | |
| 456 | 501 | /* Adds a DLL instance to the list of DLLs in which to search for symbols. */
|
| 457 | -static void addDLLHandle(pathchar* dll_name, HINSTANCE instance) {
|
|
| 502 | +static void addDLLHandle(const pathchar* dll_name, HINSTANCE instance) {
|
|
| 458 | 503 | |
| 459 | 504 | IF_DEBUG(linker, debugBelch("addDLLHandle(%" PATH_FMT ")...\n", dll_name));
|
| 460 | 505 | /* At this point, we actually know what was loaded.
|
| ... | ... | @@ -796,14 +841,19 @@ uint8_t* getSymShortName ( COFF_HEADER_INFO *info, COFF_symbol* sym ) |
| 796 | 841 | }
|
| 797 | 842 | |
| 798 | 843 | const char *
|
| 799 | -addDLL_PEi386( pathchar *dll_name, HINSTANCE *loaded )
|
|
| 844 | +addDLL_PEi386( const pathchar *dll_name, HINSTANCE *loaded )
|
|
| 800 | 845 | {
|
| 801 | - /* ------------------- Win32 DLL loader ------------------- */
|
|
| 802 | - |
|
| 803 | - pathchar* buf;
|
|
| 804 | - HINSTANCE instance;
|
|
| 805 | - |
|
| 806 | - IF_DEBUG(linker, debugBelch("addDLL; dll_name = `%" PATH_FMT "'\n", dll_name));
|
|
| 846 | + /* ------------------- Win32 DLL loader ------------------- */
|
|
| 847 | + IF_DEBUG(linker, debugBelch("addDLL; dll_name = `%" PATH_FMT "'\n", dll_name));
|
|
| 848 | + |
|
| 849 | + // See Note [Avoiding repeated DLL loading]
|
|
| 850 | + HINSTANCE instance = isDllLoaded(&loaded_dll_cache, dll_name);
|
|
| 851 | + if (instance) {
|
|
| 852 | + if (loaded) {
|
|
| 853 | + *loaded = instance;
|
|
| 854 | + }
|
|
| 855 | + return NULL;
|
|
| 856 | + }
|
|
| 807 | 857 | |
| 808 | 858 | /* The file name has no suffix (yet) so that we can try
|
| 809 | 859 | both foo.dll and foo.drv
|
| ... | ... | @@ -816,45 +866,32 @@ addDLL_PEi386( pathchar *dll_name, HINSTANCE *loaded ) |
| 816 | 866 | extension. */
|
| 817 | 867 | |
| 818 | 868 | size_t bufsize = pathlen(dll_name) + 10;
|
| 819 | - buf = stgMallocBytes(bufsize * sizeof(wchar_t), "addDLL");
|
|
| 869 | + pathchar *buf = stgMallocBytes(bufsize * sizeof(wchar_t), "addDLL");
|
|
| 820 | 870 | |
| 821 | 871 | /* These are ordered by probability of success and order we'd like them. */
|
| 822 | 872 | const wchar_t *formats[] = { L"%ls.DLL", L"%ls.DRV", L"lib%ls.DLL", L"%ls" };
|
| 823 | 873 | const DWORD flags[] = { LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS, 0 };
|
| 824 | 874 | |
| 825 | - int cFormat, cFlag;
|
|
| 826 | - int flags_start = 1; /* Assume we don't support the new API. */
|
|
| 827 | - |
|
| 828 | - /* Detect if newer API are available, if not, skip the first flags entry. */
|
|
| 829 | - if (GetProcAddress((HMODULE)LoadLibraryW(L"Kernel32.DLL"), "AddDllDirectory")) {
|
|
| 830 | - flags_start = 0;
|
|
| 831 | - }
|
|
| 832 | - |
|
| 833 | 875 | /* Iterate through the possible flags and formats. */
|
| 834 | - for (cFlag = flags_start; cFlag < 2; cFlag++)
|
|
| 835 | - {
|
|
| 836 | - for (cFormat = 0; cFormat < 4; cFormat++)
|
|
| 837 | - {
|
|
| 876 | + for (int cFlag = 0; cFlag < 2; cFlag++) {
|
|
| 877 | + for (int cFormat = 0; cFormat < 4; cFormat++) {
|
|
| 838 | 878 | snwprintf(buf, bufsize, formats[cFormat], dll_name);
|
| 839 | 879 | instance = LoadLibraryExW(buf, NULL, flags[cFlag]);
|
| 840 | 880 | if (instance == NULL) {
|
| 841 | - if (GetLastError() != ERROR_MOD_NOT_FOUND)
|
|
| 842 | - {
|
|
| 881 | + if (GetLastError() != ERROR_MOD_NOT_FOUND) {
|
|
| 843 | 882 | goto error;
|
| 844 | 883 | }
|
| 845 | - }
|
|
| 846 | - else
|
|
| 847 | - {
|
|
| 848 | - break; /* We're done. DLL has been loaded. */
|
|
| 884 | + } else {
|
|
| 885 | + goto loaded; /* We're done. DLL has been loaded. */
|
|
| 849 | 886 | }
|
| 850 | 887 | }
|
| 851 | 888 | }
|
| 852 | 889 | |
| 853 | - /* Check if we managed to load the DLL. */
|
|
| 854 | - if (instance == NULL) {
|
|
| 855 | - goto error;
|
|
| 856 | - }
|
|
| 890 | + // We failed to load
|
|
| 891 | + goto error;
|
|
| 857 | 892 | |
| 893 | +loaded:
|
|
| 894 | + addLoadedDll(&loaded_dll_cache, dll_name, instance);
|
|
| 858 | 895 | addDLLHandle(buf, instance);
|
| 859 | 896 | if (loaded) {
|
| 860 | 897 | *loaded = instance;
|
| ... | ... | @@ -1658,7 +1695,7 @@ ocGetNames_PEi386 ( ObjectCode* oc ) |
| 1658 | 1695 | }
|
| 1659 | 1696 | |
| 1660 | 1697 | addSection(section, kind, SECTION_NOMEM, start, sz, 0, 0, 0);
|
| 1661 | - addProddableBlock(oc, oc->sections[i].start, sz);
|
|
| 1698 | + addProddableBlock(&oc->proddables, oc->sections[i].start, sz);
|
|
| 1662 | 1699 | }
|
| 1663 | 1700 | |
| 1664 | 1701 | /* Copy exported symbols into the ObjectCode. */
|
| ... | ... | @@ -1690,7 +1727,7 @@ ocGetNames_PEi386 ( ObjectCode* oc ) |
| 1690 | 1727 | SECTIONKIND_RWDATA, SECTION_MALLOC,
|
| 1691 | 1728 | bss, globalBssSize, 0, 0, 0);
|
| 1692 | 1729 | IF_DEBUG(linker_verbose, debugBelch("bss @ %p %" FMT_Word "\n", bss, globalBssSize));
|
| 1693 | - addProddableBlock(oc, bss, globalBssSize);
|
|
| 1730 | + addProddableBlock(&oc->proddables, bss, globalBssSize);
|
|
| 1694 | 1731 | } else {
|
| 1695 | 1732 | addSection(&oc->sections[oc->n_sections-1],
|
| 1696 | 1733 | SECTIONKIND_OTHER, SECTION_NOMEM, NULL, 0, 0, 0, 0);
|
| ... | ... | @@ -2067,13 +2104,13 @@ ocResolve_PEi386 ( ObjectCode* oc ) |
| 2067 | 2104 | IF_DEBUG(linker_verbose, debugBelch("S=%zx\n", S));
|
| 2068 | 2105 | |
| 2069 | 2106 | /* All supported relocations write at least 4 bytes */
|
| 2070 | - checkProddableBlock(oc, pP, 4);
|
|
| 2107 | + checkProddableBlock(&oc->proddables, pP, 4);
|
|
| 2071 | 2108 | switch (reloc->Type) {
|
| 2072 | 2109 | #if defined(x86_64_HOST_ARCH)
|
| 2073 | 2110 | case 1: /* R_X86_64_64 (ELF constant 1) - IMAGE_REL_AMD64_ADDR64 (PE constant 1) */
|
| 2074 | 2111 | {
|
| 2075 | 2112 | uint64_t A;
|
| 2076 | - checkProddableBlock(oc, pP, 8);
|
|
| 2113 | + checkProddableBlock(&oc->proddables, pP, 8);
|
|
| 2077 | 2114 | A = *(uint64_t*)pP;
|
| 2078 | 2115 | *(uint64_t *)pP = S + A;
|
| 2079 | 2116 | break;
|
| ... | ... | @@ -2114,7 +2151,7 @@ ocResolve_PEi386 ( ObjectCode* oc ) |
| 2114 | 2151 | {
|
| 2115 | 2152 | /* mingw will emit this for a pc-rel 64 relocation */
|
| 2116 | 2153 | uint64_t A;
|
| 2117 | - checkProddableBlock(oc, pP, 8);
|
|
| 2154 | + checkProddableBlock(&oc->proddables, pP, 8);
|
|
| 2118 | 2155 | A = *(uint64_t*)pP;
|
| 2119 | 2156 | *(uint64_t *)pP = S + A - (intptr_t)pP;
|
| 2120 | 2157 | break;
|
| ... | ... | @@ -45,7 +45,7 @@ typedef struct _COFF_HEADER_INFO { |
| 45 | 45 | |
| 46 | 46 | void initLinker_PEi386( void );
|
| 47 | 47 | void exitLinker_PEi386( void );
|
| 48 | -const char * addDLL_PEi386( pathchar *dll_name, HINSTANCE *instance );
|
|
| 48 | +const char * addDLL_PEi386( const pathchar *dll_name, HINSTANCE *instance );
|
|
| 49 | 49 | void freePreloadObjectFile_PEi386( ObjectCode *oc );
|
| 50 | 50 | |
| 51 | 51 | bool checkAndLoadImportLibrary( pathchar* arch_name, char* member_name, FILE* f);
|
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team, 2025
|
|
| 4 | + *
|
|
| 5 | + * RTS Object Linker
|
|
| 6 | + *
|
|
| 7 | + * ---------------------------------------------------------------------------*/
|
|
| 8 | + |
|
| 9 | + |
|
| 10 | +/*
|
|
| 11 | + * Note [Proddable blocks]
|
|
| 12 | + * ~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 13 | + * For each ObjectCode, we maintain a ProddableBlockSet representing the set of
|
|
| 14 | + * address ranges containing data belonging to the object. This set is
|
|
| 15 | + * represented here as an array of intervals sorted by start address. This
|
|
| 16 | + * allows us to efficiently query and insert via binary search. Array resizing
|
|
| 17 | + * is done according to an exponential growth schedule.
|
|
| 18 | + *
|
|
| 19 | + * While performing relocations we check against this set and and abort if we
|
|
| 20 | + * try and write outside any of these.
|
|
| 21 | + */
|
|
| 22 | + |
|
| 23 | +#include "Rts.h"
|
|
| 24 | +#include "RtsUtils.h"
|
|
| 25 | +#include "linker/ProddableBlocks.h"
|
|
| 26 | + |
|
| 27 | +#include <stdlib.h>
|
|
| 28 | +#include <string.h>
|
|
| 29 | + |
|
| 30 | +typedef struct _ProddableBlock {
|
|
| 31 | + uintptr_t start; // inclusive
|
|
| 32 | + uintptr_t end; // inclusive
|
|
| 33 | +} ProddableBlock;
|
|
| 34 | + |
|
| 35 | +void
|
|
| 36 | +initProddableBlockSet ( ProddableBlockSet* set )
|
|
| 37 | +{
|
|
| 38 | + set->data = NULL;
|
|
| 39 | + set->capacity = 0;
|
|
| 40 | + set->size = 0;
|
|
| 41 | +}
|
|
| 42 | + |
|
| 43 | +void
|
|
| 44 | +freeProddableBlocks (ProddableBlockSet *set)
|
|
| 45 | +{
|
|
| 46 | + stgFree(set->data);
|
|
| 47 | + set->data = NULL;
|
|
| 48 | + set->size = 0;
|
|
| 49 | + set->capacity = 0;
|
|
| 50 | +}
|
|
| 51 | + |
|
| 52 | +// Binary search for the first interval with start >= value. Returns index or
|
|
| 53 | +// size if none.
|
|
| 54 | +static size_t
|
|
| 55 | +findLower(const ProddableBlockSet *set, uintptr_t value)
|
|
| 56 | +{
|
|
| 57 | + size_t l = 0;
|
|
| 58 | + size_t r = set->size;
|
|
| 59 | + while (l < r) {
|
|
| 60 | + size_t mid = l + (r - l) / 2;
|
|
| 61 | + if (set->data[mid].start < value) {
|
|
| 62 | + l = mid + 1;
|
|
| 63 | + } else {
|
|
| 64 | + r = mid;
|
|
| 65 | + }
|
|
| 66 | + }
|
|
| 67 | + return l;
|
|
| 68 | +}
|
|
| 69 | + |
|
| 70 | +// Check whether a given value is a member of the set.
|
|
| 71 | +bool
|
|
| 72 | +containsSpan ( const ProddableBlockSet *set, uintptr_t start, uintptr_t end )
|
|
| 73 | +{
|
|
| 74 | + size_t i = findLower(set, start+1);
|
|
| 75 | + return i > 0
|
|
| 76 | + && set->data[i-1].start <= start
|
|
| 77 | + && end <= set->data[i-1].end;
|
|
| 78 | +}
|
|
| 79 | + |
|
| 80 | +void
|
|
| 81 | +checkProddableBlock (const ProddableBlockSet *set, void *addr, size_t size )
|
|
| 82 | +{
|
|
| 83 | + if (! containsSpan(set, (uintptr_t) addr, (uintptr_t) addr+size)) {
|
|
| 84 | + barf("checkProddableBlock: invalid fixup in runtime linker: %p", addr);
|
|
| 85 | + }
|
|
| 86 | +}
|
|
| 87 | + |
|
| 88 | +// Ensure capacity for at least new_capacity intervals
|
|
| 89 | +static void
|
|
| 90 | +ensureCapacity(ProddableBlockSet *set, size_t new_capacity) {
|
|
| 91 | + if (new_capacity > set->capacity) {
|
|
| 92 | + size_t cap = set->capacity ? set->capacity * 2 : 4;
|
|
| 93 | + if (cap < new_capacity) {
|
|
| 94 | + cap = new_capacity;
|
|
| 95 | + }
|
|
| 96 | + ProddableBlock *tmp = stgReallocBytes(set->data, cap * sizeof(ProddableBlock), "addProddableBlock");
|
|
| 97 | + set->data = tmp;
|
|
| 98 | + set->capacity = cap;
|
|
| 99 | + }
|
|
| 100 | +}
|
|
| 101 | + |
|
| 102 | +void
|
|
| 103 | +addProddableBlock ( ProddableBlockSet* set, void* start_ptr, size_t size )
|
|
| 104 | +{
|
|
| 105 | + const uintptr_t start = (uintptr_t) start_ptr;
|
|
| 106 | + const uintptr_t end = (uintptr_t) start + size;
|
|
| 107 | + size_t i = findLower(set, start);
|
|
| 108 | + |
|
| 109 | + // check previous interval if it is overlapping or adjacent
|
|
| 110 | + if (i > 0 && start <= set->data[i-1].end + 1) {
|
|
| 111 | + // merge with left interval
|
|
| 112 | + i--;
|
|
| 113 | + if (end > set->data[i].end) {
|
|
| 114 | + set->data[i].end = end;
|
|
| 115 | + }
|
|
| 116 | + } else {
|
|
| 117 | + // insert new interval
|
|
| 118 | + ensureCapacity(set, set->size + 1);
|
|
| 119 | + memmove(&set->data[i+1], &set->data[i], sizeof(ProddableBlock) * (set->size - i));
|
|
| 120 | + set->data[i].start = start;
|
|
| 121 | + set->data[i].end = end;
|
|
| 122 | + set->size++;
|
|
| 123 | + }
|
|
| 124 | + |
|
| 125 | + // coalesce overlaps on right
|
|
| 126 | + size_t j = i;
|
|
| 127 | + while (j < set->size && set->data[j].start <= set->data[i].end + 1) {
|
|
| 128 | + set->data[i].end = set->data[j].end;
|
|
| 129 | + j++;
|
|
| 130 | + }
|
|
| 131 | + |
|
| 132 | + if (j != i) {
|
|
| 133 | + memmove(&set->data[i+1], &set->data[j], sizeof(ProddableBlock) * (set->size - j));
|
|
| 134 | + set->size -= j - i - 1;
|
|
| 135 | + }
|
|
| 136 | +}
|
|
| 137 | + |
| 1 | +/* -----------------------------------------------------------------------------
|
|
| 2 | + *
|
|
| 3 | + * (c) The GHC Team, 2025
|
|
| 4 | + *
|
|
| 5 | + * RTS Object Linker
|
|
| 6 | + *
|
|
| 7 | + * ---------------------------------------------------------------------------*/
|
|
| 8 | + |
|
| 9 | +#pragma once
|
|
| 10 | + |
|
| 11 | +#include <stdbool.h>
|
|
| 12 | +#include <stddef.h>
|
|
| 13 | +#include <stdint.h>
|
|
| 14 | + |
|
| 15 | +// An interval set on uintptr_t.
|
|
| 16 | +struct _ProddableBlock;
|
|
| 17 | + |
|
| 18 | +typedef struct {
|
|
| 19 | + size_t size;
|
|
| 20 | + size_t capacity;
|
|
| 21 | + // sorted list of disjoint (start,end) pairs
|
|
| 22 | + struct _ProddableBlock *data;
|
|
| 23 | +} ProddableBlockSet;
|
|
| 24 | + |
|
| 25 | +void initProddableBlockSet ( ProddableBlockSet* set );
|
|
| 26 | + |
|
| 27 | +// Insert an interval.
|
|
| 28 | +void addProddableBlock ( ProddableBlockSet* set, void* start, size_t size );
|
|
| 29 | + |
|
| 30 | +// Check that an address belongs to the set.
|
|
| 31 | +void checkProddableBlock (const ProddableBlockSet *set, void *addr, size_t size );
|
|
| 32 | + |
|
| 33 | + |
|
| 34 | +// Free a set.
|
|
| 35 | +void freeProddableBlocks (ProddableBlockSet *set);
|
|
| 36 | + |
|
| 37 | +// For testing.
|
|
| 38 | +bool containsSpan ( const ProddableBlockSet *set, uintptr_t start, uintptr_t end ); |
| ... | ... | @@ -491,6 +491,7 @@ library |
| 491 | 491 | linker/MachO.c
|
| 492 | 492 | linker/macho/plt.c
|
| 493 | 493 | linker/macho/plt_aarch64.c
|
| 494 | + linker/ProddableBlocks.c
|
|
| 494 | 495 | linker/PEi386.c
|
| 495 | 496 | linker/SymbolExtras.c
|
| 496 | 497 | linker/elf_got.c
|
| ... | ... | @@ -322,7 +322,7 @@ module Control.Exception.Backtrace where |
| 322 | 322 | type BacktraceMechanism :: *
|
| 323 | 323 | data BacktraceMechanism = CostCentreBacktrace | HasCallStackBacktrace | ExecutionBacktrace | IPEBacktrace
|
| 324 | 324 | type Backtraces :: *
|
| 325 | - data Backtraces = ...
|
|
| 325 | + data Backtraces = Backtraces {btrCostCentre :: GHC.Internal.Maybe.Maybe (GHC.Internal.Ptr.Ptr GHC.Internal.Stack.CCS.CostCentreStack), btrHasCallStack :: GHC.Internal.Maybe.Maybe GHC.Internal.Stack.Types.CallStack, btrExecutionStack :: GHC.Internal.Maybe.Maybe [GHC.Internal.ExecutionStack.Internal.Location], btrIpe :: GHC.Internal.Maybe.Maybe [GHC.Internal.Stack.CloneStack.StackEntry]}
|
|
| 326 | 326 | collectBacktraces :: (?callStack::GHC.Internal.Stack.Types.CallStack) => GHC.Internal.Types.IO Backtraces
|
| 327 | 327 | displayBacktraces :: Backtraces -> GHC.Internal.Base.String
|
| 328 | 328 | getBacktraceMechanismState :: BacktraceMechanism -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| ... | ... | @@ -322,7 +322,7 @@ module Control.Exception.Backtrace where |
| 322 | 322 | type BacktraceMechanism :: *
|
| 323 | 323 | data BacktraceMechanism = CostCentreBacktrace | HasCallStackBacktrace | ExecutionBacktrace | IPEBacktrace
|
| 324 | 324 | type Backtraces :: *
|
| 325 | - data Backtraces = ...
|
|
| 325 | + data Backtraces = Backtraces {btrCostCentre :: GHC.Internal.Maybe.Maybe (GHC.Internal.Ptr.Ptr GHC.Internal.Stack.CCS.CostCentreStack), btrHasCallStack :: GHC.Internal.Maybe.Maybe GHC.Internal.Stack.Types.CallStack, btrExecutionStack :: GHC.Internal.Maybe.Maybe [GHC.Internal.ExecutionStack.Internal.Location], btrIpe :: GHC.Internal.Maybe.Maybe [GHC.Internal.Stack.CloneStack.StackEntry]}
|
|
| 326 | 326 | collectBacktraces :: (?callStack::GHC.Internal.Stack.Types.CallStack) => GHC.Internal.Types.IO Backtraces
|
| 327 | 327 | displayBacktraces :: Backtraces -> GHC.Internal.Base.String
|
| 328 | 328 | getBacktraceMechanismState :: BacktraceMechanism -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| ... | ... | @@ -322,7 +322,7 @@ module Control.Exception.Backtrace where |
| 322 | 322 | type BacktraceMechanism :: *
|
| 323 | 323 | data BacktraceMechanism = CostCentreBacktrace | HasCallStackBacktrace | ExecutionBacktrace | IPEBacktrace
|
| 324 | 324 | type Backtraces :: *
|
| 325 | - data Backtraces = ...
|
|
| 325 | + data Backtraces = Backtraces {btrCostCentre :: GHC.Internal.Maybe.Maybe (GHC.Internal.Ptr.Ptr GHC.Internal.Stack.CCS.CostCentreStack), btrHasCallStack :: GHC.Internal.Maybe.Maybe GHC.Internal.Stack.Types.CallStack, btrExecutionStack :: GHC.Internal.Maybe.Maybe [GHC.Internal.ExecutionStack.Internal.Location], btrIpe :: GHC.Internal.Maybe.Maybe [GHC.Internal.Stack.CloneStack.StackEntry]}
|
|
| 326 | 326 | collectBacktraces :: (?callStack::GHC.Internal.Stack.Types.CallStack) => GHC.Internal.Types.IO Backtraces
|
| 327 | 327 | displayBacktraces :: Backtraces -> GHC.Internal.Base.String
|
| 328 | 328 | getBacktraceMechanismState :: BacktraceMechanism -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| ... | ... | @@ -322,7 +322,7 @@ module Control.Exception.Backtrace where |
| 322 | 322 | type BacktraceMechanism :: *
|
| 323 | 323 | data BacktraceMechanism = CostCentreBacktrace | HasCallStackBacktrace | ExecutionBacktrace | IPEBacktrace
|
| 324 | 324 | type Backtraces :: *
|
| 325 | - data Backtraces = ...
|
|
| 325 | + data Backtraces = Backtraces {btrCostCentre :: GHC.Internal.Maybe.Maybe (GHC.Internal.Ptr.Ptr GHC.Internal.Stack.CCS.CostCentreStack), btrHasCallStack :: GHC.Internal.Maybe.Maybe GHC.Internal.Stack.Types.CallStack, btrExecutionStack :: GHC.Internal.Maybe.Maybe [GHC.Internal.ExecutionStack.Internal.Location], btrIpe :: GHC.Internal.Maybe.Maybe [GHC.Internal.Stack.CloneStack.StackEntry]}
|
|
| 326 | 326 | collectBacktraces :: (?callStack::GHC.Internal.Stack.Types.CallStack) => GHC.Internal.Types.IO Backtraces
|
| 327 | 327 | displayBacktraces :: Backtraces -> GHC.Internal.Base.String
|
| 328 | 328 | getBacktraceMechanismState :: BacktraceMechanism -> GHC.Internal.Types.IO GHC.Internal.Types.Bool
|
| 1 | +#include <assert.h>
|
|
| 2 | +#include <stdbool.h>
|
|
| 3 | +#include <stdint.h>
|
|
| 4 | +#include <stddef.h>
|
|
| 5 | + |
|
| 6 | +// Excerpted from ProddableBlocks.h
|
|
| 7 | +typedef struct {
|
|
| 8 | + size_t size;
|
|
| 9 | + size_t capacity;
|
|
| 10 | + // sorted list of disjoint (start,end) pairs
|
|
| 11 | + struct _ProddableBlock *data;
|
|
| 12 | +} ProddableBlockSet;
|
|
| 13 | + |
|
| 14 | +void initProddableBlockSet ( ProddableBlockSet* set );
|
|
| 15 | +void addProddableBlock ( ProddableBlockSet* set, void* start, size_t size );
|
|
| 16 | +bool containsSpan ( const ProddableBlockSet *set, uintptr_t start, uintptr_t end );
|
|
| 17 | + |
|
| 18 | +int main () {
|
|
| 19 | + ProddableBlockSet set;
|
|
| 20 | + initProddableBlockSet(&set);
|
|
| 21 | + addProddableBlock(&set, (void*) 0x20, 0x10);
|
|
| 22 | + addProddableBlock(&set, (void*) 0x30, 0x10);
|
|
| 23 | + addProddableBlock(&set, (void*) 0x100, 0x10);
|
|
| 24 | + |
|
| 25 | + assert( containsSpan(&set, 0x20, 0x30));
|
|
| 26 | + assert( containsSpan(&set, 0x30, 0x29));
|
|
| 27 | + assert(!containsSpan(&set, 0x30, 0x49));
|
|
| 28 | + assert(!containsSpan(&set, 0x60, 0x70));
|
|
| 29 | + assert(!containsSpan(&set, 0x90, 0x110));
|
|
| 30 | + assert( containsSpan(&set, 0x100, 0x101));
|
|
| 31 | + return 0;
|
|
| 32 | +}
|
|
| 33 | + |
| ... | ... | @@ -641,3 +641,5 @@ test('T25280', [unless(opsys('linux'),skip),req_process,js_skip], compile_and_ru |
| 641 | 641 | # N.B. This will likely issue a warning on stderr but we merely care that the
|
| 642 | 642 | # program doesn't crash.
|
| 643 | 643 | test('T25560', [req_c_rts, ignore_stderr], compile_and_run, [''])
|
| 644 | + |
|
| 645 | +test('TestProddableBlockSet', [req_c_rts], multimod_compile_and_run, ['TestProddableBlockSet.c', '-no-hs-main']) |