bytepatch-0.4.1: src/Binrep/Type/Assembly/Assemble.hs
{-# LANGUAGE OverloadedStrings #-}
module Binrep.Type.Assembly.Assemble where
import Binrep.Type.Assembly
import Heystone qualified as Keystone
import System.IO.Unsafe ( unsafeDupablePerformIO )
import Control.Monad.IO.Class
import Data.ByteString qualified as BS
import Data.Text.Short qualified as Text.Short
import Data.Text qualified as Text
import Data.List qualified as List
class Assemble arch where
assemble :: [AsmInstr arch] -> Either String (MachineCode arch)
assemble1
:: forall arch. Assemble arch
=> AsmInstr arch -> Either String (MachineCode arch)
assemble1 inst = assemble [inst]
instance Assemble 'ArmV8ThumbLE where
assemble =
fmap MachineCode
. unsafeDupablePerformIO
. assemble' Keystone.ArchArm modeFlags
. prepInstrs
where
modeFlags =
[Keystone.ModeV8, Keystone.ModeThumb, Keystone.ModeLittleEndian]
-- | TODO This is stupid because the assembler takes a '[String]'. Great for
-- end-user, poor for performance. I want the option to give it a
-- 'BS.ByteString' that I've already prepared (as is the interface).
prepInstrs :: forall arch. [AsmInstr arch] -> [String]
prepInstrs =
List.singleton
. Text.unpack
. Text.intercalate (Text.pack ";")
. map (Text.Short.toText . getAsmInstr)
-- | Ideally, the assembler takes a raw 'BS.ByteString'. A
-- 'BS.Short.ShortByteString' isn't appropriate, because it could be quite
-- large. But this way, this function is basically "compose a bunch of short
-- bytestrings into one big one".
prepInstrs' :: forall arch. [AsmInstr arch] -> BS.ByteString
prepInstrs' =
BS.intercalate ";"
. map (Text.Short.toByteString . getAsmInstr)
assemble'
:: MonadIO m
=> Keystone.Architecture -> [Keystone.Mode]
-> [String]
-> m (Either String BS.ByteString)
assemble' arch modes instrs = do
let as' = Keystone.open arch modes
liftIO (Keystone.runAssembler as') >>= \case
Left e -> err $ "failed to obtain assembler: "<>show e
Right as -> do
let out' = Keystone.assemble as instrs Nothing
-- TODO have to inspect engine to find error. probably say if x=1 OK, if
-- x>1 weird error, if x<1 check errno->strerror
liftIO (Keystone.runAssembler out') >>= \case
Left e -> err $ "error while assembling: "<>show e
Right (mc, _count) -> pure $ Right mc
where err = pure . Left