packages feed

elf 0.27 → 0.31

raw patch · 15 files changed

Files

LICENSE view
@@ -1,26 +1,26 @@-Copyright (c) Erik Charlebois.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. The names of the author may not be used to endorse or promote
-   products derived from this software without specific prior written
-   permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
+Copyright (c) Erik Charlebois.+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. The names of the author may not be used to endorse or promote+   products derived from this software without specific prior written+   permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
Setup.hs view
@@ -1,6 +1,2 @@-import Distribution.Simple
-import System.Cmd(system)
-
-main = defaultMainWithHooks $ simpleUserHooks { runTests = runElfTests }
-
-runElfTests a b pd lb = system "runhaskell -i./src ./tests/Test.hs" >> return ()
+import Distribution.Simple+main = defaultMain
elf.cabal view
@@ -1,25 +1,47 @@-Name:          elf
-Version:       0.27
-License:       BSD3
-License-file:  LICENSE
-Category:      Data
-Author:        Erik Charlebois
-Copyright:     Erik Charlebois
-Maintainer:    Erik Charlebois <erikcharlebois@gmail.com>
-Stability:     stable
-Cabal-Version: >= 1.6
-Homepage:      http://github.com/erikcharlebois/elf
-Build-Depends: base
-Build-Type:    Custom
-Synopsis:      Parser for ELF object format.
-Description:   Parser for ELF object format.
-Data-Files:    tests/empty.elf tests/Test.hs
-
-source-repository head
-  type:     git
-  location: http://github.com/erikcharlebois/elf
-
-library
-    build-depends:   base >= 2 && < 5, bytestring, binary
-    hs-source-dirs:  src
-    exposed-modules: Data.Elf
+Name:               elf+Version:            0.31+License:            BSD3+License-file:       LICENSE+Category:           Data+Author:             Erik Charlebois+Copyright:          Erik Charlebois+Maintainer:         Baojun Wang <wangbj@gmail.com>+Stability:          stable+Cabal-Version:      >= 1.10+Homepage:           https://github.com/wangbj/elf+Build-Type:         Simple+Synopsis:           An Elf parser+Description:        Parser for ELF object format.+Extra-Source-Files: testdata/tiny testdata/bloated testdata/HOWTO+                    testdata/bloated.cpp testdata/tiny.asm+                    testdata/empty testdata/vdso++tested-with:+    GHC == 8.6.2+  , GHC == 8.4.4+  , GHC == 8.2.2+  , GHC == 8.0.2++source-repository head+  type:     git+  location: https://github.com/wangbj/elf.git++library+  build-depends:       base >= 2 && < 5+                     , bytestring+                     , binary >= 0.6+  hs-source-dirs:    src+  exposed-modules:   Data.Elf+  default-language:  Haskell2010++test-suite tests+  type:                exitcode-stdio-1.0+  hs-source-dirs:      tests+  main-is:             Spec.hs+  other-modules:       Data.ElfSpec+  build-depends:       base+                     , bytestring+                     , hspec >= 2.4 && < 3+                     , containers >= 0.5.9.2+                     , elf+  default-language:  Haskell2010
src/Data/Elf.hs view
@@ -1,34 +1,45 @@--- | Data.Elf  is a module for parsing a ByteString of an ELF file into an Elf record.+{-# LANGUAGE ScopedTypeVariables #-}++-- | Data.Elf is a module for parsing a ByteString of an ELF file into an Elf record. module Data.Elf ( parseElf                 , parseSymbolTables+                , parseRelocations                 , findSymbolDefinition+                  -- * Top-level header                 , Elf(..)+                , ElfClass(..)+                , ElfData(..)+                , ElfOSABI(..)+                , ElfType(..)+                , ElfMachine(..)+                  -- * Sections                 , ElfSection(..)                 , ElfSectionType(..)                 , ElfSectionFlags(..)+                  -- * Segments                 , ElfSegment(..)                 , ElfSegmentType(..)                 , ElfSegmentFlag(..)-                , ElfClass(..)-                , ElfData(..)-                , ElfOSABI(..)-                , ElfType(..)-                , ElfMachine(..)+                  -- * Symbols                 , ElfSymbolTableEntry(..)                 , ElfSymbolType(..)                 , ElfSymbolBinding(..)-                , ElfSectionIndex(..)) where+                , ElfSectionIndex(..)+                  -- * Relocations+                , ElfRel(..)+                , ElfRelocationSection(..)+                ) where  import Data.Binary import Data.Binary.Get as G import Data.Bits import Data.Maybe-import Data.Word-import Numeric+import Data.Int import Control.Monad-import qualified Data.ByteString          as B-import qualified Data.ByteString.Internal as B-import qualified Data.ByteString.Lazy     as L+import qualified Data.ByteString               as B+import qualified Data.ByteString.Internal      as B+import qualified Data.ByteString.Lazy          as L+import qualified Data.ByteString.Lazy.Internal as L  data Elf = Elf     { elfClass      :: ElfClass      -- ^ Identifies the class of the object file.@@ -56,19 +67,22 @@     , elfSectionData      :: B.ByteString      -- ^ The raw data for the section.     } deriving (Eq, Show) +elfMagic :: [Word8]+elfMagic = [0x7f, 0x45, 0x4c, 0x46] -- "\DELELF"++getElfMagic :: Get [Word8] getElfMagic = do-    ei_magic <- liftM (map B.w2c) $ sequence [getWord8, getWord8, getWord8, getWord8]-    if ei_magic /= "\DELELF" then-        fail "Invalid magic number for ELF"-     else-        return ei_magic+    ei_magic <- replicateM 4 getWord8+    if ei_magic /= elfMagic+        then fail "Invalid magic number for ELF"+        else return ei_magic +getElfVersion :: Get Word8 getElfVersion = do     ei_version <- getWord8-    if ei_version /= 1 then-        fail "Invalid version number for ELF"-     else-        return ei_version+    if ei_version /= 1+        then fail "Invalid version number for ELF"+        else return ei_version  data ElfSectionType     = SHT_NULL          -- ^ Identifies an empty section header.@@ -85,6 +99,8 @@     | SHT_DYNSYM        -- ^ Contains a dynamic loader symbol table     | SHT_EXT Word32    -- ^ Processor- or environment-specific type     deriving (Eq, Show)++getElfSectionType :: ElfReader -> Get ElfSectionType getElfSectionType er = liftM getElfSectionType_ $ getWord32 er     where getElfSectionType_ 0  = SHT_NULL           getElfSectionType_ 1  = SHT_PROGBITS@@ -106,19 +122,26 @@     | SHF_EXECINSTR -- ^ Section contains executable instructions     | SHF_EXT Int   -- ^ Processor- or environment-specific flag     deriving (Eq, Show)++getElfSectionFlags :: Bits a => Int -> a -> [ElfSectionFlags] getElfSectionFlags 0 word = [] getElfSectionFlags 1 word | testBit word 0     = SHF_WRITE     : getElfSectionFlags 0 word getElfSectionFlags 2 word | testBit word 1     = SHF_ALLOC     : getElfSectionFlags 1 word getElfSectionFlags 3 word | testBit word 2     = SHF_EXECINSTR : getElfSectionFlags 2 word getElfSectionFlags n word | testBit word (n-1) = SHF_EXT (n-1) : getElfSectionFlags (n-1) word getElfSectionFlags n word = getElfSectionFlags (n-1) word-getElfSectionFlags32 = liftM (getElfSectionFlags 32) . getWord32 ++getElfSectionFlags32 :: ElfReader -> Get [ElfSectionFlags]+getElfSectionFlags64 :: ElfReader -> Get [ElfSectionFlags]+getElfSectionFlags32 = liftM (getElfSectionFlags 32) . getWord32 getElfSectionFlags64 = liftM (getElfSectionFlags 64) . getWord64-    + data ElfClass     = ELFCLASS32 -- ^ 32-bit ELF format     | ELFCLASS64 -- ^ 64-bit ELF format     deriving (Eq, Show)++getElfClass :: Get ElfClass getElfClass = getWord8 >>= getElfClass_     where getElfClass_ 1 = return ELFCLASS32           getElfClass_ 2 = return ELFCLASS64@@ -128,6 +151,8 @@     = ELFDATA2LSB -- ^ Little-endian ELF format     | ELFDATA2MSB -- ^ Big-endian ELF format     deriving (Eq, Show)++getElfData :: Get ElfData getElfData = getWord8 >>= getElfData_     where getElfData_ 1 = return ELFDATA2LSB           getElfData_ 2 = return ELFDATA2MSB@@ -152,6 +177,8 @@     | ELFOSABI_STANDALONE -- ^ Standalone (embedded) application     | ELFOSABI_EXT Word8  -- ^ Other     deriving (Eq, Show)++getElfOsabi :: Get ElfOSABI getElfOsabi = liftM getElfOsabi_ getWord8     where getElfOsabi_ 0   = ELFOSABI_SYSV           getElfOsabi_ 1   = ELFOSABI_HPUX@@ -179,6 +206,8 @@     | ET_CORE       -- ^ Core dump object file     | ET_EXT Word16 -- ^ Other     deriving (Eq, Show)++getElfType :: ElfReader -> Get ElfType getElfType = liftM getElfType_ . getWord16     where getElfType_ 0 = ET_NONE           getElfType_ 1 = ET_REL@@ -283,6 +312,8 @@     | EM_UNICORE     -- ^ Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University     | EM_EXT Word16  -- ^ Other     deriving (Eq, Show)++getElfMachine :: ElfReader -> Get ElfMachine getElfMachine = liftM getElfMachine_ . getWord16     where getElfMachine_ 0   = EM_NONE           getElfMachine_ 1   = EM_M32@@ -516,11 +547,13 @@     , getWord32 :: Get Word32     , getWord64 :: Get Word64     }++elfReader :: ElfData -> ElfReader elfReader ELFDATA2LSB = ElfReader { getWord16 = getWord16le, getWord32 = getWord32le, getWord64 = getWord64le } elfReader ELFDATA2MSB = ElfReader { getWord16 = getWord16be, getWord32 = getWord32be, getWord64 = getWord64be }  divide :: B.ByteString -> Int -> Int -> [B.ByteString]-divide bs s 0 = []+divide  _ _ 0 = [] divide bs s n = let (x,y) = B.splitAt s bs in x : divide y s (n-1)  -- | Parses a ByteString into an Elf record. Parse failures call error. 32-bit ELF objects have their@@ -623,7 +656,7 @@     deriving (Eq,Show)  parseElfSegmentFlags :: Word32 -> [ElfSegmentFlag]-parseElfSegmentFlags word = [ cvt bit | bit <- [ 0 .. 31 ], testBit word bit ]+parseElfSegmentFlags word = [ cvt bit_ | bit_ <- [ 0 .. 31 ], testBit word bit_ ]   where cvt 0 = PF_X         cvt 1 = PF_W         cvt 2 = PF_R@@ -653,40 +686,63 @@     let secs = symbolTableSections e     in map (getSymbolTableEntries e) secs --- | Assumes the given section is a symbol table, type SHT_SYMTAB+-- | Assumes the given section is a symbol table, type SHT_SYMTAB, or SHT_DYNSYM -- (guaranteed by parseSymbolTables). getSymbolTableEntries :: Elf -> ElfSection -> [ElfSymbolTableEntry] getSymbolTableEntries e s =-    let link   = elfSectionLink s-        strtab = lookup (fromIntegral link) (zip [0..] (elfSections e))-    in runGetMany (getSymbolTableEntry e strtab) (L.fromChunks [elfSectionData s])+    decodeMany (getSymbolTableEntry e strtab) (L.fromStrict (elfSectionData s))+  where+    link   = elfSectionLink s+    strtab = lookup (fromIntegral link) (zip [0..] (elfSections e)) +decodeMany :: forall a. Get a -> L.ByteString -> [a]+decodeMany get = go decoder+  where+    decoder = runGetIncremental get++    go :: Decoder a -> L.ByteString -> [a]+    go (Done leftover _ entry) input =+      entry : go decoder (L.Chunk leftover input)+    go (Partial k) input =+      go (k . takeHeadChunk $ input) (dropHeadChunk input)+    go (Fail _ _ msg) input = if L.null input+                              then []+                              else error msg++takeHeadChunk :: L.ByteString -> Maybe B.ByteString+takeHeadChunk lbs =+  case lbs of+    (L.Chunk bs _) -> Just bs+    _ -> Nothing++dropHeadChunk :: L.ByteString -> L.ByteString+dropHeadChunk lbs =+  case lbs of+    (L.Chunk _ lbs') -> lbs'+    _ -> L.Empty++ -- | Use the symbol offset and size to extract its definition -- (in the form of a ByteString). -- If the size is zero, or the offset larger than the 'elfSectionData', -- then 'Nothing' is returned. findSymbolDefinition :: ElfSymbolTableEntry -> Maybe B.ByteString-findSymbolDefinition e =-    let enclosingData = fmap elfSectionData (steEnclosingSection e)-        start = fromIntegral (steValue e)+findSymbolDefinition e = steEnclosingSection e >>= \enclosingSection ->+    let enclosingData = elfSectionData enclosingSection+        start = (fromIntegral (steValue e)) - (fromIntegral (elfSectionAddr enclosingSection))         len = fromIntegral (steSize e)-        def = fmap (B.take len . B.drop start) enclosingData-    in if def == Just B.empty then Nothing else def--runGetMany :: Get a -> L.ByteString -> [a]-runGetMany g bs-    | L.length bs == 0 = []-    | otherwise        = let (v,bs',_) = runGetState g bs 0 in v: runGetMany g bs'+        def = (B.take len . B.drop start) enclosingData+    in if B.null def then Nothing else Just def  symbolTableSections :: Elf -> [ElfSection]-symbolTableSections e = filter ((== SHT_SYMTAB) . elfSectionType) (elfSections e)+symbolTableSections e = filter ((`elem` [SHT_SYMTAB, SHT_DYNSYM]) . elfSectionType) (elfSections e)  -- | Gets a single entry from the symbol table, use with runGetMany. getSymbolTableEntry :: Elf -> Maybe ElfSection -> Get ElfSymbolTableEntry getSymbolTableEntry e strtlb =     if elfClass e == ELFCLASS32 then getSymbolTableEntry32 else getSymbolTableEntry64   where-  strs = fromMaybe B.empty (fmap elfSectionData strtlb)+  strs = maybe B.empty elfSectionData strtlb   er = elfReader (elfData e)   getSymbolTableEntry32 = do     nameIdx <- liftM fromIntegral (getWord32 er)@@ -712,7 +768,7 @@     return $ EST (nameIdx,name) sec typ bind other sTlbIdx symVal size  sectionByIndex :: Elf -> ElfSectionIndex -> Maybe ElfSection-sectionByIndex e (SHNIndex i) = lookup i . zip [1..] $ (elfSections e)+sectionByIndex e (SHNIndex i) = lookup i . zip [0..] $ elfSections e sectionByIndex _ _ = Nothing  infoToTypeAndBind :: Word8 -> (ElfSymbolType,ElfSymbolBinding)@@ -832,4 +888,60 @@     let str = (B.takeWhile (/=0) . B.drop (fromIntegral n)) strtab     in if B.length str == 0 then Nothing else Just str +data ElfRel = ElfRel+    { elfRelOffset    :: Word64+    , elfRelSymbol    :: Word64+    , elfRelType      :: Word8+    , elfRelSymAddend :: Maybe Int64+    } deriving (Eq, Show) +getElfRel :: ElfClass+          -> ElfReader+          -> Bool   -- ^ explicit addend?+          -> Get ElfRel+getElfRel ELFCLASS64 er explicit = do+    offset <- getWord64 er+    info <- getWord64 er+    addend <- if explicit then Just <$> getWord64 er else pure Nothing+    return ElfRel { elfRelOffset    = offset+                  , elfRelSymbol    = fromIntegral (info `shiftR` 32)+                  , elfRelType      = fromIntegral info+                  , elfRelSymAddend = fromIntegral <$> addend+                  }+getElfRel ELFCLASS32 er explicit = do+    offset <- getWord32 er+    info <- getWord32 er+    addend <- if explicit then Just <$> getWord32 er else pure Nothing+    return ElfRel { elfRelOffset    = fromIntegral offset+                  , elfRelSymbol    = fromIntegral (info `shiftR` 8)+                  , elfRelType      = fromIntegral info+                  , elfRelSymAddend = fromIntegral <$> addend+                  }++getRelocations :: ElfClass -> ElfReader -> ElfSection -> [ElfRel]+getRelocations elf_class er s =+    decodeMany (getElfRel elf_class er explicit) (L.fromStrict (elfSectionData s))+  where+    explicit = elfSectionType s == SHT_RELA++data ElfRelocationSection = ElfRelocationSection+    { elfRelSectSymbolTable :: [ElfSymbolTableEntry]+    , elfRelSectRelocated   :: ElfSection+    , elfRelSectRelocations :: [ElfRel]+    } deriving (Eq, Show)++parseRelocations :: Elf -> [ElfRelocationSection]+parseRelocations elf =+    [ ElfRelocationSection { elfRelSectSymbolTable = symtab+                           , elfRelSectRelocated   = relocated+                           , elfRelSectRelocations = rels+                           }+    | s <- elfSections elf+    , elfSectionType s `elem` [SHT_REL, SHT_RELA]+    , let symtab = getSymbolTableEntries elf (getSection (elfSectionLink s))+          relocated = getSection (elfSectionInfo s)+          rels = getRelocations (elfClass elf) er s+    ]+  where+    getSection i = elfSections elf !! fromIntegral i+    er = elfReader (elfData elf)
+ testdata/HOWTO view
@@ -0,0 +1,17 @@+'tiny' was generated this way on a 64bit linux system:++  $ nasm -f elf64 tiny.asm+  $ gcc -o tiny tiny.o -nostartfiles -nostdlib -nodefaultlibs+  $ strip -s tiny+  $ strip --remove-section=.note.gnu.build-id tiny++Thank you whoever wrote this tutorial: <http://timelessname.com/elfbin/>.++'bloated' was generated this way on a 64bit linux system:++  $ clang++ -m32 -o bloated bloated.cpp++This HOWTO is provided for documentation purposes, elf should not be+regenerated/changed because tests assert their content. Their content was read+by the readelf utility command.+
+ testdata/bloated view

binary file changed (absent → 7880 bytes)

+ testdata/bloated.cpp view
@@ -0,0 +1,7 @@+#include <iostream>++int main(int argc, char *argv[])+{+	std::cout << "Hello world!" << std::endl;+	return 0;+}
+ testdata/empty view
+ testdata/tiny view

binary file changed (absent → 448 bytes)

+ testdata/tiny.asm view
@@ -0,0 +1,8 @@+SECTION .text++global _start++_start:+    mov ebx,0+    mov eax,1+    int 0x80
+ testdata/vdso view

binary file changed (absent → 8192 bytes)

+ tests/Data/ElfSpec.hs view
@@ -0,0 +1,143 @@+module Data.ElfSpec (spec) where++import Test.Hspec++import Control.Exception (evaluate)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as C+import Data.Either+import Data.Foldable (find)+import qualified Data.Map as Map+import Data.Maybe+import System.IO++import Data.Elf+++getBinaryFileContents :: FilePath -> IO BS.ByteString+getBinaryFileContents fname = withBinaryFile fname ReadMode BS.hGetContents+++spec :: Spec+spec = do+    emptyContents   <- runIO $ getBinaryFileContents "./testdata/empty"+    tinyContents    <- runIO $ getBinaryFileContents "./testdata/tiny"+    bloatedContents <- runIO $ getBinaryFileContents "./testdata/bloated"+    dynsymContents  <- runIO $ getBinaryFileContents "./testdata/vdso"++    let tinyElf = parseElf tinyContents+        bloatedElf = parseElf bloatedContents+        dynsymElf  = parseElf dynsymContents++    describe "parseElf" $ do+        -- TODO: That was the original only test in this package. This test+        -- should be removed, and future versions of this library should return+        -- an 'Either ParseError Elf'.+        it "does not accept an empty elf" $+            evaluate (parseElf emptyContents) `shouldThrow` anyException++        context "Headers parsing" $ do++            it "parses the version" $+                elfVersion tinyElf `shouldBe` 1++            it "parses the architecture" $ do+                elfClass tinyElf    `shouldBe` ELFCLASS64+                elfClass bloatedElf `shouldBe` ELFCLASS32++            it "parses the endianness" $ do+                elfData tinyElf `shouldBe` ELFDATA2LSB++            it "parses the OS ABI" $+                elfOSABI tinyElf `shouldBe` ELFOSABI_SYSV++            it "parses the type" $+                elfType bloatedElf `shouldBe` ET_EXEC++            it "parses the machine type" $ do+                elfMachine tinyElf    `shouldBe` EM_X86_64+                elfMachine bloatedElf `shouldBe` EM_386++            it "parses the entry point" $ do+                elfEntry tinyElf    `shouldBe` 0x4000e0+                elfEntry bloatedElf `shouldBe` 0x8048610++        context "Segment parsing" $ do+            let tinySegments    = elfSegments tinyElf+                bloatedSegments = elfSegments bloatedElf++            it "parses the right amount of segments" $ do+                length tinySegments    `shouldBe` 2+                length bloatedSegments `shouldBe` 9++            it "parses segment types" $+                let segmentTypes = map elfSegmentType tinySegments in+                segmentTypes `shouldBe` [PT_LOAD, PT_NOTE]++            it "parses segment flags" $ do+                let segmentFlags = map elfSegmentFlags tinySegments+                segmentFlags !! 0 `shouldMatchList` [PF_R, PF_X]+                segmentFlags !! 1 `shouldMatchList` [PF_R]++        context "Section parsing" $ do+            let tinySections    = elfSections tinyElf+                bloatedSections = elfSections bloatedElf++            it "parses the right amount of sections" $ do+                length tinySections    `shouldBe` 3+                length bloatedSections `shouldBe` 31++            it "parses the section in the right order" $ do+                map elfSectionName tinySections `shouldBe` [ "", ".text", ".shstrtab" ]++            it "parses the section types" $+                let sectionTypes = map elfSectionType bloatedSections in+                take 5 sectionTypes `shouldBe` [ SHT_NULL, SHT_PROGBITS, SHT_NOTE+                                               , SHT_NOTE, SHT_EXT 1879048182]++            it "parses the data" $+                let comment  = find (\sec -> elfSectionName sec == ".comment") bloatedSections+                    expected = C.pack . concat $ [ "GCC: (GNU) 6.3.1 20161221 (Red Hat 6.3.1-1)\NUL"+                                                 , "clang version 3.8.1 (tags/RELEASE_381/final)\NUL"+                                                 ]+                in+                fmap elfSectionData comment `shouldBe` Just expected++    describe "findSymbolDefinition" $ do+        let tinySymbols    = parseSymbolTables tinyElf+            bloatedSymbols = parseSymbolTables bloatedElf++        it "parses stripped symbol" $+            -- This binary was stripped+            concat tinySymbols `shouldSatisfy` all (isNothing . snd . steName)++        let namedBloatedSymbols =+                let go sym = fmap (\ name -> (name, sym)) $ snd (steName sym)+                in Map.fromList $ catMaybes $ map go $ concat bloatedSymbols++            member k = Map.member (C.pack k)+            (!?) m k = m Map.!? (C.pack k)++        it "parses symbol symbol names" $ do+            namedBloatedSymbols `shouldSatisfy` member "_init"+            namedBloatedSymbols `shouldSatisfy` member "main"++        let initSymbol  = namedBloatedSymbols !? "_init"+            fnameSymbol = namedBloatedSymbols !? "bloated.cpp"++        it "parses symbol address" $+            fmap steValue initSymbol `shouldBe` Just 0x0804850c++        it "parses symbol type" $ do+            fmap steType initSymbol  `shouldBe` Just STTFunc+            fmap steType fnameSymbol `shouldBe` Just STTFile+    describe "parse DynSym symbols" $ do+        let dynSymbols    = parseSymbolTables dynsymElf+        it "parses dyn symbol table" $ do+          dynSymbols `shouldNotBe` []+        it "parse (x86_64) vdso dyn symbols" $ do+          let dynSyms = concat dynSymbols+          filter (\e -> (snd . steName) e == (Just . C.pack) "__vdso_time") dynSyms `shouldNotBe` []+          filter (\e -> (snd . steName) e == (Just . C.pack) "__vdso_getcpu") dynSyms `shouldNotBe` []+          filter (\e -> (snd . steName) e == (Just . C.pack) "__vdso_clock_gettime") dynSyms `shouldNotBe` []+          filter (\e -> (snd . steName) e == (Just . C.pack) "__vdso_gettimeofday") dynSyms `shouldNotBe` []
+ tests/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
− tests/Test.hs
@@ -1,21 +0,0 @@-module Main where
-
-import Data.Elf
-import System.IO
-import Test.HUnit
-import Control.Monad
-import qualified Control.Exception as E
-import qualified Data.ByteString as B
-
-testEmptyElf = withBinaryFile "./tests/empty.elf" ReadMode $ \h -> do
-    fil <- B.hGetContents h
-    res <- E.try (E.evaluate (parseElf fil)) :: IO (Either E.SomeException Elf)
-    case res of
-        Left  e -> return ()
-        Right a -> assertFailure "Empty ELF did not cause an exception."
-
-tests = TestList
-    [ TestLabel "Empty ELF" $ TestCase testEmptyElf
-    ]
-
-main = runTestTT tests
− tests/empty.elf