{-# LANGUAGE UnicodeSyntax #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE TypeFamilies #-} -- | This module provides ELF data structures and (de)serialization routines. module Data.Elf ( -- * File class FileClass(..) , invalidFileClass , elf32FileClass , elf64FileClass , IsFileClass(..) , Elf32(..) , anElf32 , Elf64(..) , anElf64 -- * File header -- ** File type , FileType(..) , noneFileType , relFileType , execFileType , dynFileType , coreFileType , loOsFileType , hiOsFileType , loProcFileType , hiProcFileType -- ** Machine code , Machine(..) , undefMachine , i386Machine , amd64Machine -- ** Machine flags , MachFlags(..) -- ** ELF version , Version(..) , invalidVersion , firstVersion -- ** Data encoding , DataEnc(..) , invalidDataEnc , lsbDataEnc , msbDataEnc -- ** Operating system ABI , OsAbi(..) , sysvOsAbi , hpuxOsAbi , netBsdOsAbi , gnuOsAbi , solarisOsAbi , aixOsAbi , irixOsAbi , freeBsdOsAbi , tru64OsAbi , modestoOsAbi , openBsdOsAbi , openVmsOsAbi , nskOsAbi , arosOsAbi , fenixOsAbi , embedOsAbi , AbiVer(..) , undefAbiVer -- ** Identification , Ident(..) , anIdent -- ** File header , FileHdr(..) , FileHdr32 , aFileHdr32 , FileHdr64 , aFileHdr64 -- * Program header -- ** Segment type , SegType(..) , unusedSegType , loadSegType , dynSegType , interpSegType , noteSegType , shlibSegType , phdrSegType , loOsSegType , hiOsSegType , loProcSegType , hiProcSegType -- ** Segment flags , SegFlags(..) , execSegFlag , writeSegFlag , readSegFlag , osSegFlags , procSegFlags -- ** Program header , ProgHdr(..) , ProgHdr32 , aProgHdr32 , ProgHdr64 , aProgHdr64 -- * Section header -- ** String table index , StrIx -- ** Section header table index , SecIx(..) , undefSecIx , lastSecIx , loOsSecIx , hiOsSecIx , loProcSecIx , hiProcSecIx , absSecIx , commonSecIx , xIndexSecIx -- ** Section type , SecType(..) , unusedSecType , progBitsSecType , symSecType , strSecType , relaSecType , hashSecType , dynSecType , noteSecType , noBitsSecType , relSecType , shlibSecType , dynSymSecType , loOsSecType , hiOsSecType , loProcSecType , hiProcSecType -- ** Section flags , SecFlags(..) , writeSecFlag , allocSecFlag , execSecFlag , mergeSecFlag , infoLinkSecFlag , osSecFlags , procSecFlags -- ** Section header , SecHdr(..) , SecHdr32 , aSecHdr32 , SecHdr64 , aSecHdr64 , zeroSecHdr -- * Symbol table -- ** Symbol type , SymType(..) , undefSymType , objSymType , funSymType , secSymType , fileSymType , commonSymType , tlsSymType , loOsSymType , hiOsSymType , loProcSymType , hiProcSymType -- ** Symbol binding , SymBind(..) , localSymBind , globalSymBind , weakSymBind , loOsSymBind , hiOsSymBind , loProcSymBind , hiProcSymBind -- ** Symbol visibility , SymVisi(..) , defSymVisi , intSymVisi , hiddenSymVisi , protSymVisi , exportSymVisi , singSymVisi , elimSymVisi -- ** Symbol table index , SymIx(..) , undefSymIx -- ** Symbol table entry , SymEnt(..) , SymEnt32 , aSymEnt32 , SymEnt64 , aSymEnt64 , zeroSymEnt -- * Relocation table -- ** Relocation type , RelType(..) , RelType32 , RelType64 -- ** Relocation table entry , RelEnt(..) , RelEnt32 , aRelEnt32 , RelEnt64 , aRelEnt64 , RelaEnt(..) , RelaEnt32 , aRelaEnt32 , RelaEnt64 , aRelaEnt64 ) where import Data.Typeable (Typeable) import Data.Data (Data) import Data.Proxy (Proxy(..)) import Data.Ix (Ix) import Data.Bits (Bits, shiftL, shiftR, (.|.), FiniteBits) import Data.Flags (Flags(noFlags), BoundedFlags) import Data.Word (Word8, Word16, Word32, Word64) import Data.ShortWord (Word4, Word24) import Data.Monoid ((<>)) import Data.Serializer (Serializable, SizedSerializable) import qualified Data.Serializer as S import Data.Deserializer (Deserializable, ()) import qualified Data.Deserializer as D import Control.Applicative ((<$>)) import Control.Monad (void, unless) -- | File class. newtype FileClass = FileClass { unFileClass ∷ Word8 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Invalid class (@ELFCLASSNONE@). invalidFileClass ∷ FileClass invalidFileClass = FileClass 0 -- | ELF32 class (@ELFCLASS32@). elf32FileClass ∷ FileClass elf32FileClass = FileClass 1 -- | ELF64 class (@ELFCLASS64@). elf64FileClass ∷ FileClass elf64FileClass = FileClass 2 -- | File class type-level index. class (Typeable c, Typeable (Addr c), Data (Addr c), Typeable (Off c), Data (Off c), Typeable (UnSymIx c), Data (UnSymIx c), Typeable (UnRelType c), Data (UnRelType c), Show (Addr c), Read (Addr c), Show (Off c), Read (Off c), Show (UnSymIx c), Read (UnSymIx c), Show (UnRelType c), Read (UnRelType c), Eq (Addr c), Ord (Addr c), Bounded (Addr c), Enum (Addr c), Num (Addr c), Integral (Addr c), Real (Addr c), Bits (Addr c), FiniteBits (Addr c), Eq (Off c), Ord (Off c), Bounded (Off c), Enum (Off c), Num (Off c), Integral (Off c), Real (Off c), Bits (Off c), FiniteBits (Off c), Eq (UnSymIx c), Ord (UnSymIx c), Bounded (UnSymIx c), Enum (UnSymIx c), Ix (UnSymIx c), Num (UnSymIx c), Integral (UnSymIx c), Real (UnSymIx c), Bits (UnSymIx c), FiniteBits (UnSymIx c), Eq (UnRelType c), Ord (UnRelType c), Bounded (UnRelType c), Enum (UnRelType c), Ix (UnRelType c)) ⇒ IsFileClass c where type Addr c type Off c type UnSymIx c type UnRelType c fileClass ∷ Proxy c → FileClass -- | 32-bit ELF class type-level index. data Elf32 = Elf32 deriving (Typeable, Data) -- | 'Elf32' proxy value. anElf32 ∷ Proxy Elf32 anElf32 = Proxy instance IsFileClass Elf32 where type Addr Elf32 = Word32 type Off Elf32 = Word32 type UnSymIx Elf32 = Word24 type UnRelType Elf32 = Word8 fileClass _ = elf32FileClass -- | 64-bit ELF class type-level index. data Elf64 = Elf64 deriving (Typeable, Data) -- | 'Elf64' proxy value. anElf64 ∷ Proxy Elf64 anElf64 = Proxy instance IsFileClass Elf64 where type Addr Elf64 = Word64 type Off Elf64 = Word64 type UnSymIx Elf64 = Word32 type UnRelType Elf64 = Word32 fileClass _ = elf64FileClass -- | File type. newtype FileType = FileType { unFileType ∷ Word16 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | No file type (@ET_NONE@). noneFileType ∷ FileType noneFileType = FileType 0 -- | Relocatable object file (@ET_REL@). relFileType ∷ FileType relFileType = FileType 1 -- | Executable file (@ET_EXEC@). execFileType ∷ FileType execFileType = FileType 2 -- | Shared object file (@ET_DYN@). dynFileType ∷ FileType dynFileType = FileType 3 -- | Core file (@ET_CORE@). coreFileType ∷ FileType coreFileType = FileType 4 -- | First environment-specific type (@ET_LOOS@). loOsFileType ∷ FileType loOsFileType = FileType 0xFE00 -- | Last environment-specific type (@ET_HIOS@). hiOsFileType ∷ FileType hiOsFileType = FileType 0xFEFF -- | First processor-specific type (@ET_LOPROC@). loProcFileType ∷ FileType loProcFileType = FileType 0xFF00 -- | Last processor-specific type (@ET_HIPROC@). hiProcFileType ∷ FileType hiProcFileType = FileType 0xFFFF -- | Machine architecture. newtype Machine = Machine { unMachine ∷ Word16 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | No machine (@EM_NONE@). undefMachine ∷ Machine undefMachine = Machine 0 -- | Intel 80386 (@EM_386@). i386Machine ∷ Machine i386Machine = Machine 3 -- | AMD x86-64 (@EM_X86_64@). amd64Machine ∷ Machine amd64Machine = Machine 62 -- | Architecture-specific flags. newtype MachFlags = MachFlags { unMachFlags ∷ Word32 } deriving (Typeable, Data, Show, Read, Eq, Flags, BoundedFlags) -- | ELF version. newtype Version = Version { unVersion ∷ Word8 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Invalid version. invalidVersion ∷ Version invalidVersion = Version 0 -- | Version 1. firstVersion ∷ Version firstVersion = Version 1 -- | Data encoding. newtype DataEnc = DataEnc { unDataEnc ∷ Word8 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Invalid data encoding. invalidDataEnc ∷ DataEnc invalidDataEnc = DataEnc 0 -- | Little-endian data encoding (@ELFDATA2LSB@). lsbDataEnc ∷ DataEnc lsbDataEnc = DataEnc 1 -- | Big-endian data encoding (@ELFDATA2MSB@). msbDataEnc ∷ DataEnc msbDataEnc = DataEnc 2 -- | System ABI. newtype OsAbi = OsAbi { unOsAbi ∷ Word8 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | System V (@ELFOSABI_NONE@). sysvOsAbi ∷ OsAbi sysvOsAbi = OsAbi 0 -- | HP-UX (@ELFOSABI_HPUX@). hpuxOsAbi ∷ OsAbi hpuxOsAbi = OsAbi 1 -- | NetBSD (@ELFOSABI_NETBSD@). netBsdOsAbi ∷ OsAbi netBsdOsAbi = OsAbi 2 -- | GNU (@ELFOSABI_GNU@, @ELFOSABI_LINUX@). gnuOsAbi ∷ OsAbi gnuOsAbi = OsAbi 3 -- | Solaris (@ELFOSABI_SOLARIS@). solarisOsAbi ∷ OsAbi solarisOsAbi = OsAbi 6 -- | AIX (@ELFOSABI_AIX@). aixOsAbi ∷ OsAbi aixOsAbi = OsAbi 7 -- | IRIX (@ELFOSABI_IRIX@). irixOsAbi ∷ OsAbi irixOsAbi = OsAbi 8 -- | FreeBSD (@ELFOSABI_FREEBSD@). freeBsdOsAbi ∷ OsAbi freeBsdOsAbi = OsAbi 9 -- | Tru64 UNIX (@ELFOSABI_TRU64@). tru64OsAbi ∷ OsAbi tru64OsAbi = OsAbi 10 -- | Novell Modesto (@ELFOSABI_MODESTO@). modestoOsAbi ∷ OsAbi modestoOsAbi = OsAbi 11 -- | OpenBSD (@ELFOSABI_OPENBSD@). openBsdOsAbi ∷ OsAbi openBsdOsAbi = OsAbi 12 -- | OpenVMS (@ELFOSABI_OPENVMS@). openVmsOsAbi ∷ OsAbi openVmsOsAbi = OsAbi 13 -- | Hewlett-Packard Non-Stop Kernel (@ELFOSABI_NSK@). nskOsAbi ∷ OsAbi nskOsAbi = OsAbi 14 -- | Amiga Research OS (@ELFOSABI_AROS@). arosOsAbi ∷ OsAbi arosOsAbi = OsAbi 15 -- | FenixOS (@ELFOSABI_FENIXOS@). fenixOsAbi ∷ OsAbi fenixOsAbi = OsAbi 16 -- | Standalone (embedded) application. embedOsAbi ∷ OsAbi embedOsAbi = OsAbi 255 -- | ABI version. newtype AbiVer = AbiVer { unAbiVer ∷ Word8 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Undefined ABI version. undefAbiVer ∷ AbiVer undefAbiVer = AbiVer 0 -- | Identification. data Ident = Ident { idClass ∷ FileClass , idVersion ∷ Version , idDataEnc ∷ DataEnc , idOsAbi ∷ OsAbi , idAbiVer ∷ AbiVer } deriving (Typeable, Data, Show, Read, Eq) -- | 'Ident' proxy value. anIdent ∷ Proxy Ident anIdent = Proxy instance Serializable Ident where put (Ident {..}) = S.byteString "\127ELF" <> S.word8 (unFileClass idClass) <> S.word8 (unDataEnc idDataEnc) <> S.word8 (unVersion idVersion) <> S.word8 (unOsAbi idOsAbi) <> S.word8 (unAbiVer idAbiVer) <> S.byteString "\0\0\0\0\0\0\0" instance SizedSerializable Ident where size _ = 16 {-# INLINE size #-} instance Deserializable Ident where get = do void $ D.bytes "\127ELF" "ELF magic number" fileCls ← FileClass <$> D.word8 "file class" dataEnc ← DataEnc <$> D.word8 "data encoding" version ← Version <$> D.word8 "file version" osAbi ← OsAbi <$> D.word8 "OS ABI code" abiVer ← AbiVer <$> D.word8 "OS ABI version" D.skip 7 return $ Ident { idClass = fileCls , idDataEnc = dataEnc , idVersion = version , idOsAbi = osAbi , idAbiVer = abiVer } -- | File header. data FileHdr c = FileHdr { fhType ∷ FileType , fhMachine ∷ Machine , fhEntry ∷ Addr c , fhFlags ∷ MachFlags , fhPhOff ∷ Off c , fhPhNum ∷ Word16 , fhShOff ∷ Off c , fhShNum ∷ Word16 , fhSnStIx ∷ SecIx } deriving Typeable deriving instance (Data c, IsFileClass c) ⇒ Data (FileHdr c) deriving instance IsFileClass c ⇒ Show (FileHdr c) deriving instance IsFileClass c ⇒ Read (FileHdr c) deriving instance IsFileClass c ⇒ Eq (FileHdr c) -- | ELF32 file header. type FileHdr32 = FileHdr Elf32 -- | 'FileHdr32' proxy value. aFileHdr32 ∷ Proxy FileHdr32 aFileHdr32 = Proxy instance Serializable FileHdr32 where put (FileHdr {..}) = S.word16 (unFileType fhType) <> S.word16 (unMachine fhMachine) <> S.word32 1 <> S.word32 fhEntry <> S.word32 fhPhOff <> S.word32 fhShOff <> S.word32 (unMachFlags fhFlags) <> S.word16 (fromIntegral $ S.size anIdent + S.size aFileHdr32) <> S.word16 (fromIntegral $ S.size aProgHdr32) <> S.word16 fhPhNum <> S.word16 (fromIntegral $ S.size aSecHdr32) <> S.word16 fhShNum <> S.word16 (unSecIx fhSnStIx) instance SizedSerializable FileHdr32 where size _ = 36 {-# INLINE size #-} instance Deserializable FileHdr32 where get = do fileType ← FileType <$> D.word16 "file type" machine ← Machine <$> D.word16 "machine" version ← D.word32 "version" unless (version == 1) $ D.unexpected "Unexpected ELF version" entry ← D.word32 "entry point address" phOff ← D.word32 "program header table offset" shOff ← D.word32 "section header table offset" flags ← MachFlags <$> D.word32 "machine flags" fileHdrSize ← D.word16 "file header size" unless (fileHdrSize == fromIntegral (S.size anIdent + S.size aFileHdr32)) $ D.unexpected "Unexpected ELF file header size" progHdrSize ← D.word16 "program header size" unless (progHdrSize == fromIntegral (S.size aProgHdr32)) $ D.unexpected "Unexpected ELF program header size" phNum ← D.word16 "program header table size" secHdrSize ← D.word16 "section header size" unless (secHdrSize == fromIntegral (S.size aSecHdr32)) $ D.unexpected "Unexpected ELF section header size" shNum ← D.word16 "section header table size" snStIx ← SecIx <$> D.word16 "section name string table section index" return $ FileHdr { fhType = fileType , fhMachine = machine , fhEntry = entry , fhPhOff = phOff , fhShOff = shOff , fhFlags = flags , fhPhNum = phNum , fhShNum = shNum , fhSnStIx = snStIx } -- | ELF64 file header. type FileHdr64 = FileHdr Elf64 -- | 'FileHdr64' proxy value. aFileHdr64 ∷ Proxy FileHdr64 aFileHdr64 = Proxy instance Serializable FileHdr64 where put (FileHdr {..}) = S.word16 (unFileType fhType) <> S.word16 (unMachine fhMachine) <> S.word32 1 <> S.word64 fhEntry <> S.word64 fhPhOff <> S.word64 fhShOff <> S.word32 (unMachFlags fhFlags) <> S.word16 (fromIntegral $ S.size anIdent + S.size aFileHdr64) <> S.word16 (fromIntegral $ S.size aProgHdr64) <> S.word16 fhPhNum <> S.word16 (fromIntegral $ S.size aSecHdr64) <> S.word16 fhShNum <> S.word16 (unSecIx fhSnStIx) instance SizedSerializable FileHdr64 where size _ = 56 {-# INLINE size #-} instance Deserializable FileHdr64 where get = do fileType ← FileType <$> D.word16 "file type" machine ← Machine <$> D.word16 "machine code" version ← D.word32 "version" unless (version == 1) $ D.unexpected "Unexpected ELF version" entry ← D.word64 "entry point address" phOff ← D.word64 "program header table offset" shOff ← D.word64 "section header table offset" flags ← MachFlags <$> D.word32 "machine flags" fileHdrSize ← D.word16 "file header size" unless (fileHdrSize == fromIntegral (S.size anIdent + S.size aFileHdr64)) $ D.unexpected "Unexpected ELF file header size" progHdrSize ← D.word16 "program header size" unless (progHdrSize == fromIntegral (S.size aProgHdr64)) $ D.unexpected "Unexpected ELF program header size" phNum ← D.word16 "program header table size" secHdrSize ← D.word16 "section header size" unless (secHdrSize == fromIntegral (S.size aSecHdr64)) $ D.unexpected "Unexpected ELF section header size" shNum ← D.word16 "section header table size" snStIx ← SecIx <$> D.word16 "section name string table section index" return $ FileHdr { fhType = fileType , fhMachine = machine , fhEntry = entry , fhPhOff = phOff , fhShOff = shOff , fhFlags = flags , fhPhNum = phNum , fhShNum = shNum , fhSnStIx = snStIx } -- | Segment type. newtype SegType = SegType { unSegType ∷ Word32 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Unused program header entry (@PT_NULL@). unusedSegType ∷ SegType unusedSegType = SegType 0 -- | Loadable segment (@PT_LOAD@). loadSegType ∷ SegType loadSegType = SegType 1 -- | Dynamic linking tables (@PT_DYNAMIC@). dynSegType ∷ SegType dynSegType = SegType 2 -- | Program interpreter path name (@PT_INTERP@). interpSegType ∷ SegType interpSegType = SegType 3 -- | Note sections (@PT_NOTE@). noteSegType ∷ SegType noteSegType = SegType 4 -- | A reserved segment type (@PT_SHLIB@). shlibSegType ∷ SegType shlibSegType = SegType 5 -- | Program header table (@PT_PHDR@). phdrSegType ∷ SegType phdrSegType = SegType 6 -- | First environment-specific segment type (@PT_LOOS@). loOsSegType ∷ SegType loOsSegType = SegType 0x60000000 -- | Last environment-specific segment type (@PT_HIOS@). hiOsSegType ∷ SegType hiOsSegType = SegType 0x6FFFFFFF -- | First processor-specific segment type (@PT_LOPROC@). loProcSegType ∷ SegType loProcSegType = SegType 0x70000000 -- | Last processor-specific segment type (@PT_HIPROC@). hiProcSegType ∷ SegType hiProcSegType = SegType 0x7FFFFFFF -- | Segment flags. newtype SegFlags = SegFlags { unSegFlags ∷ Word32 } deriving (Typeable, Data, Show, Read, Eq, Flags, BoundedFlags) -- | Execute permission (@PF_X@). execSegFlag ∷ SegFlags execSegFlag = SegFlags 1 -- | Write permission (@PF_W@). writeSegFlag ∷ SegFlags writeSegFlag = SegFlags 2 -- | Read permission (@PF_R@). readSegFlag ∷ SegFlags readSegFlag = SegFlags 4 -- | Environment-specific flags mask (@PF_MASKOS@). osSegFlags ∷ SegFlags osSegFlags = SegFlags 0x00FF0000 -- | Processor-specific flags mask (@PF_MASKPROC@). procSegFlags ∷ SegFlags procSegFlags = SegFlags 0xFF000000 -- | Program header table entry. data ProgHdr c = ProgHdr { phType ∷ SegType , phFlags ∷ SegFlags , phOff ∷ Off c , phVirtAddr ∷ Addr c , phPhysAddr ∷ Addr c , phFileSize ∷ Off c , phMemSize ∷ Addr c , phAlign ∷ Addr c } deriving Typeable deriving instance (Data c, IsFileClass c) ⇒ Data (ProgHdr c) deriving instance IsFileClass c ⇒ Show (ProgHdr c) deriving instance IsFileClass c ⇒ Read (ProgHdr c) deriving instance IsFileClass c ⇒ Eq (ProgHdr c) -- | ELF32 program header table entry. type ProgHdr32 = ProgHdr Elf32 -- | 'ProgHdr32' proxy value. aProgHdr32 ∷ Proxy ProgHdr32 aProgHdr32 = Proxy instance Serializable ProgHdr32 where put (ProgHdr {..}) = S.word32 (unSegType phType) <> S.word32 phOff <> S.word32 phVirtAddr <> S.word32 phPhysAddr <> S.word32 phFileSize <> S.word32 phMemSize <> S.word32 (unSegFlags phFlags) <> S.word32 phAlign instance SizedSerializable ProgHdr32 where size _ = 32 {-# INLINE size #-} instance Deserializable ProgHdr32 where get = do segType ← SegType <$> D.word32 "type" off ← D.word32 "offset" virtAddr ← D.word32 "virtual address" physAddr ← D.word32 "physical address" fileSize ← D.word32 "in-file size" memSize ← D.word32 "in-memory size" flags ← SegFlags <$> D.word32 "flags" align ← D.word32 "alignment" return $ ProgHdr { phType = segType , phOff = off , phVirtAddr = virtAddr , phPhysAddr = physAddr , phFileSize = fileSize , phMemSize = memSize , phFlags = flags , phAlign = align } -- | ELF64 program header table entry. type ProgHdr64 = ProgHdr Elf64 -- | 'ProgHdr64' proxy value. aProgHdr64 ∷ Proxy ProgHdr64 aProgHdr64 = Proxy instance Serializable ProgHdr64 where put (ProgHdr {..}) = S.word32 (unSegType phType) <> S.word32 (unSegFlags phFlags) <> S.word64 phOff <> S.word64 phVirtAddr <> S.word64 phPhysAddr <> S.word64 phFileSize <> S.word64 phMemSize <> S.word64 phAlign instance SizedSerializable ProgHdr64 where size _ = 56 {-# INLINE size #-} instance Deserializable ProgHdr64 where get = do segType ← SegType <$> D.word32 "type" flags ← SegFlags <$> D.word32 "flags" off ← D.word64 "offset" virtAddr ← D.word64 "virtual address" physAddr ← D.word64 "physical address" fileSize ← D.word64 "in-file size" memSize ← D.word64 "in-memory size" align ← D.word64 "alignment" return $ ProgHdr { phType = segType , phOff = off , phVirtAddr = virtAddr , phPhysAddr = physAddr , phFileSize = fileSize , phMemSize = memSize , phFlags = flags , phAlign = align } -- | String table index. type StrIx = Word32 -- | Section header entry index. newtype SecIx = SecIx { unSecIx ∷ Word16 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix, Num, Bits, FiniteBits, Integral, Real) -- | Undefined section reference (@SHN_UNDEF@). undefSecIx ∷ SecIx undefSecIx = 0 -- | Last regular section index. lastSecIx ∷ SecIx lastSecIx = 0xFEFF -- | First environment-specific section index (@SHN_LOOS@). loOsSecIx ∷ SecIx loOsSecIx = 0xFF20 -- | Last environment-specific section index (@SHN_HIOS@). hiOsSecIx ∷ SecIx hiOsSecIx = 0xFF3F -- | First processor-specific section index (@SHN_LOPROC@). loProcSecIx ∷ SecIx loProcSecIx = 0xFF00 -- | Last processor-specific section index (@SHN_HIPROC@). hiProcSecIx ∷ SecIx hiProcSecIx = 0xFF1F -- | Absolute value indicator (@SHN_ABS@). absSecIx ∷ SecIx absSecIx = 0xFFF1 -- | Common block indicator (@SHN_COMMON@). commonSecIx ∷ SecIx commonSecIx = 0xFFF2 -- | Escape value (@SHN_XINDEX@). xIndexSecIx ∷ SecIx xIndexSecIx = 0xFFFF -- | Section type. newtype SecType = SecType { unSecType ∷ Word32 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Unused section (@SHT_NULL@). unusedSecType ∷ SecType unusedSecType = SecType 0 -- | Program information (@SHT_PROGBITS@). progBitsSecType ∷ SecType progBitsSecType = SecType 1 -- | Symbol table (@SHT_SYMTAB@). symSecType ∷ SecType symSecType = SecType 2 -- | String table (@SHT_STRTAB@). strSecType ∷ SecType strSecType = SecType 3 -- | Relocation entries (@SHT_RELA@). relaSecType ∷ SecType relaSecType = SecType 4 -- | Symbol hash table (@SHT_HASH@). hashSecType ∷ SecType hashSecType = SecType 5 -- | Dynamic linking table (@SHT_DYNAMIC@). dynSecType ∷ SecType dynSecType = SecType 6 -- | Note information (@SHT_NOTE@). noteSecType ∷ SecType noteSecType = SecType 7 -- | Uninitialized space (@SHT_NOBITS@). noBitsSecType ∷ SecType noBitsSecType = SecType 8 -- | Relocation entries (@SHT_REL@). relSecType ∷ SecType relSecType = SecType 9 -- | A reserved section type (@SHT_SHLIB@). shlibSecType ∷ SecType shlibSecType = SecType 10 -- | Dynamic loader symbol table (@SHT_DYNSYM@). dynSymSecType ∷ SecType dynSymSecType = SecType 11 -- | First environment-specific section type (@SHT_LOOS@). loOsSecType ∷ SecType loOsSecType = SecType 0x60000000 -- | Last environment-specific section type (@SHT_HIOS@). hiOsSecType ∷ SecType hiOsSecType = SecType 0x6FFFFFFF -- | First processor-specific section type (@SHT_LOPROC@). loProcSecType ∷ SecType loProcSecType = SecType 0x70000000 -- | Last processor-specific section type (@SHT_HIPROC@). hiProcSecType ∷ SecType hiProcSecType = SecType 0x7FFFFFFF -- | Section flags. newtype SecFlags = SecFlags { unSecFlags ∷ Word32 } deriving (Typeable, Data, Show, Read, Eq, Flags, BoundedFlags) -- | Writable data (@SHF_WRITE@). writeSecFlag ∷ SecFlags writeSecFlag = SecFlags 1 -- | Allocated data (@SHF_ALLOC@). allocSecFlag ∷ SecFlags allocSecFlag = SecFlags 2 -- | Executable instructions (@SHF_EXECINSTR@). execSecFlag ∷ SecFlags execSecFlag = SecFlags 4 -- | Mergeable data (@SHF_MERGE@). mergeSecFlag ∷ SecFlags mergeSecFlag = SecFlags 0x10 -- | Flag that indicates that 'shInfo' is a section header table index. -- (@SHF_INFO_LINK@) infoLinkSecFlag ∷ SecFlags infoLinkSecFlag = SecFlags 0x40 -- | Environment-specific flags mask (@SHF_MASKOS@). osSecFlags ∷ SecFlags osSecFlags = SecFlags 0x0F000000 -- | Processor-specific flags mask (@SHF_MASKOS@). procSecFlags ∷ SecFlags procSecFlags = SecFlags 0xF0000000 -- | Section header table entry. data SecHdr c = SecHdr { shName ∷ StrIx , shType ∷ SecType , shFlags ∷ SecFlags , shAddr ∷ Addr c , shOff ∷ Off c , shSize ∷ Addr c , shLink ∷ Word32 , shInfo ∷ Word32 , shAlign ∷ Addr c , shEntSize ∷ Addr c } deriving Typeable deriving instance (Data c, IsFileClass c) ⇒ Data (SecHdr c) deriving instance IsFileClass c ⇒ Show (SecHdr c) deriving instance IsFileClass c ⇒ Read (SecHdr c) deriving instance IsFileClass c ⇒ Eq (SecHdr c) -- | ELF32 section header table entry. type SecHdr32 = SecHdr Elf32 -- | 'SecHdr32' proxy value. aSecHdr32 ∷ Proxy SecHdr32 aSecHdr32 = Proxy instance Serializable SecHdr32 where put (SecHdr {..}) = S.word32 shName <> S.word32 (unSecType shType) <> S.word32 (unSecFlags shFlags) <> S.word32 shAddr <> S.word32 shOff <> S.word32 shSize <> S.word32 shLink <> S.word32 shInfo <> S.word32 shAlign <> S.word32 shEntSize instance SizedSerializable SecHdr32 where size _ = 40 {-# INLINE size #-} instance Deserializable SecHdr32 where get = do name ← D.word32 "name" tp ← SecType <$> D.word32 "type" flags ← SecFlags <$> D.word32 "flags" addr ← D.word32 "address" off ← D.word32 "offset" size ← D.word32 "size" link ← D.word32 "link" info ← D.word32 "extra information" align ← D.word32 "alignment" entSize ← D.word32 "entry size" return $ SecHdr { shName = name , shType = tp , shFlags = flags , shAddr = addr , shOff = off , shSize = size , shLink = link , shInfo = info , shAlign = align , shEntSize = entSize } -- | ELF64 section header table entry. type SecHdr64 = SecHdr Elf64 -- | 'SecHdr64' proxy value. aSecHdr64 ∷ Proxy SecHdr64 aSecHdr64 = Proxy instance Serializable SecHdr64 where put (SecHdr {..}) = S.word32 shName <> S.word32 (unSecType shType) <> S.word64 (fromIntegral (unSecFlags shFlags)) <> S.word64 shAddr <> S.word64 shOff <> S.word64 shSize <> S.word32 shLink <> S.word32 shInfo <> S.word64 shAlign <> S.word64 shEntSize instance SizedSerializable SecHdr64 where size _ = 64 {-# INLINE size #-} instance Deserializable SecHdr64 where get = do name ← D.word32 "name" tp ← SecType <$> D.word32 "type" flags ← SecFlags . fromIntegral <$> D.word64 "flags" addr ← D.word64 "address" off ← D.word64 "offset" size ← D.word64 "size" link ← D.word32 "link" info ← D.word32 "extra information" align ← D.word64 "alignment" entSize ← D.word64 "entry size" return $ SecHdr { shName = name , shType = tp , shFlags = flags , shAddr = addr , shOff = off , shSize = size , shLink = link , shInfo = info , shAlign = align , shEntSize = entSize } -- | Section header filled with zeros. zeroSecHdr ∷ IsFileClass c ⇒ SecHdr c zeroSecHdr = SecHdr { shName = 0 , shType = unusedSecType , shFlags = noFlags , shAddr = 0 , shOff = 0 , shSize = 0 , shLink = 0 , shInfo = 0 , shAlign = 0 , shEntSize = 0 } -- | Symbol type. newtype SymType = SymType { unSymType ∷ Word4 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Undefined symbol type (@STT_NOTYPE@). undefSymType ∷ SymType undefSymType = SymType 0 -- | Object (@STT_OBJECT@). objSymType ∷ SymType objSymType = SymType 1 -- | Function (@STT_FUNC@). funSymType ∷ SymType funSymType = SymType 2 -- | Section (@STT_SECTION@). secSymType ∷ SymType secSymType = SymType 3 -- | Source file name (@STT_FILE@). fileSymType ∷ SymType fileSymType = SymType 4 -- | Common block label (@STT_COMMON@). commonSymType ∷ SymType commonSymType = SymType 5 -- | Thread-local storage (@STT_TLS@). tlsSymType ∷ SymType tlsSymType = SymType 6 -- | First environment-specific symbol type (@STT_LOOS@). loOsSymType ∷ SymType loOsSymType = SymType 10 -- | Last environment-specific symbol type (@STT_HIOS@). hiOsSymType ∷ SymType hiOsSymType = SymType 12 -- | First processor-specific symbol type (@STT_LOPROC@). loProcSymType ∷ SymType loProcSymType = SymType 13 -- | Last processor-specific symbol type (@STT_HIPROC@). hiProcSymType ∷ SymType hiProcSymType = SymType 15 -- | Symbol binding type. newtype SymBind = SymBind { unSymBind ∷ Word4 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Local symbol (@STB_LOCAL@). localSymBind ∷ SymBind localSymBind = SymBind 0 -- | Global symbol (@STB_GLOBAL@). globalSymBind ∷ SymBind globalSymBind = SymBind 1 -- | Lower precedence global symbol (@STB_WEAK@). weakSymBind ∷ SymBind weakSymBind = SymBind 2 -- | First environment-specific symbol binding type (@STB_LOOS@). loOsSymBind ∷ SymBind loOsSymBind = SymBind 10 -- | Last environment-specific symbol binding type (@STB_HIOS@). hiOsSymBind ∷ SymBind hiOsSymBind = SymBind 12 -- | First processor-specific symbol binding type (@STB_LOPROC@). loProcSymBind ∷ SymBind loProcSymBind = SymBind 13 -- | Last processor-specific symbol binding type (@STB_HIPROC@). hiProcSymBind ∷ SymBind hiProcSymBind = SymBind 15 -- | Symbol visibility. newtype SymVisi = SymVisi { unSymVisi ∷ Word4 } deriving (Typeable, Data, Show, Read, Eq, Ord, Bounded, Enum, Ix) -- | Default symbol visibility (specified by the binding type; -- @STV_DEFAULT@). defSymVisi ∷ SymVisi defSymVisi = SymVisi 0 -- | Internal symbol visibility (processor-specific hidden type; -- @STV_INTERNAL@). intSymVisi ∷ SymVisi intSymVisi = SymVisi 1 -- | Hidden symbol (@STV_HIDDEN@). hiddenSymVisi ∷ SymVisi hiddenSymVisi = SymVisi 2 -- | Protected symbol (@STV_PROTECTED@). protSymVisi ∷ SymVisi protSymVisi = SymVisi 3 -- | Global symbol (@STV_EXPORTED@). exportSymVisi ∷ SymVisi exportSymVisi = SymVisi 4 -- | Global singleton symbol (@STV_SINGLETON@). singSymVisi ∷ SymVisi singSymVisi = SymVisi 5 -- | Extra hidden symbol (@STV_ELIMINATE@). elimSymVisi ∷ SymVisi elimSymVisi = SymVisi 6 -- | Symbol table index. newtype SymIx c = SymIx { unSymIx ∷ UnSymIx c } deriving Typeable deriving instance (Data c, IsFileClass c) ⇒ Data (SymIx c) deriving instance IsFileClass c ⇒ Show (SymIx c) deriving instance IsFileClass c ⇒ Read (SymIx c) deriving instance IsFileClass c ⇒ Eq (SymIx c) deriving instance IsFileClass c ⇒ Ord (SymIx c) deriving instance IsFileClass c ⇒ Bounded (SymIx c) deriving instance IsFileClass c ⇒ Enum (SymIx c) deriving instance IsFileClass c ⇒ Ix (SymIx c) deriving instance IsFileClass c ⇒ Num (SymIx c) deriving instance IsFileClass c ⇒ Bits (SymIx c) deriving instance IsFileClass c ⇒ FiniteBits (SymIx c) deriving instance IsFileClass c ⇒ Integral (SymIx c) deriving instance IsFileClass c ⇒ Real (SymIx c) -- | Undefined symbol table index. undefSymIx ∷ IsFileClass c ⇒ SymIx c undefSymIx = SymIx 0 -- | Symbol table entry. data SymEnt c = SymEnt { symName ∷ StrIx , symBind ∷ SymBind , symType ∷ SymType , symVisi ∷ SymVisi , symSecIx ∷ SecIx , symAddr ∷ Addr c , symSize ∷ Addr c } deriving Typeable deriving instance (Data c, IsFileClass c) ⇒ Data (SymEnt c) deriving instance IsFileClass c ⇒ Show (SymEnt c) deriving instance IsFileClass c ⇒ Read (SymEnt c) deriving instance IsFileClass c ⇒ Eq (SymEnt c) -- | ELF32 symbol table entry. type SymEnt32 = SymEnt Elf32 -- | 'SymEnt32' proxy value. aSymEnt32 ∷ Proxy SymEnt32 aSymEnt32 = Proxy instance Serializable SymEnt32 where put (SymEnt {..}) = S.word32 symName <> S.word32 symAddr <> S.word32 symSize <> S.word8 (shiftL (fromIntegral $ unSymBind symBind) 4 .|. fromIntegral (unSymType symType)) <> S.word8 (fromIntegral (unSymVisi symVisi)) <> S.word16 (unSecIx symSecIx) instance SizedSerializable SymEnt32 where size _ = 16 {-# INLINE size #-} instance Deserializable SymEnt32 where get = do name ← D.word32 "name" addr ← D.word32 "address" size ← D.word32 "size" info ← D.word8 "info" let bind = SymBind (fromIntegral $ shiftR info 4) tp = SymType (fromIntegral info) visi ← SymVisi . fromIntegral <$> D.word8 "visibility" secIx ← SecIx <$> D.word16 "section index" return $ SymEnt { symName = name , symAddr = addr , symSize = size , symBind = bind , symType = tp , symVisi = visi , symSecIx = secIx } -- | ELF64 symbol table entry. type SymEnt64 = SymEnt Elf64 -- | 'SymEnt64' proxy value. aSymEnt64 ∷ Proxy SymEnt64 aSymEnt64 = Proxy instance Serializable SymEnt64 where put (SymEnt {..}) = S.word32 symName <> S.word8 (shiftL (fromIntegral $ unSymBind symBind) 4 .|. fromIntegral (unSymType symType)) <> S.word8 (fromIntegral (unSymVisi symVisi)) <> S.word16 (unSecIx symSecIx) <> S.word64 symAddr <> S.word64 symSize instance SizedSerializable SymEnt64 where size _ = 24 {-# INLINE size #-} instance Deserializable SymEnt64 where get = do name ← D.word32 "name" info ← D.word8 "info" let bind = SymBind (fromIntegral $ shiftR info 4) tp = SymType (fromIntegral info) visi ← SymVisi . fromIntegral <$> D.word8 "visibility" secIx ← SecIx <$> D.word16 "section index" addr ← D.word64 "address" size ← D.word64 "size" return $ SymEnt { symName = name , symAddr = addr , symSize = size , symBind = bind , symType = tp , symVisi = visi , symSecIx = secIx } -- | Symbol table entry filled with zeros. zeroSymEnt ∷ IsFileClass c ⇒ SymEnt c zeroSymEnt = SymEnt { symName = 0 , symBind = localSymBind , symType = undefSymType , symVisi = defSymVisi , symSecIx = undefSecIx , symAddr = 0 , symSize = 0 } -- | Relocation type. newtype RelType c = RelType { unRelType ∷ UnRelType c } deriving Typeable deriving instance (Data c, IsFileClass c) ⇒ Data (RelType c) deriving instance IsFileClass c ⇒ Show (RelType c) deriving instance IsFileClass c ⇒ Read (RelType c) deriving instance IsFileClass c ⇒ Eq (RelType c) deriving instance IsFileClass c ⇒ Ord (RelType c) deriving instance IsFileClass c ⇒ Bounded (RelType c) deriving instance IsFileClass c ⇒ Enum (RelType c) deriving instance IsFileClass c ⇒ Ix (RelType c) -- | ELF32 relocation type. type RelType32 = RelType Elf32 -- | ELF64 relocation type. type RelType64 = RelType Elf64 -- | Relocation table entry (REL). data RelEnt c = RelEnt { relOff ∷ Addr c , relSymIx ∷ SymIx c , relType ∷ RelType c } deriving Typeable deriving instance (Data c, IsFileClass c) ⇒ Data (RelEnt c) deriving instance IsFileClass c ⇒ Show (RelEnt c) deriving instance IsFileClass c ⇒ Read (RelEnt c) deriving instance IsFileClass c ⇒ Eq (RelEnt c) -- | ELF32 relocation table entry (REL). type RelEnt32 = RelEnt Elf32 -- | 'RelEnt32' proxy value. aRelEnt32 ∷ Proxy RelEnt32 aRelEnt32 = Proxy instance Serializable RelEnt32 where put (RelEnt {..}) = S.word32 relOff <> S.word32 (shiftL (fromIntegral relSymIx) 8 .|. fromIntegral (unRelType relType)) instance SizedSerializable RelEnt32 where size _ = 8 {-# INLINE size #-} instance Deserializable RelEnt32 where get = do off ← D.word32 "offset" info ← D.word32 "info" let symIx = SymIx $ fromIntegral $ shiftR info 8 tp = RelType $ fromIntegral info return $ RelEnt { relOff = off , relSymIx = symIx , relType = tp } -- | ELF64 relocation table entry (REL). type RelEnt64 = RelEnt Elf64 -- | 'RelEnt64' proxy value. aRelEnt64 ∷ Proxy RelEnt64 aRelEnt64 = Proxy instance Serializable RelEnt64 where put (RelEnt {..}) = S.word64 relOff <> S.word64 (shiftL (fromIntegral relSymIx) 32 .|. fromIntegral (unRelType relType)) instance SizedSerializable RelEnt64 where size _ = 16 {-# INLINE size #-} instance Deserializable RelEnt64 where get = do off ← D.word64 "offset" info ← D.word64 "info" let symIx = SymIx $ fromIntegral $ shiftR info 32 tp = RelType $ fromIntegral info return $ RelEnt { relOff = off , relSymIx = symIx , relType = tp } -- | Relocation table entry (RELA). data RelaEnt c = RelaEnt { relaOff ∷ Addr c , relaSymIx ∷ SymIx c , relaType ∷ RelType c , relaAddend ∷ Addr c } deriving Typeable deriving instance (Data c, IsFileClass c) ⇒ Data (RelaEnt c) deriving instance IsFileClass c ⇒ Show (RelaEnt c) deriving instance IsFileClass c ⇒ Read (RelaEnt c) deriving instance IsFileClass c ⇒ Eq (RelaEnt c) -- | ELF32 relocation table entry (RELA). type RelaEnt32 = RelaEnt Elf32 -- | 'RelaEnt32' proxy value. aRelaEnt32 ∷ Proxy RelaEnt32 aRelaEnt32 = Proxy instance Serializable RelaEnt32 where put (RelaEnt {..}) = S.word32 relaOff <> S.word32 (shiftL (fromIntegral relaSymIx) 8 .|. fromIntegral (unRelType relaType)) <> S.word32 relaAddend instance SizedSerializable RelaEnt32 where size _ = 12 {-# INLINE size #-} instance Deserializable RelaEnt32 where get = do off ← D.word32 "offset" info ← D.word32 "info" let symIx = SymIx $ fromIntegral $ shiftR info 8 tp = RelType $ fromIntegral info addend ← D.word32 "addend" return $ RelaEnt { relaOff = off , relaSymIx = symIx , relaType = tp , relaAddend = addend } -- | ELF64 relocation table entry (RELA). type RelaEnt64 = RelaEnt Elf64 -- | 'RelaEnt64' proxy value. aRelaEnt64 ∷ Proxy RelaEnt64 aRelaEnt64 = Proxy instance Serializable RelaEnt64 where put (RelaEnt {..}) = S.word64 relaOff <> S.word64 (shiftL (fromIntegral relaSymIx) 32 .|. fromIntegral (unRelType relaType)) <> S.word64 relaAddend instance SizedSerializable RelaEnt64 where size _ = 24 {-# INLINE size #-} instance Deserializable RelaEnt64 where get = do off ← D.word64 "offset" info ← D.word64 "info" let symIx = SymIx $ fromIntegral $ shiftR info 32 tp = RelType $ fromIntegral info addend ← D.word64 "addend" return $ RelaEnt { relaOff = off , relaSymIx = symIx , relaType = tp , relaAddend = addend }