-
Notifications
You must be signed in to change notification settings - Fork 20
/
ElfLoader.hs
1900 lines (1755 loc) · 79.9 KB
/
ElfLoader.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
{-|
Copyright : Galois Inc, 2016-18
Maintainer : [email protected]
Operations for creating a view of memory from an elf file.
-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternGuards #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ViewPatterns #-}
module Data.Macaw.Memory.ElfLoader
( memoryForElf
, memoryForElf'
, memoryForElfAllSymbols
, memoryForElfSections
, memoryForElfSegments
, memoryForElfSegments'
, SectionIndexMap
, MemLoadWarning(..)
, RelocationError
, SectionName
, resolveElfContents
, elfAddrWidth
, adjustedLoadRegionIndex
-- * Symbols
, MemSymbol(..)
, SymbolResolutionError(..)
, SymbolTable(..)
-- * Re-exports
, module Data.Macaw.Memory.LoadCommon
, module Data.Macaw.Memory
, module Data.Macaw.Memory.Symbols
) where
import Control.Lens
import Control.Monad (when)
import Control.Monad.Except (Except, ExceptT, MonadError(..), runExcept, runExceptT)
import Control.Monad.State.Strict (State, StateT(..), execStateT, gets, modify, runState)
import Data.Bits
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC
import Data.ElfEdit.Prim
( ElfWordType
, ElfClass(..)
, ElfSectionIndex(..)
, ElfSectionFlags
, ElfSegmentFlags
)
import qualified Data.ElfEdit.Prim as Elf
import Data.Foldable
import Data.IntervalMap.Strict (Interval(..), IntervalMap)
import qualified Data.IntervalMap.Strict as IMap
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe
import Data.Set (Set)
import qualified Data.Set as Set
import qualified Data.Vector as V
import Data.Word
import Numeric (showHex)
import Data.Macaw.Memory
import Data.Macaw.Memory.LoadCommon
import qualified Data.Macaw.Memory.Permissions as Perm
import Data.Macaw.Memory.Symbols
-- | Return a subrange of a bytestring.
slice :: Integral w => Elf.FileRange w -> BS.ByteString -> BS.ByteString
slice (i,c) = BS.take (fromIntegral c) . BS.drop (fromIntegral i)
-- | Return the addr width repr associated with an elf class
elfAddrWidth :: ElfClass w -> AddrWidthRepr w
elfAddrWidth ELFCLASS32 = Addr32
elfAddrWidth ELFCLASS64 = Addr64
------------------------------------------------------------------------
-- SectionIndexMap
-- | Maps section indices that are loaded in memory to their associated base
-- address and section contents.
--
-- The base address is expressed in terms of the underlying memory segment.
type SectionIndexMap w = Map Word16 (MemSegmentOff w)
------------------------------------------------------------------------
-- Flag conversion
-- | Create Reopt flags from elf flags.
flagsForSegmentFlags :: ElfSegmentFlags -> Perm.Flags
flagsForSegmentFlags f
= flagIf Elf.pf_r Perm.read
.|. flagIf Elf.pf_w Perm.write
.|. flagIf Elf.pf_x Perm.execute
where flagIf :: ElfSegmentFlags -> Perm.Flags -> Perm.Flags
flagIf ef pf | f `Elf.hasPermissions` ef = pf
| otherwise = Perm.none
-- | Convert elf section flags to a segment flags.
flagsForSectionFlags :: forall w
. (Num w, Bits w)
=> ElfSectionFlags w
-> Perm.Flags
flagsForSectionFlags f =
Perm.read .|. flagIf Elf.shf_write Perm.write .|. flagIf Elf.shf_execinstr Perm.execute
where flagIf :: ElfSectionFlags w -> Perm.Flags -> Perm.Flags
flagIf ef pf = if f `Elf.hasPermissions` ef then pf else Perm.none
------------------------------------------------------------------------
-- RelocationError
data RelocationError
= MissingSymbolTable
-- ^ The file is missing a symbol table.
| RelocationZeroSymbol
-- ^ A relocation refers to the symbol index 0.
| RelocationBadSymbolIndex !Int
-- ^ A relocation entry referenced a bad symbol index.
| RelocationUnsupportedType !String
-- ^ We do not support this type of relocation.
| RelocationFileUnsupported
-- ^ We do not allow relocations to refer to the "file" as in Elf.
| RelocationInvalidAddend !String !Integer !SymbolIdentifier
-- ^ The relocation type given does not allow the adddend with the given value.
| RelocationEvenAddend !String !Integer !BSC.ByteString !Integer
-- ^ The relocation type must have an even addend.
| RelocationDynamicError Elf.DynamicError
-- ^ Parsing the dynamic section failed when resolving a symbol.
instance Show RelocationError where
show MissingSymbolTable =
"Relocations cannot be applied due to missing symbol table."
show RelocationZeroSymbol =
"A relocation entry referred to invalid 0 symbol index."
show (RelocationBadSymbolIndex idx) =
"A relocation entry referred to invalid symbol index " ++ show idx ++ "."
show (RelocationUnsupportedType tp) =
"Unsupported relocation type " ++ tp
show RelocationFileUnsupported =
"Do not support relocations referring to file entry."
show (RelocationEvenAddend tp addr sym addend) =
let tgt = show sym ++ " + " ++ show addend
in "The " ++ tp ++ " relocation applied to " ++ show addr
++ " with target " ++ tgt ++ " must have an even addend."
show (RelocationInvalidAddend tp v sym) =
"Do not support addend of " ++ show v ++ " with relocation type " ++ tp ++ " to " ++ show sym ++ "."
show (RelocationDynamicError e) = show e
------------------------------------------------------------------------
-- MemLoader
type SectionName = BS.ByteString
data MemLoadWarning
= SectionNotAlloc !SectionName
| MultipleSectionsWithName !SectionName
| MultipleDynamicSegments
| OverlappingLoadableSegments
| UnsupportedSection !SectionName
| UnknownDefinedSymbolBinding !SymbolName Elf.ElfSymbolBinding
| UnknownDefinedSymbolType !SymbolName Elf.ElfSymbolType
| UnknownUndefinedSymbolBinding !SymbolName Elf.ElfSymbolBinding
| UnknownUndefinedSymbolType !SymbolName Elf.ElfSymbolType
| ExpectedSectionSymbolNameEmpty !SymbolName
| ExpectedSectionSymbolLocal
| InvalidSectionSymbolIndex !Elf.ElfSectionIndex
| UnsupportedProcessorSpecificSymbolIndex !SymbolName !ElfSectionIndex
| MultipleRelocationTables
-- ^ Issued if the file contains multiple relocation tables.
| RelocationParseFailure !String
| DynamicTagsOutOfRange !Elf.ElfDynamicTag !Elf.ElfDynamicTag !Word64 !Word64
-- ^ The range referenced by the dynamic tags was range.
| DynamicTagPairMismatch !Elf.ElfDynamicTag !Elf.ElfDynamicTag
-- ^ We expected either both tags or neither.
| DynamicMultipleTags !Elf.ElfDynamicTag
-- ^ We expected at most a single value of the given tag, but failed multiple.
| AndroidRelWithNonzeroAddend
-- ^ The `DT_ANDROID_REL` section contains Android relocations with non-zero addends.
| AndroidRelDecodingError !Elf.ElfDynamicTag !Elf.AndroidDecodeError
-- ^ We could not decode the table identified by the given dynamic tag.
| MultipleRelocationsAtAddr !Word64
-- ^ Multiple relocations at the given offset
| IgnoreRelocation !RelocationError
-- ^ @IgnoreRelocation err@ warns we ignored a relocation.
ppSymbol :: SymbolName -> String
ppSymbol "" = "unnamed symbol"
ppSymbol nm = "symbol " ++ BSC.unpack nm
instance Show MemLoadWarning where
show (SectionNotAlloc nm) =
"Section " ++ BSC.unpack nm ++ " was not marked as allocated."
show (MultipleSectionsWithName nm) =
"Found multiple sections named " ++ BSC.unpack nm ++ "; arbitrarily choosing first one encountered."
show MultipleDynamicSegments =
"Found multiple dynamic segments; choosing first one."
show OverlappingLoadableSegments =
"File segments containing overlapping addresses; skipping relocations."
show (UnsupportedSection nm) =
"Do not support section " ++ BSC.unpack nm
show (UnknownDefinedSymbolBinding nm bnd) =
"Unsupported binding " ++ show bnd ++ " for defined " ++ ppSymbol nm
++ "; Treating as a strong symbol."
show (UnknownDefinedSymbolType nm tp) =
"Unsupported type " ++ show tp ++ " for defined " ++ ppSymbol nm
++ "; Treating as a strong symbol."
show (UnknownUndefinedSymbolBinding nm bnd) =
"Unsupported binding " ++ show bnd ++ " for undefined " ++ ppSymbol nm
++ "; Treating as a required symbol."
show (UnknownUndefinedSymbolType nm tp) =
"Unsupported type " ++ show tp ++ " for undefined " ++ ppSymbol nm
++ "; Treating as a strong symbol."
show (ExpectedSectionSymbolNameEmpty nm) =
"Expected section symbol to have empty name instead of " ++ ppSymbol nm ++ "."
show ExpectedSectionSymbolLocal =
"Expected section symbol to have local visibility."
show (InvalidSectionSymbolIndex idx) =
"Expected section symbol to have a valid index instead of " ++ show idx ++ "."
show (UnsupportedProcessorSpecificSymbolIndex nm idx) =
"Could not resolve symbol index " ++ show idx ++ " for symbol " ++ BSC.unpack nm ++ "."
show MultipleRelocationTables =
"File contains multiple relocation tables; these are being merged."
show (RelocationParseFailure msg) =
"Error parsing relocations: " ++ msg
show (DynamicTagsOutOfRange offTag szTag off sz) =
show offTag ++ " and " ++ show szTag ++ " referenced a range [" ++ show (toInteger off)
++ " to " ++ show (toInteger off + toInteger sz) ++ "that is outside the file bounds."
show (DynamicTagPairMismatch foundTag notfoundTag) =
"Found " ++ show foundTag ++ " but missing " ++ show notfoundTag ++ "."
show (DynamicMultipleTags tag) =
"Multiple values assigned to " ++ show tag ++ " in dynamic information."
show AndroidRelWithNonzeroAddend =
"The DT_ANDROID_REL region in the dynamic is ignoring relocations with non-zero addends."
show (AndroidRelDecodingError tag nm) =
"The " ++ show tag ++ " region generated decoding error: " ++ show nm
show (MultipleRelocationsAtAddr addr) =
"Multiple relocations modify " ++ showHex addr "."
show (IgnoreRelocation err) =
show err
data MemLoaderState w = MLS { _mlsMemory :: !(Memory w)
, mlsEndianness :: !Endianness
-- ^ Endianness of elf file
, _mlsIndexMap :: !(SectionIndexMap w)
, mlsWarnings :: ![MemLoadWarning]
}
mlsMemory :: Simple Lens (MemLoaderState w) (Memory w)
mlsMemory = lens _mlsMemory (\s v -> s { _mlsMemory = v })
-- | Map from elf section indices to their offset and section
mlsIndexMap :: Simple Lens (MemLoaderState w) (SectionIndexMap w)
mlsIndexMap = lens _mlsIndexMap (\s v -> s { _mlsIndexMap = v })
addWarning :: MemLoadWarning -> MemLoader w ()
addWarning w = modify $ \s -> s { mlsWarnings = w : mlsWarnings s }
type MemLoader w = StateT (MemLoaderState w) (Except (LoadError w))
-- || Error occured from loading
data LoadError w
= LoadInsertError !String !(InsertError w)
-- ^ Error occurred in inserting a segment into memory.
| UnsupportedArchitecture !String
-- ^ Do not support relocations on given architecture.
| FormatDynamicError !Elf.DynamicError
-- ^ An error occured in parsing the dynamic segment.
instance MemWidth w => Show (LoadError w) where
show (LoadInsertError nm (OverlapSegment _ old)) =
nm ++ " overlaps with memory segment: " ++ show (segmentOffset old)
show (UnsupportedArchitecture arch) =
"Dynamic libraries are not supported on " ++ arch ++ "."
show (FormatDynamicError e) =
"Elf parsing error: " ++ show e
-- | This contains information needed to resolve elf symbol addresses.
data SymbolAddrResolver w =
SymbolAddrResolver { symSecCount :: Word16
, symResolver :: Word16 -> ElfWordType w -> Maybe (MemSegmentOff w)
-- ^ Given a section index and offset, this returns the memory addr
-- or nothing if that cannot be determined.
}
mkSymbolAddrResolver :: (MemWidth w, Integral (ElfWordType w))
=> V.Vector (Elf.Shdr Word32 (Elf.ElfWordType w))
-> SectionIndexMap w
-> SymbolAddrResolver w
mkSymbolAddrResolver v m = do
let resolveFn secIdx val = do
case Map.lookup secIdx m of
Just base
| Just s <- v V.!? fromIntegral secIdx
, Elf.shdrAddr s <= val && (val - Elf.shdrAddr s) < Elf.shdrSize s
, off <- toInteger (val - Elf.shdrAddr s) ->
incSegmentOff base off
_ -> Nothing
in SymbolAddrResolver { symSecCount = fromIntegral (V.length v)
, symResolver = resolveFn
}
runMemLoader :: Endianness
-> Memory w
-> MemLoader w ()
-> Either String (Memory w, SectionIndexMap w, [MemLoadWarning])
runMemLoader end mem m =
let s = MLS { _mlsMemory = mem
, _mlsIndexMap = Map.empty
, mlsWarnings = []
, mlsEndianness = end
}
in case runExcept $ execStateT m s of
Left e -> Left $ addrWidthClass (memAddrWidth mem) (show e)
Right mls -> do
Right (mls^.mlsMemory, mls^.mlsIndexMap, reverse (mlsWarnings mls))
-- | This adds a Macaw mem segment to the memory
loadMemSegment :: MemWidth w => String -> MemSegment w -> MemLoader w ()
loadMemSegment nm seg =
StateT $ \mls ->
case insertMemSegment seg (mls^.mlsMemory) of
Left e ->
throwError $ LoadInsertError nm e
Right mem' ->
pure ((), mls & mlsMemory .~ mem')
-- | Maps file offsets to the elf section
type ElfFileSectionMap v = IntervalMap v Word16
------------------------------------------------------------------------
-- SymbolResolver
type SymbolResolver a = ExceptT RelocationError (State [MemLoadWarning]) a
runSymbolResolver :: SymbolResolver a -> MemLoader w (Either RelocationError a)
runSymbolResolver m = do
warn <- gets mlsWarnings
let (er, warn') = runState (runExceptT m) warn
modify $ \s -> s { mlsWarnings = warn' }
pure er
symbolWarning :: MemLoadWarning -> SymbolResolver ()
symbolWarning w = modify $ \l -> w:l
------------------------------------------------------------------------
-- SymbolTable
-- | This wraps a callback function that lets users lookup symbol information by index
-- from the Elf file.
--
-- It is implemented using a callback function as the Elf dynamic
-- section doesn't provide an explicit number of symbol table
-- elements, and we decided not to depend on meta data such as section
-- names that could be stripped from executables/shared objects.
data SymbolTable w
= NoSymbolTable
| StaticSymbolTable !(V.Vector (Elf.SymtabEntry BS.ByteString (Elf.ElfWordType w)))
| DynamicSymbolTable !(Elf.DynamicSection w) !(Elf.VirtAddrMap w) !Elf.VersionDefMap !Elf.VersionReqMap
-- | Take a symbol entry and symbol version and return the identifier.
resolveSymbolId :: Elf.SymtabEntry BS.ByteString wtp
-> SymbolVersion
-> SymbolResolver SymbolIdentifier
resolveSymbolId sym ver = do
let nm = Elf.steName sym
let idx = Elf.steIndex sym
case Elf.steType sym of
Elf.STT_SECTION
| idx < Elf.SHN_LOPROC -> do
when (nm /= "") $
symbolWarning $ ExpectedSectionSymbolNameEmpty nm
when (Elf.steBind sym /= Elf.STB_LOCAL) $
symbolWarning ExpectedSectionSymbolLocal
pure $ SectionIdentifier (Elf.fromElfSectionIndex idx)
| otherwise -> do
symbolWarning $ InvalidSectionSymbolIndex idx
pure $ SymbolRelocation nm ver
Elf.STT_FILE -> do
throwError RelocationFileUnsupported
_tp -> do
when (idx >= Elf.SHN_LOPROC && idx `notElem` [Elf.SHN_ABS, Elf.SHN_COMMON]) $ do
symbolWarning $ UnsupportedProcessorSpecificSymbolIndex nm idx
pure $ SymbolRelocation nm ver
resolveSymbol :: SymbolTable w
-> Word32
-> SymbolResolver
( Elf.SymtabEntry BS.ByteString (Elf.ElfWordType w)
, SymbolVersion
)
resolveSymbol NoSymbolTable _symIdx =
throwError MissingSymbolTable
resolveSymbol (StaticSymbolTable entries) symIdx = do
when (symIdx == 0) $
throwError RelocationZeroSymbol
case entries V.!? fromIntegral symIdx of
Nothing ->
throwError $ RelocationBadSymbolIndex $ fromIntegral symIdx
Just sym ->
-- Look for '@' as it is used to separate symbol name from version information
-- in object files.
case BSC.findIndex (== '@') (Elf.steName sym) of
Just i -> do
let nm = Elf.steName sym
-- If "@@" appears in the symbol, this is a default versioned symbol
let ver | i+1 < BSC.length nm, BSC.index nm (i+1) == '@' =
ObjectDefaultSymbol (BSC.drop (i+2) nm)
-- Otherwise "@" appears in the symbol, and this is a non-default symbol.
| otherwise =
ObjectNonDefaultSymbol (BSC.drop (i+1) nm)
pure (sym { Elf.steName = BSC.take i nm }, ver)
Nothing -> do
pure (sym, UnversionedSymbol)
resolveSymbol (DynamicSymbolTable ds virtMap verDefMap verReqMap) symIdx = do
when (symIdx == 0) $
throwError RelocationZeroSymbol
case Elf.dynSymEntry ds virtMap verDefMap verReqMap symIdx of
Left e -> throwError (RelocationDynamicError e)
Right (sym, mverId) -> do
let ver = case mverId of
Elf.VersionLocal -> UnversionedSymbol
Elf.VersionGlobal -> UnversionedSymbol
Elf.VersionSpecific elfVer -> VersionedSymbol (Elf.verFile elfVer) (Elf.verName elfVer)
pure (sym, ver)
------------------------------------------------------------------------
-- Relocations
data RelFlag = IsRel | IsRela
deriving (Eq, Ord, Show)
-- | A function that resolves the architecture-specific relocation-type
-- into a symbol reference. The input
type RelocationResolver tp
= Maybe SegmentIndex
-- ^ Index of segment in which this relocation will be applied if this is
-- a dynamic relocation, and `Nothing` otherwise.
-> SymbolTable (Elf.RelocationWidth tp)
-> Elf.RelEntry tp
-- ^ Relocation information
-> MemWord (Elf.RelocationWidth tp)
-- ^ Addend to add to symbol.
-> RelFlag
-- ^ Flag to indicate if this is a rela and rel relocation
--
-- Added because some relocations (i.e. PLT ones) will ignore
-- Rel relocation addends.
-> SymbolResolver (Relocation (Elf.RelocationWidth tp))
data SomeRelocationResolver w
= forall tp
. (Elf.IsRelocationType tp, w ~ Elf.RelocationWidth tp)
=> SomeRelocationResolver (RelocationResolver tp)
-- T is 1 if the target symbol S has type STT_FUNC and the symbol addresses a Thumb instruction; it is 0 otherwise.
-- | This attempts to resolve an index in the symbol table to the
-- identifier information needed to resolve its loaded address.
resolveRelocationSym :: SymbolTable w
-- ^ A vector mapping symbol indices to the
-- associated symbol information.
-> Word32
-- ^ Index in the symbol table this refers to.
-> SymbolResolver SymbolIdentifier
resolveRelocationSym symtab symIdx = do
(symEntry, ver) <- resolveSymbol symtab symIdx
resolveSymbolId symEntry ver
-- | Attempt to resolve an X86_64 specific symbol.
relaTargetX86_64 :: Maybe SegmentIndex
-> SymbolTable 64
-- ^ Symbol table to look up symbols in/
-> Elf.RelEntry Elf.X86_64_RelocationType
-> MemWord 64
-- ^ Addend to add to symbol.
-> RelFlag
-> SymbolResolver (Relocation 64)
relaTargetX86_64 _ symtab rel addend _isRel =
case Elf.relType rel of
Elf.R_X86_64_64 -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $ Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = LittleEndian
, relocationJumpSlot = False
}
Elf.R_X86_64_PC32 -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $ Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = True
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = LittleEndian
, relocationJumpSlot = False
}
-- This is used for constructing relative jumps from a caller to the
-- PLT stub for the function it is calling. Such jumps typically modify
-- a relative call instruction with four bytes for the distance, and so
-- the distance must be an unsigned 4-byte value.
Elf.R_X86_64_PLT32 -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $ Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = True
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = LittleEndian
, relocationJumpSlot = True
}
-- R_X86_64_GLOB_DAT are used to update GOT entries with their
-- target address. They are similar to R_x86_64_64 except appear
-- inside dynamically linked executables/libraries, and are often
-- loaded lazily. We just use the eager AbsoluteRelocation here.
Elf.R_X86_64_GLOB_DAT -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $ Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = LittleEndian
, relocationJumpSlot = False
}
Elf.R_X86_64_JUMP_SLOT -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $ Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = LittleEndian
, relocationJumpSlot = True
}
Elf.R_X86_64_RELATIVE -> do
-- This relocation has the value B + A where
-- - A is the addend for the relocation, and
-- - B resolves to the difference between the
-- address at which the segment defining the symbol was
-- loaded and the address at which it was linked.
--
-- Since the address at which it was linked is a constant, we
-- create a non-relative address but subtract the link address
-- from the offset.
-- Get the address at which it was linked so we can subtract from offset.
let linktimeAddr = Elf.relAddr rel
when (Elf.relSym rel /= 0) $ do
throwError $ RelocationBadSymbolIndex (fromIntegral (Elf.relSym rel))
pure $ Relocation { relocationSym = LoadBaseAddr
, relocationOffset = addend - fromIntegral linktimeAddr
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = LittleEndian
, relocationJumpSlot = False
}
Elf.R_X86_64_32 -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $ Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = LittleEndian
, relocationJumpSlot = False
}
Elf.R_X86_64_32S -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $ Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = True
, relocationEndianness = LittleEndian
, relocationJumpSlot = False
}
Elf.R_X86_64_COPY -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
when (addend /= 0) $ do
throwError $ RelocationUnsupportedType (show (Elf.relType rel))
pure $ Relocation { relocationSym = sym
, relocationOffset = 0
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = LittleEndian
, relocationJumpSlot = False
}
-- Jhx Note. These will be needed to support thread local variables.
-- Elf.R_X86_64_TPOFF32 -> undefined
-- Elf.R_X86_64_GOTTPOFF -> undefined
tp -> throwError $ RelocationUnsupportedType (show tp)
-- | Generate an absolute 32-bit relocation.
relocARM32Abs :: Endianness
-> SymbolTable 32 -- ^ Symbol table
-> Elf.RelEntry Elf.ARM32_RelocationType -- ^ Relocation entry
-> MemWord 32
-> SymbolResolver (Relocation 32)
relocARM32Abs end symtab rel addend = do
(symEntry, ver) <- resolveSymbol symtab (Elf.relSym rel)
sym <- resolveSymbolId symEntry ver
-- These relocation relocations can apply to code or data, but we
-- want to ensure relocations do not change the thumb bit
-- of the symbol.
when (Elf.steType symEntry == Elf.STT_FUNC && addend `testBit` 0) $ do
let tp = show (Elf.relType rel)
let addr = toInteger (Elf.relAddr rel)
throwError $
RelocationEvenAddend tp addr (Elf.steName symEntry) (toInteger addend)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
-- | Attempt to resolve an X86_64 specific symbol.
relaTargetARM32 :: Endianness
-- ^ Endianness of relocations
-> Maybe SegmentIndex
-- ^ Index of segment for dynamic relocations
-> SymbolTable 32 -- ^ Symbol table
-> Elf.RelEntry Elf.ARM32_RelocationType -- ^ Relocation entry
-> MemWord 32
-- ^ Addend of symbol
-> RelFlag
-> SymbolResolver (Relocation 32)
relaTargetARM32 end msegIndex symtab rel addend relFlag =
case Elf.relType rel of
-- A static 32-bit absolute relocation
Elf.R_ARM_ABS32 -> do
relocARM32Abs end symtab rel addend
-- A dynamic 32-bit absolute relocation that typically applies to data.
Elf.R_ARM_GLOB_DAT -> do
relocARM32Abs end symtab rel addend
Elf.R_ARM_RELATIVE -> do
-- This relocation has the value B(S) + A where
-- - A is the addend for the relocation, and
-- - B(S) with S ≠ 0 resolves to the difference between the
-- address at which the segment defining the symbol S was
-- loaded and the address at which it was linked.
-- - B(S) with S = 0 resolves to the difference between the
-- address at which the segment being relocated was loaded
-- and the address at which it was linked.
--
-- Since the address at which it was linked is a constant, we
-- create a non-relative address but subtract the link address
-- from the offset.
-- Get the address at which it was linked so we can subtract from offset.
let linktimeAddr = Elf.relAddr rel
-- Resolve the symbol using the index in the relocation.
sym <-
if Elf.relSym rel == 0 then do
case msegIndex of
Nothing -> do
throwError $ RelocationZeroSymbol
Just idx ->
pure $! SegmentBaseAddr idx
else do
resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend - fromIntegral linktimeAddr
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_ARM_JUMP_SLOT -> do
-- This is a PLT relocation
sym <- resolveRelocationSym symtab (Elf.relSym rel)
-- For rela entries, check that addend is 0
-- N.B. Rel entries read from the target bits, and these typically point to the
-- start of the PLT, but are otherwise ignored for relocation purposes.
when (relFlag == IsRela && addend /= 0) $ do
throwError $ RelocationInvalidAddend (show (Elf.relType rel)) (toInteger addend) sym
pure $! Relocation { relocationSym = sym
, relocationOffset = 0
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = True
}
Elf.R_ARM_COPY -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
when (addend /= 0) $ do
throwError $ RelocationUnsupportedType (show (Elf.relType rel))
pure $ Relocation { relocationSym = sym
, relocationOffset = 0
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
tp -> do
throwError $ RelocationUnsupportedType (show tp)
-- | Attempt to resolve an X86_64 specific symbol.
relaTargetARM64 :: Endianness
-- ^ Endianness of relocations
-> Maybe SegmentIndex
-- ^ Index of segment for dynamic relocations
-> SymbolTable 64 -- ^ Symbol table
-> Elf.RelEntry Elf.AArch64_RelocationType -- ^ Relocaiton entry
-> MemWord 64
-- ^ Addend of symbol
-> RelFlag
-> SymbolResolver (Relocation 64)
relaTargetARM64 end msegIndex symtab rel addend relFlag =
case Elf.relType rel of
Elf.R_AARCH64_ABS64 -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_AARCH64_GLOB_DAT -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_AARCH64_RELATIVE -> do
-- This relocation has the value B(S) + A where
-- - A is the addend for the relocation, and
-- - B(S) with S ≠ 0 resolves to the difference between the
-- address at which the segment defining the symbol S was
-- loaded and the address at which it was linked.
-- - B(S) with S = 0 resolves to the difference between the
-- address at which the segment being relocated was loaded
-- and the address at which it was linked.
--
-- Since the address at which it was linked is a constant, we
-- create a non-relative address but subtract the link address
-- from the offset.
-- Get the address at which it was linked so we can subtract from offset.
let linktimeAddr = Elf.relAddr rel
-- Resolve the symbol using the index in the relocation.
sym <-
if Elf.relSym rel == 0 then do
case msegIndex of
Nothing -> do
throwError $ RelocationZeroSymbol
Just idx ->
pure $! SegmentBaseAddr idx
else do
resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend - fromIntegral linktimeAddr
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_AARCH64_JUMP_SLOT -> do
-- This is a PLT relocation
sym <- resolveRelocationSym symtab (Elf.relSym rel)
-- For rela entries, check that addend is 0
-- N.B. Rel entries read from the target bits, and these typically point to the
-- start of the PLT, but are otherwise ignored for relocation purposes.
when (relFlag == IsRela && addend /= 0) $ do
throwError $ RelocationInvalidAddend (show (Elf.relType rel)) (toInteger addend) sym
pure $! Relocation { relocationSym = sym
, relocationOffset = 0
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = True
}
tp -> do
throwError $ RelocationUnsupportedType (show tp)
-- | Attempt to resolve a PPC32-specific symbol.
relaTargetPPC32 :: Endianness
-- ^ Endianness of relocations
-> Maybe SegmentIndex
-- ^ Index of segment for dynamic relocations
-> SymbolTable 32 -- ^ Symbol table
-> Elf.RelEntry Elf.PPC32_RelocationType -- ^ Relocation entry
-> MemWord 32
-- ^ Addend of symbol
-> RelFlag
-> SymbolResolver (Relocation 32)
relaTargetPPC32 end msegIndex symtab rel addend _relFlag =
case Elf.relType rel of
Elf.R_PPC_ADDR32 -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_PPC_GLOB_DAT -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_PPC_RELATIVE -> do
-- This relocation has the value B + A where
-- - A is the addend for the relocation, and
-- - B resolves to the difference between the
-- address at which the segment defining the symbol was
-- loaded and the address at which it was linked.
--
-- Since the address at which it was linked is a constant, we
-- create a non-relative address but subtract the link address
-- from the offset.
-- Get the address at which it was linked so we can subtract from offset.
let linktimeAddr = Elf.relAddr rel
-- Resolve the symbol using the index in the relocation.
sym <-
if Elf.relSym rel == 0 then do
case msegIndex of
Nothing -> do
throwError $ RelocationZeroSymbol
Just idx ->
pure $! SegmentBaseAddr idx
else do
resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend - fromIntegral linktimeAddr
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_PPC_JMP_SLOT -> do
-- This is a PLT relocation
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 4
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = True
}
tp ->
throwError $ RelocationUnsupportedType (show tp)
-- | Attempt to resolve a PPC64-specific symbol.
relaTargetPPC64 :: Endianness
-- ^ Endianness of relocations
-> Maybe SegmentIndex
-- ^ Index of segment for dynamic relocations
-> SymbolTable 64 -- ^ Symbol table
-> Elf.RelEntry Elf.PPC64_RelocationType -- ^ Relocation entry
-> MemWord 64
-- ^ Addend of symbol
-> RelFlag
-> SymbolResolver (Relocation 64)
relaTargetPPC64 end msegIndex symtab rel addend _relFlag =
case Elf.relType rel of
Elf.R_PPC64_ADDR64 -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_PPC64_GLOB_DAT -> do
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_PPC64_RELATIVE -> do
-- This relocation has the value B + A where
-- - A is the addend for the relocation, and
-- - B resolves to the difference between the
-- address at which the segment defining the symbol was
-- loaded and the address at which it was linked.
--
-- Since the address at which it was linked is a constant, we
-- create a non-relative address but subtract the link address
-- from the offset.
-- Get the address at which it was linked so we can subtract from offset.
let linktimeAddr = Elf.relAddr rel
-- Resolve the symbol using the index in the relocation.
sym <-
if Elf.relSym rel == 0 then do
case msegIndex of
Nothing -> do
throwError $ RelocationZeroSymbol
Just idx ->
pure $! SegmentBaseAddr idx
else do
resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend - fromIntegral linktimeAddr
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = False
}
Elf.R_PPC64_JMP_SLOT -> do
-- This is a PLT relocation
sym <- resolveRelocationSym symtab (Elf.relSym rel)
pure $! Relocation { relocationSym = sym
, relocationOffset = addend
, relocationIsRel = False
, relocationSize = 8
, relocationIsSigned = False
, relocationEndianness = end
, relocationJumpSlot = True
}
tp ->
throwError $ RelocationUnsupportedType (show tp)
toEndianness :: Elf.ElfData -> Endianness
toEndianness Elf.ELFDATA2LSB = LittleEndian
toEndianness Elf.ELFDATA2MSB = BigEndian
-- | Creates a relocation map from the contents of a dynamic section.
getRelocationResolver
:: forall w
. Elf.ElfHeader w
-> MemLoader w (SomeRelocationResolver w)
getRelocationResolver hdr =
case (Elf.headerClass hdr, Elf.headerMachine hdr) of
(Elf.ELFCLASS64, Elf.EM_X86_64) ->
pure $ SomeRelocationResolver relaTargetX86_64
(Elf.ELFCLASS32, Elf.EM_ARM) ->
pure $ SomeRelocationResolver $ relaTargetARM32 end
(Elf.ELFCLASS64, Elf.EM_AARCH64) ->
pure $ SomeRelocationResolver $ relaTargetARM64 end
(Elf.ELFCLASS32, Elf.EM_PPC) ->
pure $ SomeRelocationResolver $ relaTargetPPC32 end
(Elf.ELFCLASS64, Elf.EM_PPC64) ->
pure $ SomeRelocationResolver $ relaTargetPPC64 end
(_,mach) -> throwError $ UnsupportedArchitecture (show mach)
where
end = toEndianness (Elf.headerData hdr)
resolveRela :: ( MemWidth w
, Elf.RelocationWidth tp ~ w
, Elf.IsRelocationType tp
, Integral (Elf.ElfIntType w)
)
=> SymbolTable w