{-# LANGUAGE DuplicateRecordFields #-}

module Binja.BinaryView
  ( Binja.BinaryView.load,
    Binja.BinaryView.close,
    Binja.BinaryView.save,
    Binja.BinaryView.hasFunctions,
    Binja.BinaryView.hasSymbols,
    Binja.BinaryView.hasDataVariables,
    Binja.BinaryView.updateAnalysis,
    Binja.BinaryView.updateAnalysisAndWait,
    Binja.BinaryView.abortAnalysis,
    Binja.BinaryView.functions,
    Binja.BinaryView.entryFunctions,
    Binja.BinaryView.entryFunction,
    Binja.BinaryView.functionsContaining,
    Binja.BinaryView.functionsAt,
    Binja.BinaryView.functionsByName,
    Binja.BinaryView.segments,
    Binja.BinaryView.sections,
    Binja.BinaryView.symbols,
    Binja.BinaryView.symbolsByName,
    Binja.BinaryView.dataVars,
    Binja.BinaryView.strings,
    Binja.BinaryView.read,
    Binja.BinaryView.symbolAt,
  )
where

import Binja.FFI
import Binja.Function
import Binja.Plugin
import Binja.Symbol
import Binja.Types.Core
import Binja.Utils
import Data.Bits (testBit)
import qualified Data.ByteString as BS
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Text.Encoding.Error as TEE

-- It accepts a Haskell String for the filename and options and a Bool for updateAnalysis.
-- Here, we pass nullFunPtr and nullPtr for the progress callback and context.
loadFilename ::
  -- | Filename to load
  String ->
  -- | updateAnalysis flag
  Bool ->
  -- | Options string (JSON)
  String ->
  IO BNBinaryViewPtr
loadFilename :: String -> Bool -> String -> IO BNBinaryViewPtr
loadFilename String
filename' Bool
updateAnalysisB String
options =
  String -> (Ptr CChar -> IO BNBinaryViewPtr) -> IO BNBinaryViewPtr
forall a. String -> (Ptr CChar -> IO a) -> IO a
withCString String
filename' ((Ptr CChar -> IO BNBinaryViewPtr) -> IO BNBinaryViewPtr)
-> (Ptr CChar -> IO BNBinaryViewPtr) -> IO BNBinaryViewPtr
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
cFilename ->
    String -> (Ptr CChar -> IO BNBinaryViewPtr) -> IO BNBinaryViewPtr
forall a. String -> (Ptr CChar -> IO a) -> IO a
withCString String
options ((Ptr CChar -> IO BNBinaryViewPtr) -> IO BNBinaryViewPtr)
-> (Ptr CChar -> IO BNBinaryViewPtr) -> IO BNBinaryViewPtr
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
cOptions ->
      Ptr CChar
-> CBool
-> Ptr CChar
-> BNProgressFunctionPtr
-> Ptr ()
-> IO BNBinaryViewPtr
c_BNLoadFilename
        Ptr CChar
cFilename
        (if Bool
updateAnalysisB then Word8 -> CBool
CBool Word8
1 else Word8 -> CBool
CBool Word8
0)
        Ptr CChar
cOptions
        BNProgressFunctionPtr
forall a. FunPtr a
nullFunPtr -- no progress callback
        Ptr ()
forall a. Ptr a
nullPtr -- no progress context

load :: String -> String -> IO BNBinaryViewPtr
load :: String -> String -> IO BNBinaryViewPtr
load String
filename' String
options = do
  _ <- Bool -> IO Bool
initPlugins Bool
False
  viewPtr' <- loadFilename filename' True options
  if viewPtr' == nullPtr
    then error $ "Failed to load binary view on file: " ++ filename'
    else pure viewPtr'

close :: BNBinaryViewPtr -> IO ()
close :: BNBinaryViewPtr -> IO ()
close BNBinaryViewPtr
view' = do
  fileMetaDataPtr <- BNBinaryViewPtr -> IO BNFileMetaDataPtr
getFileForView BNBinaryViewPtr
view'
  closeFile fileMetaDataPtr

hasFunctions :: BNBinaryViewPtr -> IO Bool
hasFunctions :: BNBinaryViewPtr -> IO Bool
hasFunctions = (CBool -> Bool) -> IO CBool -> IO Bool
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CBool -> Bool
Binja.Utils.toBool (IO CBool -> IO Bool)
-> (BNBinaryViewPtr -> IO CBool) -> BNBinaryViewPtr -> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BNBinaryViewPtr -> IO CBool
c_BNHasFunctions

hasSymbols :: BNBinaryViewPtr -> Bool
hasSymbols :: BNBinaryViewPtr -> Bool
hasSymbols = CBool -> Bool
Binja.Utils.toBool (CBool -> Bool)
-> (BNBinaryViewPtr -> CBool) -> BNBinaryViewPtr -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BNBinaryViewPtr -> CBool
c_BNHasSymbols

hasDataVariables :: BNBinaryViewPtr -> Bool
hasDataVariables :: BNBinaryViewPtr -> Bool
hasDataVariables = CBool -> Bool
Binja.Utils.toBool (CBool -> Bool)
-> (BNBinaryViewPtr -> CBool) -> BNBinaryViewPtr -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BNBinaryViewPtr -> CBool
c_BNHasDataVariables

-- saves the original binary file to the (filename)
-- absolute filepath along with any modifications
save :: BNBinaryViewPtr -> String -> IO Bool
save :: BNBinaryViewPtr -> String -> IO Bool
save BNBinaryViewPtr
view String
filename' =
  String -> (Ptr CChar -> IO Bool) -> IO Bool
forall a. String -> (Ptr CChar -> IO a) -> IO a
withCString String
filename' ((Ptr CChar -> IO Bool) -> IO Bool)
-> (Ptr CChar -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \Ptr CChar
cFilename -> do
    result <- BNBinaryViewPtr -> Ptr CChar -> IO CBool
c_BNSaveToFilename BNBinaryViewPtr
view Ptr CChar
cFilename
    pure (Binja.Utils.toBool result)

updateAnalysis :: BNBinaryViewPtr -> IO ()
updateAnalysis :: BNBinaryViewPtr -> IO ()
updateAnalysis = BNBinaryViewPtr -> IO ()
c_BNUpdateAnalysis

-- updateAnalysisAndWait
-- starts the analysis process and blocks until it is complete. This method should be
-- used when it is necessary to ensure that analysis results are fully updated before
-- proceeding with further operations.
-- If an update is already in progress, this method chains a new update request to ensure that the update processes
-- all pending changes before the call was made.
updateAnalysisAndWait :: BNBinaryViewPtr -> IO ()
updateAnalysisAndWait :: BNBinaryViewPtr -> IO ()
updateAnalysisAndWait = BNBinaryViewPtr -> IO ()
c_BNUpdateAnalysisAndWait

abortAnalysis :: BNBinaryViewPtr -> IO ()
abortAnalysis :: BNBinaryViewPtr -> IO ()
abortAnalysis = BNBinaryViewPtr -> IO ()
c_BNAbortAnalysis

getFunctionList :: BNBinaryViewPtr -> IO FunctionList
getFunctionList :: BNBinaryViewPtr -> IO FunctionList
getFunctionList BNBinaryViewPtr
view =
  (Ptr CSize -> IO FunctionList) -> IO FunctionList
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO FunctionList) -> IO FunctionList)
-> (Ptr CSize -> IO FunctionList) -> IO FunctionList
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    rawPtr <- BNBinaryViewPtr -> Ptr CSize -> IO (Ptr BNFunctionPtr)
c_BNGetAnalysisFunctionList BNBinaryViewPtr
view Ptr CSize
countPtr
    count' <- fromIntegral <$> peek countPtr
    xs <-
      if rawPtr == nullPtr || count' == 0
        then pure []
        else peekArray count' rawPtr
    arrPtr <- newForeignPtr rawPtr (c_BNFreeFunctionList rawPtr $ fromIntegral count')
    pure
      FunctionList
        { flArrayPtr = arrPtr,
          flCount = count',
          flList = xs,
          flViewPtr = view
        }

functions :: BNBinaryViewPtr -> IO [BNFunctionPtr]
functions :: BNBinaryViewPtr -> IO [BNFunctionPtr]
functions = (FunctionList -> [BNFunctionPtr])
-> IO FunctionList -> IO [BNFunctionPtr]
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FunctionList -> [BNFunctionPtr]
flList (IO FunctionList -> IO [BNFunctionPtr])
-> (BNBinaryViewPtr -> IO FunctionList)
-> BNBinaryViewPtr
-> IO [BNFunctionPtr]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BNBinaryViewPtr -> IO FunctionList
getFunctionList

getSegmentList :: BNBinaryViewPtr -> IO [BNSegmentPtr]
getSegmentList :: BNBinaryViewPtr -> IO [BNSegmentPtr]
getSegmentList BNBinaryViewPtr
view =
  (Ptr CSize -> IO [BNSegmentPtr]) -> IO [BNSegmentPtr]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO [BNSegmentPtr]) -> IO [BNSegmentPtr])
-> (Ptr CSize -> IO [BNSegmentPtr]) -> IO [BNSegmentPtr]
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    rawPtr <- BNBinaryViewPtr -> Ptr CSize -> IO (Ptr BNSegmentPtr)
c_BNGetSegments BNBinaryViewPtr
view Ptr CSize
countPtr
    count' <- fromIntegral <$> peek countPtr
    segmentHandles <- peekArray count' rawPtr
    c_BNFreeSegmentList rawPtr $ fromIntegral count'
    pure segmentHandles

segments :: BNBinaryViewPtr -> IO [Segment]
segments :: BNBinaryViewPtr -> IO [Segment]
segments BNBinaryViewPtr
view = do
  segmentHandles <- BNBinaryViewPtr -> IO [BNSegmentPtr]
getSegmentList BNBinaryViewPtr
view
  mapM createSegment segmentHandles
  where
    segmentExecutableBit :: Int
segmentExecutableBit = Int
0
    segmentWritableBit :: Int
segmentWritableBit = Int
1
    segmentReadableBit :: Int
segmentReadableBit = Int
2
    createSegment :: BNSegmentPtr -> IO Segment
    createSegment :: BNSegmentPtr -> IO Segment
createSegment BNSegmentPtr
segmentHandle = do
      start' <- BNSegmentPtr -> IO Word64
c_BNSegmentGetStart BNSegmentPtr
segmentHandle
      end' <- c_BNSegmentGetEnd segmentHandle
      flags' <- c_BNSegmentGetFlags segmentHandle
      -- 1,2,4 from SegmentFlag
      let executable' = Word32 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word32
flags' Int
segmentExecutableBit
      let writable' = Word32 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word32
flags' Int
segmentWritableBit
      let readable' = Word32 -> Int -> Bool
forall a. Bits a => a -> Int -> Bool
testBit Word32
flags' Int
segmentReadableBit
      pure
        Segment
          { handle = segmentHandle,
            start = start',
            end = end',
            executable = executable',
            writable = writable',
            readable = readable'
          }

getSectionList :: BNBinaryViewPtr -> IO [BNSectionPtr]
getSectionList :: BNBinaryViewPtr -> IO [BNSectionPtr]
getSectionList BNBinaryViewPtr
view = do
  (Ptr CSize -> IO [BNSectionPtr]) -> IO [BNSectionPtr]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO [BNSectionPtr]) -> IO [BNSectionPtr])
-> (Ptr CSize -> IO [BNSectionPtr]) -> IO [BNSectionPtr]
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    rawPtr <- BNBinaryViewPtr -> Ptr CSize -> IO (Ptr BNSectionPtr)
c_BNGetSections BNBinaryViewPtr
view Ptr CSize
countPtr
    if rawPtr == nullPtr
      then error "Binja.BinaryView.getSectionList.BNGetSections returned null"
      else do
        count' <- fromIntegral <$> peek countPtr
        sectionHandles <- peekArray count' rawPtr
        c_BNFreeSectionList rawPtr $ fromIntegral count'
        pure sectionHandles

sections :: BNBinaryViewPtr -> IO [Section]
sections :: BNBinaryViewPtr -> IO [Section]
sections BNBinaryViewPtr
view = do
  sectionHandles <- BNBinaryViewPtr -> IO [BNSectionPtr]
getSectionList BNBinaryViewPtr
view
  mapM createSection sectionHandles
  where
    createSection :: BNSectionPtr -> IO Section
    createSection :: BNSectionPtr -> IO Section
createSection BNSectionPtr
sectionPtr = do
      nameCString <- BNSectionPtr -> IO (Ptr CChar)
c_BNSectionGetName BNSectionPtr
sectionPtr
      name' <- peekCString nameCString
      start' <- c_BNSectionGetStart sectionPtr
      end' <- c_BNSectionGetEnd sectionPtr
      semantics' <- toEnum <$> fromIntegral <$> c_BNSectionGetSemantics sectionPtr
      align' <- c_BNSectionGetAlign sectionPtr
      pure
        Section
          { handle = sectionPtr,
            name = name',
            start = start',
            end = end',
            semantics = semantics',
            align = align'
          }

dataVars :: BNBinaryViewPtr -> IO [DataVariable]
dataVars :: BNBinaryViewPtr -> IO [DataVariable]
dataVars BNBinaryViewPtr
view =
  (Ptr CSize -> IO [DataVariable]) -> IO [DataVariable]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO [DataVariable]) -> IO [DataVariable])
-> (Ptr CSize -> IO [DataVariable]) -> IO [DataVariable]
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    rawPtr <- BNBinaryViewPtr -> Ptr CSize -> IO (Ptr DataVariable)
c_BNGetDataVariables BNBinaryViewPtr
view Ptr CSize
countPtr
    count' <- fromIntegral <$> peek countPtr
    xs <-
      if rawPtr == nullPtr || count' == 0
        then pure []
        else peekArray count' rawPtr
    when (rawPtr /= nullPtr && count' /= 0) $
      c_BNFreeDataVariables rawPtr (fromIntegral count')
    pure xs

getEntryFunctionList :: BNBinaryViewPtr -> IO FunctionList
getEntryFunctionList :: BNBinaryViewPtr -> IO FunctionList
getEntryFunctionList BNBinaryViewPtr
view =
  (Ptr CSize -> IO FunctionList) -> IO FunctionList
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO FunctionList) -> IO FunctionList)
-> (Ptr CSize -> IO FunctionList) -> IO FunctionList
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    rawPtr <- BNBinaryViewPtr -> Ptr CSize -> IO (Ptr BNFunctionPtr)
c_BNGetAllEntryFunctions BNBinaryViewPtr
view Ptr CSize
countPtr
    count' <- fromIntegral <$> peek countPtr
    xs <-
      if rawPtr == nullPtr || count' == 0
        then pure []
        else peekArray count' rawPtr
    arrPtr <- newForeignPtr rawPtr (c_BNFreeFunctionList rawPtr $ fromIntegral count')
    pure
      FunctionList
        { flArrayPtr = arrPtr,
          flCount = count',
          flList = xs,
          flViewPtr = view
        }

entryFunctions :: BNBinaryViewPtr -> IO [BNFunctionPtr]
entryFunctions :: BNBinaryViewPtr -> IO [BNFunctionPtr]
entryFunctions = (FunctionList -> [BNFunctionPtr])
-> IO FunctionList -> IO [BNFunctionPtr]
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FunctionList -> [BNFunctionPtr]
flList (IO FunctionList -> IO [BNFunctionPtr])
-> (BNBinaryViewPtr -> IO FunctionList)
-> BNBinaryViewPtr
-> IO [BNFunctionPtr]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BNBinaryViewPtr -> IO FunctionList
getEntryFunctionList

entryFunction :: BNBinaryViewPtr -> IO (Maybe BNFunctionPtr)
entryFunction :: BNBinaryViewPtr -> IO (Maybe BNFunctionPtr)
entryFunction BNBinaryViewPtr
view = do
  rawFunc <- BNBinaryViewPtr -> IO BNFunctionPtr
c_BNGetAnalysisEntryPoint BNBinaryViewPtr
view
  if rawFunc == nullPtr
    then pure Nothing
    else pure $ Just rawFunc

functionsByName :: BNBinaryViewPtr -> String -> IO [BNFunctionPtr]
functionsByName :: BNBinaryViewPtr -> String -> IO [BNFunctionPtr]
functionsByName BNBinaryViewPtr
view String
name' = do
  syms <- BNBinaryViewPtr -> String -> IO [Symbol]
symbolsByName BNBinaryViewPtr
view String
name'
  let funcSyms = (Symbol -> Bool) -> [Symbol] -> [Symbol]
forall a. (a -> Bool) -> [a] -> [a]
filter Symbol -> Bool
Binja.Symbol.isFunction [Symbol]
syms
  xs <- mapM (\Symbol {address :: Symbol -> Word64
address = Word64
addr} -> BNBinaryViewPtr -> Word64 -> IO [BNFunctionPtr]
functionsAt BNBinaryViewPtr
view Word64
addr) funcSyms
  pure $ concat xs

symbols :: BNBinaryViewPtr -> IO [Symbol]
symbols :: BNBinaryViewPtr -> IO [Symbol]
symbols BNBinaryViewPtr
view =
  (Ptr CSize -> IO [Symbol]) -> IO [Symbol]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO [Symbol]) -> IO [Symbol])
-> (Ptr CSize -> IO [Symbol]) -> IO [Symbol]
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    rawPtr <- BNBinaryViewPtr
-> Ptr CSize -> BNNameSpacePtr -> IO (Ptr BNSymbolPtr)
c_BNGetSymbols BNBinaryViewPtr
view Ptr CSize
countPtr BNNameSpacePtr
forall a. Ptr a
nullPtr
    count' <- fromIntegral <$> peek countPtr
    xs <-
      if rawPtr == nullPtr || count' == 0
        then pure []
        else peekArray count' rawPtr
    _ <- newForeignPtr rawPtr (c_BNFreeSymbolList rawPtr $ fromIntegral count')
    mapM Binja.Symbol.create xs

symbolsByName :: BNBinaryViewPtr -> String -> IO [Symbol]
symbolsByName :: BNBinaryViewPtr -> String -> IO [Symbol]
symbolsByName BNBinaryViewPtr
view String
name' = do
  syms <- BNBinaryViewPtr -> IO [Symbol]
Binja.BinaryView.symbols BNBinaryViewPtr
view
  pure $ filter (\Symbol {name :: Symbol -> String
name = String
n} -> String
n String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
name') syms

functionsContaining :: BNBinaryViewPtr -> Word64 -> IO [BNFunctionPtr]
functionsContaining :: BNBinaryViewPtr -> Word64 -> IO [BNFunctionPtr]
functionsContaining BNBinaryViewPtr
view Word64
addr =
  (Ptr CSize -> IO [BNFunctionPtr]) -> IO [BNFunctionPtr]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO [BNFunctionPtr]) -> IO [BNFunctionPtr])
-> (Ptr CSize -> IO [BNFunctionPtr]) -> IO [BNFunctionPtr]
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    arrPtr <- BNBinaryViewPtr -> Word64 -> Ptr CSize -> IO (Ptr BNFunctionPtr)
c_BNGetAnalysisFunctionsContainingAddress BNBinaryViewPtr
view Word64
addr Ptr CSize
countPtr
    count' <- peek countPtr
    if arrPtr == nullPtr || count' == 0
      then pure []
      else do
        refs <- peekArray (fromIntegral count') (castPtr arrPtr :: Ptr BNFunctionPtr)
        c_BNFreeFunctionList arrPtr count'
        pure refs

functionsAt :: BNBinaryViewPtr -> Word64 -> IO [BNFunctionPtr]
functionsAt :: BNBinaryViewPtr -> Word64 -> IO [BNFunctionPtr]
functionsAt BNBinaryViewPtr
view Word64
addr =
  (Ptr CSize -> IO [BNFunctionPtr]) -> IO [BNFunctionPtr]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO [BNFunctionPtr]) -> IO [BNFunctionPtr])
-> (Ptr CSize -> IO [BNFunctionPtr]) -> IO [BNFunctionPtr]
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    arrPtr <- BNBinaryViewPtr -> Word64 -> Ptr CSize -> IO (Ptr BNFunctionPtr)
c_BNGetAnalysisFunctionsForAddress BNBinaryViewPtr
view Word64
addr Ptr CSize
countPtr
    count' <- peek countPtr
    if arrPtr == nullPtr || count' == 0
      then pure []
      else do
        refs <- peekArray (fromIntegral count') (castPtr arrPtr :: Ptr BNFunctionPtr)
        c_BNFreeFunctionList arrPtr count'
        pure refs

strings :: BNBinaryViewPtr -> IO [Maybe String]
strings :: BNBinaryViewPtr -> IO [Maybe String]
strings BNBinaryViewPtr
view =
  (Ptr CSize -> IO [Maybe String]) -> IO [Maybe String]
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CSize -> IO [Maybe String]) -> IO [Maybe String])
-> (Ptr CSize -> IO [Maybe String]) -> IO [Maybe String]
forall a b. (a -> b) -> a -> b
$ \Ptr CSize
countPtr -> do
    arrPtr <- BNBinaryViewPtr -> Ptr CSize -> IO (Ptr BNStringRefPtr)
c_BNGetStrings BNBinaryViewPtr
view Ptr CSize
countPtr
    count' <- fromIntegral <$> peek countPtr
    if arrPtr == nullPtr || count' == 0
      then pure []
      else do
        refs <- peekArray count' (castPtr arrPtr :: Ptr BNStringRef)
        c_BNFreeStringReferenceList arrPtr
        forM refs $ \(BNStringRef BNStringType
t Word64
s CSize
l) -> do
          mbs <- BNBinaryViewPtr -> Word64 -> CSize -> IO (Maybe ByteString)
Binja.BinaryView.read BNBinaryViewPtr
view Word64
s CSize
l
          pure $ fmap (T.unpack . decodeByType t) mbs

decodeByType :: BNStringType -> BS.ByteString -> T.Text
decodeByType :: BNStringType -> ByteString -> Text
decodeByType BNStringType
ty' = ByteString -> Text
go
  where
    go :: ByteString -> Text
go = case BNStringType
ty' of
      BNStringType
AsciiString -> ByteString -> Text
TE.decodeLatin1
      BNStringType
Utf8String -> OnDecodeError -> ByteString -> Text
TE.decodeUtf8With OnDecodeError
TEE.lenientDecode
      BNStringType
Utf16String -> ByteString -> Text
decodeUtf16Auto
      BNStringType
Utf32String -> ByteString -> Text
decodeUtf32Auto

    -- UTF-16: detect BOM; otherwise assume LE
    decodeUtf16Auto :: ByteString -> Text
decodeUtf16Auto ByteString
bs
      | [Word8] -> ByteString -> Bool
hasPrefix [Word8
0xFF, Word8
0xFE] ByteString
bs = OnDecodeError -> ByteString -> Text
TE.decodeUtf16LEWith OnDecodeError
TEE.lenientDecode (Int -> ByteString -> ByteString
BS.drop Int
2 ByteString
bs)
      | [Word8] -> ByteString -> Bool
hasPrefix [Word8
0xFE, Word8
0xFF] ByteString
bs = OnDecodeError -> ByteString -> Text
TE.decodeUtf16BEWith OnDecodeError
TEE.lenientDecode (Int -> ByteString -> ByteString
BS.drop Int
2 ByteString
bs)
      | Bool
otherwise = OnDecodeError -> ByteString -> Text
TE.decodeUtf16LEWith OnDecodeError
TEE.lenientDecode ByteString
bs

    -- UTF-32: detect BOM; otherwise assume LE
    decodeUtf32Auto :: ByteString -> Text
decodeUtf32Auto ByteString
bs
      | [Word8] -> ByteString -> Bool
hasPrefix [Word8
0xFF, Word8
0xFE, Word8
0x00, Word8
0x00] ByteString
bs = OnDecodeError -> ByteString -> Text
TE.decodeUtf32LEWith OnDecodeError
TEE.lenientDecode (Int -> ByteString -> ByteString
BS.drop Int
4 ByteString
bs)
      | [Word8] -> ByteString -> Bool
hasPrefix [Word8
0x00, Word8
0x00, Word8
0xFE, Word8
0xFF] ByteString
bs = OnDecodeError -> ByteString -> Text
TE.decodeUtf32BEWith OnDecodeError
TEE.lenientDecode (Int -> ByteString -> ByteString
BS.drop Int
4 ByteString
bs)
      | Bool
otherwise = OnDecodeError -> ByteString -> Text
TE.decodeUtf32LEWith OnDecodeError
TEE.lenientDecode ByteString
bs

    hasPrefix :: [Word8] -> BS.ByteString -> Bool
    hasPrefix :: [Word8] -> ByteString -> Bool
hasPrefix [Word8]
pfx ByteString
bs = [Word8] -> ByteString
BS.pack [Word8]
pfx ByteString -> ByteString -> Bool
`BS.isPrefixOf` ByteString
bs

read :: BNBinaryViewPtr -> Word64 -> CSize -> IO (Maybe BS.ByteString)
read :: BNBinaryViewPtr -> Word64 -> CSize -> IO (Maybe ByteString)
read BNBinaryViewPtr
view Word64
addr CSize
len = do
  if Word64
addr Word64 -> Word64 -> Bool
forall a. Ord a => a -> a -> Bool
< Word64
0 Bool -> Bool -> Bool
|| CSize
len CSize -> CSize -> Bool
forall a. Ord a => a -> a -> Bool
< CSize
0
    then
      String -> IO (Maybe ByteString)
forall a. HasCallStack => String -> a
error (String -> IO (Maybe ByteString))
-> String -> IO (Maybe ByteString)
forall a b. (a -> b) -> a -> b
$
        String
"Address and length must be positive, address: "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ Word64 -> String
forall a. Show a => a -> String
show Word64
addr
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" , length: "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ CSize -> String
forall a. Show a => a -> String
show CSize
len
    else do
      dataBuffer <- BNBinaryViewPtr -> Word64 -> CSize -> IO BNDataBufferPtr
c_BNReadViewBuffer BNBinaryViewPtr
view Word64
addr CSize
len
      if dataBuffer == nullPtr
        then pure Nothing
        else do
          dataPtr <- c_BNGetDataBufferContents dataBuffer
          if dataPtr == nullPtr
            then pure Nothing
            else do
              bs <- BS.packCStringLen (dataPtr, fromIntegral len)
              c_BNFreeDataBuffer dataBuffer
              pure $ Just bs

symbolAt :: BNBinaryViewPtr -> Word64 -> IO (Maybe Binja.Types.Core.Symbol)
symbolAt :: BNBinaryViewPtr -> Word64 -> IO (Maybe Symbol)
symbolAt BNBinaryViewPtr
view Word64
addr = do
  symbolPtr <- BNBinaryViewPtr -> Word64 -> BNNameSpacePtr -> IO BNSymbolPtr
c_BNGetSymbolByAddress BNBinaryViewPtr
view Word64
addr BNNameSpacePtr
forall a. Ptr a
nullPtr
  if symbolPtr == nullPtr
    then do
      funcs <- functionsAt view addr
      case funcs of
        [] -> Maybe Symbol -> IO (Maybe Symbol)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe Symbol
forall a. Maybe a
Nothing
        (BNFunctionPtr
hd : [BNFunctionPtr]
_) -> do
          sym <- BNFunctionPtr -> IO Symbol
Binja.Function.symbol BNFunctionPtr
hd
          pure $ Just sym
    else do
      sym <- Binja.Symbol.create symbolPtr
      pure $ Just sym