diff --git a/quasar-wayland.cabal b/quasar-wayland.cabal
index 027da6714da4a0f0a9772415fd1601cf3dddba1c..82a0113e133aea2bbed7ca2748c55f825a125a85 100644
--- a/quasar-wayland.cabal
+++ b/quasar-wayland.cabal
@@ -84,12 +84,16 @@ library
     Quasar.Wayland.Protocol.TH
   other-modules:
     Quasar.Wayland.Protocol.Core
+    Quasar.Wayland.Utils.InlineC
+    Quasar.Wayland.Utils.SharedMemory
   build-depends:
     base >=4.7 && <5,
     binary,
     bytestring,
+    containers,
     exceptions,
     filepath,
+    inline-c,
     mtl,
     network,
     quasar,
diff --git a/src/Quasar/Wayland/Utils/InlineC.hs b/src/Quasar/Wayland/Utils/InlineC.hs
new file mode 100644
index 0000000000000000000000000000000000000000..73d1d8d39d94f74ef1a3e63eb3746b712b7aae1d
--- /dev/null
+++ b/src/Quasar/Wayland/Utils/InlineC.hs
@@ -0,0 +1,33 @@
+module Quasar.Wayland.Utils.InlineC (
+  ctx
+) where
+
+import Data.Map.Strict as Map
+import Language.C.Inline.Context
+import Language.C.Types
+import Language.Haskell.TH
+import Quasar.Prelude
+import System.Posix.Types (COff(..))
+
+ctx :: Context
+ctx = baseCtx <> extraTypesCtx
+
+emptyCtx :: Context
+emptyCtx = Context {
+  ctxTypesTable = mempty,
+  ctxAntiQuoters = mempty,
+  ctxOutput = mempty,
+  ctxForeignSrcLang = Nothing,
+  ctxEnableCpp = False
+}
+
+extraTypesCtx :: Context
+extraTypesCtx =
+  emptyCtx {
+    ctxTypesTable = Map.fromList types
+  }
+
+types :: [(TypeSpecifier, TypeQ)]
+types = [
+  (TypeName "off_t", [t|COff|])
+  ]
diff --git a/src/Quasar/Wayland/Utils/SharedMemory.hs b/src/Quasar/Wayland/Utils/SharedMemory.hs
new file mode 100644
index 0000000000000000000000000000000000000000..4de2aab8be377a8383e6b645be60190782abaa6e
--- /dev/null
+++ b/src/Quasar/Wayland/Utils/SharedMemory.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+module Quasar.Wayland.Utils.SharedMemory (
+  allocateShmFd,
+) where
+
+import Foreign.C.Error
+import Language.C.Inline qualified as C
+import Language.C.Inline.Unsafe qualified as CU
+import Quasar.Prelude
+import Quasar.Wayland.Utils.InlineC
+import System.Posix.Types (COff(..), Fd(Fd))
+
+C.context ctx
+
+C.verbatim "#define _GNU_SOURCE"
+C.include "<unistd.h>"
+C.include "<sys/mman.h>"
+
+allocateShmFd :: COff -> IO Fd
+allocateShmFd size = Fd <$> throwErrnoIfMinus1 "allocateShmFd"
+  [CU.block|
+    int {
+      int fd = memfd_create("shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+      if (fd < 0) {
+              return fd;
+      }
+
+      if (ftruncate(fd, $(off_t size)) < 0) {
+              close(fd);
+              return -1;
+      }
+      return fd;
+    }
+  |]