packages feed

hdis86 0.1 → 0.2

raw patch · 9 files changed

+304/−42 lines, 9 filesdep +QuickCheckPVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck

API changes (from Hackage documentation)

- Hdis86.C: get_base :: Ptr Operand -> IO UD_type
- Hdis86.C: get_index :: Ptr Operand -> IO UD_type
- Hdis86.C: get_offset :: Ptr Operand -> IO (Word8)
- Hdis86.C: get_operand1 :: Ptr UD_t -> Ptr Operand
- Hdis86.C: get_operand2 :: Ptr UD_t -> Ptr Operand
- Hdis86.C: get_operand3 :: Ptr UD_t -> Ptr Operand
- Hdis86.C: get_pfx_adr :: Ptr UD_t -> IO (Word8)
- Hdis86.C: get_pfx_lock :: Ptr UD_t -> IO (Word8)
- Hdis86.C: get_pfx_opr :: Ptr UD_t -> IO (Word8)
- Hdis86.C: get_pfx_rep :: Ptr UD_t -> IO (Word8)
- Hdis86.C: get_pfx_repe :: Ptr UD_t -> IO (Word8)
- Hdis86.C: get_pfx_repne :: Ptr UD_t -> IO (Word8)
- Hdis86.C: get_pfx_rex :: Ptr UD_t -> IO (Word8)
- Hdis86.C: get_pfx_seg :: Ptr UD_t -> IO (Word8)
- Hdis86.C: get_scale :: Ptr Operand -> IO (Word8)
- Hdis86.C: get_size :: Ptr Operand -> IO (Word8)
- Hdis86.C: get_type :: Ptr Operand -> IO UD_type
- Hdis86.Pure: instance Data Metadata
- Hdis86.Pure: instance Eq Metadata
- Hdis86.Pure: instance Ord Metadata
- Hdis86.Pure: instance Read Metadata
- Hdis86.Pure: instance Show Metadata
- Hdis86.Pure: instance Typeable Metadata
- Hdis86.Types: amd32 :: Config
- Hdis86.Types: amd64 :: Config
- Hdis86.Types: intel32 :: Config
- Hdis86.Types: intel64 :: Config
+ Hdis86: disassemble :: Config -> ByteString -> [Instruction]
+ Hdis86: disassembleMetadata :: Config -> ByteString -> [Metadata]
+ Hdis86.C: get_operand1, get_operand3, get_operand2 :: Ptr UD_t -> Ptr Operand
+ Hdis86.C: get_pfx_rex, get_pfx_repne, get_pfx_repe, get_pfx_rep, get_pfx_lock, get_pfx_adr, get_pfx_opr, get_pfx_seg :: Ptr UD_t -> IO (Word8)
+ Hdis86.C: get_size, get_scale, get_offset :: Ptr Operand -> IO (Word8)
+ Hdis86.C: get_type, get_index, get_base :: Ptr Operand -> IO UD_type
+ Hdis86.IO: getMetadata :: UD -> IO Metadata
+ Hdis86.Incremental: disassembleLazy :: Config -> ByteString -> [Metadata]
+ Hdis86.Incremental: disassembleOne :: Config -> ByteString -> Maybe (Metadata, ByteString)
+ Hdis86.Internal.Map: data UDTM v
+ Hdis86.Internal.Map: lookupUDTM :: UD_type -> UDTM v -> Maybe v
+ Hdis86.Internal.Map: makeUDTM :: [(UD_type, v)] -> UDTM v
+ Hdis86.Internal.Map: opcode :: CUInt -> Opcode
+ Hdis86.Internal.Map: register :: CUInt -> Register
+ Hdis86.Types: Metadata :: Word64 -> Word -> String -> ByteString -> String -> Instruction -> Metadata
+ Hdis86.Types: data Metadata
+ Hdis86.Types: inOpcode :: Instruction -> Opcode
+ Hdis86.Types: inOperands :: Instruction -> [Operand]
+ Hdis86.Types: inPrefixes :: Instruction -> [Prefix]
+ Hdis86.Types: instance Arbitrary CPUMode
+ Hdis86.Types: instance Arbitrary Config
+ Hdis86.Types: instance Arbitrary ControlRegister
+ Hdis86.Types: instance Arbitrary DebugRegister
+ Hdis86.Types: instance Arbitrary GPR
+ Hdis86.Types: instance Arbitrary Half
+ Hdis86.Types: instance Arbitrary Instruction
+ Hdis86.Types: instance Arbitrary MMXRegister
+ Hdis86.Types: instance Arbitrary Memory
+ Hdis86.Types: instance Arbitrary Opcode
+ Hdis86.Types: instance Arbitrary Operand
+ Hdis86.Types: instance Arbitrary Pointer
+ Hdis86.Types: instance Arbitrary Prefix
+ Hdis86.Types: instance Arbitrary Register
+ Hdis86.Types: instance Arbitrary Segment
+ Hdis86.Types: instance Arbitrary Syntax
+ Hdis86.Types: instance Arbitrary Vendor
+ Hdis86.Types: instance Arbitrary WordSize
+ Hdis86.Types: instance Arbitrary X87Register
+ Hdis86.Types: instance Arbitrary XMMRegister
+ Hdis86.Types: instance Arbitrary t => Arbitrary (Immediate t)
+ Hdis86.Types: instance Data Metadata
+ Hdis86.Types: instance Eq Metadata
+ Hdis86.Types: instance Ord Metadata
+ Hdis86.Types: instance Read Metadata
+ Hdis86.Types: instance Show Metadata
+ Hdis86.Types: instance Typeable Metadata
+ Hdis86.Types: intel32, amd64, amd32, intel64 :: Config
+ Hdis86.Types: mdAssembly :: Metadata -> String
+ Hdis86.Types: mdBytes :: Metadata -> ByteString
+ Hdis86.Types: mdHex :: Metadata -> String
+ Hdis86.Types: mdInst :: Metadata -> Instruction
+ Hdis86.Types: mdLength :: Metadata -> Word
+ Hdis86.Types: mdOffset :: Metadata -> Word64

Files

Hdis86.hs view
@@ -2,8 +2,9 @@ -- -- Exports the simplest, most high-level interface. module Hdis86-  ( module Hdis86.Types-  , module Hdis86.Pure+  ( disassemble+  , disassembleMetadata+  , module Hdis86.Types   ) where  import Hdis86.Types
Hdis86/C.hsc view
@@ -5,7 +5,7 @@ -- | Bare import of the @udis86@ C library. -- -- This module is not recommended for most users. What you see is what you get.--- The modules @'Hdis86.IO'@ and @'Hdis86.Pure'@ provide a more Haskell-friendly+-- The modules "Hdis86.IO" and "Hdis86.Pure" provide a more Haskell-friendly -- interface to the same functionality. -- -- If you want to use this module, see the @udis86@ documentation: <http://udis86.sourceforge.net>
Hdis86/IO.hs view
@@ -12,7 +12,7 @@ -- this module's API is thoroughly imperative, but uses -- Haskellish types and automatic resource management. ----- For a higher-level, @IO@-free API, see @'Hdis86.Pure'@.+-- For a higher-level, @IO@-free API, see "Hdis86.Pure". -- -- This module is fully thread-safe: any number of threads -- may manipulate one or several @'UD'@ objects at the same@@ -36,6 +36,7 @@   , getInstruction   , getLength, getOffset   , getHex, getBytes, getAssembly+  , getMetadata      -- * Configuration   , setConfig@@ -310,6 +311,17 @@ getAssembly :: UD -> IO String getAssembly = flip withUDPtr $ \p ->   C.insn_asm p >>= peekCString++-- | Get all metadata about the current instruction,+-- along with the instruction itself.+getMetadata :: UD -> IO Metadata+getMetadata ud = Metadata+  <$> getOffset      ud+  <*> getLength      ud+  <*> getHex         ud+  <*> getBytes       ud+  <*> getAssembly    ud+  <*> getInstruction ud  -- | Skip the next /n/ bytes of the input. skip :: UD -> Word -> IO ()
+ Hdis86/Incremental.hs view
@@ -0,0 +1,60 @@+-- | Incremental pure disassembly.+module Hdis86.Incremental+  ( disassembleOne+  , disassembleLazy+  ) where++import Hdis86.Types+import qualified Hdis86.Pure as P+import qualified Hdis86.IO   as I++import Data.Maybe++import System.IO.Unsafe ( unsafePerformIO )++import qualified Data.ByteString      as BS+import qualified Data.ByteString.Lazy as BL++disOne :: Config -> BS.ByteString -> Maybe Metadata+disOne cfg bs = unsafePerformIO $ do+  ud <- I.newUD+  I.setInputBuffer ud bs+  I.setConfig      ud cfg+  n <- I.advance ud+  case n of+    Just _  -> Just `fmap` I.getMetadata ud+    Nothing -> return Nothing++-- | Split a @'BS.ByteString'@ into an instruction and the remaining+-- @'BS.ByteString'@.+--+-- Returns @'Nothing'@ if the input is empty or contains an+-- incomplete instruction.+disassembleOne :: Config -> BS.ByteString -> Maybe (Metadata, BS.ByteString)+disassembleOne cfg bs = disOne cfg bs >>= (fmap getRem . getMD) where+  -- invalid instruction: distinguish from incomplete instruction+  -- by appending 0x90 = nop+  getMD m@Metadata { mdInst  = i0@Inst { inOpcode = Iinvalid },+                     mdBytes = ibs }+    = case P.disassemble cfg (ibs `BS.snoc` 0x90) of+        [i1, Inst [] Inop []] | i0 == i1  -> Just m+        _ -> Nothing++  getMD  m = Just m++  getRem m = (m, BS.drop (fromIntegral $ mdLength m) bs)++-- | Disassemble a lazy @'BL.ByteString'@.+--+-- The output is produced lazily.+disassembleLazy :: Config -> BL.ByteString -> [Metadata]+disassembleLazy cfg = go (cfgOrigin cfg) BS.empty . BL.toChunks where+  go n bs chunks = let newcfg = cfg { cfgOrigin = n } in+    case disassembleOne newcfg bs of+      Just (md, bsrem) -> md : go nn bsrem chunks where+        nn = n + fromIntegral (mdLength md)++      Nothing -> case chunks of+        (x:xs) -> go n (bs `BS.append` x) xs+        -- end of input; produce a final incomplete instruction if any+        []     -> maybeToList $ disOne newcfg bs
Hdis86/Internal/Map.hs view
@@ -1,5 +1,4 @@-{-# OPTIONS_HADDOCK-    hide #-}+-- | Internal module; use at your own risk. module Hdis86.Internal.Map   ( UDTM, makeUDTM, lookupUDTM   , register, opcode
Hdis86/Pure.hs view
@@ -1,11 +1,8 @@-{-# LANGUAGE-    DeriveDataTypeable #-}- -- | Interface to the @udis86@ disassembler. -- -- This is the simplest, most high-level interface. ----- See @'Hdis86.IO'@ if you need more control or performance.+-- See "Hdis86.IO" if you need more control or performance. module Hdis86.Pure   ( -- * Simple disassembly     disassemble@@ -19,12 +16,6 @@ import Hdis86.IO ( UD ) import qualified Hdis86.IO as I -import Data.Typeable ( Typeable )-import Data.Data     ( Data )--import Control.Applicative-import Data.Word- import System.IO.Unsafe ( unsafePerformIO )  import qualified Data.ByteString as BS@@ -40,30 +31,14 @@ -- | Disassemble machine code. -- -- Common values for @'Config'@ such as @'intel32'@ or @'amd64'@--- are provided in @'Hdis86.Types'@.+-- are provided in "Hdis86.Types". -- -- The output is produced lazily. disassemble :: Config -> BS.ByteString -> [Instruction] disassemble = disWith I.getInstruction --- | An instruction with full metadata.-data Metadata = Metadata-  { mdOffset   :: Word64         -- ^ Offset of the start of this instruction-  , mdLength   :: Word           -- ^ Length of this instruction in bytes-  , mdHex      :: String         -- ^ Hexadecimal representation of this instruction-  , mdBytes    :: BS.ByteString  -- ^ Bytes that make up this instruction-  , mdAssembly :: String         -- ^ Assembly code for this instruction-  , mdInst     :: Instruction    -- ^ The instruction itself-  } deriving (Eq, Ord, Show, Read, Typeable, Data)- -- | Disassemble machine code, with full metadata. -- -- The output is produced lazily. disassembleMetadata :: Config -> BS.ByteString -> [Metadata]-disassembleMetadata = disWith $ \ud -> Metadata-  <$> I.getOffset      ud-  <*> I.getLength      ud-  <*> I.getHex         ud-  <*> I.getBytes       ud-  <*> I.getAssembly    ud-  <*> I.getInstruction ud+disassembleMetadata = disWith I.getMetadata
Hdis86/Types.hs view
@@ -1,5 +1,8 @@ {-# LANGUAGE-    DeriveDataTypeable #-}+    DeriveDataTypeable+  , ScopedTypeVariables #-}+{-# OPTIONS_GHC+    -fno-warn-orphans #-}  -- | Types provided by the disassembler. module Hdis86.Types@@ -16,6 +19,9 @@     -- * Word sizes   , WordSize(..), wordSize, bitsInWord +    -- * Instruction with metadata+  , Metadata(..)+     -- * Configuration   , Config(..)   , Vendor(..), CPUMode(..), Syntax(..)@@ -33,7 +39,12 @@ import Data.Data     ( Data ) import Data.Word import Data.Int+import Control.Applicative hiding ( Const ) +import qualified Data.ByteString as BS+import qualified Text.Read       as R+import qualified Test.QuickCheck as Q+ -- | Machine word sizes. -- -- Some fields, such as immediate operands, come in different@@ -151,10 +162,27 @@ -- -- The destination @'Operand'@ (if any) will precede the source -- @'Operand'@s.-data Instruction-  = Inst [Prefix] Opcode [Operand]-  deriving (Eq, Ord, Show, Read, Typeable, Data)+data Instruction = Inst+  { inPrefixes :: [Prefix]+  , inOpcode   :: Opcode+  , inOperands :: [Operand]+  } deriving (Eq, Ord, Typeable, Data) +instance Show Instruction where+  showsPrec p (Inst pfx opc opr) = showParen (p >= 11) body+    where+      body = foldr (.) id+        [("Inst "++), showsPrec 11 pfx, (" "++),+         showsPrec 11 opc, (" "++), showsPrec 11 opr]++instance Read Instruction where+  readsPrec d = R.readParen (d > 10) $ \r ->+    [(Inst pfx opc opr, xd)+      | ("Inst", xa) <- R.lex r+      , (pfx, xb) <- readsPrec 11 xa+      , (opc, xc) <- readsPrec 11 xb+      , (opr, xd) <- readsPrec 11 xc]+ -- | Prefixes, used to modify an instruction. data Prefix   = Seg Segment  -- ^ Segment override@@ -198,6 +226,16 @@   , iValue :: t         -- ^ Immediate value, e.g @'Int64'@ or @'Word64'@   } deriving (Eq, Ord, Show, Read, Typeable, Data) +-- | An instruction with full metadata.+data Metadata = Metadata+  { mdOffset   :: Word64         -- ^ Offset of the start of this instruction+  , mdLength   :: Word           -- ^ Length of this instruction in bytes+  , mdHex      :: String         -- ^ Hexadecimal representation of this instruction+  , mdBytes    :: BS.ByteString  -- ^ Bytes that make up this instruction+  , mdAssembly :: String         -- ^ Assembly code for this instruction+  , mdInst     :: Instruction    -- ^ The instruction itself+  } deriving (Eq, Ord, Show, Read, Typeable, Data)+ -- | CPU vendors, supporting slightly different instruction sets. data Vendor   = Intel@@ -231,3 +269,82 @@ intel64 = Config Intel Mode64 SyntaxNone 0 amd32   = Config AMD   Mode32 SyntaxNone 0 amd64   = Config AMD   Mode64 SyntaxNone 0+++-- QuickCheck properties++_prop_Instruction_ReadShow :: Instruction -> Bool+_prop_Instruction_ReadShow i = read (show i) == i+++-- QuickCheck instances++arbEnum :: forall a. (Enum a, Bounded a) => Q.Gen a+arbEnum = toEnum <$> Q.choose (fromEnum lb, fromEnum ub) where+  lb, ub :: a+  (lb, ub) = (minBound, maxBound)++instance Q.Arbitrary GPR             where arbitrary = arbEnum+instance Q.Arbitrary Half            where arbitrary = arbEnum+instance Q.Arbitrary Segment         where arbitrary = arbEnum+instance Q.Arbitrary ControlRegister where arbitrary = arbEnum+instance Q.Arbitrary DebugRegister   where arbitrary = arbEnum+instance Q.Arbitrary MMXRegister     where arbitrary = arbEnum+instance Q.Arbitrary X87Register     where arbitrary = arbEnum+instance Q.Arbitrary XMMRegister     where arbitrary = arbEnum+instance Q.Arbitrary WordSize        where arbitrary = arbEnum+instance Q.Arbitrary Vendor          where arbitrary = arbEnum+instance Q.Arbitrary CPUMode         where arbitrary = arbEnum+instance Q.Arbitrary Syntax          where arbitrary = arbEnum+instance Q.Arbitrary Opcode          where arbitrary = arbEnum++instance Q.Arbitrary Register where+  arbitrary = Q.oneof [+      pure RegNone+    , pure RegIP+    , Reg8   <$> Q.arbitrary <*> Q.arbitrary+    , Reg16  <$> Q.arbitrary+    , Reg32  <$> Q.arbitrary+    , Reg64  <$> Q.arbitrary+    , RegSeg <$> Q.arbitrary+    , RegCtl <$> Q.arbitrary+    , RegDbg <$> Q.arbitrary+    , RegMMX <$> Q.arbitrary+    , RegX87 <$> Q.arbitrary+    , RegXMM <$> Q.arbitrary ]++instance (Q.Arbitrary t) => Q.Arbitrary (Immediate t) where+  arbitrary = Immediate <$> Q.arbitrary <*> Q.arbitrary++instance Q.Arbitrary Pointer where+  arbitrary = Pointer <$> Q.arbitrary <*> Q.arbitrary++instance Q.Arbitrary Memory where+  arbitrary = Memory <$> Q.arbitrary <*> Q.arbitrary <*> Q.arbitrary+                     <*> Q.arbitrary <*> Q.arbitrary++instance Q.Arbitrary Operand where+  arbitrary = Q.oneof [+      Mem   <$> Q.arbitrary+    , Reg   <$> Q.arbitrary+    , Ptr   <$> Q.arbitrary+    , Imm   <$> Q.arbitrary+    , Jump  <$> Q.arbitrary+    , Const <$> Q.arbitrary ]++instance Q.Arbitrary Prefix where+  arbitrary = Q.oneof (+    (Seg <$> Q.arbitrary)+    : map pure [Rex, OperSize, AddrSize, Lock, Rep, RepE, RepNE] )++instance Q.Arbitrary Instruction where+  arbitrary = do+    np <- Q.choose (0,3)+    no <- Q.choose (0,3)+    Inst <$> Q.vectorOf np Q.arbitrary+         <*> Q.arbitrary+         <*> Q.vectorOf no Q.arbitrary++instance Q.Arbitrary Config where+  arbitrary = Config <$> Q.arbitrary <*> Q.arbitrary+                     <*> Q.arbitrary <*> Q.arbitrary
hdis86.cabal view
@@ -1,5 +1,5 @@ name:                hdis86-version:             0.1+version:             0.2 license:             BSD3 license-file:        LICENSE synopsis:            Interface to the udis86 disassembler for x86 and x86-64 / AMD64@@ -8,7 +8,7 @@ maintainer:          Keegan McAllister <mcallister.keegan@gmail.com> homepage:            https://github.com/kmcallister/hdis86 build-type:          Simple-cabal-version:       >=1.2+cabal-version:       >=1.6 description:   @hdis86@ is an interface to the @udis86@ disassembler, which decodes machine   code for 16-, 32-, and 64-bit x86  and x86-64 / AMD64 processors.  @hdis86@@@ -30,7 +30,9 @@   .   Many users can simply import @Hdis86@.   .-+  The @Incremental@ module provides disassembly of lazy @ByteString@s, and a+  function for building other incremental operations.+  .   By default, @hdis86@ will statically link a built-in copy of @udis86-1.7@,   which is provided by its author under a similar BSD license.  See inside the   tarball for more information.  If you have @udis86@ installed on your system,@@ -40,10 +42,21 @@   This code is available on GitHub at <https://github.com/kmcallister/hdis86>.   .   The @udis86@ project website is located at <http://udis86.sourceforge.net/>.+  .+  New in version 0.2:+  .+    * Disassembly of lazy @ByteString@s+  .+    * A function for building other incremental operations+  .+    * Record selectors on @Instruction@+  .+    * QuickCheck @Arbitrary@ instances for @Instruction@ and related types  extra-source-files:     tools/hdcli.hs   , tools/gen_opcode.sh+  , tools/test_lazy_bytestring.hs   , udis86-1.7/libudis86/decode.h   , udis86-1.7/libudis86/input.h   , udis86-1.7/libudis86/syn.h@@ -65,15 +78,18 @@       Hdis86     , Hdis86.Types     , Hdis86.Pure+    , Hdis86.Incremental     , Hdis86.IO     , Hdis86.C-    , Hdis86.Internal.Opcode     , Hdis86.Internal.Map+  other-modules:+      Hdis86.Internal.Opcode   ghc-options:       -Wall   build-depends:       base       >= 3 && < 5     , containers >= 0.3     , bytestring >= 0.9+    , QuickCheck >= 2.4    if flag(external-udis86)     extra-libraries: udis86@@ -87,3 +103,16 @@       udis86-1.7/libudis86/syn.c       udis86-1.7/libudis86/syn-att.c       udis86-1.7/libudis86/syn-intel.c++  other-extensions:+      DeriveDataTypeable+    , RecordWildCards+    , NamedFieldPuns+    , ViewPatterns+    , ForeignFunctionInterface+    , EmptyDataDecls+    , ScopedTypeVariables++source-repository head+    type:     git+    location: git://github.com/kmcallister/hdis86.git
+ tools/test_lazy_bytestring.hs view
@@ -0,0 +1,69 @@+module Main(main) where++{- Check correctness and speed of lazy ByteString disassembly+   versus strict ByteString disassembly.++   $ for p in mwc-random clock groom; do cabal install $p; done+   $ ghc --make -O test_lazy_bytestring.hs+   $ ./test_lazy_bytestring+-}++import qualified Data.ByteString      as BS+import qualified Data.ByteString.Lazy as BL+import qualified Data.Vector.Unboxed  as V+import qualified System.Random.MWC    as R+import qualified System.Posix.Clock   as C++import Control.Monad+import Control.Exception ( evaluate )+import System.Exit+import System.IO+import Text.Groom+import Text.Printf++import Hdis86+import Hdis86.Incremental++splitBS :: R.GenIO -> BS.ByteString -> IO [BS.ByteString]+splitBS _   bs | BS.length bs < 2 = return [bs]+splitBS gen bs = do+    die <- R.uniformR (0,4) gen+    if die == (0 :: Int)+        then return [bs]+        else do+            i <- R.uniformR (1, BS.length bs - 1) gen+            let (x,y) = BS.splitAt i bs+            fmap (x:) (splitBS gen y)++time :: IO () -> IO Double+time x = do+    let get = C.getTime C.Monotonic+    C.TimeSpec s0 ns0 <- get+    x+    C.TimeSpec s1 ns1 <- get+    let scale = ((10 ** (-9)) *) . fromIntegral+    return (fromIntegral (s1 - s0) + scale (ns1 - ns0))++main :: IO ()+main = R.withSystemRandom $ \gen -> forever $ do+    size   <- R.uniformR (2^16, 2^21) gen+    vec    <- R.uniformVector gen size+    let bs =  BS.pack (V.toList vec)+    chunks <- splitBS gen bs+    let bl =  BL.fromChunks chunks+    _      <- evaluate (BS.length bs)+    _      <- evaluate (BL.length bl)++    let ms = disassembleMetadata amd64 bs+        ml = disassembleLazy     amd64 bl+        force = time . mapM_ (evaluate . inOpcode . mdInst)+    ts <- force $ disassembleMetadata amd64 bs+    tl <- force $ disassembleLazy     amd64 bl++    _  <- printf "%4d kB in %3d chunks: %5.2f s / %5.2f s = %6.2f%% speed\n"+        (size `div` 1024) (length chunks) ts tl (100 * ts / tl)+    when (ms /= ml) $ do+        hPutStr stderr "FAIL; check out.*\n"+        writeFile "out.strict" $ groom ms+        writeFile "out.lazy"   $ groom ml+        exitWith (ExitFailure 1)