1 // Format of a relocation entry of a Mach-O file.  Modified from the 4.3BSD
2 // format.  The modifications from the original format were changing the value
3 // of the r_symbolnum field for "local" (r_extern == 0) relocation entries.
4 // This modification is required to support symbols in an arbitrary number of
5 // sections not just the three sections (text, data and bss) in a 4.3BSD file.
6 // Also the last 4 bits have had the r_type tag added to them.
7 
8 // The r_address is not really the address as it's name indicates but an offset.
9 // In 4.3BSD a.out objects this offset is from the start of the "segment" for
10 // which relocation entry is for (text or data).  For Mach-O object files it is
11 // also an offset but from the start of the "section" for which the relocation
12 // entry is for.  See comments in <mach-o/loader.h> about the r_address feild
13 // in images for used with the dynamic linker.
14 
15 // In 4.3BSD a.out objects if r_extern is zero then r_symbolnum is an ordinal
16 // for the segment the symbol being relocated is in.  These ordinals are the
17 // symbol types N_TEXT, N_DATA, N_BSS or N_ABS.  In Mach-O object files these
18 // ordinals refer to the sections in the object file in the order their section
19 // structures appear in the headers of the object file they are in.  The first
20 // section has the ordinal 1, the second 2, and so on.  This means that the
21 // same ordinal in two different object files could refer to two different
22 // sections.  And further could have still different ordinals when combined
23 // by the link-editor.  The value R_ABS is used for relocation entries for
24 // absolute symbols which need no further relocation.
25 use core::fmt;
26 use crate::mach;
27 use scroll::{Pread, Pwrite, IOwrite, SizeWith, IOread};
28 
29 // TODO: armv7 relocations are scattered, must and r_address with 0x8000_0000 to check if its scattered or not
30 #[derive(Copy, Clone, Pread, Pwrite, IOwrite, SizeWith, IOread)]
31 #[repr(C)]
32 pub struct RelocationInfo {
33     /// Offset in the section to what is being relocated
34     pub r_address: i32,
35     /// Contains all of the relocation info as a bitfield.
36     /// r_symbolnum, 24 bits, r_pcrel 1 bit, r_length 2 bits, r_extern 1 bit, r_type 4 bits
37     pub r_info: u32,
38 }
39 
40 pub const SIZEOF_RELOCATION_INFO: usize = 8;
41 
42 impl RelocationInfo {
43     /// Symbol index if `r_extern` == 1 or section ordinal if `r_extern` == 0. In bits :24
44     #[inline]
r_symbolnum(self) -> usize45     pub fn r_symbolnum(self) -> usize {
46         (self.r_info & 0x00ff_ffffu32) as usize
47     }
48     /// Was relocated pc relative already, 1 bit
49     #[inline]
r_pcrel(self) -> u850     pub fn r_pcrel(self) -> u8 {
51         ((self.r_info & 0x0100_0000u32) >> 24) as u8
52     }
53     /// The length of the relocation, 0=byte, 1=word, 2=long, 3=quad, 2 bits
54     #[inline]
r_length(self) -> u855     pub fn r_length(self) -> u8 {
56         ((self.r_info & 0x0600_0000u32) >> 25) as u8
57     }
58     /// Does not include value of sym referenced, 1 bit
59     #[inline]
r_extern(self) -> u860     pub fn r_extern(self) -> u8 {
61         ((self.r_info & 0x0800_0000) >> 27) as u8
62     }
63     /// Ff not 0, machine specific relocation type, in bits :4
64     #[inline]
r_type(self) -> u865     pub fn r_type(self) -> u8 {
66         ((self.r_info & 0xf000_0000) >> 28) as u8
67     }
68     /// If true, this relocation is for a symbol; if false,  or a section ordinal otherwise
69     #[inline]
is_extern(self) -> bool70     pub fn is_extern(self) -> bool {
71         self.r_extern() == 1
72     }
73     /// If true, this is a PIC relocation
74     #[inline]
is_pic(self) -> bool75     pub fn is_pic(self) -> bool {
76         self.r_pcrel() > 0
77     }
78     /// Returns a string representation of this relocation, given the machine `cputype`
to_str(self, cputype: mach::cputype::CpuType) -> &'static str79     pub fn to_str(self, cputype: mach::cputype::CpuType) -> &'static str {
80         reloc_to_str(self.r_type(), cputype)
81     }
82 }
83 
84 /// Absolute relocation type for Mach-O files
85 pub const R_ABS: u8 = 0;
86 
87 impl fmt::Debug for RelocationInfo {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result88     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89         f.debug_struct("RelocationInfo")
90             .field("r_address", &format_args!("{:#x}", &self.r_address))
91             .field("r_info", &format_args!("{:#x}", &self.r_info))
92             .field("r_symbolnum", &format_args!("{:#x}", &self.r_symbolnum()))
93             .field("r_pcrel", &(self.r_pcrel()))
94             .field("r_length", &self.r_length())
95             .field("r_extern", &self.r_extern())
96             .field("r_type", &self.r_type())
97             .finish()
98     }
99 }
100 
101 pub type RelocType = u8;
102 
103 /// Absolute address
104 pub const X86_64_RELOC_UNSIGNED: RelocType = 0;
105 /// Signed 32-bit displacement
106 pub const X86_64_RELOC_SIGNED: RelocType = 1;
107 /// A CALL/JMP instruction with 32-bit displacement
108 pub const X86_64_RELOC_BRANCH: RelocType = 2;
109 /// A MOVQ load of a GOT entry
110 pub const X86_64_RELOC_GOT_LOAD: RelocType = 3;
111 /// Other GOT references
112 pub const X86_64_RELOC_GOT: RelocType = 4;
113 /// Must be followed by a X86_64_RELOC_UNSIGNED relocation
114 pub const X86_64_RELOC_SUBTRACTOR: RelocType = 5;
115 /// for signed 32-bit displacement with a -1 addend
116 pub const X86_64_RELOC_SIGNED_1: RelocType = 6;
117 /// for signed 32-bit displacement with a -2 addend
118 pub const X86_64_RELOC_SIGNED_2: RelocType = 7;
119 /// for signed 32-bit displacement with a -4 addend
120 pub const X86_64_RELOC_SIGNED_4: RelocType = 8;
121 /// for thread local variables
122 pub const X86_64_RELOC_TLV: RelocType = 9;
123 
124 // x86 relocations
125 pub const GENERIC_RELOC_VANILLA: RelocType = 0;
126 pub const GENERIC_RELOC_PAIR: RelocType = 1;
127 pub const GENERIC_RELOC_SECTDIFF: RelocType = 2;
128 pub const GENERIC_RELOC_LOCAL_SECTDIFF: RelocType = 3;
129 pub const GENERIC_RELOC_PB_LA_P: RelocType = 4;
130 
131 // arm relocations
132 pub const ARM_RELOC_VANILLA: RelocType = GENERIC_RELOC_VANILLA;
133 pub const ARM_RELOC_PAIR: RelocType = GENERIC_RELOC_PAIR;
134 pub const ARM_RELOC_SECTDIFF: RelocType = GENERIC_RELOC_SECTDIFF;
135 pub const ARM_RELOC_LOCAL_SECTDIFF: RelocType = 3;
136 pub const ARM_RELOC_PB_LA_PTR: RelocType = 4;
137 pub const ARM_RELOC_BR24: RelocType = 5;
138 pub const ARM_THUMB_RELOC_BR22: RelocType = 6;
139 /// Obsolete
140 pub const ARM_THUMB_32BIT_BRANCH: RelocType = 7;
141 pub const ARM_RELOC_HALF: RelocType = 8;
142 pub const ARM_RELOC_HALF_SECTDIFF: RelocType = 9;
143 
144 /// For pointers.
145 pub const ARM64_RELOC_UNSIGNED: RelocType = 0;
146 /// Must be followed by an ARM64_RELOC_UNSIGNED
147 pub const ARM64_RELOC_SUBTRACTOR: RelocType = 1;
148 /// A B/BL instruction with 26-bit displacement.
149 pub const ARM64_RELOC_BRANCH26: RelocType = 2;
150 /// PC-rel distance to page of target.
151 pub const ARM64_RELOC_PAGE21: RelocType = 3;
152 /// Offset within page, scaled by r_length.
153 pub const ARM64_RELOC_PAGEOFF12: RelocType = 4;
154 /// PC-rel distance to page of GOT slot.
155 pub const ARM64_RELOC_GOT_LOAD_PAGE21: RelocType = 5;
156 /// Offset within page of GOT slot, scaled by r_length.
157 pub const ARM64_RELOC_GOT_LOAD_PAGEOFF12: RelocType = 6;
158 /// For pointers to GOT slots.
159 pub const ARM64_RELOC_POINTER_TO_GOT: RelocType = 7;
160 /// PC-rel distance to page of TLVP slot.
161 pub const ARM64_RELOC_TLVP_LOAD_PAGE21: RelocType = 8;
162 /// Offset within page of TLVP slot, scaled by r_length.
163 pub const ARM64_RELOC_TLVP_LOAD_PAGEOFF12: RelocType = 9;
164 /// Must be followed by ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12.
165 pub const ARM64_RELOC_ADDEND: RelocType = 10;
166 
reloc_to_str(reloc: RelocType, cputype: mach::cputype::CpuType) -> &'static str167 pub fn reloc_to_str(reloc: RelocType, cputype: mach::cputype::CpuType) -> &'static str {
168     use crate::mach::constants::cputype::*;
169     match cputype {
170         CPU_TYPE_ARM64 | CPU_TYPE_ARM64_32 => {
171             match reloc {
172                 ARM64_RELOC_UNSIGNED => "ARM64_RELOC_UNSIGNED",
173                 ARM64_RELOC_SUBTRACTOR => "ARM64_RELOC_SUBTRACTOR",
174                 ARM64_RELOC_BRANCH26 => "ARM64_RELOC_BRANCH26",
175                 ARM64_RELOC_PAGE21 => "ARM64_RELOC_PAGE21",
176                 ARM64_RELOC_PAGEOFF12 => "ARM64_RELOC_PAGEOFF12",
177                 ARM64_RELOC_GOT_LOAD_PAGE21 => "ARM64_RELOC_GOT_LOAD_PAGE21",
178                 ARM64_RELOC_GOT_LOAD_PAGEOFF12 => "ARM64_RELOC_GOT_LOAD_PAGEOFF12",
179                 ARM64_RELOC_POINTER_TO_GOT => "ARM64_RELOC_POINTER_TO_GOT",
180                 ARM64_RELOC_TLVP_LOAD_PAGE21 => "ARM64_RELOC_TLVP_LOAD_PAGE21",
181                 ARM64_RELOC_TLVP_LOAD_PAGEOFF12 => "ARM64_RELOC_TLVP_LOAD_PAGEOFF12",
182                 ARM64_RELOC_ADDEND => "ARM64_RELOC_ADDEND",
183                 _ => "UNKNOWN",
184             }
185         },
186         CPU_TYPE_X86_64 => {
187             match reloc {
188                 X86_64_RELOC_UNSIGNED => "X86_64_RELOC_UNSIGNED",
189                 X86_64_RELOC_SIGNED => "X86_64_RELOC_SIGNED",
190                 X86_64_RELOC_BRANCH => "X86_64_RELOC_BRANCH",
191                 X86_64_RELOC_GOT_LOAD => "X86_64_RELOC_GOT_LOAD",
192                 X86_64_RELOC_GOT => "X86_64_RELOC_GOT",
193                 X86_64_RELOC_SUBTRACTOR => "X86_64_RELOC_SUBTRACTOR",
194                 X86_64_RELOC_SIGNED_1 => "X86_64_RELOC_SIGNED_1",
195                 X86_64_RELOC_SIGNED_2 => "X86_64_RELOC_SIGNED_2",
196                 X86_64_RELOC_SIGNED_4 => "X86_64_RELOC_SIGNED_4",
197                 X86_64_RELOC_TLV => "X86_64_RELOC_TLV",
198                 _ => "UNKNOWN",
199             }
200         },
201         CPU_TYPE_ARM => {
202             match reloc {
203                 ARM_RELOC_VANILLA => "ARM_RELOC_VANILLA",
204                 ARM_RELOC_PAIR => "ARM_RELOC_PAIR",
205                 ARM_RELOC_SECTDIFF => "ARM_RELOC_SECTDIFF",
206                 ARM_RELOC_LOCAL_SECTDIFF => "ARM_RELOC_LOCAL_SECTDIFF",
207                 ARM_RELOC_PB_LA_PTR => "ARM_RELOC_PB_LA_PTR",
208                 ARM_RELOC_BR24 => "ARM_RELOC_BR24",
209                 ARM_THUMB_RELOC_BR22 => "ARM_THUMB_RELOC_BR22",
210                 ARM_THUMB_32BIT_BRANCH => "ARM_THUMB_32BIT_BRANCH",
211                 ARM_RELOC_HALF => "ARM_RELOC_HALF",
212                 ARM_RELOC_HALF_SECTDIFF => "ARM_RELOC_HALF_SECTDIFF",
213                 _ => "UNKNOWN",
214             }
215         },
216         CPU_TYPE_X86 => {
217             match reloc {
218                 GENERIC_RELOC_VANILLA => "GENERIC_RELOC_VANILLA",
219                 GENERIC_RELOC_PAIR => "GENERIC_RELOC_PAIR",
220                 GENERIC_RELOC_SECTDIFF => "GENERIC_RELOC_SECTDIFF",
221                 GENERIC_RELOC_LOCAL_SECTDIFF => "GENERIC_RELOC_LOCAL_SECTDIFF",
222                 GENERIC_RELOC_PB_LA_P => "GENERIC_RELOC_PB_LA_P",
223                 _ => "UNKNOWN",
224             }
225         },
226         _ => "BAD_CPUTYPE"
227     }
228 }
229