packages feed

elf 0.28 → 0.29

raw patch · 14 files changed

+301/−104 lines, 14 filesdep +containersdep +elfdep +hspecdep ~basedep ~binarysetup-changedbinary-addedPVP ok

version bump matches the API change (PVP)

Dependencies added: containers, elf, hspec

Dependency ranges changed: base, binary

API changes (from Hackage documentation)

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,24 +1,46 @@-Name:          elf-Version:       0.28-License:       BSD3-License-file:  LICENSE-Category:      Data-Author:        Erik Charlebois-Copyright:     Erik Charlebois-Maintainer:    Baojun Wang <wangbj@gmail.com>-Stability:     stable-Cabal-Version: >= 1.6-Homepage:      https://github.com/wangbj/elf-Build-Type:    Custom-Synopsis:      Parser for ELF object format.-Description:   Parser for ELF object format.-Data-Files:    tests/empty.elf tests/Test.hs+Name:               elf+Version:            0.29+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 +tested-with:+    GHC == 8.4.3+  , 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-    hs-source-dirs:  src-    exposed-modules: Data.Elf+  build-depends:       base >= 2 && < 5+                     , bytestring+                     , binary+  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,4 +1,4 @@--- | Data.Elf  is a module for parsing a ByteString of an ELF file into an Elf record.+-- | Data.Elf is a module for parsing a ByteString of an ELF file into an Elf record. module Data.Elf ( parseElf                 , parseSymbolTables                 , findSymbolDefinition@@ -23,12 +23,11 @@ import Data.Binary.Get as G import Data.Bits import Data.Maybe-import Data.Word-import Numeric 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 +55,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 +87,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 +110,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 +139,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 +165,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 +194,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 +300,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 +535,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 +644,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@@ -656,11 +677,33 @@ -- | Assumes the given section is a symbol table, type SHT_SYMTAB -- (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])+getSymbolTableEntries e s = go decoder (L.fromChunks [elfSectionData s])+  where+    link   = elfSectionLink s+    strtab = lookup (fromIntegral link) (zip [0..] (elfSections e))+    decoder = runGetIncremental (getSymbolTableEntry e strtab)+    go :: Decoder ElfSymbolTableEntry -> L.ByteString -> [ElfSymbolTableEntry]+    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',@@ -668,16 +711,11 @@ findSymbolDefinition :: ElfSymbolTableEntry -> Maybe B.ByteString findSymbolDefinition e = steEnclosingSection e >>= \enclosingSection ->     let enclosingData = elfSectionData enclosingSection-        start = ( (fromIntegral (steValue e)) - (fromIntegral (elfSectionAddr enclosingSection) ) )+        start = (fromIntegral (steValue e)) - (fromIntegral (elfSectionAddr enclosingSection))         len = fromIntegral (steSize e)         def = (B.take len . B.drop start) enclosingData     in if B.null def then Nothing else Just 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'- symbolTableSections :: Elf -> [ElfSection] symbolTableSections e = filter ((== SHT_SYMTAB) . elfSectionType) (elfSections e) @@ -686,7 +724,7 @@ 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 +750,7 @@     return $ EST (nameIdx,name) sec typ bind other sTlbIdx symVal size  sectionByIndex :: Elf -> ElfSectionIndex -> Maybe ElfSection-sectionByIndex e (SHNIndex i) = lookup i . zip [0..] $ (elfSections e)+sectionByIndex e (SHNIndex i) = lookup i . zip [0..] $ elfSections e sectionByIndex _ _ = Nothing  infoToTypeAndBind :: Word8 -> (ElfSymbolType,ElfSymbolBinding)@@ -831,5 +869,3 @@ stringByIndex n strtab =     let str = (B.takeWhile (/=0) . B.drop (fromIntegral n)) strtab     in if B.length str == 0 then Nothing else Just str--
+ 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
+ tests/Data/ElfSpec.hs view
@@ -0,0 +1,131 @@+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"++    let tinyElf = parseElf tinyContents+        bloatedElf = parseElf bloatedContents++    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
+ 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