diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@
 
 This project adheres to the [Haskell Package Versioning Policy](https://pvp.haskell.org/).
 
+## [0.4.0.1] - 2023-06-27
+
+- Build with GHC 9.4
+
 ## [0.4.0.0] - 2022-12-08
 
 - Add `writeMutableByteArray`
diff --git a/posix-api.cabal b/posix-api.cabal
--- a/posix-api.cabal
+++ b/posix-api.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.2
 name: posix-api
-version: 0.4.0.0
+version: 0.4.0.1
 synopsis: posix bindings
 description:
   This library provides a very thin wrapper around POSIX APIs. It can be
@@ -103,14 +103,15 @@
     Posix.Socket.Types
     Assertion
   build-depends:
-    , base >=4.11.1 && <5
+    , base >=4.17.1 && <5
     , byte-order >= 0.1.2 && <0.2
-    , byteslice >= 0.1.3 && <0.3
+    , byteslice >= 0.2.10 && <0.3
     , primitive >= 0.7 && <0.8
     , primitive-addr >= 0.1 && <0.2
     , primitive-offset >= 0.2 && <0.3
-    , primitive-unlifted >= 0.1 && <0.2
+    , primitive-unlifted >= 2.0 && <2.1
     , run-st >= 0.1.1 && <0.2
+    , text-short >=0.1.5
   hs-source-dirs: src
   if flag(assertions)
     hs-source-dirs: src-assertions
diff --git a/src/Foreign/C/String/Managed.hs b/src/Foreign/C/String/Managed.hs
--- a/src/Foreign/C/String/Managed.hs
+++ b/src/Foreign/C/String/Managed.hs
@@ -14,6 +14,7 @@
   , unterminated
   , fromBytes
   , fromLatinString
+  , fromShortText
   , pinnedFromBytes
   , pin
   , touch
@@ -25,6 +26,7 @@
 import Data.Bytes.Types (Bytes(Bytes))
 import Data.Char (ord)
 import Data.Primitive (ByteArray(..),MutableByteArray)
+import Data.Text.Short (ShortText)
 import Data.Word (Word8)
 import Foreign.C.String (CString)
 import Foreign.Ptr (castPtr)
@@ -32,7 +34,9 @@
 import GHC.IO (IO(IO))
 
 import qualified Data.Bytes as Bytes
+import qualified Data.Bytes.Text.Utf8 as Utf8
 import qualified Data.Primitive as PM
+import qualified Data.Text.Short as TS
 import qualified GHC.Exts as Exts
 
 -- | An unsliced byte sequence with @NUL@ as the final byte.
@@ -75,6 +79,9 @@
 
 unterminated :: ManagedCString -> Bytes
 unterminated (ManagedCString x) = Bytes x 0 (PM.sizeofByteArray x - 1)
+
+fromShortText :: ShortText -> ManagedCString
+fromShortText !ts = fromBytes (Utf8.fromShortText ts)
 
 -- | Copies the slice, appending a @NUL@ byte to the end.
 fromBytes :: Bytes -> ManagedCString
diff --git a/src/Linux/Socket.hs b/src/Linux/Socket.hs
--- a/src/Linux/Socket.hs
+++ b/src/Linux/Socket.hs
@@ -51,16 +51,18 @@
 import Data.Bits ((.|.))
 import Data.Primitive.Addr (Addr(..),plusAddr,nullAddr)
 import Data.Primitive (MutableByteArray(..),ByteArray(..),MutablePrimArray(..))
-import Data.Primitive.Unlifted.Array (MutableUnliftedArray(..),UnliftedArray)
+import Data.Primitive.Unlifted.Array (MutableUnliftedArray,UnliftedArray)
+import Data.Primitive.Unlifted.Array (MutableUnliftedArray_(MutableUnliftedArray))
 import Data.Word (Word8)
 import Foreign.C.Error (Errno,getErrno)
 import Foreign.C.Types (CInt(..),CSize(..),CUInt(..))
-import GHC.Exts (Ptr(..),RealWorld,MutableByteArray#,Addr#,MutableArrayArray#,Int(I#))
+import GHC.Exts (Ptr(..),RealWorld,MutableArray#,MutableByteArray#,Addr#,Int(I#))
 import GHC.Exts (shrinkMutableByteArray#,touch#,nullAddr#)
 import GHC.IO (IO(..))
 import Linux.Socket.Types (SocketFlags(..))
 import Posix.Socket (Type(..),MessageFlags(..),Message(Receive),SocketAddress(..))
 import System.Posix.Types (Fd(..),CSsize(..))
+import Data.Primitive.Unlifted.Array.Primops (MutableUnliftedArray#(MutableUnliftedArray#))
 
 import qualified Data.Primitive as PM
 import qualified Data.Primitive.Unlifted.Array as PM
@@ -88,7 +90,7 @@
        Fd
     -> MutableByteArray# RealWorld -- lengths
     -> MutableByteArray# RealWorld -- sockaddrs
-    -> MutableArrayArray# RealWorld -- buffers
+    -> MutableArray# RealWorld (MutableByteArray# RealWorld) -- buffers
     -> CUInt -- Length of msghdr array
     -> MessageFlags 'Receive
     -> IO CInt
@@ -97,7 +99,7 @@
   c_unsafe_recvmmsg_sockaddr_discard ::
        Fd
     -> MutableByteArray# RealWorld -- lengths
-    -> MutableArrayArray# RealWorld -- buffers
+    -> MutableArray# RealWorld (MutableByteArray# RealWorld) -- buffers
     -> CUInt -- Length of msghdr array
     -> MessageFlags 'Receive
     -> IO CInt
@@ -137,7 +139,8 @@
   -> MessageFlags 'Receive -- ^ Flags
   -> IO (Either Errno (CUInt,UnliftedArray ByteArray))
 uninterruptibleReceiveMultipleMessageA !s !msgSize !msgCount !flags = do
-  bufs <- PM.unsafeNewUnliftedArray (cuintToInt msgCount)
+  placeholder <- PM.newByteArray 0
+  bufs <- PM.newUnliftedArray (cuintToInt msgCount) placeholder
   mmsghdrsBuf <- PM.newPinnedByteArray (cuintToInt msgCount * cintToInt LST.sizeofMultipleMessageHeader)
   iovecsBuf <- PM.newPinnedByteArray (cuintToInt msgCount * cintToInt S.sizeofIOVector)
   let !mmsghdrsAddr@(Addr mmsghdrsAddr#) = ptrToAddr (PM.mutableByteArrayContents mmsghdrsBuf)
@@ -190,7 +193,8 @@
   -> MessageFlags 'Receive -- ^ Flags
   -> IO (Either Errno (CInt,ByteArray,CUInt,UnliftedArray ByteArray))
 uninterruptibleReceiveMultipleMessageB !s !expSockAddrSize !msgSize !msgCount !flags = do
-  bufs <- PM.unsafeNewUnliftedArray (cuintToInt msgCount)
+  placeholder <- PM.newByteArray 0
+  bufs <- PM.newUnliftedArray (cuintToInt msgCount) placeholder
   mmsghdrsBuf <- PM.newPinnedByteArray (cuintToInt msgCount * cintToInt LST.sizeofMultipleMessageHeader)
   iovecsBuf <- PM.newPinnedByteArray (cuintToInt msgCount * cintToInt S.sizeofIOVector)
   sockaddrsBuf <- PM.newPinnedByteArray (cuintToInt msgCount * cintToInt expSockAddrSize)
@@ -226,7 +230,7 @@
   -> CUInt -- ^ Maximum number of datagrams to receive, length of buffers
   -> MessageFlags 'Receive -- ^ Flags
   -> IO (Either Errno CInt)
-uninterruptibleReceiveMultipleMessageC !s (MutablePrimArray lens) (MutablePrimArray addrs) (PM.MutableUnliftedArray payloads) !msgCount !flags =
+uninterruptibleReceiveMultipleMessageC !s (MutablePrimArray lens) (MutablePrimArray addrs) (MutableUnliftedArray (MutableUnliftedArray# payloads)) !msgCount !flags =
   c_unsafe_recvmmsg_sockaddr_in s lens addrs payloads msgCount flags >>= errorsFromInt
 
 -- | All three buffer arguments need to have the same length (in elements, not bytes).
@@ -238,7 +242,7 @@
   -> CUInt -- ^ Maximum number of datagrams to receive, length of buffers
   -> MessageFlags 'Receive -- ^ Flags
   -> IO (Either Errno CInt)
-uninterruptibleReceiveMultipleMessageD !s (MutablePrimArray lens) (PM.MutableUnliftedArray payloads) !msgCount !flags =
+uninterruptibleReceiveMultipleMessageD !s (MutablePrimArray lens) (MutableUnliftedArray (MutableUnliftedArray# payloads)) !msgCount !flags =
   c_unsafe_recvmmsg_sockaddr_discard s lens payloads msgCount flags >>= errorsFromInt
 
 -- This sets up an array of mmsghdr. Each msghdr has msg_iov set to
@@ -263,7 +267,7 @@
 -- be an array of iovec with a single element. One giant buffer with
 -- space for all of the @sockaddr@s is used.
 initializeMultipleMessageHeadersWithSockAddr ::
-     MutableUnliftedArray RealWorld (MutableByteArray RealWorld) -- buffers
+     MutableUnliftedArray RealWorld (MutableByteArray RealWorld)
   -> Addr -- array of iovec
   -> Addr -- array of message headers
   -> Addr -- array of sockaddrs
@@ -382,14 +386,14 @@
   then pure (Right r)
   else fmap Left getErrno
 
-touchMutableUnliftedArray :: MutableUnliftedArray RealWorld a -> IO ()
-touchMutableUnliftedArray (MutableUnliftedArray x) = touchMutableUnliftedArray# x
-
 touchMutableByteArray :: MutableByteArray RealWorld -> IO ()
 touchMutableByteArray (MutableByteArray x) = touchMutableByteArray# x
 
-touchMutableUnliftedArray# :: MutableArrayArray# RealWorld -> IO ()
-touchMutableUnliftedArray# x = IO $ \s -> case touch# x s of s' -> (# s', () #)
-
 touchMutableByteArray# :: MutableByteArray# RealWorld -> IO ()
 touchMutableByteArray# x = IO $ \s -> case touch# x s of s' -> (# s', () #)
+
+touchMutableUnliftedArray :: MutableUnliftedArray RealWorld a -> IO ()
+touchMutableUnliftedArray (MutableUnliftedArray x) = touchMutableUnliftedArray# x
+
+touchMutableUnliftedArray# :: MutableUnliftedArray# RealWorld a -> IO ()
+touchMutableUnliftedArray# x = IO $ \s -> case touch# x s of s' -> (# s', () #)
diff --git a/src/Posix/Socket.hs b/src/Posix/Socket.hs
--- a/src/Posix/Socket.hs
+++ b/src/Posix/Socket.hs
@@ -206,7 +206,9 @@
 import GHC.IO (IO(..))
 import Data.Primitive.Addr (Addr(..),plusAddr,nullAddr)
 import Data.Primitive (MutablePrimArray(..),MutableByteArray(..),ByteArray(..))
-import Data.Primitive.Unlifted.Array (MutableUnliftedArray(..),UnliftedArray(..))
+import Data.Primitive.Unlifted.Array (MutableUnliftedArray,UnliftedArray,UnliftedArray_(UnliftedArray))
+import Data.Primitive.Unlifted.Array (MutableUnliftedArray_(MutableUnliftedArray))
+import Data.Primitive.Unlifted.Array.Primops (UnliftedArray#(UnliftedArray#),MutableUnliftedArray#)
 import Data.Primitive.ByteArray.Offset (MutableByteArrayOffset(..))
 import Data.Primitive.PrimArray.Offset (MutablePrimArrayOffset(..))
 import Data.Word (Word8,Word16,Word32,byteSwap16,byteSwap32)
@@ -217,7 +219,7 @@
 import Foreign.Ptr (nullPtr)
 import GHC.Exts (Ptr,RealWorld,ByteArray#,MutableByteArray#)
 import GHC.Exts (Addr#,TYPE)
-import GHC.Exts (ArrayArray#,MutableArrayArray#,Int(I#))
+import GHC.Exts (Int(I#))
 import GHC.Exts (shrinkMutableByteArray#,touch#)
 import Posix.Socket.Types (Family(..),Protocol(..),Type(..),SocketAddress(..))
 import Posix.Socket.Types (SocketAddressInternet(..))
@@ -389,7 +391,7 @@
   c_unsafe_sendmsg_b :: Fd -> MutableByteArray# RealWorld -> Int -> CSize -> Addr# -> CSize -> MessageFlags 'Send -> IO CSsize
 
 foreign import ccall unsafe "HaskellPosix.h sendmsg_bytearrays"
-  c_unsafe_sendmsg_bytearrays :: Fd -> ArrayArray# -> Int -> Int -> Int -> MessageFlags 'Send -> IO CSsize
+  c_unsafe_sendmsg_bytearrays :: Fd -> UnliftedArray# ByteArray# -> Int -> Int -> Int -> MessageFlags 'Send -> IO CSsize
 
 foreign import ccall safe "sys/uio.h writev"
   c_safe_writev :: Fd -> MutableByteArray# RealWorld -> CInt -> IO CSsize
@@ -1417,10 +1419,10 @@
 touchMutableByteArray :: MutableByteArray RealWorld -> IO ()
 touchMutableByteArray (MutableByteArray x) = touchMutableByteArray# x
 
-touchMutableUnliftedArray# :: MutableArrayArray# RealWorld -> IO ()
+touchMutableUnliftedArray# :: MutableUnliftedArray# RealWorld a -> IO ()
 touchMutableUnliftedArray# x = IO $ \s -> case touch# x s of s' -> (# s', () #)
 
-touchUnliftedArray# :: ArrayArray# -> IO ()
+touchUnliftedArray# :: UnliftedArray# a -> IO ()
 touchUnliftedArray# x = IO $ \s -> case touch# x s of s' -> (# s', () #)
 
 touchMutableByteArray# :: MutableByteArray# RealWorld -> IO ()
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -55,7 +55,7 @@
 
 testSocketsA :: Assertion
 testSocketsA = do
-  (a,b) <- demand =<< S.uninterruptibleSocketPair S.unix S.datagram S.defaultProtocol
+  (a,b) <- demand =<< S.uninterruptibleSocketPair S.Unix S.datagram S.defaultProtocol
   m <- PM.newEmptyMVar
   _ <- forkIO $ S.receiveByteArray b 5 mempty >>= PM.putMVar m
   bytesSent <- demand =<< S.sendByteArray a sample 0 5 mempty
@@ -68,7 +68,7 @@
   let limit = 10
       wordSz = PM.sizeOf (undefined :: Int)
       cwordSz = fromIntegral wordSz :: CSize
-  (a,b) <- demand =<< S.uninterruptibleSocketPair S.unix S.datagram S.defaultProtocol
+  (a,b) <- demand =<< S.uninterruptibleSocketPair S.Unix S.datagram S.defaultProtocol
   lock <- PM.newEmptyMVar
   let go1 !(ix :: Int) !(n :: Int) = if (ix < limit)
         then do
@@ -95,7 +95,7 @@
 
 testSocketsC :: Assertion
 testSocketsC = do
-  (a,b) <- demand =<< S.uninterruptibleSocketPair S.unix S.datagram S.defaultProtocol
+  (a,b) <- demand =<< S.uninterruptibleSocketPair S.Unix S.datagram S.defaultProtocol
   m <- PM.newEmptyMVar
   _ <- forkIO $ S.receiveByteArray a 5 mempty >>= PM.putMVar m
   bytesSent <- demand =<< S.sendByteArray b sample 0 5 mempty
@@ -105,7 +105,7 @@
 
 testSocketsD :: Assertion
 testSocketsD = do
-  (a,b) <- demand =<< S.uninterruptibleSocketPair S.unix S.datagram S.defaultProtocol
+  (a,b) <- demand =<< S.uninterruptibleSocketPair S.Unix S.datagram S.defaultProtocol
   _ <- forkIO $ do
     bytesSent <- demand =<< S.sendByteArray b sample 0 5 mempty
     when (bytesSent /= 5) (fail "testSocketsD: bytesSent was wrong")
@@ -114,7 +114,7 @@
 
 testSocketsE :: Assertion
 testSocketsE = do
-  (a,b) <- demand =<< S.uninterruptibleSocketPair S.unix S.datagram S.defaultProtocol
+  (a,b) <- demand =<< S.uninterruptibleSocketPair S.Unix S.datagram S.defaultProtocol
   _ <- forkIO $ do
     threadWaitWrite b
     bytesSent <- demand =<< S.uninterruptibleSendByteArray b sample 0 5 mempty
@@ -125,14 +125,14 @@
 
 testSocketsF :: Assertion
 testSocketsF = do
-  a <- demand =<< S.uninterruptibleSocket S.internet S.datagram S.defaultProtocol
+  a <- demand =<< S.uninterruptibleSocket S.Internet S.datagram S.defaultProtocol
   demand =<< S.uninterruptibleBind a (S.encodeSocketAddressInternet (S.SocketAddressInternet {S.port = 0, S.address = localhost}))
   (expectedSzA,expectedSockAddrA) <- demand =<< S.uninterruptibleGetSocketName a 128
   when (expectedSzA > 128) (fail "testSocketsF: bad socket address size for socket A")
   portA <- case S.decodeSocketAddressInternet expectedSockAddrA of
     Nothing -> fail "testSocketsF: not a sockaddr_in"
     Just (S.SocketAddressInternet {S.port}) -> pure port
-  b <- demand =<< S.uninterruptibleSocket S.internet S.datagram S.defaultProtocol
+  b <- demand =<< S.uninterruptibleSocket S.Internet S.datagram S.defaultProtocol
   demand =<< S.uninterruptibleBind b (S.encodeSocketAddressInternet (S.SocketAddressInternet {S.port = 0, S.address = localhost}))
   threadWaitWrite b
   bytesSent <- demand =<< S.uninterruptibleSendToByteArray b sample 0 5 mempty (S.encodeSocketAddressInternet (S.SocketAddressInternet {S.port = portA, S.address = localhost}))
@@ -145,7 +145,7 @@
 
 testSocketsG :: Assertion
 testSocketsG = do
-  (a,b) <- demand =<< S.uninterruptibleSocketPair S.unix S.datagram S.defaultProtocol
+  (a,b) <- demand =<< S.uninterruptibleSocketPair S.Unix S.datagram S.defaultProtocol
   _ <- forkIO $ do
     bytesSent <- demand =<< S.writeVector b
       ( E.fromList
@@ -159,7 +159,7 @@
 
 testLinuxSocketsA :: Assertion
 testLinuxSocketsA = do
-  (a,b) <- demand =<< S.uninterruptibleSocketPair S.unix S.datagram S.defaultProtocol
+  (a,b) <- demand =<< S.uninterruptibleSocketPair S.Unix S.datagram S.defaultProtocol
   threadWaitWrite b
   bytesSent1 <- demand =<< S.uninterruptibleSendByteArray b sample 0 5 mempty
   threadWaitWrite b
@@ -172,7 +172,7 @@
 
 testLinuxSocketsB :: Assertion
 testLinuxSocketsB = do
-  a <- demand =<< S.uninterruptibleSocket S.internet S.datagram S.defaultProtocol
+  a <- demand =<< S.uninterruptibleSocket S.Internet S.datagram S.defaultProtocol
   demand =<< S.uninterruptibleBind a (S.encodeSocketAddressInternet (S.SocketAddressInternet {S.port = 0, S.address = localhost}))
   (expectedSzA,expectedSockAddrA) <- demand
     =<< S.uninterruptibleGetSocketName a 128
@@ -181,7 +181,7 @@
   portA <- case S.decodeSocketAddressInternet expectedSockAddrA of
     Nothing -> fail "testLinixSocketsB: not a sockaddr_in"
     Just (S.SocketAddressInternet {S.port}) -> pure port
-  b <- demand =<< S.uninterruptibleSocket S.internet S.datagram S.defaultProtocol
+  b <- demand =<< S.uninterruptibleSocket S.Internet S.datagram S.defaultProtocol
   demand =<< S.uninterruptibleBind b
     (S.encodeSocketAddressInternet $ S.SocketAddressInternet
       { S.port = 0
@@ -208,7 +208,7 @@
 
 testLinuxSocketsC :: Assertion
 testLinuxSocketsC = do
-  a <- demand =<< S.uninterruptibleSocket S.internet S.datagram S.defaultProtocol
+  a <- demand =<< S.uninterruptibleSocket S.Internet S.datagram S.defaultProtocol
   demand =<< S.uninterruptibleBind a (S.encodeSocketAddressInternet (S.SocketAddressInternet {S.port = 0, S.address = localhost}))
   (expectedSzA,expectedSockAddrA) <- demand
     =<< S.uninterruptibleGetSocketName a 128
@@ -217,7 +217,7 @@
   portA <- case S.decodeSocketAddressInternet expectedSockAddrA of
     Nothing -> fail "testLinuxSocketsC: not a sockaddr_in"
     Just (S.SocketAddressInternet {S.port}) -> pure port
-  b <- demand =<< S.uninterruptibleSocket S.internet S.datagram S.defaultProtocol
+  b <- demand =<< S.uninterruptibleSocket S.Internet S.datagram S.defaultProtocol
   demand =<< S.uninterruptibleBind b
     (S.encodeSocketAddressInternet $ S.SocketAddressInternet
       { S.port = 0
@@ -262,7 +262,7 @@
 -- that has happened.
 testLinuxEpollA :: Assertion
 testLinuxEpollA = do
-  (a,b) <- demand =<< S.uninterruptibleSocketPair S.unix S.datagram S.defaultProtocol
+  (a,b) <- demand =<< S.uninterruptibleSocketPair S.Unix S.datagram S.defaultProtocol
   epfd <- demand =<< Epoll.uninterruptibleCreate 1
   reg <- PM.newPrimArray 1
   PM.writePrimArray reg 0 $ Epoll.Event
