summaryrefslogtreecommitdiff
path: root/libraries/ghci/GHCi/InfoTable.hsc
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ghci/GHCi/InfoTable.hsc')
-rw-r--r--libraries/ghci/GHCi/InfoTable.hsc348
1 files changed, 348 insertions, 0 deletions
diff --git a/libraries/ghci/GHCi/InfoTable.hsc b/libraries/ghci/GHCi/InfoTable.hsc
new file mode 100644
index 0000000000..d9d63146dd
--- /dev/null
+++ b/libraries/ghci/GHCi/InfoTable.hsc
@@ -0,0 +1,348 @@
+{-# LANGUAGE CPP, MagicHash, ScopedTypeVariables #-}
+
+-- |
+-- Run-time info table support. This module provides support for
+-- creating and reading info tables /in the running program/.
+-- We use the RTS data structures directly via hsc2hs.
+--
+module GHCi.InfoTable
+ ( mkConInfoTable
+ , peekItbl, StgInfoTable(..)
+ , conInfoPtr
+ ) where
+
+import Foreign
+import Foreign.C
+import GHC.Ptr
+import GHC.Exts
+import System.IO.Unsafe
+
+mkConInfoTable
+ :: Int -- ptr words
+ -> Int -- non-ptr words
+ -> Int -- constr tag
+ -> [Word8] -- con desc
+ -> IO (Ptr ())
+ -- resulting info table is allocated with allocateExec(), and
+ -- should be freed with freeExec().
+
+mkConInfoTable ptr_words nonptr_words tag con_desc =
+ castFunPtrToPtr <$> newExecConItbl itbl con_desc
+ where
+ entry_addr = stg_interp_constr_entry
+ code' = mkJumpToAddr entry_addr
+ itbl = StgInfoTable {
+ entry = if ghciTablesNextToCode
+ then Nothing
+ else Just entry_addr,
+ ptrs = fromIntegral ptr_words,
+ nptrs = fromIntegral nonptr_words,
+ tipe = fromIntegral cONSTR,
+ srtlen = fromIntegral tag,
+ code = if ghciTablesNextToCode
+ then Just code'
+ else Nothing
+ }
+
+
+-- -----------------------------------------------------------------------------
+-- Building machine code fragments for a constructor's entry code
+
+type ItblCodes = Either [Word8] [Word32]
+
+funPtrToInt :: FunPtr a -> Int
+funPtrToInt (FunPtr a) = I## (addr2Int## a)
+
+data Arch = ArchSPARC | ArchPPC | ArchX86 | ArchX86_64 | ArchAlpha | ArchARM
+ deriving Show
+
+platform :: Arch
+platform =
+#if defined(sparc_HOST_ARCH)
+ ArchSparc
+#elif defined(ppc_HOST_ARCH)
+ ArchPPC
+#elif defined(x86_HOST_ARCH)
+ ArchX86
+#elif defined(x86_64_HOST_ARCH)
+ ArchX86_64
+#elif defined(alpha_HOST_ARCH)
+ ArchAlpha
+#elif defined(arm_HOST_ARCH)
+ ArchARM
+#endif
+
+mkJumpToAddr :: EntryFunPtr -> ItblCodes
+mkJumpToAddr a = case platform of
+ ArchSPARC ->
+ -- After some consideration, we'll try this, where
+ -- 0x55555555 stands in for the address to jump to.
+ -- According to includes/rts/MachRegs.h, %g3 is very
+ -- likely indeed to be baggable.
+ --
+ -- 0000 07155555 sethi %hi(0x55555555), %g3
+ -- 0004 8610E155 or %g3, %lo(0x55555555), %g3
+ -- 0008 81C0C000 jmp %g3
+ -- 000c 01000000 nop
+
+ let w32 = fromIntegral (funPtrToInt a)
+
+ hi22, lo10 :: Word32 -> Word32
+ lo10 x = x .&. 0x3FF
+ hi22 x = (x `shiftR` 10) .&. 0x3FFFF
+
+ in Right [ 0x07000000 .|. (hi22 w32),
+ 0x8610E000 .|. (lo10 w32),
+ 0x81C0C000,
+ 0x01000000 ]
+
+ ArchPPC ->
+ -- We'll use r12, for no particular reason.
+ -- 0xDEADBEEF stands for the address:
+ -- 3D80DEAD lis r12,0xDEAD
+ -- 618CBEEF ori r12,r12,0xBEEF
+ -- 7D8903A6 mtctr r12
+ -- 4E800420 bctr
+
+ let w32 = fromIntegral (funPtrToInt a)
+ hi16 x = (x `shiftR` 16) .&. 0xFFFF
+ lo16 x = x .&. 0xFFFF
+ in Right [ 0x3D800000 .|. hi16 w32,
+ 0x618C0000 .|. lo16 w32,
+ 0x7D8903A6, 0x4E800420 ]
+
+ ArchX86 ->
+ -- Let the address to jump to be 0xWWXXYYZZ.
+ -- Generate movl $0xWWXXYYZZ,%eax ; jmp *%eax
+ -- which is
+ -- B8 ZZ YY XX WW FF E0
+
+ let w32 = fromIntegral (funPtrToInt a) :: Word32
+ insnBytes :: [Word8]
+ insnBytes
+ = [0xB8, byte0 w32, byte1 w32,
+ byte2 w32, byte3 w32,
+ 0xFF, 0xE0]
+ in
+ Left insnBytes
+
+ ArchX86_64 ->
+ -- Generates:
+ -- jmpq *.L1(%rip)
+ -- .align 8
+ -- .L1:
+ -- .quad <addr>
+ --
+ -- which looks like:
+ -- 8: ff 25 02 00 00 00 jmpq *0x2(%rip) # 10 <f+0x10>
+ -- with addr at 10.
+ --
+ -- We need a full 64-bit pointer (we can't assume the info table is
+ -- allocated in low memory). Assuming the info pointer is aligned to
+ -- an 8-byte boundary, the addr will also be aligned.
+
+ let w64 = fromIntegral (funPtrToInt a) :: Word64
+ insnBytes :: [Word8]
+ insnBytes
+ = [0xff, 0x25, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ byte0 w64, byte1 w64, byte2 w64, byte3 w64,
+ byte4 w64, byte5 w64, byte6 w64, byte7 w64]
+ in
+ Left insnBytes
+
+ ArchAlpha ->
+ let w64 = fromIntegral (funPtrToInt a) :: Word64
+ in Right [ 0xc3800000 -- br at, .+4
+ , 0xa79c000c -- ldq at, 12(at)
+ , 0x6bfc0000 -- jmp (at) # with zero hint -- oh well
+ , 0x47ff041f -- nop
+ , fromIntegral (w64 .&. 0x0000FFFF)
+ , fromIntegral ((w64 `shiftR` 32) .&. 0x0000FFFF) ]
+
+ ArchARM { } ->
+ -- Generates Arm sequence,
+ -- ldr r1, [pc, #0]
+ -- bx r1
+ --
+ -- which looks like:
+ -- 00000000 <.addr-0x8>:
+ -- 0: 00109fe5 ldr r1, [pc] ; 8 <.addr>
+ -- 4: 11ff2fe1 bx r1
+ let w32 = fromIntegral (funPtrToInt a) :: Word32
+ in Left [ 0x00, 0x10, 0x9f, 0xe5
+ , 0x11, 0xff, 0x2f, 0xe1
+ , byte0 w32, byte1 w32, byte2 w32, byte3 w32]
+
+
+byte0 :: (Integral w) => w -> Word8
+byte0 w = fromIntegral w
+
+byte1, byte2, byte3, byte4, byte5, byte6, byte7
+ :: (Integral w, Bits w) => w -> Word8
+byte1 w = fromIntegral (w `shiftR` 8)
+byte2 w = fromIntegral (w `shiftR` 16)
+byte3 w = fromIntegral (w `shiftR` 24)
+byte4 w = fromIntegral (w `shiftR` 32)
+byte5 w = fromIntegral (w `shiftR` 40)
+byte6 w = fromIntegral (w `shiftR` 48)
+byte7 w = fromIntegral (w `shiftR` 56)
+
+
+-- -----------------------------------------------------------------------------
+-- read & write intfo tables
+
+-- Get definitions for the structs, constants & config etc.
+#include "Rts.h"
+
+-- entry point for direct returns for created constr itbls
+foreign import ccall "&stg_interp_constr_entry"
+ stg_interp_constr_entry :: EntryFunPtr
+
+-- Ultra-minimalist version specially for constructors
+#if SIZEOF_VOID_P == 8
+type HalfWord = Word32
+#elif SIZEOF_VOID_P == 4
+type HalfWord = Word16
+#else
+#error Uknown SIZEOF_VOID_P
+#endif
+
+data StgConInfoTable = StgConInfoTable {
+ conDesc :: Ptr Word8,
+ infoTable :: StgInfoTable
+}
+
+type EntryFunPtr = FunPtr (Ptr () -> IO (Ptr ()))
+
+data StgInfoTable = StgInfoTable {
+ entry :: Maybe EntryFunPtr, -- Just <=> not ghciTablesNextToCode
+ ptrs :: HalfWord,
+ nptrs :: HalfWord,
+ tipe :: HalfWord,
+ srtlen :: HalfWord,
+ code :: Maybe ItblCodes -- Just <=> ghciTablesNextToCode
+ }
+
+pokeConItbl
+ :: Ptr StgConInfoTable -> Ptr StgConInfoTable -> StgConInfoTable
+ -> IO ()
+pokeConItbl wr_ptr ex_ptr itbl = do
+ let _con_desc = conDesc itbl `minusPtr` (ex_ptr `plusPtr` conInfoTableSizeB)
+#if defined(TABLES_NEXT_TO_CODE)
+ (#poke StgConInfoTable, con_desc) wr_ptr _con_desc
+#else
+ (#poke StgConInfoTable, con_desc) wr_ptr (conDesc itbl)
+#endif
+ pokeItbl (wr_ptr `plusPtr` (#offset StgConInfoTable, i)) (infoTable itbl)
+
+sizeOfEntryCode :: Int
+sizeOfEntryCode
+ | not ghciTablesNextToCode = 0
+ | otherwise =
+ case mkJumpToAddr undefined of
+ Left xs -> sizeOf (head xs) * length xs
+ Right xs -> sizeOf (head xs) * length xs
+
+pokeItbl :: Ptr StgInfoTable -> StgInfoTable -> IO ()
+pokeItbl a0 itbl = do
+#if !defined(TABLES_NEXT_TO_CODE)
+ (#poke StgInfoTable, entry) a0 (fromJust (entry itbl))
+#endif
+ (#poke StgInfoTable, layout.payload.ptrs) a0 (ptrs itbl)
+ (#poke StgInfoTable, layout.payload.nptrs) a0 (nptrs itbl)
+ (#poke StgInfoTable, type) a0 (tipe itbl)
+ (#poke StgInfoTable, srt_bitmap) a0 (srtlen itbl)
+#if defined(TABLES_NEXT_TO_CODE)
+ let code_offset = (a0 `plusPtr` (#offset StgInfoTable, code))
+ case code itbl of
+ Nothing -> return ()
+ Just (Left xs) -> pokeArray code_offset xs
+ Just (Right xs) -> pokeArray code_offset xs
+#endif
+
+peekItbl :: Ptr StgInfoTable -> IO StgInfoTable
+peekItbl a0 = do
+#if defined(TABLES_NEXT_TO_CODE)
+ let entry' = Nothing
+#else
+ entry' <- Just <$> (#peek StgInfoTable, entry) a0
+#endif
+ ptrs' <- (#peek StgInfoTable, layout.payload.ptrs) a0
+ nptrs' <- (#peek StgInfoTable, layout.payload.nptrs) a0
+ tipe' <- (#peek StgInfoTable, type) a0
+ srtlen' <- (#peek StgInfoTable, srt_bitmap) a0
+ return StgInfoTable
+ { entry = entry'
+ , ptrs = ptrs'
+ , nptrs = nptrs'
+ , tipe = tipe'
+ , srtlen = srtlen'
+ , code = Nothing
+ }
+
+newExecConItbl :: StgInfoTable -> [Word8] -> IO (FunPtr ())
+newExecConItbl obj con_desc
+ = alloca $ \pcode -> do
+ let lcon_desc = length con_desc + 1{- null terminator -}
+ sz = fromIntegral ((#size StgConInfoTable) + sizeOfEntryCode)
+ -- Note: we need to allocate the conDesc string next to the info
+ -- table, because on a 64-bit platform we reference this string
+ -- with a 32-bit offset relative to the info table, so if we
+ -- allocated the string separately it might be out of range.
+ wr_ptr <- _allocateExec (sz + fromIntegral lcon_desc) pcode
+ ex_ptr <- peek pcode
+ let cinfo = StgConInfoTable { conDesc = ex_ptr `plusPtr` fromIntegral sz
+ , infoTable = obj }
+ pokeConItbl wr_ptr ex_ptr cinfo
+ pokeArray0 0 (castPtr wr_ptr `plusPtr` fromIntegral sz) con_desc
+ _flushExec sz ex_ptr -- Cache flush (if needed)
+ return (castPtrToFunPtr ex_ptr)
+
+foreign import ccall unsafe "allocateExec"
+ _allocateExec :: CUInt -> Ptr (Ptr a) -> IO (Ptr a)
+
+foreign import ccall unsafe "flushExec"
+ _flushExec :: CUInt -> Ptr a -> IO ()
+
+-- | Convert a pointer to an StgConInfo into an info pointer that can be
+-- used in the header of a closure.
+conInfoPtr :: Ptr () -> Ptr ()
+conInfoPtr ptr
+ | ghciTablesNextToCode = ptr `plusPtr` (#size StgConInfoTable)
+ | otherwise = ptr
+
+-- -----------------------------------------------------------------------------
+-- Constants and config
+
+wORD_SIZE :: Int
+wORD_SIZE = (#const SIZEOF_HSINT)
+
+fixedInfoTableSizeB :: Int
+fixedInfoTableSizeB = 2 * wORD_SIZE
+
+profInfoTableSizeB :: Int
+profInfoTableSizeB = (#size StgProfInfo)
+
+stdInfoTableSizeB :: Int
+stdInfoTableSizeB
+ = (if ghciTablesNextToCode then 0 else wORD_SIZE)
+ + (if rtsIsProfiled then profInfoTableSizeB else 0)
+ + fixedInfoTableSizeB
+
+conInfoTableSizeB :: Int
+conInfoTableSizeB = stdInfoTableSizeB + wORD_SIZE
+
+foreign import ccall unsafe "rts_isProfiled" rtsIsProfiledIO :: IO CInt
+
+rtsIsProfiled :: Bool
+rtsIsProfiled = unsafeDupablePerformIO rtsIsProfiledIO /= 0
+
+cONSTR :: Int -- Defined in ClosureTypes.h
+cONSTR = (#const CONSTR)
+
+ghciTablesNextToCode :: Bool
+#ifdef TABLES_NEXT_TO_CODE
+ghciTablesNextToCode = True
+#else
+ghciTablesNextToCode = False
+#endif