1 {-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
3 -- -----------------------------------------------------------------------------
6 -- This script processes the assembly produced by LLVM, rearranging the code
7 -- so that an info table appears before its corresponding function.
8 module LlvmMangler ( llvmFixupAsm ) where
10 import Data.ByteString.Char8 ( ByteString )
11 import qualified Data.ByteString.Char8 as BS
16 newSection, oldSection, functionSuf, tableSuf, funDivider, eol :: ByteString
17 newSection = BS.pack "\n.text\n"
18 oldSection = BS.pack "__STRIP,__me"
19 functionSuf = BS.pack "_info:"
20 tableSuf = BS.pack "_info_itable:"
21 funDivider = BS.pack "\n\n"
24 eolPred :: Char -> Bool
27 -- | Read in assembly file and process
28 llvmFixupAsm :: FilePath -> FilePath -> IO ()
29 llvmFixupAsm f1 f2 = do
31 BS.writeFile f2 BS.empty
35 -- | Run over whole assembly file
36 allTables :: FilePath -> ByteString -> IO ()
44 Look for the next function that needs to have its info table
45 arranged to be before it and process it. This will print out
46 any code before this function, then the info table, then the
47 function. It will return the remainder of the assembly code
50 We rely here on the fact that LLVM prints all global variables
51 at the end of the file, so an info table will always appear
54 To try to help explain the string searches, here is some
55 assembly code that would be processed by this program, with
56 split markers placed in it like so, <split marker>:
63 .globl _Main_main_info
64 _Main_main<bl|al>_info:
76 .global _Main_main_entry
78 <bit|itable>_Main_main_entry:
84 oneTable :: FilePath -> ByteString -> IO ByteString
86 let last' xs = if (null xs) then 0 else last xs
89 (bl, al) = BS.breakSubstring functionSuf str
90 start = last' $ BS.findSubstrings funDivider bl
91 (before, fheader) = BS.splitAt start bl
92 (fun, after) = BS.breakSubstring funDivider al
93 label = snd $ BS.breakEnd eolPred bl
96 ilabel = label `BS.append` tableSuf
97 (bit, itable) = BS.breakSubstring ilabel after
98 (itable', ait) = BS.breakSubstring funDivider itable
99 istart = last' $ BS.findSubstrings funDivider bit
100 (bit', iheader) = BS.splitAt istart bit
103 fheader' = replaceSection fheader
104 iheader' = replaceSection iheader
106 function = [before, eol, iheader', itable', eol, fheader', fun, eol]
107 remainder = bit' `BS.append` ait
113 else if BS.null itable
114 then error $ "Function without matching info table! ("
115 ++ (BS.unpack label) ++ ")"
118 mapM_ (BS.appendFile f) function
121 -- | Replace the current section in a function or table header with the
122 -- text section specifier.
123 replaceSection :: ByteString -> ByteString
125 let (s1, s2) = BS.breakSubstring oldSection sec
126 s1' = fst $ BS.breakEnd eolPred s1
127 s2' = snd $ BS.break eolPred s2
128 in s1' `BS.append` newSection `BS.append` s2'