├── .gitignore ├── .travis.yml ├── ChangeLog.md ├── LICENSE ├── README.md ├── Setup.hs ├── elf.cabal ├── src └── Data │ └── Elf.hs ├── testdata ├── HOWTO ├── bloated ├── bloated.cpp ├── empty ├── tiny ├── tiny.asm └── vdso └── tests ├── Data └── ElfSpec.hs └── Spec.hs /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | dist-* 3 | cabal-dev 4 | *.o 5 | *.hi 6 | *.chi 7 | *.chs.h 8 | *.dyn_o 9 | *.dyn_hi 10 | .hpc 11 | .hsenv 12 | .cabal-sandbox/ 13 | cabal.sandbox.config 14 | *.prof 15 | *.aux 16 | *.hp 17 | *.eventlog 18 | .stack-work/ 19 | cabal.project.local 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # This Travis job script has been generated by a script via 2 | # 3 | # runghc make_travis_yml_2.hs 'elf.cabal' 4 | # 5 | # For more information, see https://github.com/hvr/multi-ghc-travis 6 | # 7 | language: c 8 | sudo: false 9 | 10 | git: 11 | submodules: false # whether to recursively clone submodules 12 | 13 | cache: 14 | directories: 15 | - $HOME/.cabal/packages 16 | - $HOME/.cabal/store 17 | 18 | before_cache: 19 | - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log 20 | # remove files that are regenerated by 'cabal update' 21 | - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.* 22 | - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json 23 | - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache 24 | - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar 25 | - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx 26 | 27 | - rm -rfv $HOME/.cabal/packages/head.hackage 28 | 29 | matrix: 30 | include: 31 | - compiler: "ghc-8.6.3" 32 | # env: TEST=--disable-tests BENCH=--disable-benchmarks 33 | addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.4,ghc-8.6.3], sources: [hvr-ghc]}} 34 | - compiler: "ghc-8.4.4" 35 | # env: TEST=--disable-tests BENCH=--disable-benchmarks 36 | addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.4], sources: [hvr-ghc]}} 37 | - compiler: "ghc-8.2.2" 38 | # env: TEST=--disable-tests BENCH=--disable-benchmarks 39 | addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}} 40 | - compiler: "ghc-8.0.2" 41 | # env: TEST=--disable-tests BENCH=--disable-benchmarks 42 | addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}} 43 | 44 | before_install: 45 | - HC=${CC} 46 | - HCPKG=${HC/ghc/ghc-pkg} 47 | - unset CC 48 | - ROOTDIR=$(pwd) 49 | - mkdir -p $HOME/.local/bin 50 | - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH" 51 | - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') )) 52 | - echo $HCNUMVER 53 | 54 | install: 55 | - cabal --version 56 | - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]" 57 | - BENCH=${BENCH---enable-benchmarks} 58 | - TEST=${TEST---enable-tests} 59 | - HADDOCK=${HADDOCK-true} 60 | - INSTALLED=${INSTALLED-true} 61 | - GHCHEAD=${GHCHEAD-false} 62 | - travis_retry cabal update -v 63 | - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config" 64 | - rm -fv cabal.project cabal.project.local 65 | - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$' 66 | - "printf 'packages: \".\"\\n' > cabal.project" 67 | - cat cabal.project 68 | - if [ -f "./configure.ac" ]; then 69 | (cd "." && autoreconf -i); 70 | fi 71 | - rm -f cabal.project.freeze 72 | - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all 73 | - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all 74 | - rm -rf .ghc.environment.* "."/dist 75 | - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX) 76 | 77 | # Here starts the actual work to be performed for the package under test; 78 | # any command which exits with a non-zero exit code causes the build to fail. 79 | script: 80 | # test that source-distributions can be generated 81 | - (cd "." && cabal sdist) 82 | - mv "."/dist/elf-*.tar.gz ${DISTDIR}/ 83 | - cd ${DISTDIR} || false 84 | - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \; 85 | - "printf 'packages: elf-*/*.cabal\\n' > cabal.project" 86 | - cat cabal.project 87 | # this builds all libraries and executables (without tests/benchmarks) 88 | - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all 89 | 90 | # Build with installed constraints for packages in global-db 91 | - if $INSTALLED; then echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks $(${HCPKG} list --global --simple-output --names-only | sed 's/\([a-zA-Z0-9-]\{1,\}\) */--constraint="\1 installed" /g') all | sh; else echo "Not building with installed constraints"; fi 92 | 93 | # install hspec-discover required by tests 94 | - cabal new-install hspec-discover 95 | # build & run tests, build benchmarks 96 | - cabal new-build -w ${HC} ${TEST} ${BENCH} all 97 | - if [ "x$TEST" = "x--enable-tests" ]; then cabal new-test -w ${HC} ${TEST} ${BENCH} all; fi 98 | 99 | # cabal check 100 | - (cd elf-* && cabal check) 101 | 102 | # haddock 103 | - rm -rf ./dist-newstyle 104 | - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi 105 | 106 | # REGENDATA ["elf.cabal"] 107 | # EOF 108 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog for detTrace 2 | 3 | # 0.30 4 | `parseSymbolTables` now parses `SHT_DYNSYM` in addition to `SHT_SYMTAB` 5 | 6 | # 0.29 7 | Add few more testcases 8 | remove calls to deprecated functions in `binary` 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Erik Charlebois. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. The names of the author may not be used to endorse or promote 13 | products derived from this software without specific prior written 14 | permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # elf 2 | Parser for ELF object format. 3 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /elf.cabal: -------------------------------------------------------------------------------- 1 | Name: elf 2 | Version: 0.31 3 | License: BSD3 4 | License-file: LICENSE 5 | Category: Data 6 | Author: Erik Charlebois 7 | Copyright: Erik Charlebois 8 | Maintainer: Baojun Wang 9 | Stability: stable 10 | Cabal-Version: >= 1.10 11 | Homepage: https://github.com/wangbj/elf 12 | Build-Type: Simple 13 | Synopsis: An Elf parser 14 | Description: Parser for ELF object format. 15 | Extra-Source-Files: testdata/tiny testdata/bloated testdata/HOWTO 16 | testdata/bloated.cpp testdata/tiny.asm 17 | testdata/empty testdata/vdso 18 | 19 | tested-with: 20 | GHC == 8.6.2 21 | , GHC == 8.4.4 22 | , GHC == 8.2.2 23 | , GHC == 8.0.2 24 | 25 | source-repository head 26 | type: git 27 | location: https://github.com/wangbj/elf.git 28 | 29 | library 30 | build-depends: base >= 2 && < 5 31 | , bytestring 32 | , binary >= 0.6 33 | hs-source-dirs: src 34 | exposed-modules: Data.Elf 35 | default-language: Haskell2010 36 | 37 | test-suite tests 38 | type: exitcode-stdio-1.0 39 | hs-source-dirs: tests 40 | main-is: Spec.hs 41 | other-modules: Data.ElfSpec 42 | build-depends: base 43 | , bytestring 44 | , hspec >= 2.4 && < 3 45 | , containers >= 0.5.9.2 46 | , elf 47 | default-language: Haskell2010 48 | -------------------------------------------------------------------------------- /src/Data/Elf.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ScopedTypeVariables #-} 2 | 3 | -- | Data.Elf is a module for parsing a ByteString of an ELF file into an Elf record. 4 | module Data.Elf ( parseElf 5 | , parseSymbolTables 6 | , parseRelocations 7 | , findSymbolDefinition 8 | -- * Top-level header 9 | , Elf(..) 10 | , ElfClass(..) 11 | , ElfData(..) 12 | , ElfOSABI(..) 13 | , ElfType(..) 14 | , ElfMachine(..) 15 | -- * Sections 16 | , ElfSection(..) 17 | , ElfSectionType(..) 18 | , ElfSectionFlags(..) 19 | -- * Segments 20 | , ElfSegment(..) 21 | , ElfSegmentType(..) 22 | , ElfSegmentFlag(..) 23 | -- * Symbols 24 | , ElfSymbolTableEntry(..) 25 | , ElfSymbolType(..) 26 | , ElfSymbolBinding(..) 27 | , ElfSectionIndex(..) 28 | -- * Relocations 29 | , ElfRel(..) 30 | , ElfRelocationSection(..) 31 | ) where 32 | 33 | import Data.Binary 34 | import Data.Binary.Get as G 35 | import Data.Bits 36 | import Data.Maybe 37 | import Data.Int 38 | import Control.Monad 39 | import qualified Data.ByteString as B 40 | import qualified Data.ByteString.Internal as B 41 | import qualified Data.ByteString.Lazy as L 42 | import qualified Data.ByteString.Lazy.Internal as L 43 | 44 | data Elf = Elf 45 | { elfClass :: ElfClass -- ^ Identifies the class of the object file. 46 | , elfData :: ElfData -- ^ Identifies the data encoding of the object file. 47 | , elfVersion :: Int -- ^ Identifies the version of the object file format. 48 | , elfOSABI :: ElfOSABI -- ^ Identifies the operating system and ABI for which the object is prepared. 49 | , elfABIVersion :: Int -- ^ Identifies the ABI version for which the object is prepared. 50 | , elfType :: ElfType -- ^ Identifies the object file type. 51 | , elfMachine :: ElfMachine -- ^ Identifies the target architecture. 52 | , elfEntry :: Word64 -- ^ Virtual address of the program entry point. 0 for non-executable Elfs. 53 | , elfSections :: [ElfSection] -- ^ List of sections in the file. 54 | , elfSegments :: [ElfSegment] -- ^ List of segments in the file. 55 | } deriving (Eq, Show) 56 | 57 | data ElfSection = ElfSection 58 | { elfSectionName :: String -- ^ Identifies the name of the section. 59 | , elfSectionType :: ElfSectionType -- ^ Identifies the type of the section. 60 | , elfSectionFlags :: [ElfSectionFlags] -- ^ Identifies the attributes of the section. 61 | , elfSectionAddr :: Word64 -- ^ The virtual address of the beginning of the section in memory. 0 for sections that are not loaded into target memory. 62 | , elfSectionSize :: Word64 -- ^ The size of the section. Except for SHT_NOBITS sections, this is the size of elfSectionData. 63 | , elfSectionLink :: Word32 -- ^ Contains a section index of an associated section, depending on section type. 64 | , elfSectionInfo :: Word32 -- ^ Contains extra information for the index, depending on type. 65 | , elfSectionAddrAlign :: Word64 -- ^ Contains the required alignment of the section. Must be a power of two. 66 | , elfSectionEntSize :: Word64 -- ^ Size of entries if section has a table. 67 | , elfSectionData :: B.ByteString -- ^ The raw data for the section. 68 | } deriving (Eq, Show) 69 | 70 | elfMagic :: [Word8] 71 | elfMagic = [0x7f, 0x45, 0x4c, 0x46] -- "\DELELF" 72 | 73 | getElfMagic :: Get [Word8] 74 | getElfMagic = do 75 | ei_magic <- replicateM 4 getWord8 76 | if ei_magic /= elfMagic 77 | then fail "Invalid magic number for ELF" 78 | else return ei_magic 79 | 80 | getElfVersion :: Get Word8 81 | getElfVersion = do 82 | ei_version <- getWord8 83 | if ei_version /= 1 84 | then fail "Invalid version number for ELF" 85 | else return ei_version 86 | 87 | data ElfSectionType 88 | = SHT_NULL -- ^ Identifies an empty section header. 89 | | SHT_PROGBITS -- ^ Contains information defined by the program 90 | | SHT_SYMTAB -- ^ Contains a linker symbol table 91 | | SHT_STRTAB -- ^ Contains a string table 92 | | SHT_RELA -- ^ Contains "Rela" type relocation entries 93 | | SHT_HASH -- ^ Contains a symbol hash table 94 | | SHT_DYNAMIC -- ^ Contains dynamic linking tables 95 | | SHT_NOTE -- ^ Contains note information 96 | | SHT_NOBITS -- ^ Contains uninitialized space; does not occupy any space in the file 97 | | SHT_REL -- ^ Contains "Rel" type relocation entries 98 | | SHT_SHLIB -- ^ Reserved 99 | | SHT_DYNSYM -- ^ Contains a dynamic loader symbol table 100 | | SHT_EXT Word32 -- ^ Processor- or environment-specific type 101 | deriving (Eq, Show) 102 | 103 | getElfSectionType :: ElfReader -> Get ElfSectionType 104 | getElfSectionType er = liftM getElfSectionType_ $ getWord32 er 105 | where getElfSectionType_ 0 = SHT_NULL 106 | getElfSectionType_ 1 = SHT_PROGBITS 107 | getElfSectionType_ 2 = SHT_SYMTAB 108 | getElfSectionType_ 3 = SHT_STRTAB 109 | getElfSectionType_ 4 = SHT_RELA 110 | getElfSectionType_ 5 = SHT_HASH 111 | getElfSectionType_ 6 = SHT_DYNAMIC 112 | getElfSectionType_ 7 = SHT_NOTE 113 | getElfSectionType_ 8 = SHT_NOBITS 114 | getElfSectionType_ 9 = SHT_REL 115 | getElfSectionType_ 10 = SHT_SHLIB 116 | getElfSectionType_ 11 = SHT_DYNSYM 117 | getElfSectionType_ n = SHT_EXT n 118 | 119 | data ElfSectionFlags 120 | = SHF_WRITE -- ^ Section contains writable data 121 | | SHF_ALLOC -- ^ Section is allocated in memory image of program 122 | | SHF_EXECINSTR -- ^ Section contains executable instructions 123 | | SHF_EXT Int -- ^ Processor- or environment-specific flag 124 | deriving (Eq, Show) 125 | 126 | getElfSectionFlags :: Bits a => Int -> a -> [ElfSectionFlags] 127 | getElfSectionFlags 0 word = [] 128 | getElfSectionFlags 1 word | testBit word 0 = SHF_WRITE : getElfSectionFlags 0 word 129 | getElfSectionFlags 2 word | testBit word 1 = SHF_ALLOC : getElfSectionFlags 1 word 130 | getElfSectionFlags 3 word | testBit word 2 = SHF_EXECINSTR : getElfSectionFlags 2 word 131 | getElfSectionFlags n word | testBit word (n-1) = SHF_EXT (n-1) : getElfSectionFlags (n-1) word 132 | getElfSectionFlags n word = getElfSectionFlags (n-1) word 133 | 134 | getElfSectionFlags32 :: ElfReader -> Get [ElfSectionFlags] 135 | getElfSectionFlags64 :: ElfReader -> Get [ElfSectionFlags] 136 | getElfSectionFlags32 = liftM (getElfSectionFlags 32) . getWord32 137 | getElfSectionFlags64 = liftM (getElfSectionFlags 64) . getWord64 138 | 139 | data ElfClass 140 | = ELFCLASS32 -- ^ 32-bit ELF format 141 | | ELFCLASS64 -- ^ 64-bit ELF format 142 | deriving (Eq, Show) 143 | 144 | getElfClass :: Get ElfClass 145 | getElfClass = getWord8 >>= getElfClass_ 146 | where getElfClass_ 1 = return ELFCLASS32 147 | getElfClass_ 2 = return ELFCLASS64 148 | getElfClass_ _ = fail "Invalid ELF class" 149 | 150 | data ElfData 151 | = ELFDATA2LSB -- ^ Little-endian ELF format 152 | | ELFDATA2MSB -- ^ Big-endian ELF format 153 | deriving (Eq, Show) 154 | 155 | getElfData :: Get ElfData 156 | getElfData = getWord8 >>= getElfData_ 157 | where getElfData_ 1 = return ELFDATA2LSB 158 | getElfData_ 2 = return ELFDATA2MSB 159 | getElfData_ _ = fail "Invalid ELF data" 160 | 161 | data ElfOSABI 162 | = ELFOSABI_SYSV -- ^ No extensions or unspecified 163 | | ELFOSABI_HPUX -- ^ Hewlett-Packard HP-UX 164 | | ELFOSABI_NETBSD -- ^ NetBSD 165 | | ELFOSABI_LINUX -- ^ Linux 166 | | ELFOSABI_SOLARIS -- ^ Sun Solaris 167 | | ELFOSABI_AIX -- ^ AIX 168 | | ELFOSABI_IRIX -- ^ IRIX 169 | | ELFOSABI_FREEBSD -- ^ FreeBSD 170 | | ELFOSABI_TRU64 -- ^ Compaq TRU64 UNIX 171 | | ELFOSABI_MODESTO -- ^ Novell Modesto 172 | | ELFOSABI_OPENBSD -- ^ Open BSD 173 | | ELFOSABI_OPENVMS -- ^ Open VMS 174 | | ELFOSABI_NSK -- ^ Hewlett-Packard Non-Stop Kernel 175 | | ELFOSABI_AROS -- ^ Amiga Research OS 176 | | ELFOSABI_ARM -- ^ ARM 177 | | ELFOSABI_STANDALONE -- ^ Standalone (embedded) application 178 | | ELFOSABI_EXT Word8 -- ^ Other 179 | deriving (Eq, Show) 180 | 181 | getElfOsabi :: Get ElfOSABI 182 | getElfOsabi = liftM getElfOsabi_ getWord8 183 | where getElfOsabi_ 0 = ELFOSABI_SYSV 184 | getElfOsabi_ 1 = ELFOSABI_HPUX 185 | getElfOsabi_ 2 = ELFOSABI_NETBSD 186 | getElfOsabi_ 3 = ELFOSABI_LINUX 187 | getElfOsabi_ 6 = ELFOSABI_SOLARIS 188 | getElfOsabi_ 7 = ELFOSABI_AIX 189 | getElfOsabi_ 8 = ELFOSABI_IRIX 190 | getElfOsabi_ 9 = ELFOSABI_FREEBSD 191 | getElfOsabi_ 10 = ELFOSABI_TRU64 192 | getElfOsabi_ 11 = ELFOSABI_MODESTO 193 | getElfOsabi_ 12 = ELFOSABI_OPENBSD 194 | getElfOsabi_ 13 = ELFOSABI_OPENVMS 195 | getElfOsabi_ 14 = ELFOSABI_NSK 196 | getElfOsabi_ 15 = ELFOSABI_AROS 197 | getElfOsabi_ 97 = ELFOSABI_ARM 198 | getElfOsabi_ 255 = ELFOSABI_STANDALONE 199 | getElfOsabi_ n = ELFOSABI_EXT n 200 | 201 | data ElfType 202 | = ET_NONE -- ^ Unspecified type 203 | | ET_REL -- ^ Relocatable object file 204 | | ET_EXEC -- ^ Executable object file 205 | | ET_DYN -- ^ Shared object file 206 | | ET_CORE -- ^ Core dump object file 207 | | ET_EXT Word16 -- ^ Other 208 | deriving (Eq, Show) 209 | 210 | getElfType :: ElfReader -> Get ElfType 211 | getElfType = liftM getElfType_ . getWord16 212 | where getElfType_ 0 = ET_NONE 213 | getElfType_ 1 = ET_REL 214 | getElfType_ 2 = ET_EXEC 215 | getElfType_ 3 = ET_DYN 216 | getElfType_ 4 = ET_CORE 217 | getElfType_ n = ET_EXT n 218 | 219 | data ElfMachine 220 | = EM_NONE -- ^ No machine 221 | | EM_M32 -- ^ AT&T WE 32100 222 | | EM_SPARC -- ^ SPARC 223 | | EM_386 -- ^ Intel 80386 224 | | EM_68K -- ^ Motorola 68000 225 | | EM_88K -- ^ Motorola 88000 226 | | EM_486 -- ^ Intel i486 (DO NOT USE THIS ONE) 227 | | EM_860 -- ^ Intel 80860 228 | | EM_MIPS -- ^ MIPS I Architecture 229 | | EM_S370 -- ^ IBM System/370 Processor 230 | | EM_MIPS_RS3_LE -- ^ MIPS RS3000 Little-endian 231 | | EM_SPARC64 -- ^ SPARC 64-bit 232 | | EM_PARISC -- ^ Hewlett-Packard PA-RISC 233 | | EM_VPP500 -- ^ Fujitsu VPP500 234 | | EM_SPARC32PLUS -- ^ Enhanced instruction set SPARC 235 | | EM_960 -- ^ Intel 80960 236 | | EM_PPC -- ^ PowerPC 237 | | EM_PPC64 -- ^ 64-bit PowerPC 238 | | EM_S390 -- ^ IBM System/390 Processor 239 | | EM_SPU -- ^ Cell SPU 240 | | EM_V800 -- ^ NEC V800 241 | | EM_FR20 -- ^ Fujitsu FR20 242 | | EM_RH32 -- ^ TRW RH-32 243 | | EM_RCE -- ^ Motorola RCE 244 | | EM_ARM -- ^ Advanced RISC Machines ARM 245 | | EM_ALPHA -- ^ Digital Alpha 246 | | EM_SH -- ^ Hitachi SH 247 | | EM_SPARCV9 -- ^ SPARC Version 9 248 | | EM_TRICORE -- ^ Siemens TriCore embedded processor 249 | | EM_ARC -- ^ Argonaut RISC Core, Argonaut Technologies Inc. 250 | | EM_H8_300 -- ^ Hitachi H8/300 251 | | EM_H8_300H -- ^ Hitachi H8/300H 252 | | EM_H8S -- ^ Hitachi H8S 253 | | EM_H8_500 -- ^ Hitachi H8/500 254 | | EM_IA_64 -- ^ Intel IA-64 processor architecture 255 | | EM_MIPS_X -- ^ Stanford MIPS-X 256 | | EM_COLDFIRE -- ^ Motorola ColdFire 257 | | EM_68HC12 -- ^ Motorola M68HC12 258 | | EM_MMA -- ^ Fujitsu MMA Multimedia Accelerator 259 | | EM_PCP -- ^ Siemens PCP 260 | | EM_NCPU -- ^ Sony nCPU embedded RISC processor 261 | | EM_NDR1 -- ^ Denso NDR1 microprocessor 262 | | EM_STARCORE -- ^ Motorola Star*Core processor 263 | | EM_ME16 -- ^ Toyota ME16 processor 264 | | EM_ST100 -- ^ STMicroelectronics ST100 processor 265 | | EM_TINYJ -- ^ Advanced Logic Corp. TinyJ embedded processor family 266 | | EM_X86_64 -- ^ AMD x86-64 architecture 267 | | EM_PDSP -- ^ Sony DSP Processor 268 | | EM_FX66 -- ^ Siemens FX66 microcontroller 269 | | EM_ST9PLUS -- ^ STMicroelectronics ST9+ 8/16 bit microcontroller 270 | | EM_ST7 -- ^ STMicroelectronics ST7 8-bit microcontroller 271 | | EM_68HC16 -- ^ Motorola MC68HC16 Microcontroller 272 | | EM_68HC11 -- ^ Motorola MC68HC11 Microcontroller 273 | | EM_68HC08 -- ^ Motorola MC68HC08 Microcontroller 274 | | EM_68HC05 -- ^ Motorola MC68HC05 Microcontroller 275 | | EM_SVX -- ^ Silicon Graphics SVx 276 | | EM_ST19 -- ^ STMicroelectronics ST19 8-bit microcontroller 277 | | EM_VAX -- ^ Digital VAX 278 | | EM_CRIS -- ^ Axis Communications 32-bit embedded processor 279 | | EM_JAVELIN -- ^ Infineon Technologies 32-bit embedded processor 280 | | EM_FIREPATH -- ^ Element 14 64-bit DSP Processor 281 | | EM_ZSP -- ^ LSI Logic 16-bit DSP Processor 282 | | EM_MMIX -- ^ Donald Knuth's educational 64-bit processor 283 | | EM_HUANY -- ^ Harvard University machine-independent object files 284 | | EM_PRISM -- ^ SiTera Prism 285 | | EM_AVR -- ^ Atmel AVR 8-bit microcontroller 286 | | EM_FR30 -- ^ Fujitsu FR30 287 | | EM_D10V -- ^ Mitsubishi D10V 288 | | EM_D30V -- ^ Mitsubishi D30V 289 | | EM_V850 -- ^ NEC v850 290 | | EM_M32R -- ^ Mitsubishi M32R 291 | | EM_MN10300 -- ^ Matsushita MN10300 292 | | EM_MN10200 -- ^ Matsushita MN10200 293 | | EM_PJ -- ^ picoJava 294 | | EM_OPENRISC -- ^ OpenRISC 32-bit embedded processor 295 | | EM_ARC_A5 -- ^ ARC Cores Tangent-A5 296 | | EM_XTENSA -- ^ Tensilica Xtensa Architecture 297 | | EM_VIDEOCORE -- ^ Alphamosaic VideoCore processor 298 | | EM_TMM_GPP -- ^ Thompson Multimedia General Purpose Processor 299 | | EM_NS32K -- ^ National Semiconductor 32000 series 300 | | EM_TPC -- ^ Tenor Network TPC processor 301 | | EM_SNP1K -- ^ Trebia SNP 1000 processor 302 | | EM_ST200 -- ^ STMicroelectronics (www.st.com) ST200 microcontroller 303 | | EM_IP2K -- ^ Ubicom IP2xxx microcontroller family 304 | | EM_MAX -- ^ MAX Processor 305 | | EM_CR -- ^ National Semiconductor CompactRISC microprocessor 306 | | EM_F2MC16 -- ^ Fujitsu F2MC16 307 | | EM_MSP430 -- ^ Texas Instruments embedded microcontroller msp430 308 | | EM_BLACKFIN -- ^ Analog Devices Blackfin (DSP) processor 309 | | EM_SE_C33 -- ^ S1C33 Family of Seiko Epson processors 310 | | EM_SEP -- ^ Sharp embedded microprocessor 311 | | EM_ARCA -- ^ Arca RISC Microprocessor 312 | | EM_UNICORE -- ^ Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University 313 | | EM_EXT Word16 -- ^ Other 314 | deriving (Eq, Show) 315 | 316 | getElfMachine :: ElfReader -> Get ElfMachine 317 | getElfMachine = liftM getElfMachine_ . getWord16 318 | where getElfMachine_ 0 = EM_NONE 319 | getElfMachine_ 1 = EM_M32 320 | getElfMachine_ 2 = EM_SPARC 321 | getElfMachine_ 3 = EM_386 322 | getElfMachine_ 4 = EM_68K 323 | getElfMachine_ 5 = EM_88K 324 | getElfMachine_ 6 = EM_486 325 | getElfMachine_ 7 = EM_860 326 | getElfMachine_ 8 = EM_MIPS 327 | getElfMachine_ 9 = EM_S370 328 | getElfMachine_ 10 = EM_MIPS_RS3_LE 329 | getElfMachine_ 11 = EM_SPARC64 330 | getElfMachine_ 15 = EM_PARISC 331 | getElfMachine_ 17 = EM_VPP500 332 | getElfMachine_ 18 = EM_SPARC32PLUS 333 | getElfMachine_ 19 = EM_960 334 | getElfMachine_ 20 = EM_PPC 335 | getElfMachine_ 21 = EM_PPC64 336 | getElfMachine_ 22 = EM_S390 337 | getElfMachine_ 23 = EM_SPU 338 | getElfMachine_ 36 = EM_V800 339 | getElfMachine_ 37 = EM_FR20 340 | getElfMachine_ 38 = EM_RH32 341 | getElfMachine_ 39 = EM_RCE 342 | getElfMachine_ 40 = EM_ARM 343 | getElfMachine_ 41 = EM_ALPHA 344 | getElfMachine_ 42 = EM_SH 345 | getElfMachine_ 43 = EM_SPARCV9 346 | getElfMachine_ 44 = EM_TRICORE 347 | getElfMachine_ 45 = EM_ARC 348 | getElfMachine_ 46 = EM_H8_300 349 | getElfMachine_ 47 = EM_H8_300H 350 | getElfMachine_ 48 = EM_H8S 351 | getElfMachine_ 49 = EM_H8_500 352 | getElfMachine_ 50 = EM_IA_64 353 | getElfMachine_ 51 = EM_MIPS_X 354 | getElfMachine_ 52 = EM_COLDFIRE 355 | getElfMachine_ 53 = EM_68HC12 356 | getElfMachine_ 54 = EM_MMA 357 | getElfMachine_ 55 = EM_PCP 358 | getElfMachine_ 56 = EM_NCPU 359 | getElfMachine_ 57 = EM_NDR1 360 | getElfMachine_ 58 = EM_STARCORE 361 | getElfMachine_ 59 = EM_ME16 362 | getElfMachine_ 60 = EM_ST100 363 | getElfMachine_ 61 = EM_TINYJ 364 | getElfMachine_ 62 = EM_X86_64 365 | getElfMachine_ 63 = EM_PDSP 366 | getElfMachine_ 66 = EM_FX66 367 | getElfMachine_ 67 = EM_ST9PLUS 368 | getElfMachine_ 68 = EM_ST7 369 | getElfMachine_ 69 = EM_68HC16 370 | getElfMachine_ 70 = EM_68HC11 371 | getElfMachine_ 71 = EM_68HC08 372 | getElfMachine_ 72 = EM_68HC05 373 | getElfMachine_ 73 = EM_SVX 374 | getElfMachine_ 74 = EM_ST19 375 | getElfMachine_ 75 = EM_VAX 376 | getElfMachine_ 76 = EM_CRIS 377 | getElfMachine_ 77 = EM_JAVELIN 378 | getElfMachine_ 78 = EM_FIREPATH 379 | getElfMachine_ 79 = EM_ZSP 380 | getElfMachine_ 80 = EM_MMIX 381 | getElfMachine_ 81 = EM_HUANY 382 | getElfMachine_ 82 = EM_PRISM 383 | getElfMachine_ 83 = EM_AVR 384 | getElfMachine_ 84 = EM_FR30 385 | getElfMachine_ 85 = EM_D10V 386 | getElfMachine_ 86 = EM_D30V 387 | getElfMachine_ 87 = EM_V850 388 | getElfMachine_ 88 = EM_M32R 389 | getElfMachine_ 89 = EM_MN10300 390 | getElfMachine_ 90 = EM_MN10200 391 | getElfMachine_ 91 = EM_PJ 392 | getElfMachine_ 92 = EM_OPENRISC 393 | getElfMachine_ 93 = EM_ARC_A5 394 | getElfMachine_ 94 = EM_XTENSA 395 | getElfMachine_ 95 = EM_VIDEOCORE 396 | getElfMachine_ 96 = EM_TMM_GPP 397 | getElfMachine_ 97 = EM_NS32K 398 | getElfMachine_ 98 = EM_TPC 399 | getElfMachine_ 99 = EM_SNP1K 400 | getElfMachine_ 100 = EM_ST200 401 | getElfMachine_ 101 = EM_IP2K 402 | getElfMachine_ 102 = EM_MAX 403 | getElfMachine_ 103 = EM_CR 404 | getElfMachine_ 104 = EM_F2MC16 405 | getElfMachine_ 105 = EM_MSP430 406 | getElfMachine_ 106 = EM_BLACKFIN 407 | getElfMachine_ 107 = EM_SE_C33 408 | getElfMachine_ 108 = EM_SEP 409 | getElfMachine_ 109 = EM_ARCA 410 | getElfMachine_ 110 = EM_UNICORE 411 | getElfMachine_ n = EM_EXT n 412 | 413 | getElf_Shdr_OffsetSize :: ElfClass -> ElfReader -> Get (Word64, Word64) 414 | getElf_Shdr_OffsetSize ei_class er = 415 | case ei_class of 416 | ELFCLASS32 -> do 417 | skip 16 418 | sh_offset <- liftM fromIntegral $ getWord32 er 419 | sh_size <- liftM fromIntegral $ getWord32 er 420 | return (sh_offset, sh_size) 421 | ELFCLASS64 -> do 422 | skip 24 423 | sh_offset <- getWord64 er 424 | sh_size <- getWord64 er 425 | return (sh_offset, sh_size) 426 | 427 | getElf_Shdr :: ElfClass -> ElfReader -> B.ByteString -> B.ByteString -> Get ElfSection 428 | getElf_Shdr ei_class er elf_file string_section = 429 | case ei_class of 430 | ELFCLASS32 -> do 431 | sh_name <- getWord32 er 432 | sh_type <- getElfSectionType er 433 | sh_flags <- getElfSectionFlags32 er 434 | sh_addr <- getWord32 er 435 | sh_offset <- getWord32 er 436 | sh_size <- getWord32 er 437 | sh_link <- getWord32 er 438 | sh_info <- getWord32 er 439 | sh_addralign <- getWord32 er 440 | sh_entsize <- getWord32 er 441 | return ElfSection 442 | { elfSectionName = map B.w2c $ B.unpack $ B.takeWhile (/= 0) $ B.drop (fromIntegral sh_name) string_section 443 | , elfSectionType = sh_type 444 | , elfSectionFlags = sh_flags 445 | , elfSectionAddr = fromIntegral sh_addr 446 | , elfSectionSize = fromIntegral sh_size 447 | , elfSectionLink = sh_link 448 | , elfSectionInfo = sh_info 449 | , elfSectionAddrAlign = fromIntegral sh_addralign 450 | , elfSectionEntSize = fromIntegral sh_entsize 451 | , elfSectionData = B.take (fromIntegral sh_size) $ B.drop (fromIntegral sh_offset) elf_file 452 | } 453 | ELFCLASS64 -> do 454 | sh_name <- getWord32 er 455 | sh_type <- getElfSectionType er 456 | sh_flags <- getElfSectionFlags64 er 457 | sh_addr <- getWord64 er 458 | sh_offset <- getWord64 er 459 | sh_size <- getWord64 er 460 | sh_link <- getWord32 er 461 | sh_info <- getWord32 er 462 | sh_addralign <- getWord64 er 463 | sh_entsize <- getWord64 er 464 | return ElfSection 465 | { elfSectionName = map B.w2c $ B.unpack $ B.takeWhile (/= 0) $ B.drop (fromIntegral sh_name) string_section 466 | , elfSectionType = sh_type 467 | , elfSectionFlags = sh_flags 468 | , elfSectionAddr = sh_addr 469 | , elfSectionSize = sh_size 470 | , elfSectionLink = sh_link 471 | , elfSectionInfo = sh_info 472 | , elfSectionAddrAlign = sh_addralign 473 | , elfSectionEntSize = sh_entsize 474 | , elfSectionData = B.take (fromIntegral sh_size) $ B.drop (fromIntegral sh_offset) elf_file 475 | } 476 | 477 | data TableInfo = TableInfo { tableOffset :: Int, entrySize :: Int, entryNum :: Int } 478 | 479 | getElf_Ehdr :: Get (Elf, TableInfo, TableInfo, Word16) 480 | getElf_Ehdr = do 481 | ei_magic <- getElfMagic 482 | ei_class <- getElfClass 483 | ei_data <- getElfData 484 | ei_version <- liftM fromIntegral getElfVersion 485 | ei_osabi <- getElfOsabi 486 | ei_abiver <- liftM fromIntegral getWord8 487 | skip 7 488 | er <- return $ elfReader ei_data 489 | case ei_class of 490 | ELFCLASS32 -> do 491 | e_type <- getElfType er 492 | e_machine <- getElfMachine er 493 | e_version <- getWord32 er 494 | e_entry <- liftM fromIntegral $ getWord32 er 495 | e_phoff <- getWord32 er 496 | e_shoff <- liftM fromIntegral $ getWord32 er 497 | e_flags <- getWord32 er 498 | e_ehsize <- getWord16 er 499 | e_phentsize <- getWord16 er 500 | e_phnum <- getWord16 er 501 | e_shentsize <- getWord16 er 502 | e_shnum <- getWord16 er 503 | e_shstrndx <- getWord16 er 504 | return (Elf { elfClass = ei_class 505 | , elfData = ei_data 506 | , elfVersion = ei_version 507 | , elfOSABI = ei_osabi 508 | , elfABIVersion = ei_abiver 509 | , elfType = e_type 510 | , elfMachine = e_machine 511 | , elfEntry = e_entry 512 | , elfSections = [] 513 | , elfSegments = [] } 514 | , TableInfo { tableOffset = fromIntegral e_phoff, entrySize = fromIntegral e_phentsize, entryNum = fromIntegral e_phnum } 515 | , TableInfo { tableOffset = fromIntegral e_shoff, entrySize = fromIntegral e_shentsize, entryNum = fromIntegral e_shnum } 516 | , e_shstrndx) 517 | ELFCLASS64 -> do 518 | e_type <- getElfType er 519 | e_machine <- getElfMachine er 520 | e_version <- getWord32 er 521 | e_entry <- getWord64 er 522 | e_phoff <- getWord64 er 523 | e_shoff <- getWord64 er 524 | e_flags <- getWord32 er 525 | e_ehsize <- getWord16 er 526 | e_phentsize <- getWord16 er 527 | e_phnum <- getWord16 er 528 | e_shentsize <- getWord16 er 529 | e_shnum <- getWord16 er 530 | e_shstrndx <- getWord16 er 531 | return (Elf { elfClass = ei_class 532 | , elfData = ei_data 533 | , elfVersion = ei_version 534 | , elfOSABI = ei_osabi 535 | , elfABIVersion = ei_abiver 536 | , elfType = e_type 537 | , elfMachine = e_machine 538 | , elfEntry = e_entry 539 | , elfSections = [] 540 | , elfSegments = [] } 541 | , TableInfo { tableOffset = fromIntegral e_phoff, entrySize = fromIntegral e_phentsize, entryNum = fromIntegral e_phnum } 542 | , TableInfo { tableOffset = fromIntegral e_shoff, entrySize = fromIntegral e_shentsize, entryNum = fromIntegral e_shnum } 543 | , e_shstrndx) 544 | 545 | data ElfReader = ElfReader 546 | { getWord16 :: Get Word16 547 | , getWord32 :: Get Word32 548 | , getWord64 :: Get Word64 549 | } 550 | 551 | elfReader :: ElfData -> ElfReader 552 | elfReader ELFDATA2LSB = ElfReader { getWord16 = getWord16le, getWord32 = getWord32le, getWord64 = getWord64le } 553 | elfReader ELFDATA2MSB = ElfReader { getWord16 = getWord16be, getWord32 = getWord32be, getWord64 = getWord64be } 554 | 555 | divide :: B.ByteString -> Int -> Int -> [B.ByteString] 556 | divide _ _ 0 = [] 557 | divide bs s n = let (x,y) = B.splitAt s bs in x : divide y s (n-1) 558 | 559 | -- | Parses a ByteString into an Elf record. Parse failures call error. 32-bit ELF objects have their 560 | -- fields promoted to 64-bit so that the 32- and 64-bit ELF records can be the same. 561 | parseElf :: B.ByteString -> Elf 562 | parseElf b = 563 | let ph = table segTab 564 | sh = table secTab 565 | (shstroff, shstrsize) = parseEntry getElf_Shdr_OffsetSize $ head $ drop (fromIntegral e_shstrndx) sh 566 | sh_str = B.take (fromIntegral shstrsize) $ B.drop (fromIntegral shstroff) b 567 | segments = map (parseEntry (\c r -> parseElfSegmentEntry c r b)) ph 568 | sections = map (parseEntry (\c r -> getElf_Shdr c r b sh_str)) sh 569 | in e { elfSections = sections, elfSegments = segments } 570 | 571 | where table i = divide (B.drop (tableOffset i) b) (entrySize i) (entryNum i) 572 | parseEntry p x = runGet (p (elfClass e) (elfReader (elfData e))) (L.fromChunks [x]) 573 | (e, segTab, secTab, e_shstrndx) = runGet getElf_Ehdr $ L.fromChunks [b] 574 | 575 | 576 | data ElfSegment = ElfSegment 577 | { elfSegmentType :: ElfSegmentType -- ^ Segment type 578 | , elfSegmentFlags :: [ElfSegmentFlag] -- ^ Segment flags 579 | , elfSegmentVirtAddr :: Word64 -- ^ Virtual address for the segment 580 | , elfSegmentPhysAddr :: Word64 -- ^ Physical address for the segment 581 | , elfSegmentAlign :: Word64 -- ^ Segment alignment 582 | , elfSegmentData :: B.ByteString -- ^ Data for the segment 583 | , elfSegmentMemSize :: Word64 -- ^ Size in memory (may be larger then the segment's data) 584 | } deriving (Eq,Show) 585 | 586 | -- | Segment Types. 587 | data ElfSegmentType 588 | = PT_NULL -- ^ Unused entry 589 | | PT_LOAD -- ^ Loadable segment 590 | | PT_DYNAMIC -- ^ Dynamic linking tables 591 | | PT_INTERP -- ^ Program interpreter path name 592 | | PT_NOTE -- ^ Note sectionks 593 | | PT_SHLIB -- ^ Reserved 594 | | PT_PHDR -- ^ Program header table 595 | | PT_Other Word32 -- ^ Some other type 596 | deriving (Eq,Show) 597 | 598 | parseElfSegmentType :: Word32 -> ElfSegmentType 599 | parseElfSegmentType x = 600 | case x of 601 | 0 -> PT_NULL 602 | 1 -> PT_LOAD 603 | 2 -> PT_DYNAMIC 604 | 3 -> PT_INTERP 605 | 4 -> PT_NOTE 606 | 5 -> PT_SHLIB 607 | 6 -> PT_PHDR 608 | _ -> PT_Other x 609 | 610 | 611 | parseElfSegmentEntry :: ElfClass -> ElfReader -> B.ByteString -> Get ElfSegment 612 | parseElfSegmentEntry elf_class er elf_file = case elf_class of 613 | ELFCLASS64 -> do 614 | p_type <- parseElfSegmentType `fmap` getWord32 er 615 | p_flags <- parseElfSegmentFlags `fmap` getWord32 er 616 | p_offset <- getWord64 er 617 | p_vaddr <- getWord64 er 618 | p_paddr <- getWord64 er 619 | p_filesz <- getWord64 er 620 | p_memsz <- getWord64 er 621 | p_align <- getWord64 er 622 | return ElfSegment 623 | { elfSegmentType = p_type 624 | , elfSegmentFlags = p_flags 625 | , elfSegmentVirtAddr = p_vaddr 626 | , elfSegmentPhysAddr = p_paddr 627 | , elfSegmentAlign = p_align 628 | , elfSegmentData = B.take (fromIntegral p_filesz) $ B.drop (fromIntegral p_offset) elf_file 629 | , elfSegmentMemSize = p_memsz 630 | } 631 | 632 | ELFCLASS32 -> do 633 | p_type <- parseElfSegmentType `fmap` getWord32 er 634 | p_offset <- fromIntegral `fmap` getWord32 er 635 | p_vaddr <- fromIntegral `fmap` getWord32 er 636 | p_paddr <- fromIntegral `fmap` getWord32 er 637 | p_filesz <- fromIntegral `fmap` getWord32 er 638 | p_memsz <- fromIntegral `fmap` getWord32 er 639 | p_flags <- parseElfSegmentFlags `fmap` getWord32 er 640 | p_align <- fromIntegral `fmap` getWord32 er 641 | return ElfSegment 642 | { elfSegmentType = p_type 643 | , elfSegmentFlags = p_flags 644 | , elfSegmentVirtAddr = p_vaddr 645 | , elfSegmentPhysAddr = p_paddr 646 | , elfSegmentAlign = p_align 647 | , elfSegmentData = B.take (fromIntegral p_filesz) $ B.drop (fromIntegral p_offset) elf_file 648 | , elfSegmentMemSize = p_memsz 649 | } 650 | 651 | data ElfSegmentFlag 652 | = PF_X -- ^ Execute permission 653 | | PF_W -- ^ Write permission 654 | | PF_R -- ^ Read permission 655 | | PF_Ext Int -- ^ Some other flag, the Int is the bit number for the flag. 656 | deriving (Eq,Show) 657 | 658 | parseElfSegmentFlags :: Word32 -> [ElfSegmentFlag] 659 | parseElfSegmentFlags word = [ cvt bit_ | bit_ <- [ 0 .. 31 ], testBit word bit_ ] 660 | where cvt 0 = PF_X 661 | cvt 1 = PF_W 662 | cvt 2 = PF_R 663 | cvt n = PF_Ext n 664 | 665 | -- | The symbol table entries consist of index information to be read from other 666 | -- parts of the ELF file. Some of this information is automatically retrieved 667 | -- for your convenience (including symbol name, description of the enclosing 668 | -- section, and definition). 669 | data ElfSymbolTableEntry = EST 670 | { steName :: (Word32,Maybe B.ByteString) 671 | , steEnclosingSection :: Maybe ElfSection -- ^ Section from steIndex 672 | , steType :: ElfSymbolType 673 | , steBind :: ElfSymbolBinding 674 | , steOther :: Word8 675 | , steIndex :: ElfSectionIndex -- ^ Section in which the def is held 676 | , steValue :: Word64 677 | , steSize :: Word64 678 | } deriving (Eq, Show) 679 | 680 | -- | Parse the symbol table section into a list of symbol table entries. If 681 | -- no symbol table is found then an empty list is returned. 682 | -- This function does not consult flags to look for SHT_STRTAB (when naming symbols), 683 | -- it just looks for particular sections of ".strtab" and ".shstrtab". 684 | parseSymbolTables :: Elf -> [[ElfSymbolTableEntry]] 685 | parseSymbolTables e = 686 | let secs = symbolTableSections e 687 | in map (getSymbolTableEntries e) secs 688 | 689 | -- | Assumes the given section is a symbol table, type SHT_SYMTAB, or SHT_DYNSYM 690 | -- (guaranteed by parseSymbolTables). 691 | getSymbolTableEntries :: Elf -> ElfSection -> [ElfSymbolTableEntry] 692 | getSymbolTableEntries e s = 693 | decodeMany (getSymbolTableEntry e strtab) (L.fromStrict (elfSectionData s)) 694 | where 695 | link = elfSectionLink s 696 | strtab = lookup (fromIntegral link) (zip [0..] (elfSections e)) 697 | 698 | decodeMany :: forall a. Get a -> L.ByteString -> [a] 699 | decodeMany get = go decoder 700 | where 701 | decoder = runGetIncremental get 702 | 703 | go :: Decoder a -> L.ByteString -> [a] 704 | go (Done leftover _ entry) input = 705 | entry : go decoder (L.Chunk leftover input) 706 | go (Partial k) input = 707 | go (k . takeHeadChunk $ input) (dropHeadChunk input) 708 | go (Fail _ _ msg) input = if L.null input 709 | then [] 710 | else error msg 711 | 712 | takeHeadChunk :: L.ByteString -> Maybe B.ByteString 713 | takeHeadChunk lbs = 714 | case lbs of 715 | (L.Chunk bs _) -> Just bs 716 | _ -> Nothing 717 | 718 | dropHeadChunk :: L.ByteString -> L.ByteString 719 | dropHeadChunk lbs = 720 | case lbs of 721 | (L.Chunk _ lbs') -> lbs' 722 | _ -> L.Empty 723 | 724 | 725 | -- | Use the symbol offset and size to extract its definition 726 | -- (in the form of a ByteString). 727 | -- If the size is zero, or the offset larger than the 'elfSectionData', 728 | -- then 'Nothing' is returned. 729 | findSymbolDefinition :: ElfSymbolTableEntry -> Maybe B.ByteString 730 | findSymbolDefinition e = steEnclosingSection e >>= \enclosingSection -> 731 | let enclosingData = elfSectionData enclosingSection 732 | start = (fromIntegral (steValue e)) - (fromIntegral (elfSectionAddr enclosingSection)) 733 | len = fromIntegral (steSize e) 734 | def = (B.take len . B.drop start) enclosingData 735 | in if B.null def then Nothing else Just def 736 | 737 | symbolTableSections :: Elf -> [ElfSection] 738 | symbolTableSections e = filter ((`elem` [SHT_SYMTAB, SHT_DYNSYM]) . elfSectionType) (elfSections e) 739 | 740 | -- | Gets a single entry from the symbol table, use with runGetMany. 741 | getSymbolTableEntry :: Elf -> Maybe ElfSection -> Get ElfSymbolTableEntry 742 | getSymbolTableEntry e strtlb = 743 | if elfClass e == ELFCLASS32 then getSymbolTableEntry32 else getSymbolTableEntry64 744 | where 745 | strs = maybe B.empty elfSectionData strtlb 746 | er = elfReader (elfData e) 747 | getSymbolTableEntry32 = do 748 | nameIdx <- liftM fromIntegral (getWord32 er) 749 | value <- liftM fromIntegral (getWord32 er) 750 | size <- liftM fromIntegral (getWord32 er) 751 | info <- getWord8 752 | other <- getWord8 753 | sTlbIdx <- liftM (toEnum . fromIntegral) (getWord16 er) 754 | let name = stringByIndex nameIdx strs 755 | (typ,bind) = infoToTypeAndBind info 756 | sec = sectionByIndex e sTlbIdx 757 | return $ EST (nameIdx,name) sec typ bind other sTlbIdx value size 758 | getSymbolTableEntry64 = do 759 | nameIdx <- liftM fromIntegral (getWord32 er) 760 | info <- getWord8 761 | other <- getWord8 762 | sTlbIdx <- liftM (toEnum . fromIntegral) (getWord16 er) 763 | symVal <- getWord64 er 764 | size <- getWord64 er 765 | let name = stringByIndex nameIdx strs 766 | (typ,bind) = infoToTypeAndBind info 767 | sec = sectionByIndex e sTlbIdx 768 | return $ EST (nameIdx,name) sec typ bind other sTlbIdx symVal size 769 | 770 | sectionByIndex :: Elf -> ElfSectionIndex -> Maybe ElfSection 771 | sectionByIndex e (SHNIndex i) = lookup i . zip [0..] $ elfSections e 772 | sectionByIndex _ _ = Nothing 773 | 774 | infoToTypeAndBind :: Word8 -> (ElfSymbolType,ElfSymbolBinding) 775 | infoToTypeAndBind i = 776 | let t = fromIntegral $ i .&. 0x0F 777 | b = fromIntegral $ (i .&. 0xF0) `shiftR` 4 778 | in (toEnum t, toEnum b) 779 | 780 | data ElfSymbolBinding 781 | = STBLocal 782 | | STBGlobal 783 | | STBWeak 784 | | STBLoOS 785 | | STBHiOS 786 | | STBLoProc 787 | | STBHiProc 788 | deriving (Eq, Ord, Show, Read) 789 | 790 | instance Enum ElfSymbolBinding where 791 | fromEnum STBLocal = 0 792 | fromEnum STBGlobal = 1 793 | fromEnum STBWeak = 2 794 | fromEnum STBLoOS = 10 795 | fromEnum STBHiOS = 12 796 | fromEnum STBLoProc = 13 797 | fromEnum STBHiProc = 15 798 | toEnum 0 = STBLocal 799 | toEnum 1 = STBGlobal 800 | toEnum 2 = STBWeak 801 | toEnum 10 = STBLoOS 802 | toEnum 12 = STBHiOS 803 | toEnum 13 = STBLoProc 804 | toEnum 15 = STBHiProc 805 | 806 | data ElfSymbolType 807 | = STTNoType 808 | | STTObject 809 | | STTFunc 810 | | STTSection 811 | | STTFile 812 | | STTCommon 813 | | STTTLS 814 | | STTLoOS 815 | | STTHiOS 816 | | STTLoProc 817 | | STTHiProc 818 | deriving (Eq, Ord, Show, Read) 819 | 820 | instance Enum ElfSymbolType where 821 | fromEnum STTNoType = 0 822 | fromEnum STTObject = 1 823 | fromEnum STTFunc = 2 824 | fromEnum STTSection = 3 825 | fromEnum STTFile = 4 826 | fromEnum STTCommon = 5 827 | fromEnum STTTLS = 6 828 | fromEnum STTLoOS = 10 829 | fromEnum STTHiOS = 12 830 | fromEnum STTLoProc = 13 831 | fromEnum STTHiProc = 15 832 | toEnum 0 = STTNoType 833 | toEnum 1 = STTObject 834 | toEnum 2 = STTFunc 835 | toEnum 3 = STTSection 836 | toEnum 4 = STTFile 837 | toEnum 5 = STTCommon 838 | toEnum 6 = STTTLS 839 | toEnum 10 = STTLoOS 840 | toEnum 12 = STTHiOS 841 | toEnum 13 = STTLoProc 842 | toEnum 15 = STTHiProc 843 | 844 | data ElfSectionIndex 845 | = SHNUndef 846 | | SHNLoProc 847 | | SHNCustomProc Word64 848 | | SHNHiProc 849 | | SHNLoOS 850 | | SHNCustomOS Word64 851 | | SHNHiOS 852 | | SHNAbs 853 | | SHNCommon 854 | | SHNIndex Word64 855 | deriving (Eq, Ord, Show, Read) 856 | 857 | instance Enum ElfSectionIndex where 858 | fromEnum SHNUndef = 0 859 | fromEnum SHNLoProc = 0xFF00 860 | fromEnum SHNHiProc = 0xFF1F 861 | fromEnum SHNLoOS = 0xFF20 862 | fromEnum SHNHiOS = 0xFF3F 863 | fromEnum SHNAbs = 0xFFF1 864 | fromEnum SHNCommon = 0xFFF2 865 | fromEnum (SHNCustomProc x) = fromIntegral x 866 | fromEnum (SHNCustomOS x) = fromIntegral x 867 | fromEnum (SHNIndex x) = fromIntegral x 868 | toEnum 0 = SHNUndef 869 | toEnum 0xff00 = SHNLoProc 870 | toEnum 0xFF1F = SHNHiProc 871 | toEnum 0xFF20 = SHNLoOS 872 | toEnum 0xFF3F = SHNHiOS 873 | toEnum 0xFFF1 = SHNAbs 874 | toEnum 0xFFF2 = SHNCommon 875 | toEnum x 876 | | x > fromEnum SHNLoProc && x < fromEnum SHNHiProc = SHNCustomProc (fromIntegral x) 877 | | x > fromEnum SHNLoOS && x < fromEnum SHNHiOS = SHNCustomOS (fromIntegral x) 878 | | x < fromEnum SHNLoProc || x > 0xFFFF = SHNIndex (fromIntegral x) 879 | | otherwise = error "Section index number is in a reserved range but we don't recognize the value from any standard." 880 | 881 | -- | Given a section name, extract the ElfSection. 882 | findSectionByName :: String -> Elf -> Maybe ElfSection 883 | findSectionByName name = listToMaybe . filter ((==) name . elfSectionName) . elfSections 884 | 885 | -- Get a string from a strtab ByteString. 886 | stringByIndex :: Integral n => n -> B.ByteString -> Maybe B.ByteString 887 | stringByIndex n strtab = 888 | let str = (B.takeWhile (/=0) . B.drop (fromIntegral n)) strtab 889 | in if B.length str == 0 then Nothing else Just str 890 | 891 | data ElfRel = ElfRel 892 | { elfRelOffset :: Word64 893 | , elfRelSymbol :: Word64 894 | , elfRelType :: Word8 895 | , elfRelSymAddend :: Maybe Int64 896 | } deriving (Eq, Show) 897 | 898 | getElfRel :: ElfClass 899 | -> ElfReader 900 | -> Bool -- ^ explicit addend? 901 | -> Get ElfRel 902 | getElfRel ELFCLASS64 er explicit = do 903 | offset <- getWord64 er 904 | info <- getWord64 er 905 | addend <- if explicit then Just <$> getWord64 er else pure Nothing 906 | return ElfRel { elfRelOffset = offset 907 | , elfRelSymbol = fromIntegral (info `shiftR` 32) 908 | , elfRelType = fromIntegral info 909 | , elfRelSymAddend = fromIntegral <$> addend 910 | } 911 | getElfRel ELFCLASS32 er explicit = do 912 | offset <- getWord32 er 913 | info <- getWord32 er 914 | addend <- if explicit then Just <$> getWord32 er else pure Nothing 915 | return ElfRel { elfRelOffset = fromIntegral offset 916 | , elfRelSymbol = fromIntegral (info `shiftR` 8) 917 | , elfRelType = fromIntegral info 918 | , elfRelSymAddend = fromIntegral <$> addend 919 | } 920 | 921 | getRelocations :: ElfClass -> ElfReader -> ElfSection -> [ElfRel] 922 | getRelocations elf_class er s = 923 | decodeMany (getElfRel elf_class er explicit) (L.fromStrict (elfSectionData s)) 924 | where 925 | explicit = elfSectionType s == SHT_RELA 926 | 927 | data ElfRelocationSection = ElfRelocationSection 928 | { elfRelSectSymbolTable :: [ElfSymbolTableEntry] 929 | , elfRelSectRelocated :: ElfSection 930 | , elfRelSectRelocations :: [ElfRel] 931 | } deriving (Eq, Show) 932 | 933 | parseRelocations :: Elf -> [ElfRelocationSection] 934 | parseRelocations elf = 935 | [ ElfRelocationSection { elfRelSectSymbolTable = symtab 936 | , elfRelSectRelocated = relocated 937 | , elfRelSectRelocations = rels 938 | } 939 | | s <- elfSections elf 940 | , elfSectionType s `elem` [SHT_REL, SHT_RELA] 941 | , let symtab = getSymbolTableEntries elf (getSection (elfSectionLink s)) 942 | relocated = getSection (elfSectionInfo s) 943 | rels = getRelocations (elfClass elf) er s 944 | ] 945 | where 946 | getSection i = elfSections elf !! fromIntegral i 947 | er = elfReader (elfData elf) 948 | -------------------------------------------------------------------------------- /testdata/HOWTO: -------------------------------------------------------------------------------- 1 | 'tiny' was generated this way on a 64bit linux system: 2 | 3 | $ nasm -f elf64 tiny.asm 4 | $ gcc -o tiny tiny.o -nostartfiles -nostdlib -nodefaultlibs 5 | $ strip -s tiny 6 | $ strip --remove-section=.note.gnu.build-id tiny 7 | 8 | Thank you whoever wrote this tutorial: . 9 | 10 | 'bloated' was generated this way on a 64bit linux system: 11 | 12 | $ clang++ -m32 -o bloated bloated.cpp 13 | 14 | This HOWTO is provided for documentation purposes, elf should not be 15 | regenerated/changed because tests assert their content. Their content was read 16 | by the readelf utility command. 17 | 18 | -------------------------------------------------------------------------------- /testdata/bloated: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangbj/elf/aca4c8eb6720d7ddcec161e0e4b89bc61eca1f90/testdata/bloated -------------------------------------------------------------------------------- /testdata/bloated.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | std::cout << "Hello world!" << std::endl; 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /testdata/empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangbj/elf/aca4c8eb6720d7ddcec161e0e4b89bc61eca1f90/testdata/empty -------------------------------------------------------------------------------- /testdata/tiny: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangbj/elf/aca4c8eb6720d7ddcec161e0e4b89bc61eca1f90/testdata/tiny -------------------------------------------------------------------------------- /testdata/tiny.asm: -------------------------------------------------------------------------------- 1 | SECTION .text 2 | 3 | global _start 4 | 5 | _start: 6 | mov ebx,0 7 | mov eax,1 8 | int 0x80 9 | -------------------------------------------------------------------------------- /testdata/vdso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wangbj/elf/aca4c8eb6720d7ddcec161e0e4b89bc61eca1f90/testdata/vdso -------------------------------------------------------------------------------- /tests/Data/ElfSpec.hs: -------------------------------------------------------------------------------- 1 | module Data.ElfSpec (spec) where 2 | 3 | import Test.Hspec 4 | 5 | import Control.Exception (evaluate) 6 | import qualified Data.ByteString as BS 7 | import qualified Data.ByteString.Char8 as C 8 | import Data.Either 9 | import Data.Foldable (find) 10 | import qualified Data.Map as Map 11 | import Data.Maybe 12 | import System.IO 13 | 14 | import Data.Elf 15 | 16 | 17 | getBinaryFileContents :: FilePath -> IO BS.ByteString 18 | getBinaryFileContents fname = withBinaryFile fname ReadMode BS.hGetContents 19 | 20 | 21 | spec :: Spec 22 | spec = do 23 | emptyContents <- runIO $ getBinaryFileContents "./testdata/empty" 24 | tinyContents <- runIO $ getBinaryFileContents "./testdata/tiny" 25 | bloatedContents <- runIO $ getBinaryFileContents "./testdata/bloated" 26 | dynsymContents <- runIO $ getBinaryFileContents "./testdata/vdso" 27 | 28 | let tinyElf = parseElf tinyContents 29 | bloatedElf = parseElf bloatedContents 30 | dynsymElf = parseElf dynsymContents 31 | 32 | describe "parseElf" $ do 33 | -- TODO: That was the original only test in this package. This test 34 | -- should be removed, and future versions of this library should return 35 | -- an 'Either ParseError Elf'. 36 | it "does not accept an empty elf" $ 37 | evaluate (parseElf emptyContents) `shouldThrow` anyException 38 | 39 | context "Headers parsing" $ do 40 | 41 | it "parses the version" $ 42 | elfVersion tinyElf `shouldBe` 1 43 | 44 | it "parses the architecture" $ do 45 | elfClass tinyElf `shouldBe` ELFCLASS64 46 | elfClass bloatedElf `shouldBe` ELFCLASS32 47 | 48 | it "parses the endianness" $ do 49 | elfData tinyElf `shouldBe` ELFDATA2LSB 50 | 51 | it "parses the OS ABI" $ 52 | elfOSABI tinyElf `shouldBe` ELFOSABI_SYSV 53 | 54 | it "parses the type" $ 55 | elfType bloatedElf `shouldBe` ET_EXEC 56 | 57 | it "parses the machine type" $ do 58 | elfMachine tinyElf `shouldBe` EM_X86_64 59 | elfMachine bloatedElf `shouldBe` EM_386 60 | 61 | it "parses the entry point" $ do 62 | elfEntry tinyElf `shouldBe` 0x4000e0 63 | elfEntry bloatedElf `shouldBe` 0x8048610 64 | 65 | context "Segment parsing" $ do 66 | let tinySegments = elfSegments tinyElf 67 | bloatedSegments = elfSegments bloatedElf 68 | 69 | it "parses the right amount of segments" $ do 70 | length tinySegments `shouldBe` 2 71 | length bloatedSegments `shouldBe` 9 72 | 73 | it "parses segment types" $ 74 | let segmentTypes = map elfSegmentType tinySegments in 75 | segmentTypes `shouldBe` [PT_LOAD, PT_NOTE] 76 | 77 | it "parses segment flags" $ do 78 | let segmentFlags = map elfSegmentFlags tinySegments 79 | segmentFlags !! 0 `shouldMatchList` [PF_R, PF_X] 80 | segmentFlags !! 1 `shouldMatchList` [PF_R] 81 | 82 | context "Section parsing" $ do 83 | let tinySections = elfSections tinyElf 84 | bloatedSections = elfSections bloatedElf 85 | 86 | it "parses the right amount of sections" $ do 87 | length tinySections `shouldBe` 3 88 | length bloatedSections `shouldBe` 31 89 | 90 | it "parses the section in the right order" $ do 91 | map elfSectionName tinySections `shouldBe` [ "", ".text", ".shstrtab" ] 92 | 93 | it "parses the section types" $ 94 | let sectionTypes = map elfSectionType bloatedSections in 95 | take 5 sectionTypes `shouldBe` [ SHT_NULL, SHT_PROGBITS, SHT_NOTE 96 | , SHT_NOTE, SHT_EXT 1879048182] 97 | 98 | it "parses the data" $ 99 | let comment = find (\sec -> elfSectionName sec == ".comment") bloatedSections 100 | expected = C.pack . concat $ [ "GCC: (GNU) 6.3.1 20161221 (Red Hat 6.3.1-1)\NUL" 101 | , "clang version 3.8.1 (tags/RELEASE_381/final)\NUL" 102 | ] 103 | in 104 | fmap elfSectionData comment `shouldBe` Just expected 105 | 106 | describe "findSymbolDefinition" $ do 107 | let tinySymbols = parseSymbolTables tinyElf 108 | bloatedSymbols = parseSymbolTables bloatedElf 109 | 110 | it "parses stripped symbol" $ 111 | -- This binary was stripped 112 | concat tinySymbols `shouldSatisfy` all (isNothing . snd . steName) 113 | 114 | let namedBloatedSymbols = 115 | let go sym = fmap (\ name -> (name, sym)) $ snd (steName sym) 116 | in Map.fromList $ catMaybes $ map go $ concat bloatedSymbols 117 | 118 | member k = Map.member (C.pack k) 119 | (!?) m k = m Map.!? (C.pack k) 120 | 121 | it "parses symbol symbol names" $ do 122 | namedBloatedSymbols `shouldSatisfy` member "_init" 123 | namedBloatedSymbols `shouldSatisfy` member "main" 124 | 125 | let initSymbol = namedBloatedSymbols !? "_init" 126 | fnameSymbol = namedBloatedSymbols !? "bloated.cpp" 127 | 128 | it "parses symbol address" $ 129 | fmap steValue initSymbol `shouldBe` Just 0x0804850c 130 | 131 | it "parses symbol type" $ do 132 | fmap steType initSymbol `shouldBe` Just STTFunc 133 | fmap steType fnameSymbol `shouldBe` Just STTFile 134 | describe "parse DynSym symbols" $ do 135 | let dynSymbols = parseSymbolTables dynsymElf 136 | it "parses dyn symbol table" $ do 137 | dynSymbols `shouldNotBe` [] 138 | it "parse (x86_64) vdso dyn symbols" $ do 139 | let dynSyms = concat dynSymbols 140 | filter (\e -> (snd . steName) e == (Just . C.pack) "__vdso_time") dynSyms `shouldNotBe` [] 141 | filter (\e -> (snd . steName) e == (Just . C.pack) "__vdso_getcpu") dynSyms `shouldNotBe` [] 142 | filter (\e -> (snd . steName) e == (Just . C.pack) "__vdso_clock_gettime") dynSyms `shouldNotBe` [] 143 | filter (\e -> (snd . steName) e == (Just . C.pack) "__vdso_gettimeofday") dynSyms `shouldNotBe` [] 144 | -------------------------------------------------------------------------------- /tests/Spec.hs: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_GHC -F -pgmF hspec-discover #-} 2 | --------------------------------------------------------------------------------