frust_hotpatch/
jump_table.rs1use std::collections::HashMap;
9use std::hash::{BuildHasherDefault, Hasher};
10use std::path::PathBuf;
11
12use serde::{Deserialize, Serialize};
13
14#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
16pub struct JumpTable {
17 pub lib: PathBuf,
19
20 pub map: AddressMap,
24
25 pub aslr_reference: u64,
27
28 pub new_base_address: u64,
30
31 pub ifunc_count: u64,
33}
34
35pub type AddressMap = HashMap<u64, u64, BuildAddressHasher>;
37
38pub type BuildAddressHasher = BuildHasherDefault<AddressHasher>;
40
41#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
43pub struct AddressHasher(u64);
44
45impl Hasher for AddressHasher {
46 fn write(&mut self, _: &[u8]) {
47 panic!("AddressHasher only hashes integer keys")
48 }
49 fn write_u8(&mut self, n: u8) {
50 self.0 = u64::from(n)
51 }
52 fn write_u16(&mut self, n: u16) {
53 self.0 = u64::from(n)
54 }
55 fn write_u32(&mut self, n: u32) {
56 self.0 = u64::from(n)
57 }
58 fn write_u64(&mut self, n: u64) {
59 self.0 = n
60 }
61 fn write_usize(&mut self, n: usize) {
62 self.0 = n as u64
63 }
64 fn write_i8(&mut self, n: i8) {
65 self.0 = n as u64
66 }
67 fn write_i16(&mut self, n: i16) {
68 self.0 = n as u64
69 }
70 fn write_i32(&mut self, n: i32) {
71 self.0 = n as u64
72 }
73 fn write_i64(&mut self, n: i64) {
74 self.0 = n as u64
75 }
76 fn write_isize(&mut self, n: isize) {
77 self.0 = n as u64
78 }
79 fn finish(&self) -> u64 {
80 self.0
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 fn upstream_table() -> subsecond_types::JumpTable {
89 let mut map = subsecond_types::AddressMap::default();
90 map.insert(0x1_0000_1234, 0x4567);
91 map.insert(0x1_0000_9abc, 0xdef0);
92 map.insert(u64::MAX, 0);
93 subsecond_types::JumpTable {
94 lib: PathBuf::from("/tmp/patch-1.dylib"),
95 map,
96 aslr_reference: 0x1_0000_0000,
97 new_base_address: 0x2000,
98 ifunc_count: 3,
99 }
100 }
101
102 #[test]
103 fn wire_compat_with_subsecond_types() {
104 let upstream = upstream_table();
105 let json = serde_json::to_value(&upstream).expect("upstream table serializes");
106
107 let ours: JumpTable = serde_json::from_value(json.clone()).expect("dx's JSON deserializes");
108 assert_eq!(ours.lib, upstream.lib);
109 assert_eq!(ours.aslr_reference, upstream.aslr_reference);
110 assert_eq!(ours.new_base_address, upstream.new_base_address);
111 assert_eq!(ours.ifunc_count, upstream.ifunc_count);
112 assert_eq!(ours.map.len(), upstream.map.len());
113 for (old, new) in &upstream.map {
114 assert_eq!(ours.map.get(old), Some(new));
115 }
116
117 let back = serde_json::to_value(&ours).expect("our table serializes");
119 assert_eq!(back, json);
120 let round: subsecond_types::JumpTable =
121 serde_json::from_value(back).expect("upstream reads our JSON");
122 assert_eq!(round, upstream);
123 }
124
125 #[test]
126 fn address_hasher_is_identity() {
127 let mut h = AddressHasher::default();
128 h.write_u64(0xdead_beef);
129 assert_eq!(h.finish(), 0xdead_beef);
130 }
131}