From e9eec710d8fe76dacb330dc507e948a902ddd544 Mon Sep 17 00:00:00 2001 From: Ryan Scott Date: Fri, 26 Jul 2024 09:24:52 -0400 Subject: [PATCH] Implement macaw-loader-riscv Fixes #13. --- .github/workflows/ci.yaml | 6 + .gitmodules | 12 ++ cabal.project.dist | 6 + macaw-loader-riscv/ChangeLog.md | 5 + macaw-loader-riscv/LICENSE | 30 +++++ macaw-loader-riscv/Setup.hs | 2 + macaw-loader-riscv/macaw-loader-riscv.cabal | 29 ++++ .../src/Data/Macaw/BinaryLoader/RISCV.hs | 125 ++++++++++++++++++ submodules/bv-sized | 1 + submodules/bv-sized-float | 1 + submodules/grift | 1 + submodules/softfloat-hs | 1 + 12 files changed, 219 insertions(+) create mode 100644 macaw-loader-riscv/ChangeLog.md create mode 100644 macaw-loader-riscv/LICENSE create mode 100644 macaw-loader-riscv/Setup.hs create mode 100644 macaw-loader-riscv/macaw-loader-riscv.cabal create mode 100644 macaw-loader-riscv/src/Data/Macaw/BinaryLoader/RISCV.hs create mode 160000 submodules/bv-sized create mode 160000 submodules/bv-sized-float create mode 160000 submodules/grift create mode 160000 submodules/softfloat-hs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a7933dd..a1f45dc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -26,6 +26,12 @@ jobs: with: ghc-version: ${{ matrix.ghc-ver }} cabal-version: ${{ matrix.cabal }} + - name: System Dependencies + run: | + # Softfloat repo needs to be recursively cloned + cd submodules/softfloat-hs + git submodule update --init --recursive + cd ../.. - uses: actions/cache/restore@v3 name: Restore cabal store cache with: diff --git a/.gitmodules b/.gitmodules index a5e5ac3..e6b9ce8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,15 @@ [submodule "submodules/llvm-pretty-bc-parser"] path = submodules/llvm-pretty-bc-parser url = https://github.com/GaloisInc/llvm-pretty-bc-parser +[submodule "submodules/grift"] + path = submodules/grift + url = https://github.com/GaloisInc/grift.git +[submodule "submodules/bv-sized"] + path = submodules/bv-sized + url = https://github.com/GaloisInc/bv-sized.git +[submodule "submodules/bv-sized-float"] + path = submodules/bv-sized-float + url = https://github.com/GaloisInc/bv-sized-float.git +[submodule "submodules/softfloat-hs"] + path = submodules/softfloat-hs + url = https://github.com/GaloisInc/softfloat-hs.git diff --git a/cabal.project.dist b/cabal.project.dist index 841359a..9325507 100644 --- a/cabal.project.dist +++ b/cabal.project.dist @@ -3,6 +3,7 @@ packages: macaw-loader-x86 macaw-loader-ppc macaw-loader-aarch32 + macaw-loader-riscv submodules/elf-edit submodules/dwarf submodules/flexdis86 @@ -24,9 +25,14 @@ packages: submodules/macaw/x86 submodules/macaw/macaw-ppc submodules/macaw/macaw-aarch32 + submodules/macaw/macaw-riscv submodules/macaw/macaw-semmc submodules/crucible/crucible submodules/crucible/crucible-llvm submodules/crucible/crucible-symio submodules/llvm-pretty submodules/llvm-pretty-bc-parser + submodules/grift/grift/ + submodules/bv-sized/ + submodules/bv-sized-float/ + submodules/softfloat-hs/ diff --git a/macaw-loader-riscv/ChangeLog.md b/macaw-loader-riscv/ChangeLog.md new file mode 100644 index 0000000..4558de7 --- /dev/null +++ b/macaw-loader-riscv/ChangeLog.md @@ -0,0 +1,5 @@ +# Revision history for macaw-loader-riscv + +## 0.1.0.0 -- YYYY-mm-dd + +* First version. Released on an unsuspecting world. diff --git a/macaw-loader-riscv/LICENSE b/macaw-loader-riscv/LICENSE new file mode 100644 index 0000000..4fe92f4 --- /dev/null +++ b/macaw-loader-riscv/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2024, Galois, Inc. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of Galois, Inc. nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT +OWNER 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/macaw-loader-riscv/Setup.hs b/macaw-loader-riscv/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/macaw-loader-riscv/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain diff --git a/macaw-loader-riscv/macaw-loader-riscv.cabal b/macaw-loader-riscv/macaw-loader-riscv.cabal new file mode 100644 index 0000000..7e78f5f --- /dev/null +++ b/macaw-loader-riscv/macaw-loader-riscv.cabal @@ -0,0 +1,29 @@ +name: macaw-loader-riscv +version: 0.1.0.0 +synopsis: Library to load RISC-V binary images and get macaw Memory and entry points. +-- description: +homepage: https://github.com/GaloisInc/macaw-loader/macaw-loader-riscv +license: BSD3 +license-file: LICENSE +author: Galois, Inc. +maintainer: rscott@galois.com +copyright: 2024, Galois Inc. +category: Development +build-type: Simple +extra-source-files: ChangeLog.md +cabal-version: >=1.10 + +library + exposed-modules: Data.Macaw.BinaryLoader.RISCV + build-depends: base >=4.9 && <5, + bytestring, + containers, + elf-edit >= 0.39, + exceptions, + grift, + macaw-base, + macaw-loader, + macaw-riscv + hs-source-dirs: src + default-language: Haskell2010 + ghc-options: -Wall -Wcompat diff --git a/macaw-loader-riscv/src/Data/Macaw/BinaryLoader/RISCV.hs b/macaw-loader-riscv/src/Data/Macaw/BinaryLoader/RISCV.hs new file mode 100644 index 0000000..f069615 --- /dev/null +++ b/macaw-loader-riscv/src/Data/Macaw/BinaryLoader/RISCV.hs @@ -0,0 +1,125 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE ExistentialQuantification #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE TypeOperators #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -Wno-orphans #-} +module Data.Macaw.BinaryLoader.RISCV ( + RISCVLoadException(..) + ) where + +import qualified Control.Monad.Catch as X +import qualified Data.ByteString as BS +import qualified Data.ElfEdit as EE +import qualified Data.Foldable as F +import qualified Data.List.NonEmpty as DLN +import qualified Data.Macaw.BinaryLoader as MBL +import qualified Data.Macaw.BinaryLoader.ELF as BLE +import qualified Data.Macaw.Memory as MM +import qualified Data.Macaw.Memory.ElfLoader as EL +import qualified Data.Macaw.Memory.LoadCommon as LC +import qualified Data.Map.Strict as Map +import Data.Maybe ( fromMaybe, mapMaybe ) +import qualified GRIFT.Types as G + +import qualified Data.Macaw.RISCV as MR + +data RISCVElfData w = + RISCVElfData { elf :: EE.ElfHeaderInfo w + , memSymbols :: [EL.MemSymbol w] + , symbolIndex :: Map.Map (MM.MemAddr w) BS.ByteString + } + +instance (w ~ G.RVWidth rv, MR.RISCVConstraints rv) + => MBL.BinaryLoader (MR.RISCV rv) (EE.ElfHeaderInfo w) where + type ArchBinaryData (MR.RISCV rv) (EE.ElfHeaderInfo w) = () + type BinaryFormatData (MR.RISCV rv) (EE.ElfHeaderInfo w) = RISCVElfData w + type Diagnostic (MR.RISCV rv) (EE.ElfHeaderInfo w) = EL.MemLoadWarning + loadBinary = loadRiscvBinary + entryPoints = riscvEntryPoints + symbolFor = riscvLookupSymbol + +riscvLookupSymbol :: (X.MonadThrow m) + => MBL.LoadedBinary (MR.RISCV rv) (EE.ElfHeaderInfo (G.RVWidth rv)) + -> MM.MemAddr (G.RVWidth rv) + -> m BS.ByteString +riscvLookupSymbol loadedBinary addr = + case Map.lookup addr (symbolIndex (MBL.binaryFormatData loadedBinary)) of + Just sym -> return sym + Nothing -> X.throwM (MissingSymbolFor addr) + +riscvEntryPoints :: forall m rv + . (X.MonadThrow m, MR.RISCVConstraints rv) + => MBL.LoadedBinary (MR.RISCV rv) (EE.ElfHeaderInfo (G.RVWidth rv)) + -> m (DLN.NonEmpty (MM.MemSegmentOff (G.RVWidth rv))) +riscvEntryPoints loadedBinary = + EE.elfClassInstances elfClass $ + let addr = MM.memWord (offset + fromIntegral (EE.headerEntry (EE.header (elf (MBL.binaryFormatData loadedBinary))))) in + let symbols = [ MM.memWord (offset + fromIntegral (EE.steValue entry)) + | entry <- staticSyms ++ dynSyms + , EE.steType entry == EE.STT_FUNC + ] in + case BLE.resolveAbsoluteAddress mem addr of + Nothing -> X.throwM (InvalidEntryPoint addr) + Just entryPoint -> + return (entryPoint DLN.:| mapMaybe (BLE.resolveAbsoluteAddress mem) symbols) + where + offset = fromMaybe 0 (LC.loadOffset (MBL.loadOptions loadedBinary)) + mem = MBL.memoryImage loadedBinary + elfData = elf (MBL.binaryFormatData loadedBinary) + elfHeader = EE.header elfData + elfClass = EE.headerClass elfHeader + staticSyms = symtabEntriesList $ EE.decodeHeaderSymtab elfData + dynSyms = symtabEntriesList $ EE.decodeHeaderDynsym elfData + + symtabEntriesList :: Maybe (Either EE.SymtabError (EE.Symtab (G.RVWidth rv))) + -> [EE.SymtabEntry BS.ByteString (EE.ElfWordType (G.RVWidth rv))] + symtabEntriesList symtab = + case symtab of + Nothing -> [] + Just (Left _) -> [] + Just (Right st) -> F.toList (EE.symtabEntries st) + +loadRiscvBinary :: forall m rv + . (X.MonadThrow m) + => LC.LoadOptions + -> EE.ElfHeaderInfo (G.RVWidth rv) + -> m (MBL.LoadedBinary (MR.RISCV rv) (EE.ElfHeaderInfo (G.RVWidth rv))) +loadRiscvBinary lopts e = + case EL.memoryForElf lopts e of + Left err -> X.throwM (RISCVElfLoadError err) + Right (mem, symbols, warnings, _) -> + return MBL.LoadedBinary { MBL.memoryImage = mem + , MBL.memoryEndianness = MM.LittleEndian + , MBL.archBinaryData = () + , MBL.binaryFormatData = + RISCVElfData { elf = e + , memSymbols = symbols + , symbolIndex = indexSymbols symbols + } + , MBL.loadDiagnostics = warnings + , MBL.binaryRepr = + case EE.headerClass (EE.header e) of + EE.ELFCLASS32 -> MBL.Elf32Repr + EE.ELFCLASS64 -> MBL.Elf64Repr + , MBL.originalBinary = e + , MBL.loadOptions = lopts + } + +indexSymbols :: [EL.MemSymbol w] -> Map.Map (MM.MemAddr w) BS.ByteString +indexSymbols = F.foldl' doIndex Map.empty + where + doIndex m memSym = + Map.insert (MM.segoffAddr (EL.memSymbolStart memSym)) (EL.memSymbolName memSym) m + +data RISCVLoadException = RISCVElfLoadError String + | forall w. InvalidEntryPoint (MM.MemWord w) + | forall w. MissingSymbolFor (MM.MemAddr w) + +deriving instance Show RISCVLoadException + +instance X.Exception RISCVLoadException diff --git a/submodules/bv-sized b/submodules/bv-sized new file mode 160000 index 0000000..8546010 --- /dev/null +++ b/submodules/bv-sized @@ -0,0 +1 @@ +Subproject commit 8546010a7bdb8c0281302310c73ed64b3bb39a92 diff --git a/submodules/bv-sized-float b/submodules/bv-sized-float new file mode 160000 index 0000000..69ebb2a --- /dev/null +++ b/submodules/bv-sized-float @@ -0,0 +1 @@ +Subproject commit 69ebb2ab3c67b9711aa74dc1dc62b2fe799783f3 diff --git a/submodules/grift b/submodules/grift new file mode 160000 index 0000000..cc3efa0 --- /dev/null +++ b/submodules/grift @@ -0,0 +1 @@ +Subproject commit cc3efa0fba9ab67bdea4cb1e18356300bbea93fe diff --git a/submodules/softfloat-hs b/submodules/softfloat-hs new file mode 160000 index 0000000..a74bb91 --- /dev/null +++ b/submodules/softfloat-hs @@ -0,0 +1 @@ +Subproject commit a74bb916c48322895d5e009c170a9bb632c47173