-- | Parses a ByteString into an Elf record. Parse failures call error. 32-bit ELF objects have their
-- fields promoted to 64-bit so that the 32- and 64-bit ELF records can be the same.
parseElf :: B.ByteString -> Elf
parseElf b =
    let ph                                             = table segTab
        sh                                             = table secTab
        (shstroff, shstrsize)                          = parseEntry getElf_Shdr_OffsetSize $ head $ drop (fromIntegral e_shstrndx) sh
        sh_str                                         = B.take (fromIntegral shstrsize) $ B.drop (fromIntegral shstroff) b
        segments                                       = filter (\seg -> elfSegmentType seg /= PT_NULL)  $ map (parseEntry (\c r -> parseElfSegmentEntry c r b)) ph
        sections                                       = filter (\sec -> elfSectionType sec /= SHT_NULL) $ map (parseEntry (\c r -> getElf_Shdr c r b sh_str)) sh
    in e { elfSections = sections, elfSegments = segments }

  where table i                         = divide (B.drop (tableOffset i) b) (entrySize i) (entryNum i)
        parseEntry p x                  = runGet (p (elfClass e) (elfReader (elfData e))) (L.fromChunks [x])
        (e, segTab, secTab, e_shstrndx) = runGet getElf_Ehdr $ L.fromChunks [b]
parseElf :: B.ByteString -> Elf
parseElf b =
    let ph                                             = table segTab
        sh                                             = table secTab
        (shstroff, shstrsize)                          = parseEntry getElf_Shdr_OffsetSize $ head $ drop (fromIntegral e_shstrndx) sh
        sh_str                                         = B.take (fromIntegral shstrsize) $ B.drop (fromIntegral shstroff) b
        segments                                       = filter (\seg -> elfSegmentType seg /= PT_NULL)  $ map (parseEntry (\c r -> parseElfSegmentEntry c r b)) ph
        sections                                       = filter (\sec -> elfSectionType sec /= SHT_NULL) $ map (parseEntry (\c r -> getElf_Shdr c r b sh_str)) sh
    in e { elfSections = sections, elfSegments = segments }

  where table i                         = divide (B.drop (tableOffset i) b) (entrySize i) (entryNum i)
        parseEntry p x                  = runGet (p (elfClass e) (elfReader (elfData e))) (L.fromChunks [x])
        (e, segTab, secTab, e_shstrndx) = runGet getElf_Ehdr $ L.fromChunks [b]
parseElf b =
    let ph                                             = table segTab
        sh                                             = table secTab
        (shstroff, shstrsize)                          = parseEntry getElf_Shdr_OffsetSize $ head $ drop (fromIntegral e_shstrndx) sh
        sh_str                                         = B.take (fromIntegral shstrsize) $ B.drop (fromIntegral shstroff) b
        segments                                       = filter (\seg -> elfSegmentType seg /= PT_NULL)  $ map (parseEntry (\c r -> parseElfSegmentEntry c r b)) ph
        sections                                       = filter (\sec -> elfSectionType sec /= SHT_NULL) $ map (parseEntry (\c r -> getElf_Shdr c r b sh_str)) sh
    in e { elfSections = sections, elfSegments = segments }

  where table i                         = divide (B.drop (tableOffset i) b) (entrySize i) (entryNum i)
        parseEntry p x                  = runGet (p (elfClass e) (elfReader (elfData e))) (L.fromChunks [x])
        (e, segTab, secTab, e_shstrndx) = runGet getElf_Ehdr $ L.fromChunks [b]