diff --git a/System/Posix/Directory.hsc b/System/Posix/Directory.hsc
--- a/System/Posix/Directory.hsc
+++ b/System/Posix/Directory.hsc
@@ -49,7 +49,6 @@
   ) where
 
 import Data.Maybe
-import System.IO.Error
 import System.Posix.Error
 import System.Posix.Types
 import Foreign
@@ -157,18 +156,16 @@
 --   the current working directory to @dir@.
 changeWorkingDirectory :: FilePath -> IO ()
 changeWorkingDirectory path =
-  modifyIOError (`ioeSetFileName` path) $
-    withFilePath path $ \s ->
-       throwErrnoIfMinus1Retry_ "changeWorkingDirectory" (c_chdir s)
+  withFilePath path $ \s ->
+     throwErrnoPathIfMinus1Retry_ "changeWorkingDirectory" path (c_chdir s)
 
 foreign import ccall unsafe "chdir"
    c_chdir :: CString -> IO CInt
 
 removeDirectory :: FilePath -> IO ()
 removeDirectory path =
-  modifyIOError (`ioeSetFileName` path) $
-    withFilePath path $ \s ->
-       throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
+  withFilePath path $ \s ->
+     throwErrnoPathIfMinus1Retry_ "removeDirectory" path (c_rmdir s)
 
 foreign import ccall unsafe "rmdir"
    c_rmdir :: CString -> IO CInt
diff --git a/System/Posix/Directory/ByteString.hsc b/System/Posix/Directory/ByteString.hsc
--- a/System/Posix/Directory/ByteString.hsc
+++ b/System/Posix/Directory/ByteString.hsc
@@ -49,7 +49,6 @@
   ) where
 
 import Data.Maybe
-import System.IO.Error
 import System.Posix.Types
 import Foreign
 import Foreign.C
@@ -158,18 +157,16 @@
 --   the current working directory to @dir@.
 changeWorkingDirectory :: RawFilePath -> IO ()
 changeWorkingDirectory path =
-  modifyIOError (`ioeSetFileName` (BC.unpack path)) $
-    withFilePath path $ \s ->
-       throwErrnoIfMinus1Retry_ "changeWorkingDirectory" (c_chdir s)
+  withFilePath path $ \s ->
+     throwErrnoPathIfMinus1Retry_ "changeWorkingDirectory" path (c_chdir s)
 
 foreign import ccall unsafe "chdir"
    c_chdir :: CString -> IO CInt
 
 removeDirectory :: RawFilePath -> IO ()
 removeDirectory path =
-  modifyIOError (`ioeSetFileName` BC.unpack path) $
-    withFilePath path $ \s ->
-       throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
+  withFilePath path $ \s ->
+     throwErrnoPathIfMinus1Retry_ "removeDirectory" path (c_rmdir s)
 
 foreign import ccall unsafe "rmdir"
    c_rmdir :: CString -> IO CInt
diff --git a/System/Posix/Directory/Common.hsc b/System/Posix/Directory/Common.hsc
--- a/System/Posix/Directory/Common.hsc
+++ b/System/Posix/Directory/Common.hsc
@@ -36,6 +36,11 @@
 import Foreign hiding (void)
 import Foreign.C
 
+#if !defined(HAVE_FCHDIR)
+import System.IO.Error ( ioeSetLocation )
+import GHC.IO.Exception ( unsupportedOperation )
+#endif
+
 newtype DirStream = DirStream (Ptr CDir)
 
 data {-# CTYPE "DIR" #-} CDir
@@ -111,9 +116,19 @@
   c_telldir :: Ptr CDir -> IO CLong
 #endif
 
+#if defined(HAVE_FCHDIR)
+
 changeWorkingDirectoryFd :: Fd -> IO ()
 changeWorkingDirectoryFd (Fd fd) =
   throwErrnoIfMinus1Retry_ "changeWorkingDirectoryFd" (c_fchdir fd)
 
 foreign import ccall unsafe "fchdir"
   c_fchdir :: CInt -> IO CInt
+
+#else
+
+{-# WARNING changeWorkingDirectoryFd "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_FCHDIR@)" #-}
+changeWorkingDirectoryFd :: Fd -> IO ()
+changeWorkingDirectoryFd _ = ioError (ioeSetLocation unsupportedOperation "changeWorkingDirectoryFd")
+
+#endif // HAVE_FCHDIR
diff --git a/System/Posix/Directory/PosixPath.hsc b/System/Posix/Directory/PosixPath.hsc
--- a/System/Posix/Directory/PosixPath.hsc
+++ b/System/Posix/Directory/PosixPath.hsc
@@ -46,15 +46,11 @@
    changeWorkingDirectoryFd,
   ) where
 
-import System.IO.Error
 import System.Posix.Types
 import Foreign
 import Foreign.C
 
 import System.OsPath.Types
-import GHC.IO.Encoding.UTF8 ( mkUTF8 )
-import GHC.IO.Encoding.Failure ( CodingFailureMode(..) )
-import System.OsPath.Posix
 import System.Posix.Directory hiding (createDirectory, openDirStream, readDirStream, getWorkingDirectory, changeWorkingDirectory, removeDirectory)
 import qualified System.Posix.Directory.Common as Common
 import System.Posix.PosixPath.FilePath
@@ -145,22 +141,16 @@
 --   the current working directory to @dir@.
 changeWorkingDirectory :: PosixPath -> IO ()
 changeWorkingDirectory path =
-  modifyIOError (`ioeSetFileName` (_toStr path)) $
-    withFilePath path $ \s ->
-       throwErrnoIfMinus1Retry_ "changeWorkingDirectory" (c_chdir s)
+  withFilePath path $ \s ->
+     throwErrnoPathIfMinus1Retry_ "changeWorkingDirectory" path (c_chdir s)
 
 foreign import ccall unsafe "chdir"
    c_chdir :: CString -> IO CInt
 
 removeDirectory :: PosixPath -> IO ()
 removeDirectory path =
-  modifyIOError (`ioeSetFileName` _toStr path) $
-    withFilePath path $ \s ->
-       throwErrnoIfMinus1Retry_ "removeDirectory" (c_rmdir s)
+  withFilePath path $ \s ->
+     throwErrnoPathIfMinus1Retry_ "removeDirectory" path (c_rmdir s)
 
 foreign import ccall unsafe "rmdir"
    c_rmdir :: CString -> IO CInt
-
-_toStr :: PosixPath -> String
-_toStr fp = either (error . show) id $ decodeWith (mkUTF8 TransliterateCodingFailure) fp
-
diff --git a/System/Posix/DynamicLinker/Module.hsc b/System/Posix/DynamicLinker/Module.hsc
--- a/System/Posix/DynamicLinker/Module.hsc
+++ b/System/Posix/DynamicLinker/Module.hsc
@@ -56,6 +56,7 @@
 
 #include "HsUnix.h"
 
+import Prelude hiding (head, tail)
 import System.Posix.DynamicLinker
 import System.Posix.DynamicLinker.Common
 import Foreign.Ptr      ( Ptr, nullPtr, FunPtr )
@@ -101,9 +102,10 @@
 withModule mdir file flags p = do
   let modPath = case mdir of
                   Nothing -> file
-                  Just dir  -> dir ++ if ((head (reverse dir)) == '/')
-                                       then file
-                                       else ('/':file)
+                  Just dir  -> dir ++ case unsnoc dir of
+                    Just (_, '/') -> file
+                    Just{}        -> '/' : file
+                    Nothing       -> error "System.Posix.DynamicLinker.Module.withModule: directory should not be Just \"\", pass Nothing instead"
   modu <- moduleOpen modPath flags
   result <- p modu
   moduleClose modu
@@ -115,3 +117,10 @@
             -> (Module -> IO a)
             -> IO ()
 withModule_ dir file flags p = withModule dir file flags p >>= \ _ -> return ()
+
+-- Dual to 'Data.List.uncons'.
+unsnoc :: [a] -> Maybe ([a], a)
+unsnoc = foldr go Nothing
+  where
+    go x Nothing = Just ([], x)
+    go x (Just (xs, lst)) = Just (x : xs, lst)
diff --git a/System/Posix/DynamicLinker/Prim.hsc b/System/Posix/DynamicLinker/Prim.hsc
--- a/System/Posix/DynamicLinker/Prim.hsc
+++ b/System/Posix/DynamicLinker/Prim.hsc
@@ -1,3 +1,4 @@
+{-# LANGUAGE CApiFFI #-}
 {-# LANGUAGE Trustworthy #-}
 {-# OPTIONS_GHC -Wno-trustworthy-safe #-}
 
@@ -84,10 +85,17 @@
   | RTLD_LOCAL
     deriving (Show, Read)
 
-foreign import ccall unsafe "dlopen" c_dlopen :: CString -> CInt -> IO (Ptr ())
+#if defined(HAVE_DLFCN_H)
+foreign import capi safe "dlfcn.h dlopen" c_dlopen :: CString -> CInt -> IO (Ptr ())
+foreign import capi unsafe "dlfcn.h dlsym"  c_dlsym  :: Ptr () -> CString -> IO (FunPtr a)
+foreign import capi unsafe "dlfcn.h dlerror" c_dlerror :: IO CString
+foreign import capi safe "dlfcn.h dlclose" c_dlclose :: (Ptr ()) -> IO CInt
+#else
+foreign import ccall safe "dlopen" c_dlopen :: CString -> CInt -> IO (Ptr ())
 foreign import ccall unsafe "dlsym"  c_dlsym  :: Ptr () -> CString -> IO (FunPtr a)
 foreign import ccall unsafe "dlerror" c_dlerror :: IO CString
-foreign import ccall unsafe "dlclose" c_dlclose :: (Ptr ()) -> IO CInt
+foreign import ccall safe "dlclose" c_dlclose :: (Ptr ()) -> IO CInt
+#endif // HAVE_DLFCN_H
 
 packRTLDFlags :: [RTLDFlags] -> CInt
 packRTLDFlags flags = foldl (\ s f -> (packRTLDFlag f) .|. s) 0 flags
diff --git a/System/Posix/Files.hsc b/System/Posix/Files.hsc
--- a/System/Posix/Files.hsc
+++ b/System/Posix/Files.hsc
@@ -108,7 +108,7 @@
 
 import Data.Time.Clock.POSIX (POSIXTime)
 
-#if !defined(HAVE_MKNOD)
+#if !defined(HAVE_MKNOD) || !defined(HAVE_CHOWN)
 import System.IO.Error ( ioeSetLocation )
 import GHC.IO.Exception ( unsupportedOperation )
 #endif
@@ -323,6 +323,8 @@
 -- -----------------------------------------------------------------------------
 -- chown()
 
+#if defined(HAVE_CHOWN)
+
 -- | @setOwnerAndGroup path uid gid@ changes the owner and group of @path@ to
 -- @uid@ and @gid@, respectively.
 --
@@ -336,6 +338,14 @@
 
 foreign import ccall unsafe "chown"
   c_chown :: CString -> CUid -> CGid -> IO CInt
+
+#else
+
+{-# WARNING setOwnerAndGroup "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_CHOWN@)" #-}
+setOwnerAndGroup :: FilePath -> UserID -> GroupID -> IO ()
+setOwnerAndGroup _ _ _ = ioError (ioeSetLocation unsupportedOperation "setOwnerAndGroup")
+
+#endif // HAVE_CHOWN
 
 #if HAVE_LCHOWN
 -- | Acts as 'setOwnerAndGroup' but does not follow symlinks (and thus
diff --git a/System/Posix/Files/ByteString.hsc b/System/Posix/Files/ByteString.hsc
--- a/System/Posix/Files/ByteString.hsc
+++ b/System/Posix/Files/ByteString.hsc
@@ -109,7 +109,7 @@
 
 import Data.Time.Clock.POSIX (POSIXTime)
 
-#if !defined(HAVE_MKNOD)
+#if !defined(HAVE_MKNOD) || !defined(HAVE_CHOWN)
 import System.IO.Error ( ioeSetLocation )
 import GHC.IO.Exception ( unsupportedOperation )
 #endif
@@ -319,6 +319,8 @@
 -- -----------------------------------------------------------------------------
 -- chown()
 
+#if defined(HAVE_CHOWN)
+
 -- | @setOwnerAndGroup path uid gid@ changes the owner and group of @path@ to
 -- @uid@ and @gid@, respectively.
 --
@@ -332,6 +334,14 @@
 
 foreign import ccall unsafe "chown"
   c_chown :: CString -> CUid -> CGid -> IO CInt
+
+#else
+
+{-# WARNING setOwnerAndGroup "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_CHOWN@)" #-}
+setOwnerAndGroup :: RawFilePath -> UserID -> GroupID -> IO ()
+setOwnerAndGroup _ _ _ = ioError (ioeSetLocation unsupportedOperation "setOwnerAndGroup")
+
+#endif // HAVE_CHOWN
 
 #if HAVE_LCHOWN
 -- | Acts as 'setOwnerAndGroup' but does not follow symlinks (and thus
diff --git a/System/Posix/Files/Common.hsc b/System/Posix/Files/Common.hsc
--- a/System/Posix/Files/Common.hsc
+++ b/System/Posix/Files/Common.hsc
@@ -1,3 +1,4 @@
+{-# LANGUAGE CApiFFI #-}
 {-# LANGUAGE Trustworthy #-}
 
 -----------------------------------------------------------------------------
@@ -97,6 +98,11 @@
 import Foreign.Ptr
 import Foreign.Storable
 
+#if !defined(HAVE_FCHMOD) || !defined(HAVE_CHOWN)
+import System.IO.Error ( ioeSetLocation )
+import GHC.IO.Exception ( unsupportedOperation )
+#endif
+
 -- -----------------------------------------------------------------------------
 -- POSIX file modes
 
@@ -207,6 +213,8 @@
 socketMode :: FileMode
 socketMode = (#const S_IFSOCK)
 
+#if defined(HAVE_FCHMOD)
+
 -- | @setFdMode fd mode@ acts like 'setFileMode' but uses a file descriptor
 -- @fd@ instead of a 'FilePath'.
 --
@@ -218,6 +226,14 @@
 foreign import ccall unsafe "fchmod"
   c_fchmod :: CInt -> CMode -> IO CInt
 
+#else
+
+{-# WARNING setFdMode "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_FCHMOD@)" #-}
+setFdMode :: Fd -> FileMode -> IO ()
+setFdMode _ _ = ioError (ioeSetLocation unsupportedOperation "setFdMode")
+
+#endif // HAVE_FCHMOD
+
 -- | @setFileCreationMask mode@ sets the file mode creation mask to @mode@.
 -- Modes set by this operation are subtracted from files and directories upon
 -- creation. The previous file creation mask is returned.
@@ -452,12 +468,12 @@
 #endif
 
 #ifdef HAVE_UTIMENSAT
-foreign import ccall unsafe "utimensat"
+foreign import capi unsafe "sys/stat.h utimensat"
     c_utimensat :: CInt -> CString -> Ptr CTimeSpec -> CInt -> IO CInt
 #endif
 
 #if HAVE_FUTIMENS
-foreign import ccall unsafe "futimens"
+foreign import capi unsafe "sys/stat.h futimens"
     c_futimens :: CInt -> Ptr CTimeSpec -> IO CInt
 #endif
 
@@ -480,16 +496,16 @@
     (sec, frac) = if (frac' < 0) then (sec' - 1, frac' + 1) else (sec', frac')
     (sec', frac') = properFraction $ toRational t
 
-foreign import ccall unsafe "utimes"
+foreign import capi unsafe "sys/time.h utimes"
     c_utimes :: CString -> Ptr CTimeVal -> IO CInt
 
 #ifdef HAVE_LUTIMES
-foreign import ccall unsafe "lutimes"
+foreign import capi unsafe "sys/time.h lutimes"
     c_lutimes :: CString -> Ptr CTimeVal -> IO CInt
 #endif
 
 #if HAVE_FUTIMES
-foreign import ccall unsafe "futimes"
+foreign import capi unsafe "sys/time.h futimes"
     c_futimes :: CInt -> Ptr CTimeVal -> IO CInt
 #endif
 
@@ -539,6 +555,8 @@
 -- -----------------------------------------------------------------------------
 -- fchown()
 
+#if defined(HAVE_CHOWN)
+
 -- | Acts as 'setOwnerAndGroup' but uses a file descriptor instead of a
 -- 'FilePath'.
 --
@@ -549,6 +567,14 @@
 
 foreign import ccall unsafe "fchown"
   c_fchown :: CInt -> CUid -> CGid -> IO CInt
+
+#else
+
+{-# WARNING setFdOwnerAndGroup "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_CHOWN@)" #-}
+setFdOwnerAndGroup :: Fd -> UserID -> GroupID -> IO ()
+setFdOwnerAndGroup _ _ _ = ioError (ioeSetLocation unsupportedOperation "setFdOwnerAndGroup")
+
+#endif // HAVE_CHOWN
 
 -- -----------------------------------------------------------------------------
 -- ftruncate()
diff --git a/System/Posix/Files/PosixString.hsc b/System/Posix/Files/PosixString.hsc
--- a/System/Posix/Files/PosixString.hsc
+++ b/System/Posix/Files/PosixString.hsc
@@ -107,6 +107,11 @@
 
 import Data.Time.Clock.POSIX (POSIXTime)
 
+#if !defined(HAVE_MKNOD) || !defined(HAVE_CHOWN)
+import System.IO.Error ( ioeSetLocation )
+import GHC.IO.Exception ( unsupportedOperation )
+#endif
+
 -- -----------------------------------------------------------------------------
 -- chmod()
 
@@ -205,6 +210,13 @@
   withFilePath name $ \s ->
     throwErrnoPathIfMinus1_ "createNamedPipe" name (c_mkfifo s mode)
 
+#if !defined(HAVE_MKNOD)
+
+{-# WARNING createDevice "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_MKNOD@)" #-}
+createDevice :: PosixPath -> FileMode -> DeviceID -> IO ()
+createDevice _ _ _ = ioError (ioeSetLocation unsupportedOperation "createDevice")
+
+#else
 -- | @createDevice path mode dev@ creates either a regular or a special file
 -- depending on the value of @mode@ (and @dev@).  @mode@ will normally be either
 -- 'blockSpecialMode' or 'characterSpecialMode'.  May fail with
@@ -221,6 +233,8 @@
 foreign import capi unsafe "HsUnix.h mknod"
   c_mknod :: CString -> CMode -> CDev -> IO CInt
 
+#endif // HAVE_MKNOD
+
 -- -----------------------------------------------------------------------------
 -- Hard links
 
@@ -302,6 +316,8 @@
 -- -----------------------------------------------------------------------------
 -- chown()
 
+#if defined(HAVE_CHOWN)
+
 -- | @setOwnerAndGroup path uid gid@ changes the owner and group of @path@ to
 -- @uid@ and @gid@, respectively.
 --
@@ -315,6 +331,14 @@
 
 foreign import ccall unsafe "chown"
   c_chown :: CString -> CUid -> CGid -> IO CInt
+
+#else
+
+{-# WARNING setOwnerAndGroup "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_CHOWN@)" #-}
+setOwnerAndGroup :: PosixPath -> UserID -> GroupID -> IO ()
+setOwnerAndGroup _ _ _ = ioError (ioeSetLocation unsupportedOperation "setOwnerAndGroup")
+
+#endif // HAVE_CHOWN
 
 #if HAVE_LCHOWN
 -- | Acts as 'setOwnerAndGroup' but does not follow symlinks (and thus
diff --git a/System/Posix/Semaphore.hsc b/System/Posix/Semaphore.hsc
--- a/System/Posix/Semaphore.hsc
+++ b/System/Posix/Semaphore.hsc
@@ -1,5 +1,6 @@
 {-# LANGUAGE Safe #-}
 {-# LANGUAGE CApiFFI #-}
+{-# LANGUAGE InterruptibleFFI #-}
 -----------------------------------------------------------------------------
 -- |
 -- Module      :  System.Posix.Semaphore
@@ -16,7 +17,7 @@
 
 module System.Posix.Semaphore
     (OpenSemFlags(..), Semaphore(),
-     semOpen, semUnlink, semWait, semTryWait, semThreadWait,
+     semOpen, semUnlink, semWait, semWaitInterruptible, semTryWait, semThreadWait,
      semPost, semGetValue)
     where
 
@@ -39,6 +40,13 @@
 import Foreign.Storable
 #endif
 
+#if __GLASGOW_HASKELL__ >= 902
+import System.Posix.Internals (hostIsThreaded)
+#else
+hostIsThreaded :: Bool
+hostIsThreaded = False
+#endif
+
 data OpenSemFlags = OpenSemFlags { semCreate :: Bool,
                                    -- ^ If true, create the semaphore if it
                                    --   does not yet exist.
@@ -79,6 +87,20 @@
     where semWait' sem = throwErrnoIfMinus1Retry_ "semWait" $
                          sem_wait sem
 
+-- | Lock the semaphore, blocking until it becomes available.
+--
+-- Unlike 'semWait', this wait operation can be interrupted with
+-- an asynchronous exception (e.g. a call to 'throwTo' from another thread).
+semWaitInterruptible :: Semaphore -> IO Bool
+semWaitInterruptible (Semaphore fptr) = withForeignPtr fptr semWait'
+    where semWait' sem =
+            do res <- sem_wait_interruptible sem
+               if res == 0 then return True
+                           else do errno <- getErrno
+                                   if errno == eINTR
+                                     then return False
+                                     else throwErrno "semWaitInterrruptible"
+
 -- | Attempt to lock the semaphore without blocking.  Immediately return
 --   False if it is not available.
 semTryWait :: Semaphore -> IO Bool
@@ -96,9 +118,15 @@
 --   semWait, this will block only the current thread rather than the
 --   entire process.
 semThreadWait :: Semaphore -> IO ()
-semThreadWait sem = do res <- semTryWait sem
-                       (if res then return ()
-                        else ( do { yield; semThreadWait sem } ))
+semThreadWait sem
+  -- N.B. semWait can be safely used in the case of the threaded runtime, where
+  -- the safe foreign call will be performed in its own thread, thereby not
+  -- blocking the process.
+  | hostIsThreaded = semWait sem
+  | otherwise = do
+      res <- semTryWait sem
+      if res then return ()
+             else do yield >> semThreadWait sem
 
 -- | Unlock the semaphore.
 semPost :: Semaphore -> IO ()
@@ -132,9 +160,10 @@
         sem_close :: Ptr () -> IO CInt
 foreign import capi safe "semaphore.h sem_unlink"
         sem_unlink :: CString -> IO CInt
-
 foreign import capi safe "semaphore.h sem_wait"
         sem_wait :: Ptr () -> IO CInt
+foreign import capi interruptible "semaphore.h sem_wait"
+        sem_wait_interruptible :: Ptr () -> IO CInt
 foreign import capi safe "semaphore.h sem_trywait"
         sem_trywait :: Ptr () -> IO CInt
 foreign import capi safe "semaphore.h sem_post"
diff --git a/System/Posix/Signals.hsc b/System/Posix/Signals.hsc
--- a/System/Posix/Signals.hsc
+++ b/System/Posix/Signals.hsc
@@ -101,7 +101,7 @@
 import Foreign.Marshal
 import Foreign.Ptr
 import Foreign.Storable
-#if !defined(HAVE_SIGNAL_H)
+#if !defined(HAVE_SIGNAL_H) || !defined(HAVE_ALARM)
 import System.IO.Error ( ioeSetLocation )
 import GHC.IO.Exception ( unsupportedOperation )
 #endif
diff --git a/System/Posix/Temp/PosixString.hsc b/System/Posix/Temp/PosixString.hsc
--- a/System/Posix/Temp/PosixString.hsc
+++ b/System/Posix/Temp/PosixString.hsc
@@ -35,6 +35,19 @@
 import System.Posix.IO.PosixString
 import System.Posix.Types
 
+#if !defined(HAVE_MKSTEMP)
+import System.IO.Error ( ioeSetLocation )
+import GHC.IO.Exception ( unsupportedOperation )
+#endif
+
+#if !defined(HAVE_MKSTEMP)
+
+{-# WARNING mkstemp "operation will throw 'IOError' \"unsupported operation\" (CPP guard: @#if HAVE_MKSTEMP@)" #-}
+mkstemp :: PosixString -> IO (PosixPath, Handle)
+mkstemp _ = ioError (ioeSetLocation unsupportedOperation "mkstemp")
+
+#else
+
 foreign import capi unsafe "HsUnix.h mkstemp"
   c_mkstemp :: CString -> IO CInt
 
@@ -53,6 +66,8 @@
     name <- peekFilePath ptr
     h <- fdToHandle (Fd fd)
     return (name, h)
+
+#endif // HAVE_MKSTEMP
 
 #if HAVE_MKSTEMPS
 foreign import capi unsafe "HsUnix.h mkstemps"
diff --git a/System/Posix/Terminal/Common.hsc b/System/Posix/Terminal/Common.hsc
--- a/System/Posix/Terminal/Common.hsc
+++ b/System/Posix/Terminal/Common.hsc
@@ -216,13 +216,16 @@
    | ReturnMeansLF              -- ^ @ONLRET@ - (XSI) NL performs CR function
                                 --
                                 -- @since 2.8.0.0
+#ifdef TAB0
    | TabDelayMask0              -- ^ @TABDLY(TAB0)@ - (XSI) Select horizontal-tab delays: type 0
                                 --
                                 -- @since 2.8.0.0
+#endif
+#ifdef TAB3
    | TabDelayMask3              -- ^ @TABDLY(TAB3)@ - (XSI) Select horizontal-tab delays: type 3
                                 --
                                 -- @since 2.8.0.0
-
+#endif
         -- control flags
    | LocalMode                  -- ^ @CLOCAL@ - Ignore modem status lines
    | ReadEnable                 -- ^ @CREAD@ - Enable receiver
@@ -289,8 +292,12 @@
 withoutMode termios OutputMapCRtoLF = clearOutputFlag (#const OCRNL) termios
 withoutMode termios NoCRAtColumnZero = clearOutputFlag (#const ONOCR) termios
 withoutMode termios ReturnMeansLF = clearOutputFlag (#const ONLRET) termios
+#ifdef TAB0
 withoutMode termios TabDelayMask0 = clearOutputFlag (#const TAB0) termios
+#endif
+#ifdef TAB3
 withoutMode termios TabDelayMask3 = clearOutputFlag (#const TAB3) termios
+#endif
 withoutMode termios LocalMode = clearControlFlag (#const CLOCAL) termios
 withoutMode termios ReadEnable = clearControlFlag (#const CREAD) termios
 withoutMode termios TwoStopBits = clearControlFlag (#const CSTOPB) termios
@@ -325,8 +332,12 @@
 withMode termios OutputMapCRtoLF = setOutputFlag (#const OCRNL) termios
 withMode termios NoCRAtColumnZero = setOutputFlag (#const ONOCR) termios
 withMode termios ReturnMeansLF = setOutputFlag (#const ONLRET) termios
+#ifdef TAB0
 withMode termios TabDelayMask0 = setOutputFlag (#const TAB0) termios
+#endif
+#ifdef TAB3
 withMode termios TabDelayMask3 = setOutputFlag (#const TAB3) termios
+#endif
 withMode termios LocalMode = setControlFlag (#const CLOCAL) termios
 withMode termios ReadEnable = setControlFlag (#const CREAD) termios
 withMode termios TwoStopBits = setControlFlag (#const CSTOPB) termios
@@ -361,8 +372,12 @@
 terminalMode OutputMapCRtoLF = testOutputFlag (#const OCRNL)
 terminalMode NoCRAtColumnZero = testOutputFlag (#const ONOCR)
 terminalMode ReturnMeansLF = testOutputFlag (#const ONLRET)
+#ifdef TAB0
 terminalMode TabDelayMask0 = testOutputFlag (#const TAB0)
+#endif
+#ifdef TAB3
 terminalMode TabDelayMask3 = testOutputFlag (#const TAB3)
+#endif
 terminalMode LocalMode = testControlFlag (#const CLOCAL)
 terminalMode ReadEnable = testControlFlag (#const CREAD)
 terminalMode TwoStopBits = testControlFlag (#const CSTOPB)
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,32 @@
 # Changelog for [`unix` package](http://hackage.haskell.org/package/unix)
 
+## 2.8.1.0 *Feb 2023*
+  * Fix build if HAVE_ALARM is undefined
+
+  * Add missing autoconf checks for chown/fchdir/fchmod
+
+  * Make TABX constructors and code conditional on underlying #defines
+
+  * Bump bounds to accomodate base-4.18
+
+  * Add semWaitInterruptible
+
+  * semaphore: Teach semThreadWait to use semWait with threaded RTS
+
+  * make the foreign imports of dlopen & dlclose safe
+
+  * do not use capi for dlfcn.h stuff under wasm-wasi
+
+  * Use capi for syscalls that break under musl's handling of 64-bit `time_t`
+
+  * Replace `last` with `unsnoc`
+
+  * Avoid Data.List.{head,tail}
+
+  * Consistently use `throwErrnoPathIf*`
+
+  * Fix WASI build
+
 ## 2.8.0.0 *August 2022*
   * Use ByteString for GroupEntry/UserEntry
 
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -3824,7 +3824,7 @@
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -3870,7 +3870,7 @@
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -3894,7 +3894,7 @@
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -3939,7 +3939,7 @@
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -3963,7 +3963,7 @@
     We can't simply define LARGE_OFF_T to be 9223372036854775807,
     since some C++ compilers masquerading as C compilers
     incorrectly reject 9223372036854775807.  */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
 		       && LARGE_OFF_T % 2147483647 == 1)
 		      ? 1 : -1];
@@ -4230,6 +4230,18 @@
 if test "x$ac_cv_func_times" = xyes; then :
   cat >>confdefs.h <<_ACEOF
 #define HAVE_TIMES 1
+_ACEOF
+
+fi
+done
+
+for ac_func in chown fchdir fchmod
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
 _ACEOF
 
 fi
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@
 AC_CHECK_FUNCS([mkstemp])
 AC_CHECK_FUNCS([pipe])
 AC_CHECK_FUNCS([times])
+AC_CHECK_FUNCS([chown fchdir fchmod])
 
 AC_CHECK_TYPE([struct rlimit],[AC_DEFINE([HAVE_STRUCT_RLIMIT],[1],[HAVE_STRUCT_RLIMIT])],[],[#include <sys/resource.h>])
 
diff --git a/include/HsUnixConfig.h.in b/include/HsUnixConfig.h.in
--- a/include/HsUnixConfig.h.in
+++ b/include/HsUnixConfig.h.in
@@ -105,6 +105,9 @@
 /* Define to 1 if you have the <bsd/libutil.h> header file. */
 #undef HAVE_BSD_LIBUTIL_H
 
+/* Define to 1 if you have the `chown' function. */
+#undef HAVE_CHOWN
+
 /* Define to 1 if you have the `clearenv' function. */
 #undef HAVE_CLEARENV
 
@@ -155,6 +158,12 @@
 
 /* Define to 1 if you have the `execvpe' function. */
 #undef HAVE_EXECVPE
+
+/* Define to 1 if you have the `fchdir' function. */
+#undef HAVE_FCHDIR
+
+/* Define to 1 if you have the `fchmod' function. */
+#undef HAVE_FCHMOD
 
 /* Define to 1 if you have the <fcntl.h> header file. */
 #undef HAVE_FCNTL_H
diff --git a/tests/Semaphore001.hs b/tests/Semaphore001.hs
--- a/tests/Semaphore001.hs
+++ b/tests/Semaphore001.hs
@@ -4,6 +4,6 @@
 
 main :: IO ()
 main = do
-  sem <- semOpen "/test" OpenSemFlags {semCreate = True, semExclusive = False} stdFileMode 1
+  sem <- semOpen "/test-001" OpenSemFlags {semCreate = True, semExclusive = False} stdFileMode 1
   semThreadWait sem
   semPost sem
diff --git a/tests/Semaphore002.hs b/tests/Semaphore002.hs
new file mode 100644
--- /dev/null
+++ b/tests/Semaphore002.hs
@@ -0,0 +1,15 @@
+module Main (main) where
+
+import Control.Concurrent
+import System.Posix
+
+main :: IO ()
+main = do
+  sem <- semOpen "/test-002" OpenSemFlags {semCreate = True, semExclusive = False} stdFileMode 0
+  _ <- forkIO $ do
+      threadDelay (1000*1000)
+      semPost sem
+
+  -- This should succeed after 1 second.
+  semThreadWait sem
+  semPost sem
diff --git a/tests/SemaphoreInterrupt.hs b/tests/SemaphoreInterrupt.hs
new file mode 100644
--- /dev/null
+++ b/tests/SemaphoreInterrupt.hs
@@ -0,0 +1,32 @@
+module Main (main) where
+
+import Control.Concurrent
+import Control.Monad
+import Data.IORef
+import System.Posix
+
+main :: IO ()
+main = do
+
+  sem <- semOpen "/test-interrupt" OpenSemFlags {semCreate = True, semExclusive = False} stdFileMode 0
+  ref <- newIORef False
+  _ <- forkIO $ do
+    res <- semWaitInterruptible sem
+    writeIORef ref res
+  threadDelay 100000 -- 100 ms
+  semPost sem
+  threadDelay 100000 -- 100 ms
+  succ1 <- readIORef ref
+  unless succ1 $
+    error "SemaphoreInterrupt: semWaitInterruptible failed"
+
+  writeIORef ref False
+  tid <- forkIO $ do
+    res <- semWaitInterruptible sem
+    writeIORef ref res
+  threadDelay 100000 -- 100 ms
+  killThread tid
+  threadDelay 100000 -- 100 ms
+  succ2 <- readIORef ref
+  when succ2 $
+    error "SemaphoreInterrupt: semWaitInterruptible not interrupted"
diff --git a/unix.cabal b/unix.cabal
--- a/unix.cabal
+++ b/unix.cabal
@@ -1,6 +1,6 @@
 cabal-version:  1.12
 name:           unix
-version:        2.8.0.0
+version:        2.8.1.0
 -- NOTE: Don't forget to update ./changelog.md
 
 license:        BSD3
@@ -69,7 +69,7 @@
         buildable: False
 
     build-depends:
-        base        >= 4.10    && < 4.18,
+        base        >= 4.10    && < 4.19,
         bytestring  >= 0.9.2   && < 0.12,
         filepath    >= 1.4.100.0 && < 1.5,
         time        >= 1.2     && < 1.13
@@ -262,3 +262,19 @@
     default-language: Haskell2010
     build-depends: base, unix
     ghc-options: -Wall
+
+test-suite Semaphore002
+    hs-source-dirs: tests
+    main-is: Semaphore002.hs
+    type: exitcode-stdio-1.0
+    default-language: Haskell2010
+    build-depends: base, unix
+    ghc-options: -Wall -threaded
+
+test-suite SemaphoreInterrupt
+    hs-source-dirs: tests
+    main-is: SemaphoreInterrupt.hs
+    type: exitcode-stdio-1.0
+    default-language: Haskell2010
+    build-depends: base, unix
+    ghc-options: -Wall -threaded
