1-- SPDX-FileCopyrightText: 2026 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: GPL-3.0-only45-- | This module provides utility functions for interfacing with C code.6module Language.QBE.Simulator.ABI.C7 ( charRepr,8 readString,9 readArray,10 writeBuffer,11 writeVAList,12 )13where1415import Control.Monad (foldM_)16import Data.Word (Word64)17import Language.QBE.Simulator.Expression qualified as E18import Language.QBE.Simulator.Memory qualified as MEM19import Language.QBE.Simulator.State (Simulator, readMemory, writeMemory)20import Language.QBE.Types qualified as QBE2122-- | Representation of a @char@ in C.23charRepr :: QBE.LoadType24charRepr = QBE.LSubWord QBE.SignedByte2526-- | Read a null-terminated C string from memory at the given 'MEM.Address'.27-- The return value is a list of 8-bit values.28readString :: (Simulator m v) => MEM.Address -> m [v]29readString addr = go addr []30 where31 go a acc = do32 byte <- readMemory charRepr a33 if E.toWord64 byte == 034 then pure acc35 else go (a + 1) (acc ++ [byte])36{-# INLINE readString #-}3738-- | Read an array of a fixed size, starting from a given 'MEM.Address'.39-- The array is assumed to only store elements of the given 'QBE.LoadType'.40readArray :: (Simulator m v) => MEM.Address -> QBE.LoadType -> Word64 -> m [v]41readArray addr elemTy numElems =42 mapM (\off -> readMemory elemTy (addr + off)) [0 .. numElems - 1]43{-# INLINE readArray #-}4445-- | Write a list of values to a memory buffer located at the given 'MEM.Address'.46writeBuffer :: (Simulator m v) => MEM.Address -> [v] -> m ()47writeBuffer = foldM_ go48 where49 go lastAddr el = do50 let elTy = E.getType el51 elSize = fromIntegral $ QBE.extTypeByteSize elTy52 writeMemory lastAddr elTy el53 pure $ lastAddr + elSize54{-# INLINE writeBuffer #-}5556-- | This writes the @overflow_arg_area@ of a @va_list@ in memory, as defined57-- in the x86-64-ABI psABI.58writeVAList :: (Simulator m v) => MEM.Address -> MEM.Address -> m ()59writeVAList ptr overflowPtr = do60 -- This is the layout of the `va_list` from the x86 System V ABI. It is61 -- implemented in a way that it signals that all vaargs are on the stack.62 --63 -- See also <https://github.com/klee/klee/blob/v3.3/lib/Core/Executor.cpp#L1962-L1981>.64 writeMemory ptr (QBE.Base QBE.Word) $65 E.fromLit (QBE.Base QBE.Word) 48 -- gp_offset66 writeMemory (ptr + 4) (QBE.Base QBE.Word) $67 E.fromLit (QBE.Base QBE.Word) 176 -- fp_offset68 writeMemory (ptr + 8) (QBE.Base QBE.Long) $69 E.fromLit (QBE.Base QBE.Long) overflowPtr -- overflow_arg_area70 writeMemory (ptr + 16) (QBE.Base QBE.Long) $71 E.fromLit (QBE.Base QBE.Long) 0 -- reg_save_area72{-# INLINE writeVAList #-}