8647253c26513c5288e6e089fa8b2cb1bd8c62af
[ghc.git] / compiler / nativeGen / Dwarf / Types.hs
1 module Dwarf.Types
2 ( -- * Dwarf information
3 DwarfInfo(..)
4 , pprDwarfInfo
5 , pprAbbrevDecls
6 -- * Dwarf address range table
7 , DwarfARange(..)
8 , pprDwarfARanges
9 -- * Dwarf frame
10 , DwarfFrame(..), DwarfFrameProc(..), DwarfFrameBlock(..)
11 , pprDwarfFrame
12 -- * Utilities
13 , pprByte
14 , pprHalf
15 , pprData4'
16 , pprDwWord
17 , pprWord
18 , pprLEBWord
19 , pprLEBInt
20 , wordAlign
21 , sectionOffset
22 )
23 where
24
25 import Debug
26 import CLabel
27 import CmmExpr ( GlobalReg(..) )
28 import Encoding
29 import FastString
30 import Outputable
31 import Platform
32 import Unique
33 import Reg
34
35 import Dwarf.Constants
36
37 import Data.Bits
38 import Data.List ( mapAccumL )
39 import qualified Data.Map as Map
40 import Data.Word
41 import Data.Char
42
43 import CodeGen.Platform
44
45 -- | Individual dwarf records. Each one will be encoded as an entry in
46 -- the @.debug_info@ section.
47 data DwarfInfo
48 = DwarfCompileUnit { dwChildren :: [DwarfInfo]
49 , dwName :: String
50 , dwProducer :: String
51 , dwCompDir :: String
52 , dwLowLabel :: CLabel
53 , dwHighLabel :: CLabel
54 , dwLineLabel :: LitString }
55 | DwarfSubprogram { dwChildren :: [DwarfInfo]
56 , dwName :: String
57 , dwLabel :: CLabel }
58 | DwarfBlock { dwChildren :: [DwarfInfo]
59 , dwLabel :: CLabel
60 , dwMarker :: CLabel }
61
62 -- | Abbreviation codes used for encoding above records in the
63 -- @.debug_info@ section.
64 data DwarfAbbrev
65 = DwAbbrNull -- ^ Pseudo, used for marking the end of lists
66 | DwAbbrCompileUnit
67 | DwAbbrSubprogram
68 | DwAbbrBlock
69 deriving (Eq, Enum)
70
71 -- | Generate assembly for the given abbreviation code
72 pprAbbrev :: DwarfAbbrev -> SDoc
73 pprAbbrev = pprLEBWord . fromIntegral . fromEnum
74
75 -- | Abbreviation declaration. This explains the binary encoding we
76 -- use for representing 'DwarfInfo'. Be aware that this must be updated
77 -- along with 'pprDwarfInfo'.
78 pprAbbrevDecls :: Bool -> SDoc
79 pprAbbrevDecls haveDebugLine =
80 let mkAbbrev abbr tag chld flds =
81 let fld (tag, form) = pprLEBWord tag $$ pprLEBWord form
82 in pprAbbrev abbr $$ pprLEBWord tag $$ pprByte chld $$
83 vcat (map fld flds) $$ pprByte 0 $$ pprByte 0
84 in dwarfAbbrevSection $$
85 ptext dwarfAbbrevLabel <> colon $$
86 mkAbbrev DwAbbrCompileUnit dW_TAG_compile_unit dW_CHILDREN_yes
87 ([(dW_AT_name, dW_FORM_string)
88 , (dW_AT_producer, dW_FORM_string)
89 , (dW_AT_language, dW_FORM_data4)
90 , (dW_AT_comp_dir, dW_FORM_string)
91 , (dW_AT_use_UTF8, dW_FORM_flag_present) -- not represented in body
92 , (dW_AT_low_pc, dW_FORM_addr)
93 , (dW_AT_high_pc, dW_FORM_addr)
94 ] ++
95 (if haveDebugLine
96 then [ (dW_AT_stmt_list, dW_FORM_data4) ]
97 else [])) $$
98 mkAbbrev DwAbbrSubprogram dW_TAG_subprogram dW_CHILDREN_yes
99 [ (dW_AT_name, dW_FORM_string)
100 , (dW_AT_MIPS_linkage_name, dW_FORM_string)
101 , (dW_AT_external, dW_FORM_flag)
102 , (dW_AT_low_pc, dW_FORM_addr)
103 , (dW_AT_high_pc, dW_FORM_addr)
104 , (dW_AT_frame_base, dW_FORM_block1)
105 ] $$
106 mkAbbrev DwAbbrBlock dW_TAG_lexical_block dW_CHILDREN_yes
107 [ (dW_AT_name, dW_FORM_string)
108 , (dW_AT_low_pc, dW_FORM_addr)
109 , (dW_AT_high_pc, dW_FORM_addr)
110 ] $$
111 pprByte 0
112
113 -- | Generate assembly for DWARF data
114 pprDwarfInfo :: Bool -> DwarfInfo -> SDoc
115 pprDwarfInfo haveSrc d
116 = pprDwarfInfoOpen haveSrc d $$
117 vcat (map (pprDwarfInfo haveSrc) (dwChildren d)) $$
118 pprDwarfInfoClose
119
120 -- | Prints assembler data corresponding to DWARF info records. Note
121 -- that the binary format of this is paramterized in @abbrevDecls@ and
122 -- has to be kept in synch.
123 pprDwarfInfoOpen :: Bool -> DwarfInfo -> SDoc
124 pprDwarfInfoOpen haveSrc (DwarfCompileUnit _ name producer compDir lowLabel
125 highLabel lineLbl) =
126 pprAbbrev DwAbbrCompileUnit
127 $$ pprString name
128 $$ pprString producer
129 $$ pprData4 dW_LANG_Haskell
130 $$ pprString compDir
131 $$ pprWord (ppr lowLabel)
132 $$ pprWord (ppr highLabel)
133 $$ if haveSrc
134 then sectionOffset (ptext lineLbl) (ptext dwarfLineLabel)
135 else empty
136 pprDwarfInfoOpen _ (DwarfSubprogram _ name label) = sdocWithDynFlags $ \df ->
137 pprAbbrev DwAbbrSubprogram
138 $$ pprString name
139 $$ pprString (renderWithStyle df (ppr label) (mkCodeStyle CStyle))
140 $$ pprFlag (externallyVisibleCLabel label)
141 $$ pprWord (ppr label)
142 $$ pprWord (ppr $ mkAsmTempEndLabel label)
143 $$ pprByte 1
144 $$ pprByte dW_OP_call_frame_cfa
145 pprDwarfInfoOpen _ (DwarfBlock _ label marker) = sdocWithDynFlags $ \df ->
146 pprAbbrev DwAbbrBlock
147 $$ pprString (renderWithStyle df (ppr label) (mkCodeStyle CStyle))
148 $$ pprWord (ppr marker)
149 $$ pprWord (ppr $ mkAsmTempEndLabel marker)
150
151 -- | Close a DWARF info record with children
152 pprDwarfInfoClose :: SDoc
153 pprDwarfInfoClose = pprAbbrev DwAbbrNull
154
155 -- | A DWARF address range. This is used by the debugger to quickly locate
156 -- which compilation unit a given address belongs to. This type assumes
157 -- a non-segmented address-space.
158 data DwarfARange
159 = DwarfARange
160 { dwArngStartLabel :: CLabel
161 , dwArngEndLabel :: CLabel
162 }
163
164 -- | Print assembler directives corresponding to a DWARF @.debug_aranges@
165 -- address table entry.
166 pprDwarfARanges :: [DwarfARange] -> Unique -> SDoc
167 pprDwarfARanges arngs unitU = sdocWithPlatform $ \plat ->
168 let wordSize = platformWordSize plat
169 paddingSize = 4 :: Int
170 -- header is 12 bytes long.
171 -- entry is 8 bytes (32-bit platform) or 16 bytes (64-bit platform).
172 -- pad such that first entry begins at multiple of entry size.
173 pad n = vcat $ replicate n $ pprByte 0
174 initialLength = 8 + paddingSize + 2*2*wordSize
175 in pprDwWord (ppr initialLength)
176 $$ pprHalf 2
177 $$ sectionOffset (ppr $ mkAsmTempLabel $ unitU)
178 (ptext dwarfInfoLabel)
179 $$ pprByte (fromIntegral wordSize)
180 $$ pprByte 0
181 $$ pad paddingSize
182 -- body
183 $$ vcat (map pprDwarfARange arngs)
184 -- terminus
185 $$ pprWord (char '0')
186 $$ pprWord (char '0')
187
188 pprDwarfARange :: DwarfARange -> SDoc
189 pprDwarfARange arng = pprWord (ppr $ dwArngStartLabel arng) $$ pprWord length
190 where
191 length = ppr (dwArngEndLabel arng)
192 <> char '-' <> ppr (dwArngStartLabel arng)
193
194 -- | Information about unwind instructions for a procedure. This
195 -- corresponds to a "Common Information Entry" (CIE) in DWARF.
196 data DwarfFrame
197 = DwarfFrame
198 { dwCieLabel :: CLabel
199 , dwCieInit :: UnwindTable
200 , dwCieProcs :: [DwarfFrameProc]
201 }
202
203 -- | Unwind instructions for an individual procedure. Corresponds to a
204 -- "Frame Description Entry" (FDE) in DWARF.
205 data DwarfFrameProc
206 = DwarfFrameProc
207 { dwFdeProc :: CLabel
208 , dwFdeHasInfo :: Bool
209 , dwFdeBlocks :: [DwarfFrameBlock]
210 -- ^ List of blocks. Order must match asm!
211 }
212
213 -- | Unwind instructions for a block. Will become part of the
214 -- containing FDE.
215 data DwarfFrameBlock
216 = DwarfFrameBlock
217 { dwFdeBlock :: CLabel
218 , dwFdeBlkHasInfo :: Bool
219 , dwFdeUnwind :: UnwindTable
220 }
221
222 -- | Header for the @.debug_frame@ section. Here we emit the "Common
223 -- Information Entry" record that etablishes general call frame
224 -- parameters and the default stack layout.
225 pprDwarfFrame :: DwarfFrame -> SDoc
226 pprDwarfFrame DwarfFrame{dwCieLabel=cieLabel,dwCieInit=cieInit,dwCieProcs=procs}
227 = sdocWithPlatform $ \plat ->
228 let cieStartLabel= mkAsmTempDerivedLabel cieLabel (fsLit "_start")
229 cieEndLabel = mkAsmTempEndLabel cieLabel
230 length = ppr cieEndLabel <> char '-' <> ppr cieStartLabel
231 spReg = dwarfGlobalRegNo plat Sp
232 retReg = dwarfReturnRegNo plat
233 wordSize = platformWordSize plat
234 pprInit (g, uw) = pprSetUnwind plat g (Nothing, uw)
235
236 -- Preserve C stack pointer: This necessary to override that default
237 -- unwinding behavior of setting $sp = CFA.
238 preserveSp = case platformArch plat of
239 ArchX86 -> pprByte dW_CFA_same_value $$ pprLEBWord 4
240 ArchX86_64 -> pprByte dW_CFA_same_value $$ pprLEBWord 7
241 _ -> empty
242 in vcat [ ppr cieLabel <> colon
243 , pprData4' length -- Length of CIE
244 , ppr cieStartLabel <> colon
245 , pprData4' (ptext (sLit "-1"))
246 -- Common Information Entry marker (-1 = 0xf..f)
247 , pprByte 3 -- CIE version (we require DWARF 3)
248 , pprByte 0 -- Augmentation (none)
249 , pprByte 1 -- Code offset multiplicator
250 , pprByte (128-fromIntegral wordSize)
251 -- Data offset multiplicator
252 -- (stacks grow down => "-w" in signed LEB128)
253 , pprByte retReg -- virtual register holding return address
254 ] $$
255 -- Initial unwind table
256 vcat (map pprInit $ Map.toList cieInit) $$
257 vcat [ -- RET = *CFA
258 pprByte (dW_CFA_offset+retReg)
259 , pprByte 0
260
261 -- Preserve C stack pointer
262 , preserveSp
263
264 -- Sp' = CFA
265 -- (we need to set this manually as our (STG) Sp register is
266 -- often not the architecture's default stack register)
267 , pprByte dW_CFA_val_offset
268 , pprLEBWord (fromIntegral spReg)
269 , pprLEBWord 0
270 ] $$
271 wordAlign $$
272 ppr cieEndLabel <> colon $$
273 -- Procedure unwind tables
274 vcat (map (pprFrameProc cieLabel cieInit) procs)
275
276 -- | Writes a "Frame Description Entry" for a procedure. This consists
277 -- mainly of referencing the CIE and writing state machine
278 -- instructions to describe how the frame base (CFA) changes.
279 pprFrameProc :: CLabel -> UnwindTable -> DwarfFrameProc -> SDoc
280 pprFrameProc frameLbl initUw (DwarfFrameProc procLbl hasInfo blocks)
281 = let fdeLabel = mkAsmTempDerivedLabel procLbl (fsLit "_fde")
282 fdeEndLabel = mkAsmTempDerivedLabel procLbl (fsLit "_fde_end")
283 procEnd = mkAsmTempEndLabel procLbl
284 ifInfo str = if hasInfo then text str else empty
285 -- see [Note: Info Offset]
286 in vcat [ pprData4' (ppr fdeEndLabel <> char '-' <> ppr fdeLabel)
287 , ppr fdeLabel <> colon
288 , pprData4' (ppr frameLbl <> char '-' <>
289 ptext dwarfFrameLabel) -- Reference to CIE
290 , pprWord (ppr procLbl <> ifInfo "-1") -- Code pointer
291 , pprWord (ppr procEnd <> char '-' <>
292 ppr procLbl <> ifInfo "+1") -- Block byte length
293 ] $$
294 vcat (snd $ mapAccumL pprFrameBlock initUw blocks) $$
295 wordAlign $$
296 ppr fdeEndLabel <> colon
297
298 -- | Generates unwind information for a block. We only generate
299 -- instructions where unwind information actually changes. This small
300 -- optimisations saves a lot of space, as subsequent blocks often have
301 -- the same unwind information.
302 pprFrameBlock :: UnwindTable -> DwarfFrameBlock -> (UnwindTable, SDoc)
303 pprFrameBlock oldUws (DwarfFrameBlock blockLbl hasInfo uws)
304 | uws == oldUws
305 = (oldUws, empty)
306 | otherwise
307 = (,) uws $ sdocWithPlatform $ \plat ->
308 let lbl = ppr blockLbl <> if hasInfo then text "-1" else empty
309 -- see [Note: Info Offset]
310 isChanged g v | old == Just v = Nothing
311 | otherwise = Just (old, v)
312 where old = Map.lookup g oldUws
313 changed = Map.toList $ Map.mapMaybeWithKey isChanged uws
314 died = Map.toList $ Map.difference oldUws uws
315 in pprByte dW_CFA_set_loc $$ pprWord lbl $$
316 vcat (map (uncurry $ pprSetUnwind plat) changed) $$
317 vcat (map (pprUndefUnwind plat . fst) died)
318
319 -- Note [Info Offset]
320 --
321 -- GDB was pretty much written with C-like programs in mind, and as a
322 -- result they assume that once you have a return address, it is a
323 -- good idea to look at (PC-1) to unwind further - as that's where the
324 -- "call" instruction is supposed to be.
325 --
326 -- Now on one hand, code generated by GHC looks nothing like what GDB
327 -- expects, and in fact going up from a return pointer is guaranteed
328 -- to land us inside an info table! On the other hand, that actually
329 -- gives us some wiggle room, as we expect IP to never *actually* end
330 -- up inside the info table, so we can "cheat" by putting whatever GDB
331 -- expects to see there. This is probably pretty safe, as GDB cannot
332 -- assume (PC-1) to be a valid code pointer in the first place - and I
333 -- have seen no code trying to correct this.
334 --
335 -- Note that this will not prevent GDB from failing to look-up the
336 -- correct function name for the frame, as that uses the symbol table,
337 -- which we can not manipulate as easily.
338
339 -- | Get DWARF register ID for a given GlobalReg
340 dwarfGlobalRegNo :: Platform -> GlobalReg -> Word8
341 dwarfGlobalRegNo p UnwindReturnReg = dwarfReturnRegNo p
342 dwarfGlobalRegNo p reg = maybe 0 (dwarfRegNo p . RegReal) $ globalRegMaybe p reg
343
344 -- | Generate code for setting the unwind information for a register,
345 -- optimized using its known old value in the table. Note that "Sp" is
346 -- special: We see it as synonym for the CFA.
347 pprSetUnwind :: Platform -> GlobalReg -> (Maybe UnwindExpr, UnwindExpr) -> SDoc
348 pprSetUnwind _ Sp (Just (UwReg s _), UwReg s' o') | s == s'
349 = if o' >= 0
350 then pprByte dW_CFA_def_cfa_offset $$ pprLEBWord (fromIntegral o')
351 else pprByte dW_CFA_def_cfa_offset_sf $$ pprLEBInt o'
352 pprSetUnwind plat Sp (_, UwReg s' o')
353 = if o' >= 0
354 then pprByte dW_CFA_def_cfa $$
355 pprLEBWord (fromIntegral $ dwarfGlobalRegNo plat s') $$
356 pprLEBWord (fromIntegral o')
357 else pprByte dW_CFA_def_cfa_sf $$
358 pprLEBWord (fromIntegral $ dwarfGlobalRegNo plat s') $$
359 pprLEBInt o'
360 pprSetUnwind _ Sp (_, uw)
361 = pprByte dW_CFA_def_cfa_expression $$ pprUnwindExpr False uw
362 pprSetUnwind plat g (_, UwDeref (UwReg Sp o))
363 | o < 0 && ((-o) `mod` platformWordSize plat) == 0 -- expected case
364 = pprByte (dW_CFA_offset + dwarfGlobalRegNo plat g) $$
365 pprLEBWord (fromIntegral ((-o) `div` platformWordSize plat))
366 | otherwise
367 = pprByte dW_CFA_offset_extended_sf $$
368 pprLEBWord (fromIntegral (dwarfGlobalRegNo plat g)) $$
369 pprLEBInt o
370 pprSetUnwind plat g (_, UwDeref uw)
371 = pprByte dW_CFA_expression $$
372 pprLEBWord (fromIntegral (dwarfGlobalRegNo plat g)) $$
373 pprUnwindExpr True uw
374 pprSetUnwind plat g (_, uw)
375 = pprByte dW_CFA_val_expression $$
376 pprLEBWord (fromIntegral (dwarfGlobalRegNo plat g)) $$
377 pprUnwindExpr True uw
378
379 -- | Generates a DWARF expression for the given unwind expression. If
380 -- @spIsCFA@ is true, we see @Sp@ as the frame base CFA where it gets
381 -- mentioned.
382 pprUnwindExpr :: Bool -> UnwindExpr -> SDoc
383 pprUnwindExpr spIsCFA expr
384 = sdocWithPlatform $ \plat ->
385 let pprE (UwConst i)
386 | i >= 0 && i < 32 = pprByte (dW_OP_lit0 + fromIntegral i)
387 | otherwise = pprByte dW_OP_consts $$ pprLEBInt i -- lazy...
388 pprE (UwReg Sp i) | spIsCFA
389 = if i == 0
390 then pprByte dW_OP_call_frame_cfa
391 else ppr (UwPlus (UwReg Sp 0) (UwConst i))
392 pprE (UwReg g i) = pprByte (dW_OP_breg0+dwarfGlobalRegNo plat g) $$
393 pprLEBInt i
394 pprE (UwDeref u) = pprE u $$ pprByte dW_OP_deref
395 pprE (UwPlus u1 u2) = pprE u1 $$ pprE u2 $$ pprByte dW_OP_plus
396 pprE (UwMinus u1 u2) = pprE u1 $$ pprE u2 $$ pprByte dW_OP_minus
397 pprE (UwTimes u1 u2) = pprE u1 $$ pprE u2 $$ pprByte dW_OP_mul
398 in ptext (sLit "\t.uleb128 1f-.-1") $$ -- DW_FORM_block length
399 pprE expr $$
400 ptext (sLit "1:")
401
402 -- | Generate code for re-setting the unwind information for a
403 -- register to @undefined@
404 pprUndefUnwind :: Platform -> GlobalReg -> SDoc
405 pprUndefUnwind _ Sp = panic "pprUndefUnwind Sp" -- should never happen
406 pprUndefUnwind plat g = pprByte dW_CFA_undefined $$
407 pprLEBWord (fromIntegral $ dwarfGlobalRegNo plat g)
408
409
410 -- | Align assembly at (machine) word boundary
411 wordAlign :: SDoc
412 wordAlign = sdocWithPlatform $ \plat ->
413 ptext (sLit "\t.align ") <> case platformOS plat of
414 OSDarwin -> case platformWordSize plat of
415 8 -> text "3"
416 4 -> text "2"
417 _other -> error "wordAlign: Unsupported word size!"
418 _other -> ppr (platformWordSize plat)
419
420 -- | Assembly for a single byte of constant DWARF data
421 pprByte :: Word8 -> SDoc
422 pprByte x = ptext (sLit "\t.byte ") <> ppr (fromIntegral x :: Word)
423
424 -- | Assembly for a two-byte constant integer
425 pprHalf :: Word16 -> SDoc
426 pprHalf x = ptext (sLit "\t.hword ") <> ppr (fromIntegral x :: Word)
427
428 -- | Assembly for a constant DWARF flag
429 pprFlag :: Bool -> SDoc
430 pprFlag f = pprByte (if f then 0xff else 0x00)
431
432 -- | Assembly for 4 bytes of dynamic DWARF data
433 pprData4' :: SDoc -> SDoc
434 pprData4' x = ptext (sLit "\t.long ") <> x
435
436 -- | Assembly for 4 bytes of constant DWARF data
437 pprData4 :: Word -> SDoc
438 pprData4 = pprData4' . ppr
439
440 -- | Assembly for a DWARF word of dynamic data. This means 32 bit, as
441 -- we are generating 32 bit DWARF.
442 pprDwWord :: SDoc -> SDoc
443 pprDwWord = pprData4'
444
445 -- | Assembly for a machine word of dynamic data. Depends on the
446 -- architecture we are currently generating code for.
447 pprWord :: SDoc -> SDoc
448 pprWord s = (<> s) . sdocWithPlatform $ \plat ->
449 case platformWordSize plat of
450 4 -> ptext (sLit "\t.long ")
451 8 -> ptext (sLit "\t.quad ")
452 n -> panic $ "pprWord: Unsupported target platform word length " ++
453 show n ++ "!"
454
455 -- | Prints a number in "little endian base 128" format. The idea is
456 -- to optimize for small numbers by stopping once all further bytes
457 -- would be 0. The highest bit in every byte signals whether there
458 -- are further bytes to read.
459 pprLEBWord :: Word -> SDoc
460 pprLEBWord x | x < 128 = pprByte (fromIntegral x)
461 | otherwise = pprByte (fromIntegral $ 128 .|. (x .&. 127)) $$
462 pprLEBWord (x `shiftR` 7)
463
464 -- | Same as @pprLEBWord@, but for a signed number
465 pprLEBInt :: Int -> SDoc
466 pprLEBInt x | x >= -64 && x < 64
467 = pprByte (fromIntegral (x .&. 127))
468 | otherwise = pprByte (fromIntegral $ 128 .|. (x .&. 127)) $$
469 pprLEBInt (x `shiftR` 7)
470
471 -- | Generates a dynamic null-terminated string. If required the
472 -- caller needs to make sure that the string is escaped properly.
473 pprString' :: SDoc -> SDoc
474 pprString' str = ptext (sLit "\t.asciz \"") <> str <> char '"'
475
476 -- | Generate a string constant. We take care to escape the string.
477 pprString :: String -> SDoc
478 pprString str
479 = pprString' $ hcat $ map escapeChar $
480 if utf8EncodedLength str == length str
481 then str
482 else map (chr . fromIntegral) $ bytesFS $ mkFastString str
483
484 -- | Escape a single non-unicode character
485 escapeChar :: Char -> SDoc
486 escapeChar '\\' = ptext (sLit "\\\\")
487 escapeChar '\"' = ptext (sLit "\\\"")
488 escapeChar '\n' = ptext (sLit "\\n")
489 escapeChar c
490 | isAscii c && isPrint c && c /= '?' -- prevents trigraph warnings
491 = char c
492 | otherwise
493 = char '\\' <> char (intToDigit (ch `div` 64)) <>
494 char (intToDigit ((ch `div` 8) `mod` 8)) <>
495 char (intToDigit (ch `mod` 8))
496 where ch = ord c
497
498 -- | Generate an offset into another section. This is tricky because
499 -- this is handled differently depending on platform: Mac Os expects
500 -- us to calculate the offset using assembler arithmetic. Linux expects
501 -- us to just reference the target directly, and will figure out on
502 -- their own that we actually need an offset. Finally, Windows has
503 -- a special directive to refer to relative offsets. Fun.
504 sectionOffset :: SDoc -> SDoc -> SDoc
505 sectionOffset target section = sdocWithPlatform $ \plat ->
506 case platformOS plat of
507 OSDarwin -> pprDwWord (target <> char '-' <> section)
508 OSMinGW32 -> text "\t.secrel32 " <> target
509 _other -> pprDwWord target