1use std::fs::File;
4use std::io::{self, BufReader, Read};
5use std::path::Path;
6
7use anyhow::{Context, Result};
8use goblin::elf;
9use remoteprocess::{Error as ProcessError, ProcessMemory};
10
11use flate2::bufread::GzDecoder;
12
13fn data_file(name: &str) -> Result<File> {
15 let path = Path::new(env!("CARGO_MANIFEST_DIR"))
16 .join("data")
17 .join(name);
18
19 Ok(File::open(&path).context(format!("could not open data file `{}`", path.display()))?)
20}
21
22fn data_file_gz_contents(name: &str) -> Result<Vec<u8>> {
24 let file = BufReader::new(data_file(&name)?);
25 let mut data = vec![];
26 GzDecoder::new(file)
27 .read_to_end(&mut data)
28 .context(format!("failed to read gzipped data file `{}`", name))?;
29 Ok(data)
30}
31
32fn load_coredump(name: &str) -> Result<CoreDump> {
34 CoreDump::new(data_file_gz_contents(name)?)
35}
36
37pub fn coredump_1_9_3() -> CoreDump {
38 load_coredump("ruby-coredump-1.9.3.gz").unwrap()
39}
40pub fn coredump_2_1_6() -> CoreDump {
41 load_coredump("ruby-coredump-2.1.6.gz").unwrap()
42}
43pub fn coredump_2_1_6_c_function() -> CoreDump {
44 load_coredump("ruby-coredump-2.1.6_c_function.gz").unwrap()
45}
46pub fn coredump_2_4_0() -> CoreDump {
47 load_coredump("ruby-coredump-2.4.0.gz").unwrap()
48}
49pub fn coredump_2_5_0() -> CoreDump {
50 load_coredump("ruby-coredump-2.5.0.gz").unwrap()
51}
52
53pub fn coredump_2_7_2() -> CoreDump {
54 load_coredump("ruby-coredump-2.7.2.gz").unwrap()
55}
56
57pub fn coredump_3_0_0() -> CoreDump {
58 load_coredump("ruby-coredump-3.0.0.gz").unwrap()
59}
60
61pub fn coredump_3_1_0() -> CoreDump {
62 load_coredump("ruby-coredump-3.1.0.gz").unwrap()
63}
64
65pub fn coredump_3_2_0() -> CoreDump {
66 load_coredump("ruby-coredump-3.2.0.gz").unwrap()
67}
68
69pub fn coredump_3_3_0() -> CoreDump {
70 load_coredump("ruby-coredump-3.3.0.gz").unwrap()
71}
72
73pub fn coredump_with_classes_3_3_0() -> CoreDump {
74 load_coredump("ruby-coredump-with-classes-3.3.0.gz").unwrap()
75}
76
77pub fn coredump_complex_3_4_5() -> CoreDump {
78 load_coredump("ruby-coredump-complex-3.4.5.gz").unwrap()
79}
80
81pub struct CoreDump {
83 raw_memory: Vec<u8>,
84 elf_section_headers: Vec<elf::SectionHeader>,
85}
86
87impl CoreDump {
88 pub fn new(raw_memory: Vec<u8>) -> Result<Self> {
89 let elf = elf::Elf::parse(&raw_memory).context("failed to parse ELF header")?;
90 let elf_section_headers = elf.section_headers;
91 Ok(CoreDump {
92 raw_memory,
93 elf_section_headers,
94 })
95 }
96}
97
98impl ProcessMemory for CoreDump {
99 fn read(&self, addr: usize, buf: &mut [u8]) -> Result<(), ProcessError> {
100 let start = addr as u64;
101 let end = (addr + buf.len()) as u64;
102 match self
103 .elf_section_headers
104 .iter()
105 .find(|section| section.sh_addr <= start && end <= section.sh_addr + section.sh_size)
106 {
107 Some(sec) => {
108 let start = sec.sh_offset as usize + addr - sec.sh_addr as usize;
109 let end = start + buf.len();
110 buf.copy_from_slice(&self.raw_memory[start..end]);
111 Ok(())
112 }
113 None => {
114 let io_error = io::Error::from_raw_os_error(libc::EFAULT);
115 Err(ProcessError::IOError(io_error))
116 }
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_load_coredump() {
127 let coredump = load_coredump("ruby-coredump-1.9.3.gz").unwrap();
128 assert_eq!(coredump.elf_section_headers.len(), 36);
129 let buf = &mut [0u8; 16];
130 coredump.read(0x823930, buf).expect("read failed");
131 assert_eq!(buf, &[32, 21, 73, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
132
133 let coredump = load_coredump("ruby-coredump-2.1.6.gz").unwrap();
134 assert_eq!(coredump.elf_section_headers.len(), 40);
135 let buf = &mut [0u8; 16];
136 coredump.read(0x562658abd7f0, buf).expect("read failed");
137 assert_eq!(
138 buf,
139 &[176, 165, 200, 89, 38, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
140 );
141
142 let coredump = load_coredump("ruby-coredump-2.1.6_c_function.gz").unwrap();
143 assert_eq!(coredump.elf_section_headers.len(), 102);
144 let buf = &mut [0u8; 16];
145 coredump.read(0x562efcd577f0, buf).expect("read failed");
146 assert_eq!(
147 buf,
148 &[176, 198, 255, 254, 46, 86, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
149 );
150
151 let coredump = load_coredump("ruby-coredump-2.4.0.gz").unwrap();
152 assert_eq!(coredump.elf_section_headers.len(), 38);
153 let buf = &mut [0u8; 16];
154 coredump.read(0x55df44959920, buf).expect("read failed");
155 assert_eq!(
156 buf,
157 &[208, 165, 37, 70, 223, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
158 );
159
160 let coredump = load_coredump("ruby-coredump-2.5.0.gz").unwrap();
161 assert_eq!(coredump.elf_section_headers.len(), 38);
162 let buf = &mut [0u8; 16];
163 coredump.read(0x55dd8c3b7758, buf).expect("read failed");
164 assert_eq!(
165 buf,
166 &[216, 136, 151, 140, 221, 85, 0, 0, 32, 127, 151, 140, 221, 85, 0, 0]
167 );
168
169 let coredump = load_coredump("ruby-coredump-2.7.2.gz").unwrap();
170 assert_eq!(coredump.elf_section_headers.len(), 119);
171 let buf = &mut [0u8; 16];
172 coredump.read(0x7fdd8d626070, buf).expect("read failed");
173 assert_eq!(
174 buf,
175 &[208, 166, 207, 100, 196, 85, 0, 0, 160, 155, 207, 100, 196, 85, 0, 0]
176 );
177
178 let coredump = load_coredump("ruby-coredump-3.0.0.gz").unwrap();
179 assert_eq!(coredump.elf_section_headers.len(), 119);
180 let buf = &mut [0u8; 16];
181 coredump.read(0x7fdacdab7470, buf).expect("read failed");
182 assert_eq!(
183 buf,
184 &[160, 235, 191, 181, 200, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
185 );
186
187 let coredump = load_coredump("ruby-coredump-3.1.0.gz").unwrap();
188 assert_eq!(coredump.elf_section_headers.len(), 119);
189 let buf = &mut [0u8; 16];
190 coredump.read(0x7f0dc0c83c58, buf).expect("read failed");
191 assert_eq!(
192 buf,
193 &[64, 186, 97, 2, 255, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
194 );
195
196 let coredump = load_coredump("ruby-coredump-3.2.0.gz").unwrap();
197 assert_eq!(coredump.elf_section_headers.len(), 120);
198 let buf = &mut [0u8; 16];
199 coredump.read(0xffffb8034578, buf).expect("read failed");
200 assert_eq!(
201 buf,
202 &[208, 250, 146, 227, 170, 170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
203 );
204
205 let coredump = load_coredump("ruby-coredump-3.3.0.gz").unwrap();
206 assert_eq!(coredump.elf_section_headers.len(), 151);
207 let buf = &mut [0u8; 16];
208 coredump.read(0x7f43435f4988, buf).expect("read failed");
209 assert_eq!(
210 buf,
211 &[16, 51, 89, 134, 131, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
212 );
213
214 let coredump = load_coredump("ruby-coredump-with-classes-3.3.0.gz").unwrap();
215 assert_eq!(coredump.elf_section_headers.len(), 124);
216 let buf = &mut [0u8; 16];
217 coredump.read(0x7f58cb7f4988, buf).expect("read failed");
218 assert_eq!(
219 buf,
220 &[16, 115, 177, 241, 196, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
221 );
222
223 let coredump = load_coredump("ruby-coredump-complex-3.4.5.gz").unwrap();
224 assert_eq!(coredump.elf_section_headers.len(), 152);
225 let buf = &mut [0u8; 16];
226 coredump.read(0x7f271feb5390, buf).expect("read failed");
227 assert_eq!(
228 buf,
229 &[16, 19, 104, 91, 119, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
230 );
231 }
232}