-- This file is part of Bindings-bfd.
--
-- Copyright (C) 2010 Michael Nelson
--
-- Bindings-bfd is free software: you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- Bindings-bfd is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU Lesser General Public License for more details.
-- You should have received a copy of the GNU Lesser General Public License
-- along with Bindings-bfd. If not, see .
module Bindings.Bfd (
-- * Types
Bfd
, Bfd'
, FileMode
-- * Functions
-- ** Initialization
, init
-- ** Files
, getTargetsAndFormats
, open
, close
-- ** Format
, checkFormat
-- ** Foo
, Bindings.Bfd.getByteorder
, Bindings.Bfd.getHeaderByteorder
, getDisasm
, getFilename
, Bindings.Bfd.getFlags
, Bindings.Bfd.getObjectFlags
, Bindings.Bfd.getSectionFlags
, Bindings.Bfd.getFlavour
, getFormat
, getMachine
, getOctetsPerByte
-- *** Sections
, getSectionByName
, getSections
-- *** Target
, getTarget
-- *** Foo
, getMyArchive
-- *** Symbols
, getSymbolTable
, getDynamicSymbolTable
#if 0
, getSyntheticSymbolTable
#endif
-- *** Relocations
, getDynamicRelocations
-- ** Testing
, isBigEndian
, isLittleEndian
, isHeaderBigEndian
, isHeaderLittleEndian
, isCoffFamily
) where
import Control.Exception
import Control.Monad
import Data.Bits
import Data.Maybe
import Data.Word
import Foreign.C
import Foreign.Marshal
import Foreign.Ptr
import Foreign.Storable
import Prelude hiding (catch, init)
import Bindings.Bfd.Disasm
import Bindings.Bfd.Endian
import Bindings.Bfd.Exception
import Bindings.Bfd.Flags as BfdFlags
import Bindings.Bfd.Flavour
import Bindings.Bfd.Format
import Bindings.Bfd.Relocation
import Bindings.Bfd.Section as Section
import Bindings.Bfd.Section.Flags as SectionFlags
import Bindings.Bfd.SymbolTable as SymbolTable
import Bindings.Bfd.Target as Target
#include
type FileMode = String
type Bfd = Ptr Bfd'
data Bfd' = Filename String
| XVec Target
| Format Format
| Flags Int
| Sections Section
| MyArchive Bfd
deriving (Show)
instance Storable Bfd' where
sizeOf _ = #size struct bfd
alignment = sizeOf
peekByteOff buf off
| off == (#offset struct bfd, filename) =
do
val <- (#peek struct bfd, filename) buf :: IO CString
str <- peekCString val
return $ Filename str
| off == (#offset struct bfd, xvec) =
do
val <- (#peek struct bfd, xvec) buf :: IO Word
return $ XVec $ wordPtrToPtr $ fromIntegral val
| off == (#offset struct bfd, format) =
do
val <- (#peek struct bfd, format) buf :: IO CUInt
return $ Format $ toEnum $ fromIntegral val
| off == (#offset struct bfd, flags) =
do
val <- (#peek struct bfd, flags) buf :: IO CUInt
return $ Bindings.Bfd.Flags $ fromIntegral val
| off == (#offset struct bfd, sections) =
do
val <- (#peek struct bfd, sections) buf
return $ Sections val
| off == (#offset struct bfd, my_archive) =
do
val <- (#peek struct bfd, my_archive) buf :: IO Bfd
return $ MyArchive val
| otherwise = error $ "internal error: Bfd.peekByteOff " ++ show off
poke _ _ = return ()
init
:: IO ()
-- ^ Initialize the library. Call 'init' before making any other calls.
init = c_bfd_init
getTargetsAndFormats
:: FilePath -- ^ The file to query
-> IO [(TargetName, Format)]
-- ^ Returns a 'List' of tuples representing the possible combinations of
-- 'TargetName' and 'Format' valid for this 'FilePath'.
--
-- Possible Exceptions: Same as 'open'.
getTargetsAndFormats file =
do
ts <- Target.list
let
perms = [ (t,f) | t <- ts, f <- enumFrom Object ]
foldM g [] perms
where
g xs r@(t,f) =
do
bfd <- open file (Just t) "r"
xvec1 <- getTarget bfd
tn1 <- Target.getName xvec1
ok <- catch (checkFormat bfd f) ((\_ -> return False) :: BfdException -> IO Bool)
case ok of
True ->
do
xvec2 <- getTarget bfd
tn2 <- Target.getName xvec2
if tn1 == tn2
then return $ r : xs
else return xs
False -> return xs
open
:: FilePath -- ^ File to open
-> Maybe TargetName -- ^ Target
-> FileMode
-> IO Bfd
-- ^ Opens a file with the given target and mode
-- and returns a bfd object on success. If the target is 'Nothing' then
-- the file is opened with the default target.
--
-- Possible exceptions: 'NoMemory' (if any allocation fails), 'SystemCall'
-- (if open failed), and 'InvalidTarget' (if supplied target is not supported).
--
-- /Note:/ 'open' does not validate that the supplied 'TargetName' is appropriate
-- for the supplied file.
open fp targ mode =
do
fp' <- newCString fp
targ' <- newCString targ0
mode' <- newCString mode
throwExceptionIfNull "open" fp targ0 (c_bfd_fopen fp' targ' mode' (-1))
where
targ0 = fromMaybe "default" targ
close
:: Bfd
-> IO Bool
close bfd =
do
r <- c_bfd_close bfd
return $ toBool r
checkFormat
:: Bfd
-> Format
-> IO Bool
checkFormat bfd format =
do
res <- c_bfd_check_format bfd format'
throwExceptionIfFalse "checkFormat" (show format) (return $ toBool res)
where
format' = fromIntegral $ fromEnum format
getByteorder
:: Bfd
-> IO Endian
getByteorder bfd =
do
xvec <- getTarget bfd
Target.getByteorder xvec
getHeaderByteorder
:: Bfd
-> IO Endian
getHeaderByteorder bfd =
do
xvec <- getTarget bfd
Target.getHeaderByteorder xvec
getDisasm
:: Bfd
-> IO Disasm
getDisasm = c_disassembler
getFilename
:: Bfd
-> IO String
getFilename bfd =
do
fn <- peekByteOff bfd (#offset struct bfd, filename)
return $ unBfd'Filename fn
getFlags
:: Bfd
-> IO [BfdFlags.Flags]
getFlags bfd =
do
flags <- peekByteOff bfd (#offset struct bfd, flags)
let
flags' = filter f $ enumFrom HasReloc
where
f e = unBfd'Flags flags .&. (bit $ fromEnum e) /= 0
return flags'
getObjectFlags
:: Bfd
-> IO [BfdFlags.Flags]
getObjectFlags bfd =
do
xvec <- getTarget bfd
Target.getObjectFlags xvec
getSectionFlags
:: Bfd
-> IO [SectionFlags.Flags]
getSectionFlags bfd =
do
xvec <- getTarget bfd
Target.getSectionFlags xvec
getFlavour
:: Bfd
-> IO Flavour
getFlavour bfd =
do
xvec <- getTarget bfd
Target.getFlavour xvec
getFormat
:: Bfd
-> IO Format
getFormat bfd =
do
format <- peekByteOff bfd (#offset struct bfd, format)
return $ unBfd'Format format
getMachine
:: Bfd
-> IO Int
getMachine bfd =
do
m <- c_bfd_get_mach bfd
return $ fromIntegral m
getOctetsPerByte
:: Bfd
-> IO Int
getOctetsPerByte bfd =
do
opb <- c_bfd_octets_per_byte bfd
return $ fromIntegral opb
getSectionByName
:: Bfd
-> SectionName
-> IO Section
getSectionByName bfd sn = withCString sn (\s -> c_bfd_get_section_by_name bfd s)
getSections
:: Bfd
-> IO [Section]
getSections bfd =
do
(Sections first) <- peekByteOff bfd (#offset struct bfd, sections)
getSections' first []
where
getSections' sect rs
| sect == nullPtr = return $ reverse rs
| otherwise =
do
next <- getNext sect
getSections' next (sect : rs)
getTarget
:: Bfd
-> IO Target
getTarget bfd =
do
xv <- peekByteOff bfd (#offset struct bfd, xvec)
return $ unBfd'XVec xv
getMyArchive
:: Bfd
-> IO (Maybe Bfd)
getMyArchive bfd =
do
ma <- peekByteOff bfd (#offset struct bfd, my_archive)
return $ case unBfd'MyArchive ma == nullPtr of
True -> Nothing
False -> Just $ unBfd'MyArchive ma
getSymbolTable
:: Bfd
-> IO SymbolTable
getSymbolTable bfd =
do
xvec <- getTarget bfd
bound <- getSymtabUpperBound xvec bfd
let
ptrs = bound `quot` (#const sizeof(struct bfd_symbol *))
pps <- mallocArray ptrs
count <- canonicalizeSymtab xvec bfd pps
return $ SymbolTable.mk pps count
getDynamicSymbolTable
:: Bfd
-> IO SymbolTable
getDynamicSymbolTable bfd =
do
xvec <- getTarget bfd
bound <- getDynamicSymtabUpperBound xvec bfd
let
ptrs = fromIntegral bound `quot` (#const sizeof(struct bfd_symbol *))
pps <- mallocArray ptrs
count <- canonicalizeDynamicSymtab xvec bfd pps
return $ SymbolTable.mk pps count
#if 0
getSyntheticSymbolTable
:: Bfd
-> SymbolTable -- static
-> SymbolTable -- dynamic
-> IO SymbolTable
getSyntheticSymbolTable bfd sst dst =
do
xvec <- getTarget bfd
ssyms <- malloc
count <- getSyntheticSymtab xvec bfd sst dst ssyms
return $ SymbolTable.mk ssyms count
#endif
getDynamicRelocations
:: Bfd
-> SymbolTable
-> IO [Relocation]
getDynamicRelocations bfd st =
do
xvec <- getTarget bfd
bound <- getDynamicRelocUpperBound xvec bfd
let
ptrs = fromIntegral bound `quot` (#const sizeof(arelent *))
ppr <- mallocArray ptrs
count <- canonicalizeDynamicReloc xvec bfd ppr $ tablePtr st
prs <- peekArray count ppr
mapM peek prs
isBigEndian
:: Bfd
-> IO Bool
isBigEndian bfd =
do
bo <- Bindings.Bfd.getByteorder bfd
return $ bo == Big
isLittleEndian
:: Bfd
-> IO Bool
isLittleEndian bfd =
do
bo <- Bindings.Bfd.getByteorder bfd
return $ bo == Little
isHeaderBigEndian
:: Bfd
-> IO Bool
isHeaderBigEndian bfd =
do
bo <- Bindings.Bfd.getHeaderByteorder bfd
return $ bo == Big
isHeaderLittleEndian
:: Bfd
-> IO Bool
isHeaderLittleEndian bfd =
do
bo <- Bindings.Bfd.getHeaderByteorder bfd
return $ bo == Little
isCoffFamily
:: Bfd
-> IO Bool
isCoffFamily bfd =
do
flav <- Bindings.Bfd.getFlavour bfd
return $ flav == Coff || flav == Xcoff
unBfd'Filename
:: Bfd'
-> String
unBfd'Filename (Filename fn) = fn
unBfd'Filename _ = error "unBfd'Filename"
unBfd'XVec
:: Bfd'
-> Target
unBfd'XVec (XVec p) = p
unBfd'XVec _ = error "unBfd'XVec"
unBfd'Format
:: Bfd'
-> Format
unBfd'Format (Format f) = f
unBfd'Format _ = error "unBfd'Format"
unBfd'Flags
:: Bfd'
-> Int
unBfd'Flags (Bindings.Bfd.Flags m) = m
unBfd'Flags _ = error "unBfd'Flags"
unBfd'MyArchive
:: Bfd'
-> Bfd
unBfd'MyArchive (MyArchive ma) = ma
unBfd'MyArchive _ = error "unBfd'MyArchive"
#if 0
createSections
:: Bfd
-> IO [(Section, Vma)]
createSections bfd =
do
sects <- getSections bfd
extSect <- Section.mk "externs" 5
(_, sectList) <- foldM f (0,[]) $ sects ++ [extSect]
return $ reverse sectList
where
f (vma,xs') sect =
do
(vma',snvma) <- createSection sect vma
return (vma', snvma : xs')
demangle
:: Bfd
-> String
-> IO String
demangle bfd str =
do
s <- withCString str (\s -> c_bfd_demangle bfd s 3)
case s == nullPtr of
True -> return ""
False ->
do
s' <- peekCString s
return s'
#endif
foreign import ccall unsafe "bfd.h bfd_init" c_bfd_init
:: IO ()
foreign import ccall unsafe "bfd.h bfd_fopen" c_bfd_fopen
:: CString
-> CString
-> CString
-> CInt
-> IO Bfd
foreign import ccall unsafe "bfd.h bfd_close" c_bfd_close
:: Bfd
-> IO CInt
foreign import ccall unsafe "bfd.h bfd_check_format" c_bfd_check_format
:: Bfd
-> CInt
-> IO CInt
foreign import ccall unsafe "bfd.h bfd_get_mach" c_bfd_get_mach
:: Bfd
-> IO CInt
foreign import ccall unsafe "bfd.h bfd_octets_per_byte" c_bfd_octets_per_byte
:: Bfd
-> IO CUInt
foreign import ccall unsafe "bfd.h bfd_get_section_by_name" c_bfd_get_section_by_name
:: Bfd
-> CString
-> IO Section
#if 0
foreign import ccall unsafe "bfd.h bfd_demangle" c_bfd_demangle
:: Bfd
-> CString
-> CInt
-> IO CString
#endif
foreign import ccall unsafe "dis-asm.h disassembler" c_disassembler
:: Bfd
-> IO Disasm