atom_macho/
reloc.rs

1use crate::io::{Endian, ReadExt, WriteExt};
2use num_derive::FromPrimitive;
3use num_traits::FromPrimitive;
4use std::io::{Read, Write};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct RelocationInfo {
8    /// In MH_OBJECT files, this is an offset from the start of the section to the item containing
9    /// the address requiring relocation.
10    pub r_address: i32,
11    /// Indicates symbol index if r_extern is true or section ordinal if r_extern is false.
12    /// This field is set to R_ABS for relocation entries for absolute symbols, which need no
13    /// relocation.
14    pub r_symbolnum: u32,
15    /// Indicates whether the item containing the address to be relocated is part of a CPU
16    /// instruction that uses PC-relative addressing.
17    ///
18    /// For addresses contained in PC-relative instructions, the CPU adds the address of the
19    /// instruction to the address contained in the instruction.
20    pub r_pcrel: bool,
21    pub r_length: RelocLength,
22    /// Indicates whether the r_symbolnum field is an index into the symbol table (true) or a section
23    /// number (false).
24    /// NOTE: externでないRelocationInfoがどういう場面で有用なのかわかっていない。
25    pub r_extern: bool,
26    /// if not 0, machine specific relocation type
27    pub r_type: u8,
28}
29
30impl RelocationInfo {
31    /// size in bytes
32    pub const SIZE: u32 = 8;
33
34    pub fn read_from_in<R: Read>(read: &mut R, endian: Endian) -> RelocationInfo {
35        let r_address = read.read_i32_in(endian);
36
37        let infos = read.read_u32_in(endian);
38
39        // Mach-O specification does not clearly specify
40        // memory layout of these fields. So we assume that
41        // order of bit-fields follows ordinary manner
42        // (inverse order if little endian, and vice versa).
43        let (r_symbolnum, r_pcrel, r_length, r_extern, r_type) = if endian == Endian::Little {
44            (
45                // r_symbolnum
46                infos & 0x00FF_FFFF,
47                // r_pcrel
48                (infos & 0x0100_0000) > 0,
49                // r_length
50                RelocLength::from_u32((infos & 0x0600_0000) >> 25),
51                // r_extern
52                infos & 0x0800_0000 > 0,
53                // r_type
54                ((infos & 0xF000_0000) >> 28) as u8,
55            )
56        } else {
57            (
58                // r_symbolnum
59                (infos & 0xFFFF_FF00) >> 8,
60                // r_pcrel
61                infos & 0x0000_0080 > 0,
62                // r_length
63                RelocLength::from_u32((infos & 0x0000_0060) >> 5),
64                // r_extern
65                infos & 0x0000_0010 > 0,
66                // r_type
67                (infos & 0x0000_000F) as u8,
68            )
69        };
70
71        RelocationInfo {
72            r_address,
73            r_symbolnum,
74            r_pcrel,
75            r_length,
76            r_extern,
77            r_type,
78        }
79    }
80
81    // Mach-O specification does not clearly specify
82    // memory layout of these fields. So we assume that
83    // order of bit-fields follows ordinary manner
84    // (inverse order if little endian, and vice versa).
85    #[cfg(target_endian = "little")]
86    pub fn write_into(self, write: &mut impl Write) {
87        write.write_i32_native(self.r_address);
88
89        let mut infos: u32 = 0;
90        infos |= self.r_symbolnum;
91        infos |= (self.r_pcrel as u32) * 0x0100_0000;
92        infos |= (self.r_length.to_u32()) << 25;
93        infos |= (self.r_extern as u32) * 0x0800_0000;
94        infos |= (self.r_type as u32) << 28;
95        write.write_u32_native(infos);
96    }
97
98    // Mach-O specification does not clearly specify
99    // memory layout of these fields. So we assume that
100    // order of bit-fields follows ordinary manner
101    // (inverse order if little endian, and vice versa).
102    #[cfg(target_endian = "big")]
103    pub fn write_into(self, write: &mut impl Write) {
104        write.write_i32_native(self.r_address);
105
106        let mut infos: u32 = 0;
107        infos |= self.r_symbolnum << 8;
108        infos |= (self.r_pcrel as u32) * 0x0000_0080;
109        infos |= (self.r_length.to_u32()) << 5;
110        infos |= (self.r_extern as u32) * 0x0000_0010;
111        infos |= self.r_type as u32;
112        write.write_u32_native(infos);
113    }
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
117pub enum RelocLength {
118    /// 1 byte
119    Byte = 0,
120    /// 2 byte
121    Word = 1,
122    /// 4 byte
123    Long = 2,
124    /// 8 byte
125    Quad = 3,
126}
127
128impl RelocLength {
129    pub fn to_u32(self) -> u32 {
130        self as u32
131    }
132
133    pub fn from_u32(n: u32) -> RelocLength {
134        FromPrimitive::from_u32(n).unwrap()
135    }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139pub enum X86RelocType {
140    Vanilla = 0,
141    Pair = 1,
142    Sectdiff = 2,
143    PbLaPtr = 3,
144    LocalSectdiff = 4,
145    Tlv = 5,
146}
147
148impl X86RelocType {
149    pub fn to_u8(self) -> u8 {
150        self as u8
151    }
152
153    pub fn from_u8(n: u8) -> Self {
154        match n {
155            0 => X86RelocType::Vanilla,
156            1 => X86RelocType::Pair,
157            2 => X86RelocType::Sectdiff,
158            3 => X86RelocType::PbLaPtr,
159            4 => X86RelocType::LocalSectdiff,
160            5 => X86RelocType::Tlv,
161            _ => panic!("{} is not a valid x86 reloc type", n),
162        }
163    }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub enum X86_64RelocType {
168    /// Absolute address
169    Unsigned = 0,
170    /// Signed 32-bit displacement
171    Signed = 1,
172    /// A CALL/JMP instruction with 32-bit displacement
173    Branch = 2,
174    /// A MOVQ load of a GOT entry
175    GotLoad = 3,
176    /// Other GOT references
177    Got = 4,
178    /// Must be followed by a X86_64RelocType::Unsigned relocation
179    Subtractor = 5,
180    /// for signed 32-bit displacement with a -1 addend
181    Signed1 = 6,
182    /// for signed 32-bit displacement with a -2 addend
183    Signed2 = 7,
184    /// for signed 32-bit displacement with a -4 addend
185    Signed4 = 8,
186    /// for thread local variables
187    Tlv = 9,
188}
189
190impl X86_64RelocType {
191    pub fn to_u8(self) -> u8 {
192        self as u8
193    }
194
195    pub fn from_u8(n: u8) -> Self {
196        match n {
197            0 => X86_64RelocType::Unsigned,
198            1 => X86_64RelocType::Signed,
199            2 => X86_64RelocType::Branch,
200            3 => X86_64RelocType::GotLoad,
201            4 => X86_64RelocType::Got,
202            5 => X86_64RelocType::Subtractor,
203            6 => X86_64RelocType::Signed1,
204            7 => X86_64RelocType::Signed2,
205            8 => X86_64RelocType::Signed4,
206            9 => X86_64RelocType::Tlv,
207            _ => panic!("{} is not a valid x86_64 reloc type", n),
208        }
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215
216    #[test]
217    fn write_and_read_relocation_info() {
218        let reloc = RelocationInfo {
219            r_address: 42,
220            r_symbolnum: 0x00323100,
221            r_pcrel: true,
222            r_length: RelocLength::Byte,
223            r_extern: false,
224            r_type: X86_64RelocType::Unsigned.to_u8(),
225        };
226
227        let mut buf = Vec::new();
228
229        reloc.write_into(&mut buf);
230
231        assert_eq!(buf.len(), RelocationInfo::SIZE as usize);
232
233        let read_reloc = RelocationInfo::read_from_in(&mut buf.as_slice(), Endian::NATIVE);
234
235        assert_eq!(read_reloc, reloc);
236    }
237}