diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
 ## Changelog
 
+### 0.5.0
+
+* New raw functions for `luaopen_base`, `luaopen_package`, `luaopen_string`,
+  `luaopen_table`, `luaopen_math`, `luaopen_io`, `luaopen_os`, `luaopen_debug`
+  and their high-level wrappers (with names `openbase`, `opentable` etc.)
+  implemented.
+* Remove custom versions of `loadfile` and `loadstring`.
+* Drop support for GHC versions < 7.8, avoid compiler warnings.
+* Ensure no symbols are stripped when linking the bundled lua interpreter.
+* Simplify `tostring` function definition. (Sean Proctor)
+* Explicitly decprecate `strlen`. (Sean Proctor)
+* Add links to lua documentation for functions wrapping the official lua C API.
+  (Sean Proctor).
+
 ### 0.4.1
 
 * Bugfix(#30): `tolist` wasn't popping elements of the list from stack.
diff --git a/hslua.cabal b/hslua.cabal
--- a/hslua.cabal
+++ b/hslua.cabal
@@ -1,5 +1,5 @@
 name:                   hslua
-version:                0.4.1
+version:                0.5.0
 stability:              beta
 cabal-version:          >= 1.8
 license:                MIT
@@ -42,7 +42,8 @@
   default:              False
 
 library
-  build-depends:        base == 4.*, bytestring >= 0.10.2.0 && < 0.11
+  build-depends:        base       >= 4.7    && < 5,
+                        bytestring >= 0.10.2 && < 0.11
   exposed-modules:      Scripting.Lua, Scripting.Lua.Raw
   hs-source-dirs:       src
   ghc-options:          -Wall -O2
@@ -69,18 +70,20 @@
 
   if os(linux)
     cc-options:         "-DLUA_USE_LINUX"
+    ld-options:         "-Wl,-E"
 
   if os(darwin)
     cc-options:         "-DLUA_USE_MACOSX"
 
   if os(freebsd)
     cc-options:         "-DLUA_USE_POSIX"
+    ld-options:         "-Wl,-E"
 
   if os(windows)
-     cc-options:        "-D__NO_ISOCEXT"
+    cc-options:         "-D__NO_ISOCEXT"
 
   if flag(apicheck)
-    cc-Options:         "-DLUA_USE_APICHECK"
+    cc-options:         "-DLUA_USE_APICHECK"
 
 -- This is not necessary because we have a test suite now. Still keeping here as
 -- a very simple test. Originally added with 799a4c2 to test if linking with
@@ -114,7 +117,7 @@
   main-is:              Spec.hs
   hs-source-dirs:       test
   ghc-options:          -Wall -threaded
-
+  other-modules:        HsLuaSpec
   build-depends:
     base,
     bytestring,
diff --git a/src/Scripting/Lua.hsc b/src/Scripting/Lua.hsc
--- a/src/Scripting/Lua.hsc
+++ b/src/Scripting/Lua.hsc
@@ -1,9 +1,6 @@
-{-# LANGUAGE FlexibleInstances, ForeignFunctionInterface, ScopedTypeVariables #-}
-
--- In older versions of GHC, FlexibleInstances doesn't imply
--- TypeSynonymInstances, so we need to enable it explicitly.
--- See #29.
-{-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 
 module Scripting.Lua
   ( LuaState
@@ -13,7 +10,10 @@
   , module Scripting.Lua
   ) where
 
+#if MIN_VERSION_base(4,8,0)
+#else
 import Control.Applicative ((<$>))
+#endif
 import Control.Monad
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Char8 as BC
@@ -23,7 +23,6 @@
 import Data.Maybe
 import Foreign.C
 import Foreign.Marshal.Alloc
-import Foreign.Marshal.Array
 import Foreign.Ptr
 import Foreign.StablePtr
 import qualified Foreign.Storable as F
@@ -34,7 +33,7 @@
 
 #include "lua.h"
 
--- | Enumeration used as type tag. See @lua_type@ in Lua Reference Manual.
+-- | Enumeration used as type tag. See <https://www.lua.org/manual/5.1/manual.html#lua_type lua_type>.
 data LTYPE
     = TNONE
     | TNIL
@@ -84,51 +83,52 @@
     | GCSETSTEPMUL
     deriving (Eq,Ord,Show,Enum)
 
--- | See @LUA_MULTRET@ in Lua Reference Manual.
+-- | Alias for C constant @LUA_MULTRET@. See <https://www.lua.org/manual/5.1/manual.html#lua_call lua_call>.
 multret :: Int
 multret = #{const LUA_MULTRET}
 
--- | See @lua_settop@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_settop lua_settop>.
 settop :: LuaState -> Int -> IO ()
 settop l n = c_lua_settop l (fromIntegral n)
 
--- | See @lua_createtable@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_createtable lua_createtable>.
 createtable :: LuaState -> Int -> Int -> IO ()
 createtable l s z = c_lua_createtable l (fromIntegral s) (fromIntegral z)
 
--- | See @lua_objlen@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_objlen lua_objlen>.
 objlen :: LuaState -> Int -> IO Int
 objlen l n = liftM fromIntegral (c_lua_objlen l (fromIntegral n))
 
--- | See @lua_pop@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pop lua_pop>.
 pop :: LuaState -> Int -> IO ()
 pop l n = settop l (-n-1)
 
--- | See @lua_newtable@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_newtable lua_newtable>.
 newtable :: LuaState -> IO ()
 newtable l = createtable l 0 0
 
--- | See @lua_pushcclosure@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushcclosure lua_pushcclosure>.
 pushcclosure :: LuaState -> FunPtr LuaCFunction -> Int -> IO ()
 pushcclosure l f n = c_lua_pushcclosure l f (fromIntegral n)
 
--- | See @lua_pushcfunction@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushcfunction lua_pushcfunction>.
 pushcfunction :: LuaState -> FunPtr LuaCFunction -> IO ()
 pushcfunction l f = pushcclosure l f 0
 
--- | See @lua_strlen@ in Lua Reference Manual.
+{-# DEPRECATED strlen "Use objlen instead." #-}
+-- | Compatibility alias for objlen
 strlen :: LuaState -> Int -> IO Int
 strlen = objlen
 
--- | See @lua_type@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_type lua_type>.
 ltype :: LuaState -> Int -> IO LTYPE
 ltype l n = liftM (toEnum . fromIntegral) (c_lua_type l (fromIntegral n))
 
--- | See @lua_isfunction@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isfunction lua_isfunction>.
 isfunction :: LuaState -> Int -> IO Bool
 isfunction l n = liftM (== TFUNCTION) (ltype l n)
 
--- | See @lua_istable@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_istable lua_istable>.
 istable :: LuaState -> Int -> IO Bool
 istable l n = liftM (== TTABLE) (ltype l n)
 
@@ -151,85 +151,82 @@
                      Nothing -> Nothing
                      Just vals -> Just (val : vals)
 
--- | See @lua_islightuserdata@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_islightuserdata lua_islightuserdata>.
 islightuserdata :: LuaState -> Int -> IO Bool
 islightuserdata l n = liftM (== TLIGHTUSERDATA) (ltype l n)
 
--- | See @lua_isnil@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isnil lua_isnil>.
 isnil :: LuaState -> Int -> IO Bool
 isnil l n = liftM (== TNIL) (ltype l n)
 
--- | See @lua_isboolean@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isboolean lua_isboolean>.
 isboolean :: LuaState -> Int -> IO Bool
 isboolean l n = liftM (== TBOOLEAN) (ltype l n)
 
--- | See @lua_isthread@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isthread lua_isthread>.
 isthread :: LuaState -> Int -> IO Bool
 isthread l n = liftM (== TTHREAD) (ltype l n)
 
--- | See @lua_none@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isnone lua_isnone>.
 isnone :: LuaState -> Int -> IO Bool
 isnone l n = liftM (== TNONE) (ltype l n)
 
--- | See @lua_noneornil@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isnoneornil lua_isnoneornil>.
 isnoneornil :: LuaState -> Int -> IO Bool
 isnoneornil l n = liftM (<= TNIL) (ltype l n)
 
--- | See @LUA_REGISTRYINDEX@ in Lua Reference Manual.
+-- | Alias for C constant @LUA_REGISTRYINDEX@. See <https://www.lua.org/manual/5.1/manual.html#3.5 Lua registry>.
 registryindex :: Int
 registryindex = #{const LUA_REGISTRYINDEX}
 
--- | See @LUA_ENVIRONINDEX@ in Lua Reference Manual.
+-- | Alias for C constant @LUA_ENVIRONINDEX@. See <https://www.lua.org/manual/5.1/manual.html#3.3 pseudo-indices>.
 environindex :: Int
 environindex = #{const LUA_ENVIRONINDEX}
 
--- | See @LUA_GLOBALSINDEX@ in Lua Reference Manual.
+-- | Alias for C constant @LUA_GLOBALSINDEX@. See <https://www.lua.org/manual/5.1/manual.html#3.3 pseudo-indices>.
 globalsindex :: Int
 globalsindex = #{const LUA_GLOBALSINDEX}
 
--- | See @lua_upvalueindex@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_upvalueindex lua_upvalueindex>.
 upvalueindex :: Int -> Int
 upvalueindex i = globalsindex - i
 
--- | See @lua_atpanic@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_atpanic lua_atpanic>.
 atpanic :: LuaState -> FunPtr LuaCFunction -> IO (FunPtr LuaCFunction)
 atpanic = c_lua_atpanic
 
--- | See @lua_tostring@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_tostring lua_tostring>.
 tostring :: LuaState -> Int -> IO B.ByteString
 tostring l n = alloca $ \lenPtr -> do
     cstr <- c_lua_tolstring l (fromIntegral n) lenPtr
-    len <- F.peek lenPtr
-    -- Lua string may change or get garbage collected, copy it
-    cstr' <- mallocBytes (fromIntegral len)
-    copyArray cstr' cstr (fromIntegral len)
-    B.unsafePackMallocCStringLen (cstr', fromIntegral len)
+    cstrLen <- F.peek lenPtr
+    B.packCStringLen (cstr, fromIntegral cstrLen)
 
--- | See @lua_tothread@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_tothread lua_tothread>.
 tothread :: LuaState -> Int -> IO LuaState
 tothread l n = c_lua_tothread l (fromIntegral n)
 
--- | See @lua_touserdata@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_touserdata lua_touserdata>.
 touserdata :: LuaState -> Int -> IO (Ptr a)
 touserdata l n = c_lua_touserdata l (fromIntegral n)
 
--- | See @lua_typename@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_typename lua_typename>.
 typename :: LuaState -> LTYPE -> IO String
 typename l n = c_lua_typename l (fromIntegral (fromEnum n)) >>= peekCString
 
--- | See @lua_xmove@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_xmove lua_xmove>.
 xmove :: LuaState -> LuaState -> Int -> IO ()
 xmove l1 l2 n = c_lua_xmove l1 l2 (fromIntegral n)
 
--- | See @lua_yield@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_yield lua_yield>.
 yield :: LuaState -> Int -> IO Int
 yield l n = liftM fromIntegral (c_lua_yield l (fromIntegral n))
 
--- | See @lua_checkstack@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_checkstack lua_checkstack>.
 checkstack :: LuaState -> Int -> IO Bool
 checkstack l n = liftM (/= 0) (c_lua_checkstack l (fromIntegral n))
 
--- | See @lua_newstate@ and @luaL_newstate@ in Lua Reference Manual.
+-- | See @lua_newstate@ and <https://www.lua.org/manual/5.1/manual.html#luaL_newstate luaL_newstate>.
 newstate :: IO LuaState
 newstate = do
     l <- c_luaL_newstate
@@ -237,49 +234,81 @@
     setglobal l "_HASKELLERR"
     return l
 
--- | See @lua_close@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_close lua_close>.
 close :: LuaState -> IO ()
 close = c_lua_close
 
--- | See @lua_concat@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_concat lua_concat>.
 concat :: LuaState -> Int -> IO ()
 concat l n = c_lua_concat l (fromIntegral n)
 
--- | See @lua_call@ and @lua_call@ in Lua Reference Manual.
+-- | See @lua_call@ and <https://www.lua.org/manual/5.1/manual.html#lua_call lua_call>.
 call :: LuaState -> Int -> Int -> IO ()
 call l a b = c_lua_call l (fromIntegral a) (fromIntegral b)
 
--- | See @lua_pcall@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pcall lua_pcall>.
 pcall :: LuaState -> Int -> Int -> Int -> IO Int
 pcall l a b c = liftM fromIntegral (c_lua_pcall l (fromIntegral a) (fromIntegral b) (fromIntegral c))
 
--- | See @lua_cpcall@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_cpcall lua_cpcall>.
 cpcall :: LuaState -> FunPtr LuaCFunction -> Ptr a -> IO Int
 cpcall l a c = liftM fromIntegral (c_lua_cpcall l a c)
 
--- | See @lua_getfield@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_getfield lua_getfield>.
 getfield :: LuaState -> Int -> String -> IO ()
 getfield l i s = withCString s $ \sPtr -> c_lua_getfield l (fromIntegral i) sPtr
 
--- | See @lua_setfield@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_setfield lua_setfield>.
 setfield :: LuaState -> Int -> String -> IO ()
 setfield l i s = withCString s $ \sPtr -> c_lua_setfield l (fromIntegral i) sPtr
 
--- | See @lua_getglobal@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_getglobal lua_getglobal>.
 getglobal :: LuaState -> String -> IO ()
 getglobal l n = getfield l globalsindex n
 
--- | See @lua_setglobal@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_setglobal lua_setglobal>.
 setglobal :: LuaState -> String -> IO ()
 setglobal l n = setfield l globalsindex n
 
--- | See @luaL_openlibs@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#luaL_openlibs luaL_openlibs>.
 openlibs :: LuaState -> IO ()
 openlibs = c_luaL_openlibs
 
+-- | See <https://www.lua.org/manual/5.1/manual.html#pdf-luaopen_base luaopen_base>.
+openbase :: LuaState -> IO ()
+openbase l = pushcfunction l c_lua_open_base_ptr >> call l 0 multret
+
+-- | See <https://www.lua.org/manual/5.1/manual.html#pdf-luaopen_table luaopen_table>.
+opentable :: LuaState -> IO ()
+opentable l = pushcfunction l c_lua_open_table_ptr >> call l 0 multret
+
+-- | See <https://www.lua.org/manual/5.1/manual.html#pdf-luaopen_io luaopen_io>.
+openio :: LuaState -> IO ()
+openio l = pushcfunction l c_lua_open_io_ptr >> call l 0 multret
+
+-- | See <https://www.lua.org/manual/5.1/manual.html#pdf-luaopen_os luaopen_os>.
+openos :: LuaState -> IO ()
+openos l = pushcfunction l c_lua_open_os_ptr >> call l 0 multret
+
+-- | See <https://www.lua.org/manual/5.1/manual.html#pdf-luaopen_string luaopen_string>.
+openstring :: LuaState -> IO ()
+openstring l = pushcfunction l c_lua_open_string_ptr >> call l 0 multret
+
+-- | See <https://www.lua.org/manual/5.1/manual.html#pdf-luaopen_math luaopen_math>.
+openmath :: LuaState -> IO ()
+openmath l = pushcfunction l c_lua_open_math_ptr >> call l 0 multret
+
+-- | See <https://www.lua.org/manual/5.1/manual.html#pdf-luaopen_debug luaopen_debug>.
+opendebug :: LuaState -> IO ()
+opendebug l = pushcfunction l c_lua_open_debug_ptr >> call l 0 multret
+
+-- | See <https://www.lua.org/manual/5.1/manual.html#pdf-luaopen_package luaopen_package>.
+openpackage :: LuaState -> IO ()
+openpackage l = pushcfunction l c_lua_open_package_ptr >> call l 0 multret
+
 foreign import ccall "wrapper" mkStringWriter :: LuaWriter -> IO (FunPtr LuaWriter)
 
--- | See @lua_dump@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_dump lua_dump>.
 dump :: LuaState -> IO String
 dump l = do
     r <- newIORef ""
@@ -293,7 +322,7 @@
     freeHaskellFunPtr writer
     readIORef r
 
--- | See @lua_equal@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_equal lua_equal>.
 equal :: LuaState -> Int -> Int -> IO Bool
 equal l i j = liftM (/= 0) (c_lua_equal l (fromIntegral i) (fromIntegral j))
 
@@ -307,111 +336,92 @@
     insert l (-2)
     return 2
 
--- | See @lua_gc@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_gc lua_gc>.
 gc :: LuaState -> GCCONTROL -> Int -> IO Int
 gc l i j= liftM fromIntegral (c_lua_gc l (fromIntegral (fromEnum i)) (fromIntegral j))
 
--- | See @lua_getfenv@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_getfenv lua_getfenv>.
 getfenv :: LuaState -> Int -> IO ()
 getfenv l n = c_lua_getfenv l (fromIntegral n)
 
--- | See @lua_getmetatable@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_getmetatable lua_getmetatable>.
 getmetatable :: LuaState -> Int -> IO Bool
 getmetatable l n = liftM (/= 0) (c_lua_getmetatable l (fromIntegral n))
 
--- | See @lua_gettable@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_gettable lua_gettable>.
 gettable :: LuaState -> Int -> IO ()
 gettable l n = c_lua_gettable l (fromIntegral n)
 
--- | See @lua_gettop@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_gettop lua_gettop>.
 gettop :: LuaState -> IO Int
 gettop l = liftM fromIntegral (c_lua_gettop l)
 
--- | See @lua_insert@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_insert lua_insert>.
 insert :: LuaState -> Int -> IO ()
 insert l n  = c_lua_insert l (fromIntegral n)
 
--- | See @lua_iscfunction@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_iscfunction lua_iscfunction>.
 iscfunction :: LuaState -> Int -> IO Bool
 iscfunction l n = liftM (/= 0) (c_lua_iscfunction l (fromIntegral n))
 
--- | See @lua_isnumber@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isnumber lua_isnumber>.
 isnumber :: LuaState -> Int -> IO Bool
 isnumber l n = liftM (/= 0) (c_lua_isnumber l (fromIntegral n))
 
--- | See @lua_isstring@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isstring lua_isstring>.
 isstring :: LuaState -> Int -> IO Bool
 isstring l n = liftM (/= 0) (c_lua_isstring l (fromIntegral n))
 
--- | See @lua_isuserdata@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_isuserdata lua_isuserdata>.
 isuserdata :: LuaState -> Int -> IO Bool
 isuserdata l n = liftM (/= 0) (c_lua_isuserdata l (fromIntegral n))
 
--- | See @lua_lessthan@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_lessthan lua_lessthan>.
 lessthan :: LuaState -> Int -> Int -> IO Bool
 lessthan l i j = liftM (/= 0) (c_lua_lessthan l (fromIntegral i) (fromIntegral j))
 
 
--- | See @luaL_loadfile@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#luaL_loadfile luaL_loadfile>.
 loadfile :: LuaState -> String -> IO Int
-loadfile l f = readFile f >>= \c -> loadstring l c f
-
-foreign import ccall "wrapper" mkStringReader :: LuaReader -> IO (FunPtr LuaReader)
+loadfile l f = withCString f $ \fPtr -> fmap fromIntegral (c_luaL_loadfile l fPtr)
 
--- | See @luaL_loadstring@ in Lua Reference Manual.
-loadstring :: LuaState -> String -> String -> IO Int
-loadstring l script cn = do
-    w <- newIORef nullPtr
-    let rd :: LuaReader
-        rd _l _d ps = do
-          k <- readIORef w
-          if k == nullPtr
-            then do
-              (s, len) <- newCStringLen script
-              writeIORef w s
-              F.poke ps (fromIntegral len)
-              return s
-            else return nullPtr
-    writer <- mkStringReader rd
-    res <- withCString cn $ \cnPtr -> c_lua_load l writer nullPtr cnPtr
-    freeHaskellFunPtr writer
-    k <- readIORef w
-    free k
-    return (fromIntegral res)
+-- | See <https://www.lua.org/manual/5.1/manual.html#luaL_loadstring luaL_loadstring>.
+loadstring :: LuaState -> String -> IO Int
+loadstring l str = withCString str $ \strPtr -> fmap fromIntegral (c_luaL_loadstring l strPtr)
 
--- | See @lua_newthread@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_newthread lua_newthread>.
 newthread :: LuaState -> IO LuaState
 newthread l = c_lua_newthread l
 
--- | See @lua_newuserdata@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_newuserdata lua_newuserdata>.
 newuserdata :: LuaState -> Int -> IO (Ptr ())
 newuserdata l s = c_lua_newuserdata l (fromIntegral s)
 
--- | See @lua_next@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_next lua_next>.
 next :: LuaState -> Int -> IO Bool
 next l i = liftM (/= 0) (c_lua_next l (fromIntegral i))
 
--- | See @lua_pushboolean@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushboolean lua_pushboolean>.
 pushboolean :: LuaState -> Bool -> IO ()
 pushboolean l v = c_lua_pushboolean l (fromIntegral (fromEnum v))
 
--- | See @lua_pushinteger@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushinteger lua_pushinteger>.
 pushinteger :: LuaState -> LuaInteger -> IO ()
 pushinteger = c_lua_pushinteger
 
--- | See @lua_pushlightuserdata@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushlightuserdata lua_pushlightuserdata>.
 pushlightuserdata :: LuaState -> Ptr a -> IO ()
 pushlightuserdata = c_lua_pushlightuserdata
 
--- | See @lua_pushnil@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushnil lua_pushnil>.
 pushnil :: LuaState -> IO ()
 pushnil = c_lua_pushnil
 
--- | See @lua_pushnumber@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushnumber lua_pushnumber>.
 pushnumber :: LuaState -> LuaNumber -> IO ()
 pushnumber = c_lua_pushnumber
 
--- | See @lua_pushstring@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushstring lua_pushstring>.
 pushstring :: LuaState -> B.ByteString -> IO ()
 pushstring l s = B.unsafeUseAsCStringLen s $ \(sPtr, z) -> c_lua_pushlstring l sPtr (fromIntegral z)
 
@@ -423,94 +433,94 @@
       push l val
       rawseti l (-2) idx
 
--- | See @lua_pushthread@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushthread lua_pushthread>.
 pushthread :: LuaState -> IO Bool
 pushthread l = liftM (/= 0) (c_lua_pushthread l)
 
--- | See @lua_pushvalue@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_pushvalue lua_pushvalue>.
 pushvalue :: LuaState -> Int -> IO ()
 pushvalue l n = c_lua_pushvalue l (fromIntegral n)
 
--- | See @lua_rawequal@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_rawequal lua_rawequal>.
 rawequal :: LuaState -> Int -> Int -> IO Bool
 rawequal l n m = liftM (/= 0) (c_lua_rawequal l (fromIntegral n) (fromIntegral m))
 
--- | See @lua_rawget@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_rawget lua_rawget>.
 rawget :: LuaState -> Int -> IO ()
 rawget l n = c_lua_rawget l (fromIntegral n)
 
--- | See @lua_rawgeti@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_rawgeti lua_rawgeti>.
 rawgeti :: LuaState -> Int -> Int -> IO ()
 rawgeti l k m = c_lua_rawgeti l (fromIntegral k) (fromIntegral m)
 
--- | See @lua_rawset@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_rawset lua_rawset>.
 rawset :: LuaState -> Int -> IO ()
 rawset l n = c_lua_rawset l (fromIntegral n)
 
--- | See @lua_rawseti@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_rawseti lua_rawseti>.
 rawseti :: LuaState -> Int -> Int -> IO ()
 rawseti l k m = c_lua_rawseti l (fromIntegral k) (fromIntegral m)
 
--- | See @lua_remove@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_remove lua_remove>.
 remove :: LuaState -> Int -> IO ()
 remove l n = c_lua_remove l (fromIntegral n)
 
--- | See @lua_replace@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_replace lua_replace>.
 replace :: LuaState -> Int -> IO ()
 replace l n = c_lua_replace l (fromIntegral n)
 
--- | See @lua_resume@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_resume lua_resume>.
 resume :: LuaState -> Int -> IO Int
 resume l n = liftM fromIntegral (c_lua_resume l (fromIntegral n))
 
--- | See @lua_setfenv@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_setfenv lua_setfenv>.
 setfenv :: LuaState -> Int -> IO Int
 setfenv l n = liftM fromIntegral (c_lua_setfenv l (fromIntegral n))
 
--- | See @lua_setmetatable@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_setmetatable lua_setmetatable>.
 setmetatable :: LuaState -> Int -> IO ()
 setmetatable l n = c_lua_setmetatable l (fromIntegral n)
 
--- | See @lua_settable@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_settable lua_settable>.
 settable :: LuaState -> Int -> IO ()
 settable l index = c_lua_settable l (fromIntegral index)
 
 
--- | See @lua_status@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_status lua_status>.
 status :: LuaState -> IO Int
 status l = liftM fromIntegral (c_lua_status l)
 
--- | See @lua_toboolean@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_toboolean lua_toboolean>.
 toboolean :: LuaState -> Int -> IO Bool
 toboolean l n = liftM (/= 0) (c_lua_toboolean l (fromIntegral n))
 
--- | See @lua_tocfunction@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_tocfunction lua_tocfunction>.
 tocfunction :: LuaState -> Int -> IO (FunPtr LuaCFunction)
 tocfunction l n = c_lua_tocfunction l (fromIntegral n)
 
--- | See @lua_tointeger@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_tointeger lua_tointeger>.
 tointeger :: LuaState -> Int -> IO LuaInteger
 tointeger l n = c_lua_tointeger l (fromIntegral n)
 
--- | See @lua_tonumber@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_tonumber lua_tonumber>.
 tonumber :: LuaState -> Int -> IO LuaNumber
 tonumber l n = c_lua_tonumber l (fromIntegral n)
 
--- | See @lua_topointer@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_topointer lua_topointer>.
 topointer :: LuaState -> Int -> IO (Ptr ())
 topointer l n = c_lua_topointer l (fromIntegral n)
 
--- | See @lua_register@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#lua_register lua_register>.
 register :: LuaState -> String -> FunPtr LuaCFunction -> IO ()
 register l n f = do
     pushcclosure l f 0
     setglobal l n
 
--- | See @luaL_newmetatable@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#luaL_newmetatable luaL_newmetatable>.
 newmetatable :: LuaState -> String -> IO Int
 newmetatable l s = withCString s $ \sPtr -> liftM fromIntegral (c_luaL_newmetatable l sPtr)
 
--- | See @luaL_argerror@ in Lua Reference Manual. Contrary to the
+-- | See <https://www.lua.org/manual/5.1/manual.html#luaL_argerror luaL_argerror>. Contrary to the
 -- manual, Haskell function does return with value less than zero.
 argerror :: LuaState -> Int -> String -> IO CInt
 argerror l n msg = withCString msg $ \msgPtr -> do
@@ -521,11 +531,11 @@
     -- here we should have error message string on top of the stack
     return (-1)
 
--- | See @luaL_ref@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#luaL_ref luaL_ref>.
 ref :: LuaState -> Int -> IO Int
 ref l n = fmap fromIntegral $ c_luaL_ref l (fromIntegral n)
 
--- | See @luaL_unref@ in Lua Reference Manual.
+-- | See <https://www.lua.org/manual/5.1/manual.html#luaL_unref luaL_unref>.
 unref :: LuaState -> Int -> Int -> IO ()
 unref l t r = c_luaL_unref l (fromIntegral t) (fromIntegral r)
 
diff --git a/src/Scripting/Lua/Raw.hsc b/src/Scripting/Lua/Raw.hsc
--- a/src/Scripting/Lua/Raw.hsc
+++ b/src/Scripting/Lua/Raw.hsc
@@ -281,7 +281,55 @@
 foreign import ccall "lualib.h luaL_openlibs"
   c_luaL_openlibs :: LuaState -> IO ()
 
+foreign import ccall "lualib.h luaopen_base"
+  c_lua_open_base :: LuaState -> IO CInt
 
+foreign import ccall "lualib.h &luaopen_base"
+  c_lua_open_base_ptr :: FunPtr (LuaState -> IO CInt)
+
+foreign import ccall "lualib.h luaopen_table"
+  c_lua_open_table :: LuaState -> IO CInt
+
+foreign import ccall "lualib.h &luaopen_table"
+  c_lua_open_table_ptr :: FunPtr (LuaState -> IO CInt)
+
+foreign import ccall "lualib.h luaopen_io"
+  c_lua_open_io :: LuaState -> IO CInt
+
+foreign import ccall "lualib.h &luaopen_io"
+  c_lua_open_io_ptr :: FunPtr (LuaState -> IO CInt)
+
+foreign import ccall "lualib.h luaopen_os"
+  c_lua_open_os :: LuaState -> IO CInt
+
+foreign import ccall "lualib.h &luaopen_os"
+  c_lua_open_os_ptr :: FunPtr (LuaState -> IO CInt)
+
+foreign import ccall "lualib.h luaopen_string"
+  c_lua_open_string :: LuaState -> IO CInt
+
+foreign import ccall "lualib.h &luaopen_string"
+  c_lua_open_string_ptr :: FunPtr (LuaState -> IO CInt)
+
+foreign import ccall "lualib.h luaopen_math"
+  c_lua_open_math :: LuaState -> IO CInt
+
+foreign import ccall "lualib.h &luaopen_math"
+  c_lua_open_math_ptr :: FunPtr (LuaState -> IO CInt)
+
+foreign import ccall "lualib.h luaopen_debug"
+  c_lua_open_debug :: LuaState -> IO CInt
+
+foreign import ccall "lualib.h &luaopen_debug"
+  c_lua_open_debug_ptr :: FunPtr (LuaState -> IO CInt)
+
+foreign import ccall "lualib.h luaopen_package"
+  c_lua_open_package :: LuaState -> IO CInt
+
+foreign import ccall "lualib.h &luaopen_package"
+  c_lua_open_package_ptr :: FunPtr (LuaState -> IO CInt)
+
+
 --------------------------------------------------------------------------------
 -- * The Auxiliary Library
 
@@ -299,3 +347,9 @@
 
 foreign import ccall "lauxlib.h luaL_unref"
   c_luaL_unref :: LuaState -> CInt -> CInt -> IO ()
+
+foreign import ccall "lauxlib.h luaL_loadfile"
+  c_luaL_loadfile :: LuaState -> Ptr CChar -> IO CInt
+
+foreign import ccall "lauxlib.h luaL_loadstring"
+  c_luaL_loadstring :: LuaState -> Ptr CChar -> IO CInt
diff --git a/test/HsLuaSpec.hs b/test/HsLuaSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/HsLuaSpec.hs
@@ -0,0 +1,210 @@
+module HsLuaSpec where
+
+import Control.Monad (forM, forM_, when)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as B
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import System.Mem (performMajorGC)
+
+import Test.Hspec
+import Test.Hspec.Contrib.HUnit
+import Test.HUnit
+
+import Test.QuickCheck
+import Test.QuickCheck.Instances ()
+import qualified Test.QuickCheck.Monadic as QM
+
+import Scripting.Lua
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+    describe "StackValue" $ mapM_ fromHUnitTest
+      [bytestring, bsShouldLive, listInstance, nulString]
+    describe "Random StackValues" $ do
+      it "can push/pop booleans" $ property prop_bool
+      it "can push/pop ints" $ property prop_int
+      it "can push/pop doubles" $ property prop_double
+      it "can push/pop bytestrings" $ property prop_bytestring
+      it "can push/pop lists of booleans" $ property prop_lists_bool
+      it "can push/pop lists of ints" $ property prop_lists_int
+      it "can push/pop lists of bytestrings" $ property prop_lists_bytestring
+    describe "luaopen_* functions" $ mapM_ fromHUnitTest $ map (uncurry testOpen) $
+      [ ("table", opentable), ("io", openio), ("os", openos),
+        ("string", openstring), ("math", openmath), ("debug", opendebug),
+        ("package", openpackage) ]
+    describe "luaopen_base returns two tables" $ fromHUnitTest $ testOpenBase
+
+bytestring :: Test
+bytestring = TestLabel "ByteString -- unicode stuff" $ TestCase $ do
+    l <- newstate
+    let val = T.pack "öçşiğüİĞı"
+    pushstring l (T.encodeUtf8 val)
+    val' <- T.decodeUtf8 `fmap` tostring l 1
+    close l
+    assertEqual "Popped a different value or pop failed" val val'
+
+bsShouldLive :: Test
+bsShouldLive = TestLabel "ByteString should survive after GC/Lua destroyed" $ TestCase $ do
+    (val, val') <- do
+      l <- newstate
+      let val = B.pack "ByteString should survive"
+      pushstring l val
+      val' <- tostring l 1
+      pop l 1
+      close l
+      return (val, val')
+    performMajorGC
+    assertEqual "Popped a different value or pop failed" val val'
+
+listInstance :: Test
+listInstance = TestLabel "Push/pop StackValue lists" $ TestCase $ do
+    let lst = [B.pack "first", B.pack "second"]
+    l <- newstate
+    pushlist l lst
+    setglobal l "mylist"
+    size0 <- gettop l
+    assertEqual
+      "After pushing the list and assigning to a variable, stack is not empty"
+      0 size0
+    getglobal l "mylist"
+    size1 <- gettop l
+    assertEqual "`getglobal` pushed more than one value to the stack" 1 size1
+    lst' <- tolist l 1
+    size2 <- gettop l
+    assertEqual "`tolist` left stuff on the stack" size1 size2
+    close l
+    assertEqual "Popped a different list or pop failed" (Just lst) lst'
+
+nulString :: Test
+nulString =
+  TestLabel "String with NUL byte should be pushed/popped correctly" $ TestCase $ do
+    l <- newstate
+    let str = "A\NULB"
+    pushstring l (B.pack str)
+    str' <- tostring l 1
+    close l
+    assertEqual "Popped string is different than what's pushed" str (B.unpack str')
+
+
+-----
+-- Random Quickcheck testing for StackValue instances
+-----
+
+-- Bools
+prop_bool :: Bool -> Property
+prop_bool = testStackValueInstance
+-- Ints
+prop_int :: Int -> Property
+prop_int = testStackValueInstance
+-- Doubles
+prop_double :: Double -> Property
+prop_double = testStackValueInstance
+-- Bytestrings
+prop_bytestring :: ByteString -> Property
+prop_bytestring = testStackValueInstance
+-- Lists of bools
+prop_lists_bool :: [Bool] -> Property
+prop_lists_bool = testStackValueInstance
+-- Lists of ints
+prop_lists_int :: [Int] -> Property
+prop_lists_int = testStackValueInstance
+-- Lists of bytestrings
+prop_lists_bytestring :: [ByteString] -> Property
+prop_lists_bytestring = testStackValueInstance
+
+-- Check that the StackValue instance for a datatype works
+testStackValueInstance :: (Show t, Eq t, StackValue t) => t -> Property
+testStackValueInstance t = QM.monadicIO $ do
+  -- Init Lua state
+  l <- QM.run newstate
+  -- Get an ascending list of small (1-100) positive integers
+  -- These are the indices at which we will push the value to be tested
+  -- Note that duplicate values don't matter so we don't need to guard against that
+  Ordered indices' <- QM.pick arbitrary
+  let indices = map getPositive indices'
+  let nItems = if null indices then 0 else last indices
+  -- Make sure there's enough room in the stack
+  QM.assert =<< QM.run (checkstack l nItems)
+  -- Push elements
+  QM.run $ forM_ [1..nItems] $ \n ->
+    if n `elem` indices
+      then push l t
+      else push l n
+  -- Check that the stack size is the same as the total number of pushed items
+  stackSize <- QM.run $ gettop l
+  QM.assert $ stackSize == nItems
+  -- Peek all items
+  vals <- QM.run $ forM indices $ peek l
+  -- Check that the stack size did not change after peeking
+  newStackSize <- QM.run $ gettop l
+  QM.assert $ stackSize == newStackSize
+  -- Check that we were able to peek at all pushed elements
+  forM_ vals $ QM.assert . (== Just t)
+
+  -- DEBUGGING -----------------------------------------------
+  -- QM.run $ putStrLn $ "Testing value -------- " ++ show t
+  -- QM.run $ putStrLn $ "Indices: " ++ show indices
+  -- QM.run $ putStrLn $ "StackSize: " ++ show stackSize
+  -- QM.run $ putStrLn $ "NewStackSize: " ++ show newStackSize
+  -- QM.run $ putStrLn $ "Peeked Values: " ++ show vals
+
+--------------------------------------------------------------------------------
+-- luaopen_* functions
+
+testOpen :: String -> (LuaState -> IO ())  -> Test
+testOpen lib openfn = TestLabel ("open" ++ lib) $ TestCase $ do
+    l <- newstate
+    openlibs l
+    loadInspect l
+
+    getglobal l "print"
+    getglobal2 l "inspect.inspect"
+    openfn l
+    call l 1 1
+    call l 1 0
+
+    close l
+
+testOpenBase :: Test
+testOpenBase = TestLabel "openbase" $ TestCase $ do
+    l <- newstate
+    openlibs l
+    loadInspect l
+
+    getglobal l "print"
+    getglobal2 l "inspect.inspect"
+    getglobal l "print"
+    getglobal2 l "inspect.inspect"
+
+    openbase l
+    insert l 3
+
+    putStrLn "--- First table ---"
+
+    call l 1 1
+    call l 1 0
+
+    putStrLn "--- Second table ---"
+
+    call l 1 1
+    call l 1 0
+
+    return ()
+
+loadInspect :: LuaState -> Assertion
+loadInspect l = do
+    loadRet <- loadfile l "test/inspect.lua"
+    assertEqual "load failed" 0 loadRet
+    pcallRet <- pcall l 0 multret 0
+
+    when (pcallRet /= 0) $ do
+      msg <- tostring l 1
+      close l
+      assertFailure ("pcall failed with " ++ show pcallRet ++ "\n" ++
+                     "error message was: " ++ B.unpack msg)
+
+    setglobal l "inspect"
