diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) Erik Charlebois.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The names of the author may not be used to endorse or promote
+   products derived from this software without specific prior written
+   permission.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,6 @@
+import Distribution.Simple
+import System.Cmd(system)
+
+main = defaultMainWithHooks $ simpleUserHooks { runTests = runElfTests }
+
+runElfTests a b pd lb = system "runhaskell -i./src ./tests/Test.hs" >> return ()
diff --git a/dwarf.cabal b/dwarf.cabal
new file mode 100644
--- /dev/null
+++ b/dwarf.cabal
@@ -0,0 +1,20 @@
+Name:          dwarf
+Version:       0.1
+License:       BSD3
+License-file:  LICENSE
+Category:      Data
+Author:        Erik Charlebois
+Copyright:     Erik Charlebois
+Maintainer:    Erik Charlebois <erikcharlebois@gmail.com>
+Stability:     unstable
+Cabal-Version: >= 1.2
+Build-Depends: base
+Build-Type:    Custom
+Synopsis:      Parser for DWARF debug format.
+Description:   Parser for DWARF debug format.
+Data-Files:    tests/Test.hs
+
+library
+    build-depends:   base, bytestring, containers, binary
+    hs-source-dirs:  src
+    exposed-modules: Data.Dwarf
diff --git a/src/Data/Dwarf.hs b/src/Data/Dwarf.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Dwarf.hs
@@ -0,0 +1,1550 @@
+-- | Parses the DWARF 2 and DWARF 3 specifications at http://www.dwarfstd.org given
+-- the debug sections in ByteString form.
+module Data.Dwarf ( parseDwarfInfo
+                  , parseDwarfPubnames
+                  , parseDwarfPubtypes
+                  , parseDwarfMacInfo
+                  , parseDwarfRanges
+                  , parseDwarfLoc
+                  , parseDwarfLine
+                  , parseDwarfFrame
+                  , parseDW_OP
+                  , dw_ate
+                  , dw_ds
+                  , dw_end
+                  , dw_access
+                  , dw_vis
+                  , dw_virtuality
+                  , dw_lang
+                  , dw_inl
+                  , dw_cc
+                  , dw_ord
+                  , dw_dsc
+                  , (!?)
+                  , DwarfReader(..)
+                  , DIE(..)
+                  , DW_CFA(..)
+                  , DW_MACINFO(..)
+                  , DW_CIEFDE(..)
+                  , DW_OP(..)
+                  , DW_TAG(..)
+                  , DW_AT(..)
+                  , DW_ATVAL(..)
+                  , DW_LNE(..)
+                  , DW_ATE(..)
+                  , DW_DS(..)
+                  , DW_END(..)
+                  , DW_ACCESS(..)
+                  , DW_VIS(..)
+                  , DW_VIRTUALITY(..)
+                  , DW_LANG(..)
+                  , DW_ID(..)
+                  , DW_INL(..)
+                  , DW_CC(..)
+                  , DW_ORD(..)
+                  , DW_DSC(..)
+                  ) where
+
+import Data.Int
+import Data.Bits
+import Data.Word
+import Data.Dynamic
+import Data.Binary
+import Data.Binary.Get
+import Data.Maybe
+import Data.Char
+import Control.Monad
+import Control.Applicative
+import qualified Data.Map as M
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as C
+import qualified Data.ByteString.Lazy as L
+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Utility functions.
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+
+-- Repeatedly perform the get operation until the boolean fails.
+getWhile :: (a -> Bool) -> Get a -> Get [a]
+getWhile cond get = do
+    el <- get
+    if cond el then
+        liftM (el :) $ getWhile cond get
+     else
+        return []
+
+-- Decode a NULL-terminated UTF-8 string.
+getNullTerminatedString :: Get String
+getNullTerminatedString2 = liftM (C.unpack . B.pack) (getWhile (/= 0) getWord8)
+getNullTerminatedString = liftM (map (chr . fromIntegral)) $ getWhile (/= 0) getCharUTF8
+    where getCharUTF8 = do
+            let getCharUTF82 b1 = do
+                    b2 <- liftM fromIntegral getWord8 :: Get Word32
+                    if b2 .&. 0xc0 == 0x80 then
+                            return $ ((b1 .&. 0x1f) `shiftL` 6) .|. (b2 .&. 0x3f)
+                         else
+                            fail "Invalid second byte in UTf8 string."
+                getCharUTF83 b1 = do
+                    b2 <- liftM fromIntegral getWord8 :: Get Word32
+                    b3 <- liftM fromIntegral getWord8 :: Get Word32
+                    if b2 .&. 0xc0 == 0x80 && b3 .&. 0xc0 == 0x80 then
+                            return $ ((b1 .&. 0x0f) `shiftL` 12) .|. ((b2 .&. 0x3f) `shiftL` 6) .|. (b3 .&. 0x3f)
+                         else
+                            fail "Invalid second or third byte in UTf8 string."
+                getCharUTF84 b1 = do
+                    b2 <- liftM fromIntegral getWord8 :: Get Word32
+                    b3 <- liftM fromIntegral getWord8 :: Get Word32
+                    b4 <- liftM fromIntegral getWord8 :: Get Word32
+                    if b2 .&. 0xc0 == 0x80 && b3 .&. 0xc0 == 0x80 && b4 .&. 0xc0 == 0x80 then
+                            return $ ((b1 .&. 0x07) `shiftL` 18) .|. ((b2 .&. 0x3f) `shiftL` 12) .|. ((b3 .&. 0x3f) `shiftL` 6) .|. (b4 .&. 0x3f)
+                         else
+                            fail "Invalid second or third byte in UTf8 string."
+            b1 <- liftM fromIntegral getWord8 :: Get Word32
+            case b1 of
+                n | n .&. 0x80 == 0x00 -> return $ fromIntegral n
+                n | n .&. 0xe0 == 0xc0 -> getCharUTF82 n
+                n | n .&. 0xf0 == 0xe0 -> getCharUTF83 n
+                n | n .&. 0xf8 == 0xf0 -> getCharUTF84 n
+                _                      -> fail "Invalid first byte in UTF8 string."
+ 
+-- Decode a signed little-endian base 128 encoded integer.
+getSLEB128 :: Get Int64
+getSLEB128 =
+    let go acc shift = do
+        byte <- liftM fromIntegral getWord8 :: Get Word64
+        temp <- return $ acc .|. (clearBit byte 7 `shiftL` shift)
+        if testBit byte 7 then
+            go temp (shift + 7)
+         else
+            if shift < 32  && testBit byte 6 then
+                return $ fromIntegral $ temp .|. ((-1) `shiftL` shift)
+             else
+                return $ fromIntegral temp
+    in go 0 0
+
+-- Decode an unsigned little-endian base 128 encoded integer.
+getULEB128 :: Get Word64
+getULEB128 =
+    let go acc shift = do
+        byte <- liftM fromIntegral getWord8 :: Get Word64
+        temp <- return $ acc .|. (clearBit byte 7 `shiftL` shift)
+        if testBit byte 7 then
+            go temp (shift + 7)
+         else
+            return temp
+    in go 0 0
+
+-- Decode the DWARF size header entry, which specifies both the size of a DWARF subsection and whether this section uses DWARF32 or DWARF64.
+getDwarfUnitLength :: DwarfEndianReader -> Get (DwarfEndianSizeReader, Word64)
+getDwarfUnitLength dr@(DwarfEndianReader e w16 w32 w64) = do
+    size <- w32
+    if size == 0xffffffff then do
+        size <- w64
+        return (dwarfEndianSizeReader True dr, size)
+     else if size >= 0xffffff00 then
+        fail ("Invalid DWARF size " ++ show size)
+      else
+        return (dwarfEndianSizeReader False dr, fromIntegral $ size)
+
+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- DWARF decoder records.
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Intermediate data structure for a partial DwarfReader.
+data DwarfEndianReader = DwarfEndianReader Bool (Get Word16) (Get Word32) (Get Word64)
+dwarfEndianReader True  = DwarfEndianReader True  getWord16le getWord32le getWord64le
+dwarfEndianReader False = DwarfEndianReader False getWord16be getWord32be getWord64be
+
+-- Intermediate data structure for a partial DwarfReader.
+data DwarfEndianSizeReader = DwarfEndianSizeReader Bool (Get Word16) (Get Word32) (Get Word64) Bool Word64 (Get Word64)
+dwarfEndianSizeReader True  (DwarfEndianReader e w16 w32 w64) = DwarfEndianSizeReader e w16 w32 w64 True  0xffffffffffffffff w64
+dwarfEndianSizeReader False (DwarfEndianReader e w16 w32 w64) = DwarfEndianSizeReader e w16 w32 w64 False 0xffffffff (liftM fromIntegral w32)
+
+-- | Type containing functions and data needed for decoding DWARF information.
+data DwarfReader = DwarfReader
+    { littleEndian          :: Bool       -- ^ True for little endian encoding.
+    , dwarf64               :: Bool       -- ^ True for 64-bit DWARF encoding.
+    , target64              :: Bool       -- ^ True for 64-bit pointers on target machine.
+    , largestOffset         :: Word64     -- ^ Largest permissible file offset.
+    , largestTargetAddress  :: Word64     -- ^ Largest permissible target address.
+    , getWord16             :: Get Word16 -- ^ Action for reading a 16-bit word.
+    , getWord32             :: Get Word32 -- ^ Action for reading a 32-bit word.
+    , getWord64             :: Get Word64 -- ^ Action for reading a 64-bit word.
+    , getDwarfOffset        :: Get Word64 -- ^ Action for reading a offset for the DWARF file.
+    , getDwarfTargetAddress :: Get Word64 -- ^ Action for reading a pointer for the target machine.
+    }
+instance Show DwarfReader where
+    show dr = "DwarfReader " ++ show (littleEndian dr) ++ " " ++ show (dwarf64 dr) ++ " " ++ show (target64 dr) 
+instance Eq DwarfReader where
+    a1 == a2 = (littleEndian a1 == littleEndian a2) && (dwarf64 a1 == dwarf64 a2) && (target64 a1 == target64 a2)
+dwarfReader True  (DwarfEndianSizeReader e w16 w32 w64 d lo sz) = DwarfReader e d True lo 0xffffffffffffffff w16 w32 w64 sz w64
+dwarfReader False (DwarfEndianSizeReader e w16 w32 w64 d lo sz) = DwarfReader e d False lo 0xffffffff w16 w32 w64 sz (liftM fromIntegral w32)
+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- Abbreviation and form parsing
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+getForm dr str cu form = getForm_ dr str cu form
+getForm_ dr str cu DW_FORM_addr      = pure (DW_ATVAL_UINT . fromIntegral) <*> (getDwarfTargetAddress dr)
+getForm_ dr str cu DW_FORM_block1    = liftM fromIntegral getWord8       >>= getByteString >>= return . DW_ATVAL_BLOB
+getForm_ dr str cu DW_FORM_block2    = liftM fromIntegral (getWord16 dr) >>= getByteString >>= return . DW_ATVAL_BLOB
+getForm_ dr str cu DW_FORM_block4    = liftM fromIntegral (getWord32 dr) >>= getByteString >>= return . DW_ATVAL_BLOB
+getForm_ dr str cu DW_FORM_block     = liftM fromIntegral getULEB128     >>= getByteString >>= return . DW_ATVAL_BLOB
+getForm_ dr str cu DW_FORM_data1     = pure (DW_ATVAL_UINT . fromIntegral) <*> getWord8
+getForm_ dr str cu DW_FORM_data2     = pure (DW_ATVAL_UINT . fromIntegral) <*> (getWord16 dr)
+getForm_ dr str cu DW_FORM_data4     = pure (DW_ATVAL_UINT . fromIntegral) <*> (getWord32 dr)
+getForm_ dr str cu DW_FORM_data8     = pure (DW_ATVAL_UINT . fromIntegral) <*> (getWord64 dr)
+getForm_ dr str cu DW_FORM_udata     = pure DW_ATVAL_UINT <*> getULEB128
+getForm_ dr str cu DW_FORM_sdata     = pure DW_ATVAL_INT <*> getSLEB128
+getForm_ dr str cu DW_FORM_flag      = pure (DW_ATVAL_BOOL . (/= 0)) <*> getWord8
+getForm_ dr str cu DW_FORM_string    = pure DW_ATVAL_STRING <*> getNullTerminatedString
+getForm_ dr str cu DW_FORM_ref1      = pure (DW_ATVAL_UINT . (+) cu) <*> liftM fromIntegral getWord8
+getForm_ dr str cu DW_FORM_ref2      = pure (DW_ATVAL_UINT . (+) cu) <*> liftM fromIntegral (getWord16 dr)
+getForm_ dr str cu DW_FORM_ref4      = pure (DW_ATVAL_UINT . (+) cu) <*> liftM fromIntegral (getWord32 dr)
+getForm_ dr str cu DW_FORM_ref8      = pure (DW_ATVAL_UINT . (+) cu) <*> liftM fromIntegral (getWord64 dr)
+getForm_ dr str cu DW_FORM_ref_udata = pure (DW_ATVAL_UINT . (+) cu) <*> liftM fromIntegral getULEB128
+getForm_ dr str cu DW_FORM_ref_addr  = pure DW_ATVAL_UINT <*> getDwarfOffset dr
+getForm_ dr str cu DW_FORM_indirect  = getULEB128 >>= getForm dr str cu . dw_form
+getForm_ dr str cu DW_FORM_strp      = do
+    offset <- liftM fromIntegral (getDwarfOffset dr)
+    return $ DW_ATVAL_STRING $ runGet getNullTerminatedString (L.fromChunks [B.drop offset str])
+
+data DW_ABBREV = DW_ABBREV
+    { abbrevNum       :: Word64
+    , abbrevTag       :: DW_TAG
+    , abbrevChildren  :: Bool
+    , abbrevAttrForms :: [(DW_AT, DW_FORM)]
+    }
+
+getAbbrevList :: Get [(Word64, DW_ABBREV)]
+getAbbrevList = getWhile ((/=) 0 . fst) getAbbrev
+    where getAbbrev = do
+            abbrev    <- getULEB128
+            tag       <- getDW_TAG
+            children  <- liftM (== 1) getWord8
+            attrForms <- getAttrFormList
+            return (abbrev, DW_ABBREV abbrev tag children attrForms)
+          getAttrFormList = liftM (map (\(x,y) -> (dw_at x, dw_form y))) $ getWhile (/= (0,0)) (liftM2 (,) getULEB128 getULEB128)
+
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- DWARF information entry and .debug_info section parsing.
+---------------------------------------------------------------------------------------------------------------------------------------------------------------
+-- | The dwarf information entries form a graph of nodes tagged with attributes. Please refer to the DWARF specification
+-- for semantics. Although it looks like a tree, there can be attributes which have adjacency information which will
+-- introduce cross-branch edges.
+data DIE = DIE
+    { dieId           :: Word64              -- ^ Unique identifier for this entry.
+    , dieParent       :: Maybe Word64        -- ^ Unique identifier of this entry's parent.
+    , dieChildren     :: [Word64]            -- ^ Unique identifiers of this entry's children.
+    , dieSiblingLeft  :: Maybe Word64        -- ^ Unique identifier of the left sibling in the DIE tree, if one exists.
+    , dieSiblingRight :: Maybe Word64        -- ^ Unique identifier of the right sibling in the DIE tree, if one exists.
+    , dieTag          :: DW_TAG              -- ^ Type tag.
+    , dieAttributes   :: [(DW_AT, DW_ATVAL)] -- ^ Attribute tag and value pairs.
+    , dieReader       :: DwarfReader         -- ^ Decoder used to decode this entry. May be needed to further parse attribute values.
+    } deriving (Show, Eq)
+
+-- | Utility function for retrieving the list of values for a specified attribute from a DWARF information entry.
+(!?) :: DIE -> DW_AT -> [DW_ATVAL]
+(!?) die at = map snd $ filter (\(a,v) -> a == at) $ dieAttributes die
+
+-- Decode a non-compilation unit DWARF information entry, its children and its siblings.
+getDieTree parent lsibling abbrev_map dr str_section cu_offset = do
+    offset <- liftM fromIntegral bytesRead
+    abbrid <- getULEB128
+    if abbrid == 0 then
+        return []
+     else do
+        let abbrev         = abbrev_map M.! abbrid
+            tag            = abbrevTag abbrev
+            has_children   = abbrevChildren abbrev
+            (attrs, forms) = unzip $ abbrevAttrForms abbrev
+        values    <- mapM (getForm dr str_section cu_offset) forms
+        ancestors <- if has_children then getDieTree (Just offset) Nothing abbrev_map dr str_section cu_offset else return []
+        siblings  <- getDieTree parent (Just offset) abbrev_map dr str_section cu_offset
+        let children = map dieId $ filter (\x -> if isJust (dieParent x) then fromJust (dieParent x) == offset else False) ancestors
+            rsibling = if null siblings then Nothing else Just $ dieId $ head siblings
+        return $ (DIE offset parent children lsibling rsibling tag (zip attrs values) dr : ancestors) ++ siblings
+
+-- Decode the compilation unit DWARF information entries.
+getDieCus cu_lsibling odr abbrev_section str_section = do
+    empty <- isEmpty
+    if empty then
+        return []
+     else do
+        cu_offset       <- liftM fromIntegral bytesRead
+        (dr@(DwarfEndianSizeReader _ w16 _ _ _ _ sz), _) <- getDwarfUnitLength odr
+        version         <- w16
+        abbrev_offset   <- sz
+        addr_size       <- getWord8
+        dr              <- return $ case addr_size of
+                            4 -> dwarfReader False dr
+                            8 -> dwarfReader True dr
+        cu_die_offset   <- liftM fromIntegral bytesRead
+        cu_abbr_num     <- getULEB128
+        let abbrev_table         = B.drop (fromIntegral abbrev_offset) abbrev_section
+            abbrev_map           = M.fromList $ runGet getAbbrevList $ L.fromChunks [abbrev_table]
+            cu_abbrev            = abbrev_map M.! cu_abbr_num
+            cu_tag               = abbrevTag cu_abbrev
+            cu_has_children      = abbrevChildren cu_abbrev
+            (cu_attrs, cu_forms) = unzip $ abbrevAttrForms cu_abbrev
+        cu_values    <- mapM (getForm dr str_section cu_offset) cu_forms
+        cu_ancestors <- if cu_has_children then getDieTree (Just cu_offset) Nothing abbrev_map dr str_section cu_offset else return []
+        cu_siblings  <- getDieCus Nothing odr abbrev_section str_section
+        let cu_children = map dieId $ filter (\x -> if isJust (dieParent x) then fromJust (dieParent x) == cu_offset else False) cu_ancestors
+            cu_rsibling = if null cu_siblings then Nothing else Just $ dieId $ head cu_siblings
+        return $ (DIE cu_die_offset Nothing cu_children cu_lsibling cu_rsibling cu_tag (zip cu_attrs cu_values) dr : cu_ancestors) ++ cu_siblings
+
+-- | Parses the .debug_info section (as ByteString) using the .debug_abbrev and .debug_str sections.
+parseDwarfInfo :: Bool             -- ^ True for little endian target addresses. False for big endian.
+               -> B.ByteString     -- ^ ByteString for the .debug_info section.
+               -> B.ByteString     -- ^ ByteString for the .debug_abbrev section.
+               -> B.ByteString     -- ^ ByteString for the .debug_str section.
+               -> M.Map Word64 DIE -- ^ A map from the unique ids to their corresponding DWARF information entries.
+parseDwarfInfo littleEndian info_section abbrev_section str_section =
+    let dr = dwarfEndianReader littleEndian
+        di = runGet (getDieCus Nothing dr abbrev_section str_section) $ L.fromChunks [info_section]
+    in M.fromList $ zip (map dieId di) di
+
+-- Section 7.19 - Name Lookup Tables
+getNameLookupEntries :: DwarfReader -> Word64 -> Get [(String, [Word64])]
+getNameLookupEntries dr cu_offset = do
+    die_offset <- getDwarfOffset dr
+    if die_offset == 0 then
+        return []
+     else do
+        name <- getNullTerminatedString
+        rest <- getNameLookupEntries dr cu_offset
+        return $ (name, [cu_offset + die_offset]) : rest
+
+getNameLookupTable :: Bool -> DwarfEndianReader -> Get [M.Map String [Word64]]
+getNameLookupTable target64 odr = do
+    empty <- isEmpty
+    if empty then
+        return []
+     else do
+        (dr, _)           <- getDwarfUnitLength odr
+        dr                <- return $ dwarfReader target64 dr
+        version           <- getWord16 dr
+        debug_info_offset <- getDwarfOffset dr
+        debug_info_length <- getDwarfOffset dr
+        pubNames          <- liftM (M.fromListWith (++)) (getNameLookupEntries dr debug_info_offset)
+        rest              <- getNameLookupTable target64 odr
+        return $ pubNames : rest
+
+-- | Parses the .debug_pubnames section (as ByteString) into a map from a value name to a debug info id in the DwarfInfo.
+parseDwarfPubnames :: Bool -> Bool -> B.ByteString -> M.Map String [Word64]
+parseDwarfPubnames littleEndian target64 pubnames_section =
+    let dr = dwarfEndianReader littleEndian
+    in M.unionsWith (++) $ runGet (getNameLookupTable target64 dr) $ L.fromChunks [pubnames_section]
+
+-- | Parses the .debug_pubtypes section (as ByteString) into a map from a type name to a debug info id in the DwarfInfo.
+parseDwarfPubtypes :: Bool -> Bool -> B.ByteString -> M.Map String [Word64]
+parseDwarfPubtypes littleEndian target64 pubtypes_section =
+    let dr = dwarfEndianReader littleEndian
+    in M.unionsWith (++) $ runGet (getNameLookupTable target64 dr) $ L.fromChunks [pubtypes_section]
+
+-- Section 7.20 - Address Range Table
+getAddressRangeTable target64 odr = do
+    empty <- isEmpty
+    if empty then
+        return []
+     else do
+        (dr, _)           <- getDwarfUnitLength odr
+        dr                <- return $ dwarfReader target64 dr
+        version           <- getWord16 dr
+        debug_info_offset <- getDwarfOffset dr
+        address_size      <- liftM fromIntegral getWord8
+        segment_size      <- liftM fromIntegral getWord8
+        bytes_read        <- bytesRead
+        skip $ fromIntegral (2 * address_size - (bytes_read `mod` (2 * address_size)))
+        address_ranges    <- case address_size of
+                            4 -> getWhile (/= (0, 0)) (liftM2 (,) (liftM fromIntegral (getWord32 dr)) (liftM fromIntegral (getWord32 dr)))
+                            8 -> getWhile (/= (0, 0)) (liftM2 (,) (getWord64 dr) (getWord64 dr))
+                            n -> fail ("Unrecognized address size " ++ show address_size ++ " in .debug_aranges section.")
+        rest              <- getAddressRangeTable target64 odr
+        return $ (address_ranges, debug_info_offset) : rest
+
+-- | Parses  the .debug_aranges section (as ByteString) into a map from an address range to a debug info id that indexes the DwarfInfo.
+parseDwarfAranges :: Bool -> Bool -> B.ByteString -> [([(Word64, Word64)], Word64)]
+parseDwarfAranges littleEndian target64 aranges_section =
+    let dr = dwarfEndianReader littleEndian
+    in runGet (getAddressRangeTable target64 dr) $ L.fromChunks [aranges_section]
+
+-- Section 7.21 - Line Number Information
+data DW_LNI
+    = DW_LNI_special Word64 Int64
+    | DW_LNS_copy
+    | DW_LNS_advance_pc Word64
+    | DW_LNS_advance_line Int64
+    | DW_LNS_set_file Word64
+    | DW_LNS_set_column Word64
+    | DW_LNS_negate_stmt
+    | DW_LNS_set_basic_block
+    | DW_LNS_const_add_pc Word64
+    | DW_LNS_fixed_advance_pc Word64
+    | DW_LNS_set_prologue_end
+    | DW_LNS_set_epilogue_begin
+    | DW_LNS_set_isa Word64
+    | DW_LNE_end_sequence
+    | DW_LNE_set_address Word64
+    | DW_LNE_define_file String Word64 Word64 Word64
+    deriving (Show, Eq)
+getDW_LNI dr line_base line_range opcode_base minimum_instruction_length = liftM fromIntegral getWord8 >>= getDW_LNI_
+    where getDW_LNI_ 0x00 = do
+            length <- liftM fromIntegral getULEB128
+            rest   <- getByteString length
+            return $ runGet getDW_LNE $ L.fromChunks [rest]
+                where getDW_LNE = getWord8 >>= getDW_LNE_
+                      getDW_LNE_ 0x01 = return DW_LNE_end_sequence
+                      getDW_LNE_ 0x02 = return DW_LNE_set_address <*> getDwarfTargetAddress dr
+                      getDW_LNE_ 0x03 = return DW_LNE_define_file <*> getNullTerminatedString <*> getULEB128 <*> getULEB128 <*> getULEB128
+                      getDW_LNE_ n | 0x80 <= n && n <= 0xff = fail $ "User DW_LNE data requires extension of parser for code " ++ show n
+                      getDW_LNE_ n = fail $ "Unexpected DW_LNE code " ++ show n
+          getDW_LNI_ 0x01 = return DW_LNS_copy
+          getDW_LNI_ 0x02 = return DW_LNS_advance_pc <*> liftM (* minimum_instruction_length) getULEB128
+          getDW_LNI_ 0x03 = return DW_LNS_advance_line <*> getSLEB128
+          getDW_LNI_ 0x04 = return DW_LNS_set_file <*> getULEB128
+          getDW_LNI_ 0x05 = return DW_LNS_set_column <*> getULEB128
+          getDW_LNI_ 0x06 = return DW_LNS_negate_stmt
+          getDW_LNI_ 0x07 = return DW_LNS_set_basic_block
+          getDW_LNI_ 0x08 = return $ DW_LNS_const_add_pc (minimum_instruction_length * ((255 - opcode_base) `div` line_range))
+          getDW_LNI_ 0x09 = return DW_LNS_fixed_advance_pc <*> liftM fromIntegral (getWord16 dr)
+          getDW_LNI_ 0x0a = return DW_LNS_set_prologue_end
+          getDW_LNI_ 0x0b = return DW_LNS_set_epilogue_begin
+          getDW_LNI_ 0x0c = return DW_LNS_set_isa <*> getULEB128
+          getDW_LNI_ n    = let addr_incr = minimum_instruction_length * ((n - opcode_base) `div` line_range)
+                                line_incr = (fromIntegral line_base) + (((fromIntegral n) - (fromIntegral opcode_base)) `mod` (fromIntegral line_range))
+                            in return $ DW_LNI_special addr_incr line_incr
+
+stepLineMachine :: Bool -> Word8 -> DW_LNE -> [DW_LNI] -> [DW_LNE]
+stepLineMachine _ _ _ [] = []
+stepLineMachine is_stmt mil lnm (DW_LNI_special addr_incr line_incr : xs) =
+    let row = lnm { lnmAddress = lnmAddress lnm + addr_incr, lnmLine = lnmLine lnm + fromIntegral line_incr }
+        new = row { lnmBasicBlock = False, lnmPrologueEnd = False, lnmEpilogueBegin = False }
+    in row : stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_copy : xs) =
+    let row = lnm
+        new = row { lnmBasicBlock = False, lnmPrologueEnd = False, lnmEpilogueBegin = False }
+    in row : stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_advance_pc incr : xs) =
+    let new = lnm { lnmAddress = lnmAddress lnm + incr }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_advance_line incr : xs) =
+    let new = lnm { lnmLine = lnmLine lnm + fromIntegral incr }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_set_file file : xs) =
+    let new = lnm { lnmFile = file }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_set_column col : xs) =
+    let new = lnm { lnmColumn = col }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_negate_stmt : xs) =
+    let new = lnm { lnmStatement = not (lnmStatement lnm) }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_set_basic_block : xs) =
+    let new = lnm { lnmBasicBlock = True }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_const_add_pc incr : xs) =
+    let new = lnm { lnmAddress = lnmAddress lnm + incr }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_fixed_advance_pc incr : xs) =
+    let new = lnm { lnmAddress = lnmAddress lnm + incr }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_set_prologue_end : xs) =
+    let new = lnm { lnmPrologueEnd = True }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_set_epilogue_begin : xs) =
+    let new = lnm { lnmEpilogueBegin = True }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNS_set_isa isa : xs) =
+    let new = lnm { lnmISA = isa }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNE_end_sequence : xs) =
+    let row = lnm { lnmEndSequence = True }
+        new = defaultLNE is_stmt (lnmFiles lnm)
+    in row : stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNE_set_address address : xs) =
+    let new = lnm { lnmAddress = address }
+    in stepLineMachine is_stmt mil new xs
+stepLineMachine is_stmt mil lnm (DW_LNE_define_file name dir_index time length : xs) =
+    let new = lnm { lnmFiles = (lnmFiles lnm) ++ [(name, dir_index, time, length)] }
+    in stepLineMachine is_stmt mil new xs
+
+data DW_LNE = DW_LNE
+    { lnmAddress       :: Word64
+    , lnmFile          :: Word64
+    , lnmLine          :: Word64
+    , lnmColumn        :: Word64
+    , lnmStatement     :: Bool
+    , lnmBasicBlock    :: Bool
+    , lnmEndSequence   :: Bool
+    , lnmPrologueEnd   :: Bool
+    , lnmEpilogueBegin :: Bool
+    , lnmISA           :: Word64
+    , lnmFiles         :: [(String, Word64, Word64, Word64)]
+    } deriving (Show, Eq)
+defaultLNE is_stmt files = DW_LNE
+    { lnmAddress       = 0
+    , lnmFile          = 1
+    , lnmLine          = 1
+    , lnmColumn        = 0
+    , lnmStatement     = is_stmt
+    , lnmBasicBlock    = False
+    , lnmEndSequence   = False
+    , lnmPrologueEnd   = False
+    , lnmEpilogueBegin = False
+    , lnmISA           = 0
+    , lnmFiles         = []
+    }
+
+getDebugLineFileNames = do
+    file_name <- getNullTerminatedString
+    if file_name == [] then
+        return []
+     else do
+        dir_index   <- getULEB128
+        last_mod    <- getULEB128
+        file_length <- getULEB128
+        liftM2 (:) (return (file_name, dir_index, last_mod, file_length)) getDebugLineFileNames
+
+-- | Retrieves the line information for a DIE from a given substring of the .debug_line section. The offset
+-- into the .debug_line section is obtained from the DW_AT_stmt_list attribute of a DIE.
+parseDwarfLine :: Bool -> Bool -> B.ByteString -> ([String], [DW_LNE])
+parseDwarfLine littleEndian target64 bs =
+    let dr = dwarfEndianReader littleEndian
+    in runGet (getDwarfLine target64 dr) (L.fromChunks [bs])
+getDwarfLine target64 dr = do
+    let getWhileInclusive cond get = do
+        el <- get
+        if cond el then
+            liftM (el :) $ getWhileInclusive cond get
+         else
+            return [el]
+    (dr, _)                    <- getDwarfUnitLength dr
+    dr                         <- return $ dwarfReader target64 dr
+    version                    <- getWord16 dr
+    header_length              <- getDwarfOffset dr
+    minimum_instruction_length <- getWord8
+    default_is_stmt            <- liftM (/= 0) getWord8
+    line_base                  <- liftM fromIntegral getWord8 :: Get Int8
+    line_range                 <- getWord8
+    opcode_base                <- getWord8
+    standard_opcode_lengths    <- liftM B.unpack $ getByteString $ fromIntegral (opcode_base - 2)
+    include_directories        <- getWhile (/= "") getNullTerminatedString
+    file_names                 <- getDebugLineFileNames
+    line_program               <- getWhileInclusive (/= DW_LNE_end_sequence) (getDW_LNI dr (fromIntegral line_base) (fromIntegral line_range) (fromIntegral opcode_base) (fromIntegral minimum_instruction_length))
+    initial_state              <- return $ defaultLNE default_is_stmt file_names
+    line_matrix                <- return $ stepLineMachine default_is_stmt minimum_instruction_length initial_state line_program
+    return (map (\(name, _, _, _) -> name) file_names, line_matrix)
+
+-- Section 7.21 - Macro Information
+data DW_MACINFO
+    = DW_MACINFO_define Word64 String     -- ^ Line number and defined symbol with definition
+    | DW_MACINFO_undef Word64 String      -- ^ Line number and undefined symbol
+    | DW_MACINFO_start_file Word64 Word64 -- ^ Marks start of file with the line where the file was included from and a source file index
+    | DW_MACINFO_end_file                 -- ^ Marks end of file
+    | DW_MACINFO_vendor_ext Word64 String -- ^ Implementation defined
+    deriving (Show, Eq)
+
+-- | Retrieves the macro information for a compilation unit from a given substring of the .debug_macinfo section. The offset
+-- into the .debug_macinfo section is obtained from the DW_AT_macro_info attribute of a compilation unit DIE.
+parseDwarfMacInfo :: B.ByteString -> [DW_MACINFO]
+parseDwarfMacInfo bs = runGet getDwarfMacInfo (L.fromChunks [bs])
+getDwarfMacInfo = do
+    x <- getWord8
+    case x of
+        0x00 -> return []
+        0x01 -> pure (:) <*> (pure DW_MACINFO_define     <*> getULEB128 <*> getNullTerminatedString) <*> getDwarfMacInfo
+        0x02 -> pure (:) <*> (pure DW_MACINFO_undef      <*> getULEB128 <*> getNullTerminatedString) <*> getDwarfMacInfo
+        0x03 -> pure (:) <*> (pure DW_MACINFO_start_file <*> getULEB128 <*> getULEB128)              <*> getDwarfMacInfo
+        0x04 -> pure (:) <*> (pure DW_MACINFO_end_file)                                              <*> getDwarfMacInfo
+        0xff -> pure (:) <*> (pure DW_MACINFO_vendor_ext <*> getULEB128 <*> getNullTerminatedString) <*> getDwarfMacInfo
+
+-- Section 7.22 - Call Frame
+data DW_CFA
+    = DW_CFA_advance_loc Word8
+    | DW_CFA_offset Word8 Word64
+    | DW_CFA_restore Word8
+    | DW_CFA_nop
+    | DW_CFA_set_loc Word64
+    | DW_CFA_advance_loc1 Word8
+    | DW_CFA_advance_loc2 Word16
+    | DW_CFA_advance_loc4 Word32
+    | DW_CFA_offset_extended Word64 Word64
+    | DW_CFA_restore_extended Word64
+    | DW_CFA_undefined Word64
+    | DW_CFA_same_value Word64
+    | DW_CFA_register Word64 Word64
+    | DW_CFA_remember_state
+    | DW_CFA_restore_state
+    | DW_CFA_def_cfa Word64 Word64
+    | DW_CFA_def_cfa_register Word64
+    | DW_CFA_def_cfa_offset Word64
+    | DW_CFA_def_cfa_expression B.ByteString
+    | DW_CFA_expression Word64 B.ByteString
+    | DW_CFA_offset_extended_sf Word64 Int64
+    | DW_CFA_def_cfa_sf Word64 Int64
+    | DW_CFA_def_cfa_offset_sf Int64
+    | DW_CFA_val_offset Word64 Word64
+    | DW_CFA_val_offset_sf Word64 Int64
+    | DW_CFA_val_expression Word64 B.ByteString
+    deriving (Show, Eq)
+getDW_CFA dr = do
+    tag <- getWord8
+    case tag `shiftR` 6 of
+        0x1 -> return $ DW_CFA_advance_loc $ tag .&. 0x3f
+        0x2 -> return (DW_CFA_offset (tag .&. 0x3f)) <*> getULEB128
+        0x3 -> return $ DW_CFA_restore $ tag .&. 0x3f
+        0x0 -> case tag .&. 0x3f of
+            0x00 -> return DW_CFA_nop
+            0x01 -> return DW_CFA_set_loc <*> getDwarfTargetAddress dr
+            0x02 -> return DW_CFA_advance_loc1 <*> getWord8
+            0x03 -> return DW_CFA_advance_loc2 <*> getWord16 dr
+            0x04 -> return DW_CFA_advance_loc4 <*> getWord32 dr
+            0x05 -> return DW_CFA_offset_extended <*> getULEB128 <*> getULEB128
+            0x06 -> return DW_CFA_restore_extended <*> getULEB128
+            0x07 -> return DW_CFA_undefined <*> getULEB128
+            0x08 -> return DW_CFA_same_value <*> getULEB128
+            0x09 -> return DW_CFA_register <*> getULEB128 <*> getULEB128
+            0x0a -> return DW_CFA_remember_state
+            0x0b -> return DW_CFA_restore_state
+            0x0c -> return DW_CFA_def_cfa <*> getULEB128 <*> getULEB128
+            0x0d -> return DW_CFA_def_cfa_register <*> getULEB128
+            0x0e -> return DW_CFA_def_cfa_offset <*> getULEB128
+            0x0f -> return DW_CFA_def_cfa_expression <*> (liftM fromIntegral getULEB128 >>= getByteString)
+            0x10 -> return DW_CFA_expression <*> getULEB128 <*> (liftM fromIntegral getULEB128 >>= getByteString)
+            0x11 -> return DW_CFA_offset_extended_sf <*> getULEB128 <*> getSLEB128
+            0x12 -> return DW_CFA_def_cfa_sf <*> getULEB128 <*> getSLEB128
+            0x13 -> return DW_CFA_def_cfa_offset_sf <*> getSLEB128
+            0x14 -> return DW_CFA_val_offset <*> getULEB128 <*> getULEB128
+            0x15 -> return DW_CFA_val_offset_sf <*> getULEB128 <*> getSLEB128
+            0x16 -> return DW_CFA_val_expression <*> getULEB128 <*> (liftM fromIntegral getULEB128 >>= getByteString)
+
+data DW_CIEFDE
+    = DW_CIE
+        { cieAugmentation          :: String
+        , cieCodeAlignmentFactor   :: Word64
+        , cieDataAlignmentFactor   :: Int64
+        , cieReturnAddressRegister :: Word64
+        , cieInitialInstructions   :: [DW_CFA]
+        }
+    | DW_FDE
+        { fdeCiePointer      :: Word64
+        , fdeInitialLocation :: Word64
+        , fdeAddressRange    :: Word64
+        , fdeInstructions    :: [DW_CFA]
+        }
+    deriving (Show, Eq)
+
+getWhileNotEmpty :: Get a -> Get [a]
+getWhileNotEmpty get = do
+    empty <- isEmpty
+    if empty then
+        return []
+     else do
+        liftM2 (:) get (getWhileNotEmpty get)
+
+getCIEFDE littleEndian target64 = do
+    dr           <- return $ dwarfEndianReader littleEndian
+    (dr, length) <- getDwarfUnitLength dr
+    dr           <- return $ dwarfReader target64 dr
+    begin        <- bytesRead
+    cie_id       <- getDwarfOffset dr
+    if cie_id == largestOffset dr then do
+        version                 <- getWord8
+        augmentation            <- getNullTerminatedString
+        code_alignment_factor   <- getULEB128
+        data_alignment_factor   <- getSLEB128
+        return_address_register <- case version of
+                                    1 -> liftM fromIntegral $ getWord8
+                                    3 -> getULEB128
+                                    n -> fail $ "Unrecognized CIE version " ++ show n
+        end                     <- bytesRead
+        raw_instructions        <- getByteString $ fromIntegral (fromIntegral length - (end - begin))
+        initial_instructions    <- return $ runGet (getWhileNotEmpty (getDW_CFA dr)) $ L.fromChunks [raw_instructions]
+        return $ DW_CIE augmentation code_alignment_factor data_alignment_factor return_address_register initial_instructions
+     else do
+        initial_location        <- getDwarfTargetAddress dr
+        address_range           <- getDwarfTargetAddress dr
+        end                     <- bytesRead
+        raw_instructions        <- getByteString $ fromIntegral (fromIntegral length - (end - begin))
+        instructions            <- return $ runGet (getWhileNotEmpty (getDW_CFA dr)) $ L.fromChunks [raw_instructions]
+        return $ DW_FDE cie_id initial_location address_range instructions
+
+-- | Parse the .debug_frame section into a list of DW_CIEFDE records.
+parseDwarfFrame :: Bool         -- ^ True for little endian data. False for big endian.
+                -> Bool         -- ^ True for 64-bit target addresses. False of 32-bit target addresses.
+                -> B.ByteString -- ^ ByteString for the .debug_frame section.
+                -> [DW_CIEFDE]
+parseDwarfFrame littleEndian target64 bs = runGet (getWhileNotEmpty $ getCIEFDE littleEndian target64) (L.fromChunks [bs])
+
+-- Section 7.23 - Non-contiguous Address Ranges
+-- | Retrieves the non-contiguous address ranges for a compilation unit from a given substring of the .debug_ranges section. The offset
+-- into the .debug_ranges section is obtained from the DW_AT_ranges attribute of a compilation unit DIE.
+-- Left results are base address entries. Right results are address ranges.
+parseDwarfRanges :: DwarfReader -> B.ByteString -> [Either Word64 (Word64, Word64)]
+parseDwarfRanges dr bs = runGet (getDwarfRanges dr) (L.fromChunks [bs])
+getDwarfRanges dr = do
+    begin <- getDwarfTargetAddress dr
+    end   <- getDwarfTargetAddress dr
+    if begin == 0 && end == 0 then
+        return []
+     else if begin == largestTargetAddress dr then
+        return (Left end :) <*> getDwarfRanges dr
+     else
+        return (Right (begin, end) :) <*> getDwarfRanges dr
+
+-- Section 7.7.3
+-- | Retrieves the location list expressions from a given substring of the .debug_loc section. The offset
+-- into the .debug_loc section is obtained from an attribute of class loclistptr for a given DIE.
+-- Left results are base address entries. Right results are address ranges and a location expression.
+parseDwarfLoc :: DwarfReader -> B.ByteString -> [Either Word64 (Word64, Word64, B.ByteString)]
+parseDwarfLoc dr bs = runGet (getDwarfLoc dr) (L.fromChunks [bs])
+getDwarfLoc dr = do
+    begin <- getDwarfTargetAddress dr
+    end   <- getDwarfTargetAddress dr
+    if begin == 0 && end == 0 then
+        return []
+     else if begin == largestTargetAddress dr then
+        return (Left end :) <*> getDwarfLoc dr
+      else do
+        len  <- liftM fromIntegral (getWord16 dr)
+        expr <- getByteString len
+        return (Right (begin, end, expr) :) <*> getDwarfLoc dr
+
+data DW_TAG
+    = DW_TAG_array_type
+    | DW_TAG_class_type
+    | DW_TAG_entry_point
+    | DW_TAG_enumeration_type
+    | DW_TAG_formal_parameter
+    | DW_TAG_imported_declaration
+    | DW_TAG_label
+    | DW_TAG_lexical_block
+    | DW_TAG_member
+    | DW_TAG_pointer_type
+    | DW_TAG_reference_type
+    | DW_TAG_compile_unit
+    | DW_TAG_string_type
+    | DW_TAG_structure_type
+    | DW_TAG_subroutine_type
+    | DW_TAG_typedef
+    | DW_TAG_union_type
+    | DW_TAG_unspecified_parameters
+    | DW_TAG_variant
+    | DW_TAG_common_block
+    | DW_TAG_common_inclusion
+    | DW_TAG_inheritance
+    | DW_TAG_inlined_subroutine
+    | DW_TAG_module
+    | DW_TAG_ptr_to_member_type
+    | DW_TAG_set_type
+    | DW_TAG_subrange_type
+    | DW_TAG_with_stmt
+    | DW_TAG_access_declaration
+    | DW_TAG_base_type
+    | DW_TAG_catch_block
+    | DW_TAG_const_type
+    | DW_TAG_constant
+    | DW_TAG_enumerator
+    | DW_TAG_file_type
+    | DW_TAG_friend
+    | DW_TAG_namelist
+    | DW_TAG_namelist_item
+    | DW_TAG_packed_type
+    | DW_TAG_subprogram
+    | DW_TAG_template_type_parameter
+    | DW_TAG_template_value_parameter
+    | DW_TAG_thrown_type
+    | DW_TAG_try_block
+    | DW_TAG_variant_part
+    | DW_TAG_variable
+    | DW_TAG_volatile_type
+    | DW_TAG_dwarf_procedure
+    | DW_TAG_restrict_type
+    | DW_TAG_interface_type
+    | DW_TAG_namespace
+    | DW_TAG_imported_module
+    | DW_TAG_unspecified_type
+    | DW_TAG_partial_unit
+    | DW_TAG_imported_unit
+    | DW_TAG_condition
+    | DW_TAG_shared_type
+    deriving (Show, Eq)
+getDW_TAG = getULEB128 >>= dw_tag
+    where dw_tag 0x01 = return DW_TAG_array_type
+          dw_tag 0x02 = return DW_TAG_class_type
+          dw_tag 0x03 = return DW_TAG_entry_point
+          dw_tag 0x04 = return DW_TAG_enumeration_type
+          dw_tag 0x05 = return DW_TAG_formal_parameter
+          dw_tag 0x08 = return DW_TAG_imported_declaration
+          dw_tag 0x0a = return DW_TAG_label
+          dw_tag 0x0b = return DW_TAG_lexical_block
+          dw_tag 0x0d = return DW_TAG_member
+          dw_tag 0x0f = return DW_TAG_pointer_type
+          dw_tag 0x10 = return DW_TAG_reference_type
+          dw_tag 0x11 = return DW_TAG_compile_unit
+          dw_tag 0x12 = return DW_TAG_string_type
+          dw_tag 0x13 = return DW_TAG_structure_type
+          dw_tag 0x15 = return DW_TAG_subroutine_type
+          dw_tag 0x16 = return DW_TAG_typedef
+          dw_tag 0x17 = return DW_TAG_union_type
+          dw_tag 0x18 = return DW_TAG_unspecified_parameters
+          dw_tag 0x19 = return DW_TAG_variant
+          dw_tag 0x1a = return DW_TAG_common_block
+          dw_tag 0x1b = return DW_TAG_common_inclusion
+          dw_tag 0x1c = return DW_TAG_inheritance
+          dw_tag 0x1d = return DW_TAG_inlined_subroutine
+          dw_tag 0x1e = return DW_TAG_module
+          dw_tag 0x1f = return DW_TAG_ptr_to_member_type
+          dw_tag 0x20 = return DW_TAG_set_type
+          dw_tag 0x21 = return DW_TAG_subrange_type
+          dw_tag 0x22 = return DW_TAG_with_stmt
+          dw_tag 0x23 = return DW_TAG_access_declaration
+          dw_tag 0x24 = return DW_TAG_base_type
+          dw_tag 0x25 = return DW_TAG_catch_block
+          dw_tag 0x26 = return DW_TAG_const_type
+          dw_tag 0x27 = return DW_TAG_constant
+          dw_tag 0x28 = return DW_TAG_enumerator
+          dw_tag 0x29 = return DW_TAG_file_type
+          dw_tag 0x2a = return DW_TAG_friend
+          dw_tag 0x2b = return DW_TAG_namelist
+          dw_tag 0x2c = return DW_TAG_namelist_item
+          dw_tag 0x2d = return DW_TAG_packed_type
+          dw_tag 0x2e = return DW_TAG_subprogram
+          dw_tag 0x2f = return DW_TAG_template_type_parameter
+          dw_tag 0x30 = return DW_TAG_template_value_parameter
+          dw_tag 0x31 = return DW_TAG_thrown_type
+          dw_tag 0x32 = return DW_TAG_try_block
+          dw_tag 0x33 = return DW_TAG_variant_part
+          dw_tag 0x34 = return DW_TAG_variable
+          dw_tag 0x35 = return DW_TAG_volatile_type
+          dw_tag 0x36 = return DW_TAG_dwarf_procedure
+          dw_tag 0x37 = return DW_TAG_restrict_type
+          dw_tag 0x38 = return DW_TAG_interface_type
+          dw_tag 0x39 = return DW_TAG_namespace
+          dw_tag 0x3a = return DW_TAG_imported_module
+          dw_tag 0x3b = return DW_TAG_unspecified_type
+          dw_tag 0x3c = return DW_TAG_partial_unit
+          dw_tag 0x3d = return DW_TAG_imported_unit
+          dw_tag 0x3f = return DW_TAG_condition
+          dw_tag 0x40 = return DW_TAG_shared_type
+          dw_tag n | 0x4080 <= n && n <= 0xffff = fail $ "User DW_TAG data requires extension of parser for code " ++ show n
+          dw_tag n = fail $ "Unrecognized DW_TAG " ++ show n
+
+data DW_AT
+    = DW_AT_sibling              -- ^ reference
+    | DW_AT_location             -- ^ block, loclistptr
+    | DW_AT_name                 -- ^ string
+    | DW_AT_ordering             -- ^ constant
+    | DW_AT_byte_size            -- ^ block, constant, reference
+    | DW_AT_bit_offset           -- ^ block, constant, reference
+    | DW_AT_bit_size             -- ^ block, constant, reference
+    | DW_AT_stmt_list            -- ^ lineptr
+    | DW_AT_low_pc               -- ^ address
+    | DW_AT_high_pc              -- ^ address
+    | DW_AT_language             -- ^ constant
+    | DW_AT_discr                -- ^ reference
+    | DW_AT_discr_value          -- ^ constant
+    | DW_AT_visibility           -- ^ constant
+    | DW_AT_import               -- ^ reference
+    | DW_AT_string_length        -- ^ block, loclistptr
+    | DW_AT_common_reference     -- ^ reference
+    | DW_AT_comp_dir             -- ^ string
+    | DW_AT_const_value          -- ^ block, constant, string
+    | DW_AT_containing_type      -- ^ reference
+    | DW_AT_default_value        -- ^ reference
+    | DW_AT_inline               -- ^ constant
+    | DW_AT_is_optional          -- ^ flag
+    | DW_AT_lower_bound          -- ^ block, constant, reference
+    | DW_AT_producer             -- ^ string
+    | DW_AT_prototyped           -- ^ flag
+    | DW_AT_return_addr          -- ^ block, loclistptr
+    | DW_AT_start_scope          -- ^ constant
+    | DW_AT_bit_stride           -- ^ constant
+    | DW_AT_upper_bound          -- ^ block, constant, reference
+    | DW_AT_abstract_origin      -- ^ reference
+    | DW_AT_accessibility        -- ^ constant
+    | DW_AT_address_class        -- ^ constant
+    | DW_AT_artificial           -- ^ flag
+    | DW_AT_base_types           -- ^ reference
+    | DW_AT_calling_convention   -- ^ constant
+    | DW_AT_count                -- ^ block, constant, reference
+    | DW_AT_data_member_location -- ^ block, constant, loclistptr
+    | DW_AT_decl_column          -- ^ constant
+    | DW_AT_decl_file            -- ^ constant
+    | DW_AT_decl_line            -- ^ constant
+    | DW_AT_declaration          -- ^ flag
+    | DW_AT_discr_list           -- ^ block
+    | DW_AT_encoding             -- ^ constant
+    | DW_AT_external             -- ^ flag
+    | DW_AT_frame_base           -- ^ block, loclistptr
+    | DW_AT_friend               -- ^ reference
+    | DW_AT_identifier_case      -- ^ constant
+    | DW_AT_macro_info           -- ^ macptr
+    | DW_AT_namelist_item        -- ^ block
+    | DW_AT_priority             -- ^ reference
+    | DW_AT_segment              -- ^ block, loclistptr
+    | DW_AT_specification        -- ^ reference
+    | DW_AT_static_link          -- ^ block, loclistptr
+    | DW_AT_type                 -- ^ reference
+    | DW_AT_use_location         -- ^ block, loclistptr
+    | DW_AT_variable_parameter   -- ^ flag
+    | DW_AT_virtuality           -- ^ constant
+    | DW_AT_vtable_elem_location -- ^ block, loclistptr
+    | DW_AT_allocated            -- ^ block, constant, reference
+    | DW_AT_associated           -- ^ block, constant, reference
+    | DW_AT_data_location        -- ^ block
+    | DW_AT_byte_stride          -- ^ block, constant, reference
+    | DW_AT_entry_pc             -- ^ address
+    | DW_AT_use_UTF8             -- ^ flag
+    | DW_AT_extension            -- ^ reference
+    | DW_AT_ranges               -- ^ rangelistptr
+    | DW_AT_trampoline           -- ^ address, flag, reference, string
+    | DW_AT_call_column          -- ^ constant
+    | DW_AT_call_file            -- ^ constant
+    | DW_AT_call_line            -- ^ constant
+    | DW_AT_description          -- ^ string
+    | DW_AT_binary_scale         -- ^ constant
+    | DW_AT_decimal_scale        -- ^ constant
+    | DW_AT_small                -- ^ reference
+    | DW_AT_decimal_sign         -- ^ constant
+    | DW_AT_digit_count          -- ^ constant
+    | DW_AT_picture_string       -- ^ string
+    | DW_AT_mutable              -- ^ flag
+    | DW_AT_threads_scaled       -- ^ flag
+    | DW_AT_explicit             -- ^ flag
+    | DW_AT_object_pointer       -- ^ reference
+    | DW_AT_endianity            -- ^ constant
+    | DW_AT_elemental            -- ^ flag
+    | DW_AT_pure                 -- ^ flag
+    | DW_AT_recursive            -- ^ flag
+    | DW_AT_user Word64          -- ^ user extension
+    deriving (Show, Eq)
+dw_at 0x01 = DW_AT_sibling
+dw_at 0x02 = DW_AT_location
+dw_at 0x03 = DW_AT_name
+dw_at 0x09 = DW_AT_ordering
+dw_at 0x0b = DW_AT_byte_size
+dw_at 0x0c = DW_AT_bit_offset
+dw_at 0x0d = DW_AT_bit_size
+dw_at 0x10 = DW_AT_stmt_list
+dw_at 0x11 = DW_AT_low_pc
+dw_at 0x12 = DW_AT_high_pc
+dw_at 0x13 = DW_AT_language
+dw_at 0x15 = DW_AT_discr
+dw_at 0x16 = DW_AT_discr_value
+dw_at 0x17 = DW_AT_visibility
+dw_at 0x18 = DW_AT_import
+dw_at 0x19 = DW_AT_string_length
+dw_at 0x1a = DW_AT_common_reference
+dw_at 0x1b = DW_AT_comp_dir
+dw_at 0x1c = DW_AT_const_value
+dw_at 0x1d = DW_AT_containing_type
+dw_at 0x1e = DW_AT_default_value
+dw_at 0x20 = DW_AT_inline
+dw_at 0x21 = DW_AT_is_optional
+dw_at 0x22 = DW_AT_lower_bound
+dw_at 0x25 = DW_AT_producer
+dw_at 0x27 = DW_AT_prototyped
+dw_at 0x2a = DW_AT_return_addr
+dw_at 0x2c = DW_AT_start_scope
+dw_at 0x2e = DW_AT_bit_stride
+dw_at 0x2f = DW_AT_upper_bound
+dw_at 0x31 = DW_AT_abstract_origin
+dw_at 0x32 = DW_AT_accessibility
+dw_at 0x33 = DW_AT_address_class
+dw_at 0x34 = DW_AT_artificial
+dw_at 0x35 = DW_AT_base_types
+dw_at 0x36 = DW_AT_calling_convention
+dw_at 0x37 = DW_AT_count
+dw_at 0x38 = DW_AT_data_member_location
+dw_at 0x39 = DW_AT_decl_column
+dw_at 0x3a = DW_AT_decl_file
+dw_at 0x3b = DW_AT_decl_line
+dw_at 0x3c = DW_AT_declaration
+dw_at 0x3d = DW_AT_discr_list
+dw_at 0x3e = DW_AT_encoding
+dw_at 0x3f = DW_AT_external
+dw_at 0x40 = DW_AT_frame_base
+dw_at 0x41 = DW_AT_friend
+dw_at 0x42 = DW_AT_identifier_case
+dw_at 0x43 = DW_AT_macro_info
+dw_at 0x44 = DW_AT_namelist_item
+dw_at 0x45 = DW_AT_priority
+dw_at 0x46 = DW_AT_segment
+dw_at 0x47 = DW_AT_specification
+dw_at 0x48 = DW_AT_static_link
+dw_at 0x49 = DW_AT_type
+dw_at 0x4a = DW_AT_use_location
+dw_at 0x4b = DW_AT_variable_parameter
+dw_at 0x4c = DW_AT_virtuality
+dw_at 0x4d = DW_AT_vtable_elem_location
+dw_at 0x4e = DW_AT_allocated
+dw_at 0x4f = DW_AT_associated
+dw_at 0x50 = DW_AT_data_location
+dw_at 0x51 = DW_AT_byte_stride
+dw_at 0x52 = DW_AT_entry_pc
+dw_at 0x53 = DW_AT_use_UTF8
+dw_at 0x54 = DW_AT_extension
+dw_at 0x55 = DW_AT_ranges
+dw_at 0x56 = DW_AT_trampoline
+dw_at 0x57 = DW_AT_call_column
+dw_at 0x58 = DW_AT_call_file
+dw_at 0x59 = DW_AT_call_line
+dw_at 0x5a = DW_AT_description
+dw_at 0x5b = DW_AT_binary_scale
+dw_at 0x5c = DW_AT_decimal_scale
+dw_at 0x5d = DW_AT_small
+dw_at 0x5e = DW_AT_decimal_sign
+dw_at 0x5f = DW_AT_digit_count
+dw_at 0x60 = DW_AT_picture_string
+dw_at 0x61 = DW_AT_mutable
+dw_at 0x62 = DW_AT_threads_scaled
+dw_at 0x63 = DW_AT_explicit
+dw_at 0x64 = DW_AT_object_pointer
+dw_at 0x65 = DW_AT_endianity
+dw_at 0x66 = DW_AT_elemental
+dw_at 0x67 = DW_AT_pure
+dw_at 0x68 = DW_AT_recursive
+dw_at n | 0x2000 <= n && n <= 0x3fff = DW_AT_user n 
+dw_at n = error $ "Unrecognized DW_AT " ++ show n
+
+data DW_ATVAL
+    = DW_ATVAL_INT    Int64
+    | DW_ATVAL_UINT   Word64
+    | DW_ATVAL_STRING String
+    | DW_ATVAL_BLOB   B.ByteString
+    | DW_ATVAL_BOOL   Bool
+    deriving (Show, Eq)
+
+data DW_FORM
+    = DW_FORM_addr              -- ^ address
+    | DW_FORM_block2 -- ^ block
+    | DW_FORM_block4 -- ^ block
+    | DW_FORM_data2 -- ^ constant
+    | DW_FORM_data4 -- ^ constant, lineptr, loclistptr, macptr, rangelistptr
+    | DW_FORM_data8 -- ^ constant, lineptr, loclistptr, macptr, rangelistptr
+    | DW_FORM_string -- ^ string
+    | DW_FORM_block -- ^ block
+    | DW_FORM_block1 -- ^ block
+    | DW_FORM_data1 -- ^ constant
+    | DW_FORM_flag -- ^ flag
+    | DW_FORM_sdata -- ^ constant
+    | DW_FORM_strp -- ^ string
+    | DW_FORM_udata -- ^ constant
+    | DW_FORM_ref_addr            -- ^ reference
+    | DW_FORM_ref1                -- ^ reference
+    | DW_FORM_ref2                -- ^ reference
+    | DW_FORM_ref4                -- ^ reference
+    | DW_FORM_ref8                -- ^ reference
+    | DW_FORM_ref_udata           -- ^ reference
+    | DW_FORM_indirect            -- ^ (see Section 7.5.3 of DWARF3 specification)
+    deriving (Show, Eq)
+dw_form 0x01 = DW_FORM_addr
+dw_form 0x03 = DW_FORM_block2
+dw_form 0x04 = DW_FORM_block4
+dw_form 0x05 = DW_FORM_data2
+dw_form 0x06 = DW_FORM_data4
+dw_form 0x07 = DW_FORM_data8
+dw_form 0x08 = DW_FORM_string
+dw_form 0x09 = DW_FORM_block
+dw_form 0x0a = DW_FORM_block1
+dw_form 0x0b = DW_FORM_data1
+dw_form 0x0c = DW_FORM_flag
+dw_form 0x0d = DW_FORM_sdata
+dw_form 0x0e = DW_FORM_strp
+dw_form 0x0f = DW_FORM_udata
+dw_form 0x10 = DW_FORM_ref_addr
+dw_form 0x11 = DW_FORM_ref1
+dw_form 0x12 = DW_FORM_ref2
+dw_form 0x13 = DW_FORM_ref4
+dw_form 0x14 = DW_FORM_ref8
+dw_form 0x15 = DW_FORM_ref_udata
+dw_form 0x16 = DW_FORM_indirect
+dw_form n    = error $ "Unrecognized DW_FORM " ++ show n
+
+data DW_OP
+    = DW_OP_addr Word64
+    | DW_OP_deref
+    | DW_OP_const1u Word8
+    | DW_OP_const1s Int8
+    | DW_OP_const2u Word16
+    | DW_OP_const2s Int16
+    | DW_OP_const4u Word32
+    | DW_OP_const4s Int32
+    | DW_OP_const8u Word64
+    | DW_OP_const8s Int64
+    | DW_OP_constu  Word64
+    | DW_OP_consts  Int64
+    | DW_OP_dup
+    | DW_OP_drop
+    | DW_OP_over
+    | DW_OP_pick Word8
+    | DW_OP_swap
+    | DW_OP_rot
+    | DW_OP_xderef
+    | DW_OP_abs
+    | DW_OP_and
+    | DW_OP_div
+    | DW_OP_minus
+    | DW_OP_mod
+    | DW_OP_mul
+    | DW_OP_neg
+    | DW_OP_not
+    | DW_OP_or
+    | DW_OP_plus
+    | DW_OP_plus_uconst Word64
+    | DW_OP_shl
+    | DW_OP_shr
+    | DW_OP_shra
+    | DW_OP_xor
+    | DW_OP_skip Int16
+    | DW_OP_bra Int16
+    | DW_OP_eq
+    | DW_OP_ge
+    | DW_OP_gt
+    | DW_OP_le
+    | DW_OP_lt
+    | DW_OP_ne
+    | DW_OP_lit0
+    | DW_OP_lit1
+    | DW_OP_lit2
+    | DW_OP_lit3
+    | DW_OP_lit4
+    | DW_OP_lit5
+    | DW_OP_lit6
+    | DW_OP_lit7
+    | DW_OP_lit8
+    | DW_OP_lit9
+    | DW_OP_lit10
+    | DW_OP_lit11
+    | DW_OP_lit12
+    | DW_OP_lit13
+    | DW_OP_lit14
+    | DW_OP_lit15
+    | DW_OP_lit16
+    | DW_OP_lit17
+    | DW_OP_lit18
+    | DW_OP_lit19
+    | DW_OP_lit20
+    | DW_OP_lit21
+    | DW_OP_lit22
+    | DW_OP_lit23
+    | DW_OP_lit24
+    | DW_OP_lit25
+    | DW_OP_lit26
+    | DW_OP_lit27
+    | DW_OP_lit28
+    | DW_OP_lit29
+    | DW_OP_lit30
+    | DW_OP_lit31
+    | DW_OP_reg0
+    | DW_OP_reg1
+    | DW_OP_reg2
+    | DW_OP_reg3
+    | DW_OP_reg4
+    | DW_OP_reg5
+    | DW_OP_reg6
+    | DW_OP_reg7
+    | DW_OP_reg8
+    | DW_OP_reg9
+    | DW_OP_reg10
+    | DW_OP_reg11
+    | DW_OP_reg12
+    | DW_OP_reg13
+    | DW_OP_reg14
+    | DW_OP_reg15
+    | DW_OP_reg16
+    | DW_OP_reg17
+    | DW_OP_reg18
+    | DW_OP_reg19
+    | DW_OP_reg20
+    | DW_OP_reg21
+    | DW_OP_reg22
+    | DW_OP_reg23
+    | DW_OP_reg24
+    | DW_OP_reg25
+    | DW_OP_reg26
+    | DW_OP_reg27
+    | DW_OP_reg28
+    | DW_OP_reg29
+    | DW_OP_reg30
+    | DW_OP_reg31
+    | DW_OP_breg0 Int64
+    | DW_OP_breg1 Int64
+    | DW_OP_breg2 Int64
+    | DW_OP_breg3 Int64
+    | DW_OP_breg4 Int64
+    | DW_OP_breg5 Int64
+    | DW_OP_breg6 Int64
+    | DW_OP_breg7 Int64
+    | DW_OP_breg8 Int64
+    | DW_OP_breg9 Int64
+    | DW_OP_breg10 Int64
+    | DW_OP_breg11 Int64
+    | DW_OP_breg12 Int64
+    | DW_OP_breg13 Int64
+    | DW_OP_breg14 Int64
+    | DW_OP_breg15 Int64
+    | DW_OP_breg16 Int64
+    | DW_OP_breg17 Int64
+    | DW_OP_breg18 Int64
+    | DW_OP_breg19 Int64
+    | DW_OP_breg20 Int64
+    | DW_OP_breg21 Int64
+    | DW_OP_breg22 Int64
+    | DW_OP_breg23 Int64
+    | DW_OP_breg24 Int64
+    | DW_OP_breg25 Int64
+    | DW_OP_breg26 Int64
+    | DW_OP_breg27 Int64
+    | DW_OP_breg28 Int64
+    | DW_OP_breg29 Int64
+    | DW_OP_breg30 Int64
+    | DW_OP_breg31 Int64
+    | DW_OP_regx Word64
+    | DW_OP_fbreg Int64
+    | DW_OP_bregx Word64 Int64
+    | DW_OP_piece Word64
+    | DW_OP_deref_size Word8
+    | DW_OP_xderef_size Word8
+    | DW_OP_nop
+    | DW_OP_push_object_address
+    | DW_OP_call2 Word16
+    | DW_OP_call4 Word32
+    | DW_OP_call_ref Word64
+    | DW_OP_form_tls_address
+    | DW_OP_call_frame_cfa
+    | DW_OP_bit_piece Word64 Word64
+    deriving (Show, Eq)
+-- | Parse a ByteString into a DWARF opcode. This will be needed for further decoding of DIE attributes.
+parseDW_OP :: DwarfReader -> B.ByteString -> DW_OP
+parseDW_OP dr bs = runGet (getDW_OP dr) (L.fromChunks [bs])
+getDW_OP dr = getWord8 >>= getDW_OP_
+    where getDW_OP_ 0x03 = return DW_OP_addr <*> getDwarfTargetAddress dr
+          getDW_OP_ 0x06 = return DW_OP_deref
+          getDW_OP_ 0x08 = return DW_OP_const1u <*> liftM fromIntegral getWord8
+          getDW_OP_ 0x09 = return DW_OP_const1s <*> liftM fromIntegral getWord8
+          getDW_OP_ 0x0a = return DW_OP_const2u <*> liftM fromIntegral (getWord16 dr)
+          getDW_OP_ 0x0b = return DW_OP_const2s <*> liftM fromIntegral (getWord16 dr)
+          getDW_OP_ 0x0c = return DW_OP_const4u <*> liftM fromIntegral (getWord32 dr)
+          getDW_OP_ 0x0d = return DW_OP_const4s <*> liftM fromIntegral (getWord32 dr)
+          getDW_OP_ 0x0e = return DW_OP_const8u <*> getWord64 dr
+          getDW_OP_ 0x0f = return DW_OP_const8s <*> liftM fromIntegral (getWord64 dr)
+          getDW_OP_ 0x10 = return DW_OP_constu  <*> getULEB128
+          getDW_OP_ 0x11 = return DW_OP_consts  <*> getSLEB128
+          getDW_OP_ 0x12 = return DW_OP_dup
+          getDW_OP_ 0x13 = return DW_OP_drop
+          getDW_OP_ 0x14 = return DW_OP_over
+          getDW_OP_ 0x15 = return DW_OP_pick <*> getWord8
+          getDW_OP_ 0x16 = return DW_OP_swap
+          getDW_OP_ 0x17 = return DW_OP_rot
+          getDW_OP_ 0x18 = return DW_OP_xderef
+          getDW_OP_ 0x19 = return DW_OP_abs
+          getDW_OP_ 0x1a = return DW_OP_and
+          getDW_OP_ 0x1b = return DW_OP_div
+          getDW_OP_ 0x1c = return DW_OP_minus
+          getDW_OP_ 0x1d = return DW_OP_mod
+          getDW_OP_ 0x1e = return DW_OP_mul
+          getDW_OP_ 0x1f = return DW_OP_neg
+          getDW_OP_ 0x20 = return DW_OP_not
+          getDW_OP_ 0x21 = return DW_OP_or
+          getDW_OP_ 0x22 = return DW_OP_plus
+          getDW_OP_ 0x23 = return DW_OP_plus_uconst <*> getULEB128
+          getDW_OP_ 0x24 = return DW_OP_shl
+          getDW_OP_ 0x25 = return DW_OP_shr
+          getDW_OP_ 0x26 = return DW_OP_shra
+          getDW_OP_ 0x27 = return DW_OP_xor
+          getDW_OP_ 0x2f = return DW_OP_skip <*> liftM fromIntegral (getWord16 dr)
+          getDW_OP_ 0x28 = return DW_OP_bra  <*> liftM fromIntegral (getWord16 dr)
+          getDW_OP_ 0x29 = return DW_OP_eq
+          getDW_OP_ 0x2a = return DW_OP_ge
+          getDW_OP_ 0x2b = return DW_OP_gt
+          getDW_OP_ 0x2c = return DW_OP_le
+          getDW_OP_ 0x2d = return DW_OP_lt
+          getDW_OP_ 0x2e = return DW_OP_ne
+          getDW_OP_ 0x30 = return DW_OP_lit0
+          getDW_OP_ 0x31 = return DW_OP_lit1
+          getDW_OP_ 0x32 = return DW_OP_lit2
+          getDW_OP_ 0x33 = return DW_OP_lit3
+          getDW_OP_ 0x34 = return DW_OP_lit4
+          getDW_OP_ 0x35 = return DW_OP_lit5
+          getDW_OP_ 0x36 = return DW_OP_lit6
+          getDW_OP_ 0x37 = return DW_OP_lit7
+          getDW_OP_ 0x38 = return DW_OP_lit8
+          getDW_OP_ 0x39 = return DW_OP_lit9
+          getDW_OP_ 0x3a = return DW_OP_lit10
+          getDW_OP_ 0x3b = return DW_OP_lit11
+          getDW_OP_ 0x3c = return DW_OP_lit12
+          getDW_OP_ 0x3d = return DW_OP_lit13
+          getDW_OP_ 0x3e = return DW_OP_lit14
+          getDW_OP_ 0x3f = return DW_OP_lit15
+          getDW_OP_ 0x40 = return DW_OP_lit16
+          getDW_OP_ 0x41 = return DW_OP_lit17
+          getDW_OP_ 0x42 = return DW_OP_lit18
+          getDW_OP_ 0x43 = return DW_OP_lit19
+          getDW_OP_ 0x44 = return DW_OP_lit20
+          getDW_OP_ 0x45 = return DW_OP_lit21
+          getDW_OP_ 0x46 = return DW_OP_lit22
+          getDW_OP_ 0x47 = return DW_OP_lit23
+          getDW_OP_ 0x48 = return DW_OP_lit24
+          getDW_OP_ 0x49 = return DW_OP_lit25
+          getDW_OP_ 0x4a = return DW_OP_lit26
+          getDW_OP_ 0x4b = return DW_OP_lit27
+          getDW_OP_ 0x4c = return DW_OP_lit28
+          getDW_OP_ 0x4d = return DW_OP_lit29
+          getDW_OP_ 0x4e = return DW_OP_lit30
+          getDW_OP_ 0x4f = return DW_OP_lit31
+          getDW_OP_ 0x50 = return DW_OP_reg0
+          getDW_OP_ 0x51 = return DW_OP_reg1
+          getDW_OP_ 0x52 = return DW_OP_reg2
+          getDW_OP_ 0x53 = return DW_OP_reg3
+          getDW_OP_ 0x54 = return DW_OP_reg4
+          getDW_OP_ 0x55 = return DW_OP_reg5
+          getDW_OP_ 0x56 = return DW_OP_reg6
+          getDW_OP_ 0x57 = return DW_OP_reg7
+          getDW_OP_ 0x58 = return DW_OP_reg8
+          getDW_OP_ 0x59 = return DW_OP_reg9
+          getDW_OP_ 0x5a = return DW_OP_reg10
+          getDW_OP_ 0x5b = return DW_OP_reg11
+          getDW_OP_ 0x5c = return DW_OP_reg12
+          getDW_OP_ 0x5d = return DW_OP_reg13
+          getDW_OP_ 0x5e = return DW_OP_reg14
+          getDW_OP_ 0x5f = return DW_OP_reg15
+          getDW_OP_ 0x60 = return DW_OP_reg16
+          getDW_OP_ 0x61 = return DW_OP_reg17
+          getDW_OP_ 0x62 = return DW_OP_reg18
+          getDW_OP_ 0x63 = return DW_OP_reg19
+          getDW_OP_ 0x64 = return DW_OP_reg20
+          getDW_OP_ 0x65 = return DW_OP_reg21
+          getDW_OP_ 0x66 = return DW_OP_reg22
+          getDW_OP_ 0x67 = return DW_OP_reg23
+          getDW_OP_ 0x68 = return DW_OP_reg24
+          getDW_OP_ 0x69 = return DW_OP_reg25
+          getDW_OP_ 0x6a = return DW_OP_reg26
+          getDW_OP_ 0x6b = return DW_OP_reg27
+          getDW_OP_ 0x6c = return DW_OP_reg28
+          getDW_OP_ 0x6d = return DW_OP_reg29
+          getDW_OP_ 0x6e = return DW_OP_reg30
+          getDW_OP_ 0x6f = return DW_OP_reg31
+          getDW_OP_ 0x70 = return DW_OP_breg0  <*> getSLEB128
+          getDW_OP_ 0x71 = return DW_OP_breg1  <*> getSLEB128
+          getDW_OP_ 0x72 = return DW_OP_breg2  <*> getSLEB128
+          getDW_OP_ 0x73 = return DW_OP_breg3  <*> getSLEB128
+          getDW_OP_ 0x74 = return DW_OP_breg4  <*> getSLEB128
+          getDW_OP_ 0x75 = return DW_OP_breg5  <*> getSLEB128
+          getDW_OP_ 0x76 = return DW_OP_breg6  <*> getSLEB128
+          getDW_OP_ 0x77 = return DW_OP_breg7  <*> getSLEB128
+          getDW_OP_ 0x78 = return DW_OP_breg8  <*> getSLEB128
+          getDW_OP_ 0x79 = return DW_OP_breg9  <*> getSLEB128
+          getDW_OP_ 0x7a = return DW_OP_breg10 <*> getSLEB128
+          getDW_OP_ 0x7b = return DW_OP_breg11 <*> getSLEB128
+          getDW_OP_ 0x7c = return DW_OP_breg12 <*> getSLEB128
+          getDW_OP_ 0x7d = return DW_OP_breg13 <*> getSLEB128
+          getDW_OP_ 0x7e = return DW_OP_breg14 <*> getSLEB128
+          getDW_OP_ 0x7f = return DW_OP_breg15 <*> getSLEB128
+          getDW_OP_ 0x80 = return DW_OP_breg16 <*> getSLEB128
+          getDW_OP_ 0x81 = return DW_OP_breg17 <*> getSLEB128
+          getDW_OP_ 0x82 = return DW_OP_breg18 <*> getSLEB128
+          getDW_OP_ 0x83 = return DW_OP_breg19 <*> getSLEB128
+          getDW_OP_ 0x84 = return DW_OP_breg20 <*> getSLEB128
+          getDW_OP_ 0x85 = return DW_OP_breg21 <*> getSLEB128
+          getDW_OP_ 0x86 = return DW_OP_breg22 <*> getSLEB128
+          getDW_OP_ 0x87 = return DW_OP_breg23 <*> getSLEB128
+          getDW_OP_ 0x88 = return DW_OP_breg24 <*> getSLEB128
+          getDW_OP_ 0x89 = return DW_OP_breg25 <*> getSLEB128
+          getDW_OP_ 0x8a = return DW_OP_breg26 <*> getSLEB128
+          getDW_OP_ 0x8b = return DW_OP_breg27 <*> getSLEB128
+          getDW_OP_ 0x8c = return DW_OP_breg28 <*> getSLEB128
+          getDW_OP_ 0x8d = return DW_OP_breg29 <*> getSLEB128
+          getDW_OP_ 0x8e = return DW_OP_breg30 <*> getSLEB128
+          getDW_OP_ 0x8f = return DW_OP_breg31 <*> getSLEB128
+          getDW_OP_ 0x90 = return DW_OP_regx   <*> getULEB128
+          getDW_OP_ 0x91 = return DW_OP_fbreg  <*> getSLEB128
+          getDW_OP_ 0x92 = return DW_OP_bregx  <*> getULEB128 <*> getSLEB128
+          getDW_OP_ 0x93 = return DW_OP_piece  <*> getULEB128
+          getDW_OP_ 0x94 = return DW_OP_deref_size <*> getWord8
+          getDW_OP_ 0x95 = return DW_OP_xderef_size <*> getWord8
+          getDW_OP_ 0x96 = return DW_OP_nop
+          getDW_OP_ 0x97 = return DW_OP_push_object_address
+          getDW_OP_ 0x98 = return DW_OP_call2 <*> getWord16 dr
+          getDW_OP_ 0x99 = return DW_OP_call4 <*> getWord32 dr
+          getDW_OP_ 0x9a = return DW_OP_call_ref <*> getDwarfTargetAddress dr
+          getDW_OP_ 0x9b = return DW_OP_form_tls_address
+          getDW_OP_ 0x9c = return DW_OP_call_frame_cfa
+          getDW_OP_ 0x9d = return DW_OP_bit_piece <*> getULEB128 <*> getULEB128
+          getDW_OP_ n | 0xe0 <= n && n <= 0xff = fail $ "User DW_OP data requires extension of parser for code " ++ show n
+          getDW_OP_ n = fail $ "Unrecognized DW_OP code " ++ show n
+
+data DW_ATE
+    = DW_ATE_address
+    | DW_ATE_boolean
+    | DW_ATE_complex_float
+    | DW_ATE_float
+    | DW_ATE_signed
+    | DW_ATE_signed_char
+    | DW_ATE_unsigned
+    | DW_ATE_unsigned_char
+    | DW_ATE_imaginary_float
+    | DW_ATE_packed_decimal
+    | DW_ATE_numeric_string
+    | DW_ATE_edited
+    | DW_ATE_signed_fixed
+    | DW_ATE_unsigned_fixed
+    | DW_ATE_decimal_float
+    deriving (Show, Eq)
+dw_ate 0x01 = DW_ATE_address
+dw_ate 0x02 = DW_ATE_boolean
+dw_ate 0x03 = DW_ATE_complex_float
+dw_ate 0x04 = DW_ATE_float
+dw_ate 0x05 = DW_ATE_signed
+dw_ate 0x06 = DW_ATE_signed_char
+dw_ate 0x07 = DW_ATE_unsigned
+dw_ate 0x08 = DW_ATE_unsigned_char
+dw_ate 0x09 = DW_ATE_imaginary_float
+dw_ate 0x0a = DW_ATE_packed_decimal
+dw_ate 0x0b = DW_ATE_numeric_string
+dw_ate 0x0c = DW_ATE_edited
+dw_ate 0x0d = DW_ATE_signed_fixed
+dw_ate 0x0e = DW_ATE_unsigned_fixed
+dw_ate 0x0f = DW_ATE_decimal_float
+dw_ate n = error $ "Unrecognized DW_ATE encoding " ++ show n
+
+data DW_DS
+    = DW_DS_unsigned
+    | DW_DS_leading_overpunch
+    | DW_DS_trailing_overpunch
+    | DW_DS_leading_separate
+    | DW_DS_trailing_separate
+    deriving (Show, Eq)
+dw_ds 0x01 = DW_DS_unsigned
+dw_ds 0x02 = DW_DS_leading_overpunch
+dw_ds 0x03 = DW_DS_trailing_overpunch
+dw_ds 0x04 = DW_DS_leading_separate
+dw_ds 0x05 = DW_DS_trailing_separate
+
+data DW_END
+    = DW_END_default
+    | DW_END_big
+    | DW_END_little
+    deriving (Show, Eq)
+dw_end 0x00 = DW_END_default
+dw_end 0x01 = DW_END_big
+dw_end 0x02 = DW_END_little
+dw_end n = error $ "Unrecognized DW_END value " ++ show n
+
+data DW_ACCESS
+    = DW_ACCESS_public
+    | DW_ACCESS_protected
+    | DW_ACCESS_private
+    deriving (Show, Eq)
+dw_access 0x01 = DW_ACCESS_public
+dw_access 0x02 = DW_ACCESS_protected
+dw_access 0x03 = DW_ACCESS_private
+
+data DW_VIS
+    = DW_VIS_local
+    | DW_VIS_exported
+    | DW_VIS_qualified
+    deriving (Show, Eq)
+dw_vis 0x01 = DW_VIS_local
+dw_vis 0x02 = DW_VIS_exported
+dw_vis 0x03 = DW_VIS_qualified
+
+data DW_VIRTUALITY
+    = DW_VIRTUALITY_none
+    | DW_VIRTUALITY_virtual
+    | DW_VIRTUALITY_pure_virtual
+    deriving (Show, Eq)
+dw_virtuality 0x00 = DW_VIRTUALITY_none
+dw_virtuality 0x01 = DW_VIRTUALITY_virtual
+dw_virtuality 0x02 = DW_VIRTUALITY_pure_virtual
+
+data DW_LANG
+    = DW_LANG_C89
+    | DW_LANG_C
+    | DW_LANG_Ada83
+    | DW_LANG_C_plus_plus
+    | DW_LANG_Cobol74
+    | DW_LANG_Cobol85
+    | DW_LANG_Fortran77
+    | DW_LANG_Fortran90
+    | DW_LANG_Pascal83
+    | DW_LANG_Modula2
+    | DW_LANG_Java
+    | DW_LANG_C99
+    | DW_LANG_Ada95
+    | DW_LANG_Fortran95
+    | DW_LANG_PLI
+    | DW_LANG_ObjC
+    | DW_LANG_ObjC_plus_plus
+    | DW_LANG_UPC
+    | DW_LANG_D
+    deriving (Show, Eq)
+dw_lang 0x0001 = DW_LANG_C89
+dw_lang 0x0002 = DW_LANG_C
+dw_lang 0x0003 = DW_LANG_Ada83
+dw_lang 0x0004 = DW_LANG_C_plus_plus
+dw_lang 0x0005 = DW_LANG_Cobol74
+dw_lang 0x0006 = DW_LANG_Cobol85
+dw_lang 0x0007 = DW_LANG_Fortran77
+dw_lang 0x0008 = DW_LANG_Fortran90
+dw_lang 0x0009 = DW_LANG_Pascal83
+dw_lang 0x000a = DW_LANG_Modula2
+dw_lang 0x000b = DW_LANG_Java
+dw_lang 0x000c = DW_LANG_C99
+dw_lang 0x000d = DW_LANG_Ada95
+dw_lang 0x000e = DW_LANG_Fortran95
+dw_lang 0x000f = DW_LANG_PLI
+dw_lang 0x0010 = DW_LANG_ObjC
+dw_lang 0x0011 = DW_LANG_ObjC_plus_plus
+dw_lang 0x0012 = DW_LANG_UPC
+dw_lang 0x0013 = DW_LANG_D
+dw_lang n = error $ "Unrecognized DW_LANG " ++ show n
+
+data DW_ID
+    = DW_ID_case_sensitive
+    | DW_ID_up_case
+    | DW_ID_down_case
+    | DW_ID_case_insensitive
+    deriving (Show, Eq)
+dw_id 0x00 = DW_ID_case_sensitive
+dw_id 0x01 = DW_ID_up_case
+dw_id 0x02 = DW_ID_down_case
+dw_id 0x03 = DW_ID_case_insensitive
+
+data DW_CC
+    = DW_CC_normal
+    | DW_CC_program
+    | DW_CC_nocall
+    deriving (Show, Eq)
+dw_cc 0x01 = DW_CC_normal
+dw_cc 0x02 = DW_CC_program
+dw_cc 0x03 = DW_CC_nocall
+dw_cc n = error $ "Unrecognized calling convention " ++ show n
+
+data DW_INL
+    = DW_INL_not_inlined
+    | DW_INL_inlined
+    | DW_INL_declared_not_inlined
+    | DW_INL_declared_inlined
+    deriving (Show, Eq)
+dw_inl 0x00 = DW_INL_not_inlined
+dw_inl 0x01 = DW_INL_inlined
+dw_inl 0x02 = DW_INL_declared_not_inlined
+dw_inl 0x03 = DW_INL_declared_inlined
+
+data DW_ORD
+    = DW_ORD_row_major
+    | DW_ORD_col_major
+    deriving (Show, Eq)
+dw_ord 0x00 = DW_ORD_row_major
+dw_ord 0x01 = DW_ORD_col_major
+
+data DW_DSC
+    = DW_DSC_label
+    | DW_DSC_range
+    deriving (Show, Eq)
+dw_dsc 0x00 = DW_DSC_label
+dw_dsc 0x01 = DW_DSC_range
+
diff --git a/tests/Test.hs b/tests/Test.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test.hs
@@ -0,0 +1,12 @@
+module Main where
+
+import Data.Dwarf
+import System.IO
+import Test.HUnit
+import Control.Monad
+import qualified Control.Exception as E
+import qualified Data.ByteString as B
+
+tests = TestList []
+
+main = runTestTT tests
