Skip to content

Commit

Permalink
Add decodeHeaderDynsym for decoding dynamic symbol tables
Browse files Browse the repository at this point in the history
Just like `decodeHeaderSymtab` decodes the static function symbol table,
`decodeHeaderDynsym` serves the same role for dynamic function symbol tables.
The functionality of `decodeHeaderDynsym` largely overlaps with the niche that
the `dynamicEntries`/`dynSymEntry` functions provide, so I have included a
comparison to those functions in the Haddocks for `decodeHeaderDynsym`.

This will be useful for eventual fixes for GaloisInc/macaw#277 and
GaloisInc/macaw-loader#12.
  • Loading branch information
RyanGlScott committed Feb 23, 2023
1 parent d6d2654 commit 5712983
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 29 deletions.
3 changes: 3 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
(i.e., in a `.gnu.version_d` section) rather than a version requirement
(i.e., in a `.gnu.version_r` section). The new `dynVersionDefMap` function
can be used to construct the `VersionDefMap`.
* Add a `decodeHeaderDynsym` function that computes the dynamic symbol table
directly from an `ElfHeaderInfo`, much like `decodeHeaderSymtab` computes
the static symbol table.

## 0.32 -- *2018 Sep 17*

Expand Down
75 changes: 53 additions & 22 deletions src/Data/ElfEdit/Prim/HeaderInfo.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module Data.ElfEdit.Prim.HeaderInfo
, Symtab(..)
, symtabSize
, decodeHeaderSymtab
, decodeHeaderDynsym
) where

import Control.Monad
Expand Down Expand Up @@ -251,37 +252,67 @@ data Symtab w =

deriving instance Show (ElfWordType w) => Show (Symtab w)

-- | Decodes the static symbol table using elf header info
-- | Decodes the static symbol table using elf header info.
--
-- If no symbol table is present then @Nothing@ is returned.
decodeHeaderSymtab :: ElfHeaderInfo w -> Maybe (Either SymtabError (Symtab w))
decodeHeaderSymtab elf = elfClassInstances (headerClass (header elf)) $ do
let shdrs = headerShdrs elf
let symtabs = V.filter (\s -> shdrType s == SHT_SYMTAB) shdrs
when (V.length symtabs == 0) $ Nothing
Just $ decodeSymbolTable elf symtabs

-- | Decodes the dynamic symbol table using ELF header info.
--
-- If no dynamic symbol table is present then @Nothing@ is returned.
--
-- The functionality of 'decodeHeaderDynsym' largely overlaps with what the
-- @dynamicEntries@ and @dynSymEntry@ functions provide, but with some minor
-- differences:
--
-- * The API for 'decodeHeaderDynsym' is much more direct, as it only requires
-- an 'ElfHeaderInfo'.
--
-- * Unlike @dynSymEntry@, 'decodeHeaderDynsym' does not compute symbol version
-- information.
decodeHeaderDynsym :: ElfHeaderInfo w -> Maybe (Either SymtabError (Symtab w))
decodeHeaderDynsym elf = elfClassInstances (headerClass (header elf)) $ do
let shdrs = headerShdrs elf
let dynSymtabs = V.filter (\s -> shdrType s == SHT_DYNSYM) shdrs
when (V.length dynSymtabs == 0) $ Nothing
Just $ decodeSymbolTable elf dynSymtabs

-- | Decodes the symbol table from the section headers using the given ELF
-- header info. This assumes the invariant that there is at least one section
-- header of the appropriate type.
decodeSymbolTable :: Integral (ElfWordType w)
=> ElfHeaderInfo w
-> V.Vector (Shdr Word32 (ElfWordType w))
-> Either SymtabError (Symtab w)
decodeSymbolTable elf symtabs = do
let hdr = header elf
let contents = headerFileContents elf
let cl = headerClass hdr
let dta = headerData hdr
let shdrs = headerShdrs elf
let symtabs = V.filter (\s -> shdrType s == SHT_SYMTAB) shdrs
when (V.length symtabs == 0) $ Nothing
Just $ do
when (V.length symtabs > 1) $ Left MultipleSymtabs
let symtabShdr = symtabs V.! 0
unless (isValidFileRange (shdrFileRange symtabShdr) contents) $ do
Left InvalidSymtabFileRange
let symtabBuffer = slice (shdrFileRange symtabShdr) contents
when (shdrLink symtabShdr >= fromIntegral (shdrCount elf)) $ do
Left InvalidSymtabLink
let strtab = shdrs V.! fromIntegral (shdrLink symtabShdr)
unless (isValidFileRange (shdrFileRange strtab) contents) $ do
Left InvalidSymtabFileRange
let strtabBuffer = slice (shdrFileRange strtab) contents
-- Decode the symbol table
v <- decodeSymtab cl dta strtabBuffer symtabBuffer
unless (toInteger (shdrInfo symtabShdr) <= toInteger (V.length v)) $ do
Left InvalidSymtabLocalCount
pure $! Symtab { symtabLocalCount = fromIntegral (shdrInfo symtabShdr)
, symtabEntries = v
}
when (V.length symtabs > 1) $ Left MultipleSymtabs
let symtabShdr = symtabs V.! 0
unless (isValidFileRange (shdrFileRange symtabShdr) contents) $ do
Left InvalidSymtabFileRange
let symtabBuffer = slice (shdrFileRange symtabShdr) contents
when (shdrLink symtabShdr >= fromIntegral (shdrCount elf)) $ do
Left InvalidSymtabLink
let strtab = shdrs V.! fromIntegral (shdrLink symtabShdr)
unless (isValidFileRange (shdrFileRange strtab) contents) $ do
Left InvalidSymtabFileRange
let strtabBuffer = slice (shdrFileRange strtab) contents
-- Decode the symbol table
v <- decodeSymtab cl dta strtabBuffer symtabBuffer
unless (toInteger (shdrInfo symtabShdr) <= toInteger (V.length v)) $ do
Left InvalidSymtabLocalCount
pure $! Symtab { symtabLocalCount = fromIntegral (shdrInfo symtabShdr)
, symtabEntries = v
}

-- | Get size of symbol table
symtabSize :: ElfClass w -> Symtab w -> ElfWordType w
Expand Down
18 changes: 13 additions & 5 deletions src/Data/ElfEdit/Prim/SymbolTable.hs
Original file line number Diff line number Diff line change
Expand Up @@ -301,23 +301,31 @@ data SymtabError
| MultipleSymtabs
-- ^ Multiple symbol tables in binary.
--
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab`
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab` and
-- `Data.ElfEdit.Prim.decodeHeaderDynsym`
| InvalidSymtabFileRange
-- ^ Invalid symbol table link
--
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab`
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab` and
-- `Data.ElfEdit.Prim.decodeHeaderDynsym`

| InvalidSymtabLink
-- ^ Invalid string table file range
--
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab`
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab` and
-- `Data.ElfEdit.Prim.decodeHeaderDynsym`

| InvalidSymtabLocalCount
-- ^ Invalid symbol table local count.
--
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab`
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab` and
-- `Data.ElfEdit.Prim.decodeHeaderDynsym`

| InvalidStrtabFileRange
-- ^ Invalid string table file range
--
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab`
-- Raised in `Data.ElfEdit.Prim.decodeHeaderSymtab` and
-- `Data.ElfEdit.Prim.decodeHeaderDynsym`

instance Show SymtabError where
show (InvalidName idx msg) = "Error parsing symbol " ++ show idx ++ " name: " ++ show msg
Expand Down
24 changes: 22 additions & 2 deletions tests/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ checkStringTableEntry bytes (str, off) = str == bstr
where
bstr = C8.take (B.length str) $ C8.drop (fromIntegral off) bytes

-- | Test that the dynamic symbol table in an ELF binary match the expected
-- results. The @elf-edit@ library provides two different ways to compute the
-- dynamic function symbols, both of which are tested here:
--
-- * The 'Elf.decodeHeaderDynsym' function, which omits symbol version
-- information.
--
-- * The 'Elf.dynamicEntries' and 'Elf.dynSymEntry' functions, which return each
-- dynamic function symbol alongside their version information.
testDynSymTable :: FilePath
-- ^ The path of the ELF file to load.
-> [(B.ByteString, Bool)]
Expand All @@ -121,6 +130,17 @@ testDynSymTable fp expectedSymInfo = do
Elf.ELFCLASS64 <- pure cl
let mach = Elf.headerMachine hdr
Elf.EM_X86_64 <- pure mach

-- Test decodeHeaderDynsym
dynSymtab <- maybe (T.assertFailure "No dynamic symbol table found") pure $
Elf.decodeHeaderDynsym e
syms1 <- either (T.assertFailure . show) (pure . V.toList . Elf.symtabEntries)
dynSymtab
T.assertEqual "Testing decodeHeaderDynsym"
(Elf.steName <$> syms1)
(fst <$> expectedSymInfo)

-- Test dynamicEntries/dynSymEntry
let contents = Elf.headerFileContents e
virtMap <- maybe (T.assertFailure "Overlapping loaded segments") pure $
Elf.virtAddrMap contents ph
Expand All @@ -130,15 +150,15 @@ testDynSymTable fp expectedSymInfo = do

versionDefs <- either (T.assertFailure . show) pure $ Elf.dynVersionDefMap dynSection virtMap
versionReqs <- either (T.assertFailure . show) pure $ Elf.dynVersionReqMap dynSection virtMap
syms <- either (T.assertFailure . show) pure $
syms2 <- either (T.assertFailure . show) pure $
traverse (Elf.dynSymEntry dynSection virtMap versionDefs versionReqs)
[0 .. fromIntegral (length expectedSymInfo - 1)]
let isVer Elf.VersionSpecific{} = True
isVer Elf.VersionLocal = False
isVer Elf.VersionGlobal = False
let symInfo :: (Elf.SymtabEntry B.ByteString u, Elf.VersionTableValue) -> (C8.ByteString, Bool)
symInfo (s,v) = (Elf.steName s, isVer v)
T.assertEqual "Testing relocations" (symInfo <$> syms) expectedSymInfo
T.assertEqual "Testing dynSymEntry" (symInfo <$> syms2) expectedSymInfo

testDynNeeded :: FilePath
-- ^ The path of the ELF file to load.
Expand Down

0 comments on commit 5712983

Please sign in to comment.