module Data.Elf (getElf, Elf(..), ElfSection(..), ElfClass(..), ElfData(..), ElfOSABI(..), ElfType(..), ElfMachine(..)) where
import Data.Binary
import Data.Binary.Get
import Data.Bits
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 System.IO.Unsafe
data Elf = Elf
{ elfClass :: ElfClass
, elfData :: ElfData
, elfVersion :: Int
, elfOSABI :: ElfOSABI
, elfABIVersion :: Int
, elfType :: ElfType
, elfMachine :: ElfMachine
, elfEntry :: Word64
, elfSections :: [ElfSection]
} deriving (Eq, Show)
data ElfSection = ElfSection
{ elfSectionName :: String
, elfSectionType :: ElfSectionType
, elfSectionFlags :: [ElfSectionFlags]
, elfSectionAddr :: Word64
, elfSectionSize :: Word64
, elfSectionLink :: Word32
, elfSectionInfo :: Word32
, elfSectionAddrAlign :: Word64
, elfSectionEntSize :: Word64
, elfSectionData :: B.ByteString
} deriving (Eq, Show)
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
getElfVersion = do
ei_version <- getWord8
if ei_version /= 1 then
fail "Invalid version number for ELF"
else
return ei_version
data ElfSectionType
= SHT_NULL
| SHT_PROGBITS
| SHT_SYMTAB
| SHT_STRTAB
| SHT_RELA
| SHT_HASH
| SHT_DYNAMIC
| SHT_NOTE
| SHT_NOBITS
| SHT_REL
| SHT_SHLIB
| SHT_DYNSYM
| SHT_EXT Word32
deriving (Eq, Show)
getElfSectionType er = getWord32 er >>= return . getElfSectionType_
where getElfSectionType_ 0 = SHT_NULL
getElfSectionType_ 1 = SHT_PROGBITS
getElfSectionType_ 2 = SHT_SYMTAB
getElfSectionType_ 3 = SHT_STRTAB
getElfSectionType_ 4 = SHT_RELA
getElfSectionType_ 5 = SHT_HASH
getElfSectionType_ 6 = SHT_DYNAMIC
getElfSectionType_ 7 = SHT_NOTE
getElfSectionType_ 8 = SHT_NOBITS
getElfSectionType_ 9 = SHT_REL
getElfSectionType_ 10 = SHT_SHLIB
getElfSectionType_ 11 = SHT_DYNSYM
getElfSectionType_ n = SHT_EXT n
data ElfSectionFlags
= SHF_WRITE
| SHF_ALLOC
| SHF_EXECINSTR
| SHF_EXT Int
deriving (Eq, Show)
getElfSectionFlags32 er = getWord32 er >>= return . getElfSectionFlags_ 31
where getElfSectionFlags_ 0 word = []
getElfSectionFlags_ 1 word | testBit word 1 = SHF_WRITE : getElfSectionFlags_ 0 word
getElfSectionFlags_ 2 word | testBit word 2 = SHF_ALLOC : getElfSectionFlags_ 1 word
getElfSectionFlags_ 3 word | testBit word 4 = SHF_EXECINSTR : getElfSectionFlags_ 2 word
getElfSectionFlags_ n word | testBit word n = SHF_EXT n : getElfSectionFlags_ (n1) word
getElfSectionFlags_ n word = getElfSectionFlags_ (n1) word
getElfSectionFlags64 er = getWord64 er >>= return . getElfSectionFlags_ 63
where getElfSectionFlags_ 0 word = []
getElfSectionFlags_ 1 word | testBit word 1 = SHF_WRITE : getElfSectionFlags_ 0 word
getElfSectionFlags_ 2 word | testBit word 2 = SHF_ALLOC : getElfSectionFlags_ 1 word
getElfSectionFlags_ 3 word | testBit word 4 = SHF_EXECINSTR : getElfSectionFlags_ 2 word
getElfSectionFlags_ n word | testBit word n = SHF_EXT n : getElfSectionFlags_ (n1) word
getElfSectionFlags_ n word = getElfSectionFlags_ (n1) word
data ElfClass
= ELFCLASS32
| ELFCLASS64
deriving (Eq, Show)
getElfClass = getWord8 >>= getElfClass_
where getElfClass_ 1 = return ELFCLASS32
getElfClass_ 2 = return ELFCLASS64
getElfClass_ _ = fail "Invalid ELF class"
data ElfData
= ELFDATA2LSB
| ELFDATA2MSB
deriving (Eq, Show)
getElfData = getWord8 >>= getElfData_
where getElfData_ 1 = return ELFDATA2LSB
getElfData_ 2 = return ELFDATA2MSB
getElfData_ _ = fail "Invalid ELF data"
data ElfOSABI
= ELFOSABI_SYSV
| ELFOSABI_HPUX
| ELFOSABI_NETBSD
| ELFOSABI_LINUX
| ELFOSABI_SOLARIS
| ELFOSABI_AIX
| ELFOSABI_IRIX
| ELFOSABI_FREEBSD
| ELFOSABI_TRU64
| ELFOSABI_MODESTO
| ELFOSABI_OPENBSD
| ELFOSABI_OPENVMS
| ELFOSABI_NSK
| ELFOSABI_AROS
| ELFOSABI_ARM
| ELFOSABI_STANDALONE
| ELFOSABI_EXT Word8
deriving (Eq, Show)
getElfOsabi = getWord8 >>= return . getElfOsabi_
where getElfOsabi_ 0 = ELFOSABI_SYSV
getElfOsabi_ 1 = ELFOSABI_HPUX
getElfOsabi_ 2 = ELFOSABI_NETBSD
getElfOsabi_ 3 = ELFOSABI_LINUX
getElfOsabi_ 6 = ELFOSABI_SOLARIS
getElfOsabi_ 7 = ELFOSABI_AIX
getElfOsabi_ 8 = ELFOSABI_IRIX
getElfOsabi_ 9 = ELFOSABI_FREEBSD
getElfOsabi_ 10 = ELFOSABI_TRU64
getElfOsabi_ 11 = ELFOSABI_MODESTO
getElfOsabi_ 12 = ELFOSABI_OPENBSD
getElfOsabi_ 13 = ELFOSABI_OPENVMS
getElfOsabi_ 14 = ELFOSABI_NSK
getElfOsabi_ 15 = ELFOSABI_AROS
getElfOsabi_ 97 = ELFOSABI_ARM
getElfOsabi_ 255 = ELFOSABI_STANDALONE
getElfOsabi_ n = ELFOSABI_EXT n
data ElfType
= ET_NONE
| ET_REL
| ET_EXEC
| ET_DYN
| ET_CORE
| ET_EXT Word16
deriving (Eq, Show)
getElfType er = getWord16 er >>= return . getElfType_
where getElfType_ 0 = ET_NONE
getElfType_ 1 = ET_REL
getElfType_ 2 = ET_EXEC
getElfType_ 3 = ET_DYN
getElfType_ 4 = ET_CORE
getElfType_ n = ET_EXT n
data ElfMachine
= EM_NONE
| EM_M32
| EM_SPARC
| EM_386
| EM_68K
| EM_88K
| EM_486
| EM_860
| EM_MIPS
| EM_S370
| EM_MIPS_RS3_LE
| EM_SPARC64
| EM_PARISC
| EM_VPP500
| EM_SPARC32PLUS
| EM_960
| EM_PPC
| EM_PPC64
| EM_S390
| EM_SPU
| EM_V800
| EM_FR20
| EM_RH32
| EM_RCE
| EM_ARM
| EM_ALPHA
| EM_SH
| EM_SPARCV9
| EM_TRICORE
| EM_ARC
| EM_H8_300
| EM_H8_300H
| EM_H8S
| EM_H8_500
| EM_IA_64
| EM_MIPS_X
| EM_COLDFIRE
| EM_68HC12
| EM_MMA
| EM_PCP
| EM_NCPU
| EM_NDR1
| EM_STARCORE
| EM_ME16
| EM_ST100
| EM_TINYJ
| EM_X86_64
| EM_PDSP
| EM_FX66
| EM_ST9PLUS
| EM_ST7
| EM_68HC16
| EM_68HC11
| EM_68HC08
| EM_68HC05
| EM_SVX
| EM_ST19
| EM_VAX
| EM_CRIS
| EM_JAVELIN
| EM_FIREPATH
| EM_ZSP
| EM_MMIX
| EM_HUANY
| EM_PRISM
| EM_AVR
| EM_FR30
| EM_D10V
| EM_D30V
| EM_V850
| EM_M32R
| EM_MN10300
| EM_MN10200
| EM_PJ
| EM_OPENRISC
| EM_ARC_A5
| EM_XTENSA
| EM_VIDEOCORE
| EM_TMM_GPP
| EM_NS32K
| EM_TPC
| EM_SNP1K
| EM_ST200
| EM_IP2K
| EM_MAX
| EM_CR
| EM_F2MC16
| EM_MSP430
| EM_BLACKFIN
| EM_SE_C33
| EM_SEP
| EM_ARCA
| EM_UNICORE
| EM_EXT Word16
deriving (Eq, Show)
getElfMachine er = getWord16 er >>= return . getElfMachine_
where getElfMachine_ 0 = EM_NONE
getElfMachine_ 1 = EM_M32
getElfMachine_ 2 = EM_SPARC
getElfMachine_ 3 = EM_386
getElfMachine_ 4 = EM_68K
getElfMachine_ 5 = EM_88K
getElfMachine_ 6 = EM_486
getElfMachine_ 7 = EM_860
getElfMachine_ 8 = EM_MIPS
getElfMachine_ 9 = EM_S370
getElfMachine_ 10 = EM_MIPS_RS3_LE
getElfMachine_ 11 = EM_SPARC64
getElfMachine_ 15 = EM_PARISC
getElfMachine_ 17 = EM_VPP500
getElfMachine_ 18 = EM_SPARC32PLUS
getElfMachine_ 19 = EM_960
getElfMachine_ 20 = EM_PPC
getElfMachine_ 21 = EM_PPC64
getElfMachine_ 22 = EM_S390
getElfMachine_ 23 = EM_SPU
getElfMachine_ 36 = EM_V800
getElfMachine_ 37 = EM_FR20
getElfMachine_ 38 = EM_RH32
getElfMachine_ 39 = EM_RCE
getElfMachine_ 40 = EM_ARM
getElfMachine_ 41 = EM_ALPHA
getElfMachine_ 42 = EM_SH
getElfMachine_ 43 = EM_SPARCV9
getElfMachine_ 44 = EM_TRICORE
getElfMachine_ 45 = EM_ARC
getElfMachine_ 46 = EM_H8_300
getElfMachine_ 47 = EM_H8_300H
getElfMachine_ 48 = EM_H8S
getElfMachine_ 49 = EM_H8_500
getElfMachine_ 50 = EM_IA_64
getElfMachine_ 51 = EM_MIPS_X
getElfMachine_ 52 = EM_COLDFIRE
getElfMachine_ 53 = EM_68HC12
getElfMachine_ 54 = EM_MMA
getElfMachine_ 55 = EM_PCP
getElfMachine_ 56 = EM_NCPU
getElfMachine_ 57 = EM_NDR1
getElfMachine_ 58 = EM_STARCORE
getElfMachine_ 59 = EM_ME16
getElfMachine_ 60 = EM_ST100
getElfMachine_ 61 = EM_TINYJ
getElfMachine_ 62 = EM_X86_64
getElfMachine_ 63 = EM_PDSP
getElfMachine_ 66 = EM_FX66
getElfMachine_ 67 = EM_ST9PLUS
getElfMachine_ 68 = EM_ST7
getElfMachine_ 69 = EM_68HC16
getElfMachine_ 70 = EM_68HC11
getElfMachine_ 71 = EM_68HC08
getElfMachine_ 72 = EM_68HC05
getElfMachine_ 73 = EM_SVX
getElfMachine_ 74 = EM_ST19
getElfMachine_ 75 = EM_VAX
getElfMachine_ 76 = EM_CRIS
getElfMachine_ 77 = EM_JAVELIN
getElfMachine_ 78 = EM_FIREPATH
getElfMachine_ 79 = EM_ZSP
getElfMachine_ 80 = EM_MMIX
getElfMachine_ 81 = EM_HUANY
getElfMachine_ 82 = EM_PRISM
getElfMachine_ 83 = EM_AVR
getElfMachine_ 84 = EM_FR30
getElfMachine_ 85 = EM_D10V
getElfMachine_ 86 = EM_D30V
getElfMachine_ 87 = EM_V850
getElfMachine_ 88 = EM_M32R
getElfMachine_ 89 = EM_MN10300
getElfMachine_ 90 = EM_MN10200
getElfMachine_ 91 = EM_PJ
getElfMachine_ 92 = EM_OPENRISC
getElfMachine_ 93 = EM_ARC_A5
getElfMachine_ 94 = EM_XTENSA
getElfMachine_ 95 = EM_VIDEOCORE
getElfMachine_ 96 = EM_TMM_GPP
getElfMachine_ 97 = EM_NS32K
getElfMachine_ 98 = EM_TPC
getElfMachine_ 99 = EM_SNP1K
getElfMachine_ 100 = EM_ST200
getElfMachine_ 101 = EM_IP2K
getElfMachine_ 102 = EM_MAX
getElfMachine_ 103 = EM_CR
getElfMachine_ 104 = EM_F2MC16
getElfMachine_ 105 = EM_MSP430
getElfMachine_ 106 = EM_BLACKFIN
getElfMachine_ 107 = EM_SE_C33
getElfMachine_ 108 = EM_SEP
getElfMachine_ 109 = EM_ARCA
getElfMachine_ 110 = EM_UNICORE
getElfMachine_ n = EM_EXT n
getElf_Shdr_OffsetSize :: ElfClass -> ElfReader -> Get (Word64, Word64)
getElf_Shdr_OffsetSize ei_class er =
case ei_class of
ELFCLASS32 -> do
skip 16
sh_offset <- liftM fromIntegral $ getWord32 er
sh_size <- liftM fromIntegral $ getWord32 er
return (sh_offset, sh_size)
ELFCLASS64 -> do
skip 24
sh_offset <- getWord64 er
sh_size <- getWord64 er
return (sh_offset, sh_size)
getElf_Shdr :: ElfClass -> ElfReader -> B.ByteString -> B.ByteString -> Get ElfSection
getElf_Shdr ei_class er elf_file string_section =
case ei_class of
ELFCLASS32 -> do
sh_name <- getWord32 er
sh_type <- getElfSectionType er
sh_flags <- getElfSectionFlags32 er
sh_addr <- getWord32 er
sh_offset <- getWord32 er
sh_size <- getWord32 er
sh_link <- getWord32 er
sh_info <- getWord32 er
sh_addralign <- getWord32 er
sh_entsize <- getWord32 er
return $ ElfSection
{ elfSectionName = map B.w2c $ B.unpack $ B.takeWhile (/= 0) $ B.drop (fromIntegral sh_name) string_section
, elfSectionType = sh_type
, elfSectionFlags = sh_flags
, elfSectionAddr = fromIntegral sh_addr
, elfSectionSize = fromIntegral sh_size
, elfSectionLink = sh_link
, elfSectionInfo = sh_info
, elfSectionAddrAlign = fromIntegral sh_addralign
, elfSectionEntSize = fromIntegral sh_entsize
, elfSectionData = B.take (fromIntegral sh_size) $ B.drop (fromIntegral sh_offset) elf_file
}
ELFCLASS64 -> do
sh_name <- getWord32 er
sh_type <- getElfSectionType er
sh_flags <- getElfSectionFlags64 er
sh_addr <- getWord64 er
sh_offset <- getWord64 er
sh_size <- getWord64 er
sh_link <- getWord32 er
sh_info <- getWord32 er
sh_addralign <- getWord64 er
sh_entsize <- getWord64 er
return $ ElfSection
{ elfSectionName = map B.w2c $ B.unpack $ B.takeWhile (/= 0) $ B.drop (fromIntegral sh_name) string_section
, elfSectionType = sh_type
, elfSectionFlags = sh_flags
, elfSectionAddr = sh_addr
, elfSectionSize = sh_size
, elfSectionLink = sh_link
, elfSectionInfo = sh_info
, elfSectionAddrAlign = sh_addralign
, elfSectionEntSize = sh_entsize
, elfSectionData = B.take (fromIntegral sh_size) $ B.drop (fromIntegral sh_offset) elf_file
}
getElf_Ehdr :: Get (Elf, Word64, Word16, Word16, Word16)
getElf_Ehdr = do
ei_magic <- getElfMagic
ei_class <- getElfClass
ei_data <- getElfData
ei_version <- liftM fromIntegral getElfVersion
ei_osabi <- getElfOsabi
ei_abiver <- liftM fromIntegral getWord8
skip 7
er <- return $ elfReader ei_data
case ei_class of
ELFCLASS32 -> do
e_type <- getElfType er
e_machine <- getElfMachine er
e_version <- getWord32 er
e_entry <- getWord32 er >>= return . fromIntegral
e_phoff <- getWord32 er
e_shoff <- getWord32 er >>= return . fromIntegral
e_flags <- getWord32 er
e_ehsize <- getWord16 er
e_phentsize <- getWord16 er
e_phnum <- getWord16 er
e_shentsize <- getWord16 er
e_shnum <- getWord16 er
e_shstrndx <- getWord16 er
return (Elf { elfClass = ei_class
, elfData = ei_data
, elfVersion = ei_version
, elfOSABI = ei_osabi
, elfABIVersion = ei_abiver
, elfType = e_type
, elfMachine = e_machine
, elfEntry = e_entry
, elfSections = [] }
, e_shoff, e_shentsize, e_shnum, e_shstrndx)
ELFCLASS64 -> do
e_type <- getElfType er
e_machine <- getElfMachine er
e_version <- getWord32 er
e_entry <- getWord64 er
e_phoff <- getWord64 er
e_shoff <- getWord64 er
e_flags <- getWord32 er
e_ehsize <- getWord16 er
e_phentsize <- getWord16 er
e_phnum <- getWord16 er
e_shentsize <- getWord16 er
e_shnum <- getWord16 er
e_shstrndx <- getWord16 er
return (Elf { elfClass = ei_class
, elfData = ei_data
, elfVersion = ei_version
, elfOSABI = ei_osabi
, elfABIVersion = ei_abiver
, elfType = e_type
, elfMachine = e_machine
, elfEntry = e_entry
, elfSections = [] }
, e_shoff, e_shentsize, e_shnum, e_shstrndx)
data ElfReader = ElfReader
{ getWord16 :: Get Word16
, getWord32 :: Get Word32
, getWord64 :: Get Word64
}
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 bs s n = let (x,y) = B.splitAt s bs in x:(divide y s (n1))
getElf :: B.ByteString -> Elf
getElf b =
let (e, e_shoff, e_shentsize, e_shnum, e_shstrndx) = runGet getElf_Ehdr $ L.fromChunks [b]
sh = B.take (fromIntegral (e_shentsize * e_shnum)) $ B.drop (fromIntegral e_shoff) b
(shstroff, shstrsize) = runGet (getElf_Shdr_OffsetSize (elfClass e) (elfReader $ elfData e)) $ L.fromChunks [B.drop (fromIntegral (e_shentsize * e_shstrndx)) sh]
sh_str = B.take (fromIntegral shstrsize) $ B.drop (fromIntegral shstroff) b
sections = filter (\sec -> elfSectionType sec /= SHT_NULL) $ map (runGet (getElf_Shdr (elfClass e) (elfReader $ elfData e) b sh_str)) (map (\x -> L.fromChunks [x]) (divide sh (fromIntegral e_shentsize) (fromIntegral e_shnum)))
in e { elfSections = sections }