elf 0.27 → 0.31
raw patch · 15 files changed
Files
- LICENSE +26/−26
- Setup.hs +2/−6
- elf.cabal +47/−25
- src/Data/Elf.hs +154/−42
- testdata/HOWTO +17/−0
- testdata/bloated binary
- testdata/bloated.cpp +7/−0
- testdata/empty +0/−0
- testdata/tiny binary
- testdata/tiny.asm +8/−0
- testdata/vdso binary
- tests/Data/ElfSpec.hs +143/−0
- tests/Spec.hs +1/−0
- tests/Test.hs +0/−21
- tests/empty.elf +0/−0
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