1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: GPL-3.0-only45module Main (main) where67import Control.Exception (Exception, throwIO)8import Data.List (find)9import Language.QBE (globalFuncs, parse)10import Language.QBE.Backend.Store qualified as ST11import Language.QBE.Backend.Tracer qualified as T12import Language.QBE.Simulator.Concolic.State (mkEnv)13import Language.QBE.Simulator.Explorer (defSolver, explore, logSolver, newEngine)14import Language.QBE.Simulator.Memory qualified as MEM15import Language.QBE.Types qualified as QBE16import Options.Applicative qualified as OPT17import System.IO (IOMode (WriteMode), withFile)18import Text.Parsec (ParseError)1920data Opts = Opts21 { optMemStart :: MEM.Address,22 optMemSize :: MEM.Size,23 optLog :: Maybe FilePath,24 optSeed :: Maybe Int,25 optQBEFile :: FilePath26 }2728optsParser :: OPT.Parser Opts29optsParser =30 Opts31 <$> OPT.option32 OPT.auto33 ( OPT.long "memory-start"34 <> OPT.short 'm'35 <> OPT.value 0x036 )37 <*> OPT.option38 OPT.auto39 ( OPT.long "memory-size"40 <> OPT.short 's'41 <> OPT.value (1024 * 1024) -- 1 MB RAM42 <> OPT.help "Size of the memory region"43 )44 <*> OPT.optional45 ( OPT.strOption46 ( OPT.long "dump-smt2"47 <> OPT.short 'd'48 <> OPT.metavar "FILE"49 <> OPT.help "Output queries as an SMT-LIB file"50 )51 )52 <*> OPT.optional53 ( OPT.option54 OPT.auto55 ( OPT.long "random-seed"56 <> OPT.short 'r'57 <> OPT.help "Initial seed to for the random number generator"58 )59 )60 <*> OPT.argument OPT.str (OPT.metavar "FILE")6162------------------------------------------------------------------------6364data ExecError65 = SyntaxError ParseError66 | UnknownFunction QBE.GlobalIdent67 deriving (Show)6869instance Exception ExecError7071exploreFile :: Opts -> IO [(ST.Assign, T.ExecTrace)]72exploreFile opts = do73 let filePath = optQBEFile opts74 content <- readFile filePath75 prog <- case parse filePath content of76 Right rt -> pure rt77 Left err -> throwIO $ SyntaxError err7879 let funcs = globalFuncs prog80 func <- case find (\f -> QBE.fName f == entryFunc) funcs of81 Just x -> pure x82 Nothing -> throwIO $ UnknownFunction entryFunc8384 env <- mkEnv prog (optMemStart opts) (optMemSize opts) (optSeed opts)85 case optLog opts of86 Just fn -> withFile fn WriteMode (exploreWithHandle env func)87 Nothing -> do88 engine <- newEngine <$> defSolver89 explore engine env func params90 where91 params :: [(String, QBE.ExtType)]92 params = []9394 entryFunc :: QBE.GlobalIdent95 entryFunc = QBE.GlobalIdent "main"9697 exploreWithHandle env func handle = do98 engine <- newEngine <$> logSolver handle99 explore engine env func params100101main :: IO ()102main = do103 args <- OPT.execParser cmd104 paths <- exploreFile args105 putStrLn $ "Amount of paths: " ++ show (length paths)106 where107 cmd :: OPT.ParserInfo Opts108 cmd =109 OPT.info110 (optsParser OPT.<**> OPT.helper)111 ( OPT.fullDesc112 <> OPT.progDesc "Symbolic execution of programs in the QBE intermediate language"113 )