1 -- -----------------------------------------------------------------------------
4 -- This script processes the assembly produced by LLVM, rearranging the code
5 -- so that an info table appears before its corresponding function. We also
6 -- use it to fix up the stack alignment, which needs to be 16 byte aligned
7 -- but always ends up off by 4 bytes because GHC sets it to the 'wrong'
8 -- starting value in the RTS.
10 -- We only need this for Mac OS X, other targets don't use it.
13 module LlvmMangler ( llvmFixupAsm ) where
15 #include "HsVersions.h"
17 import Control.Exception
18 import qualified Data.ByteString.Char8 as B
20 import qualified Data.IntMap as I
24 infoSec, newInfoSec, newLine, spInst, jmpInst :: B.ByteString
25 infoSec = B.pack "\t.section\t__STRIP,__me"
26 newInfoSec = B.pack "\n\t.text"
28 jmpInst = B.pack "\n\tjmp"
30 infoLen, labelStart, spFix :: Int
31 infoLen = B.length infoSec
32 labelStart = B.length jmpInst
34 #if x86_64_TARGET_ARCH
35 spInst = B.pack ", %rsp\n"
38 spInst = B.pack ", %esp\n"
43 eolPred, dollarPred, commaPred :: Char -> Bool
45 dollarPred = ((==) '$')
46 commaPred = ((==) ',')
48 -- | Read in assembly file and process
49 llvmFixupAsm :: FilePath -> FilePath -> IO ()
50 llvmFixupAsm f1 f2 = do
51 r <- openBinaryFile f1 ReadMode
52 w <- openBinaryFile f2 WriteMode
54 B.hPut w (B.pack "\n\n")
60 Here we process the assembly file one function and data
61 defenition at a time. When a function is encountered that
62 should have a info table we store it in a map. Otherwise
63 we print it. When an info table is found we retrieve its
64 function from the map and print them both.
66 For all functions we fix up the stack alignment. We also
67 fix up the section defenition for functions and info tables.
69 fixTables :: Handle -> Handle -> I.IntMap B.ByteString -> IO ()
74 else let fun = fixupStack f B.empty
75 (a,b) = B.breakSubstring infoSec fun
76 (x,c) = B.break eolPred b
77 fun' = a `B.append` newInfoSec `B.append` c
78 n = readInt $ B.drop infoLen x
79 (bs, m') | B.null b = ([fun], m)
80 | even n = ([], I.insert n fun' m)
81 | otherwise = case I.lookup (n+1) m of
82 Just xf' -> ([fun',xf'], m)
83 Nothing -> ([fun'], m)
84 in mapM_ (B.hPut w) bs >> fixTables r w m'
86 -- | Read in the next function/data defenition
87 getFun :: Handle -> B.ByteString -> IO B.ByteString
89 l <- (try (B.hGetLine r))::IO (Either IOError B.ByteString)
91 Right l' | B.null l' -> return f
92 | otherwise -> getFun r (f `B.append` newLine `B.append` l')
93 Left _ -> return B.empty
96 Mac OS X requires that the stack be 16 byte aligned when making a function
97 call (only really required though when making a call that will pass through
98 the dynamic linker). The alignment isn't correctly generated by LLVM as
99 LLVM rightly assumes that the stack wil be aligned to 16n + 12 on entry
100 (since the function call was 16 byte aligned and the return address should
101 have been pushed, so sub 4). GHC though since it always uses jumps keeps
102 the stack 16 byte aligned on both function calls and function entry.
104 We correct the alignment here.
106 fixupStack :: B.ByteString -> B.ByteString -> B.ByteString
107 fixupStack f f' | B.null f' =
109 (a, c) = B.breakSubstring spInst f
110 (b, n) = B.breakEnd dollarPred a
111 num = B.pack $ show $ readInt n + spFix
114 else fixupStack c $ f' `B.append` b `B.append` num
118 (a, c) = B.breakSubstring jmpInst f
119 -- we matched on a '\n' so go past it
120 (l', b) = B.break eolPred $ B.tail c
121 l = (B.head c) `B.cons` l'
122 (a', n) = B.breakEnd dollarPred a
123 (n', x) = B.break commaPred n
124 num = B.pack $ show $ readInt n' + spFix
125 -- We need to avoid processing jumps to labels, they are of the form:
126 -- jmp\tL..., jmp\t_f..., jmpl\t_f..., jmpl\t*%eax..., jmpl *L...
127 targ = B.dropWhile ((==)'*') $ B.drop 1 $ B.dropWhile ((/=)'\t') $
131 else if B.head targ == 'L'
132 then fixupStack b $ f' `B.append` a `B.append` l
133 else fixupStack b $ f' `B.append` a' `B.append` num `B.append`
136 -- | read an int or error
137 readInt :: B.ByteString -> Int
138 readInt str | B.all isDigit str = (read . B.unpack) str
139 | otherwise = error $ "LLvmMangler Cannot read" ++ show str
140 ++ "as it's not an Int"