xref: /minix/minix/servers/vfs/coredump.c (revision 045e0ed3)
1 #include "fs.h"
2 #include <fcntl.h>
3 #include <string.h>
4 #include <minix/vm.h>
5 #include <sys/mman.h>
6 #include <sys/exec_elf.h>
7 
8 /* Include ELF headers */
9 #include <sys/elf_core.h>
10 #include <machine/elf.h>
11 
12 static void fill_elf_header(Elf32_Ehdr *elf_header, int phnum);
13 static void fill_prog_header(Elf32_Phdr *prog_header, Elf32_Word
14 	p_type, Elf32_Off p_offset, Elf32_Addr p_vaddr, Elf32_Word p_flags,
15 	Elf32_Word p_filesz, Elf32_Word p_memsz);
16 static int get_memory_regions(Elf32_Phdr phdrs[]);
17 static void fill_note_segment_and_entries_hdrs(Elf32_Phdr phdrs[],
18 	Elf32_Nhdr nhdrs[]);
19 static void adjust_offsets(Elf32_Phdr phdrs[], int phnum);
20 static void dump_elf_header(struct filp *f, Elf32_Ehdr elf_header);
21 static void dump_notes(struct filp *f, Elf32_Nhdr nhdrs[], int csig,
22 	char *proc_name);
23 static void dump_program_headers(struct filp *f, Elf_Phdr phdrs[], int
24 	phnum);
25 static void dump_segments(struct filp *f, Elf32_Phdr phdrs[], int
26 	phnum);
27 static void write_buf(struct filp *f, char *buf, size_t size);
28 
29 /*===========================================================================*
30  *				write_elf_core_file			     *
31  *===========================================================================*/
32 void write_elf_core_file(struct filp *f, int csig, char *proc_name)
33 {
34 /* First, fill in all the required headers, second, adjust the offsets,
35  * third, dump everything into the core file
36  */
37 #define MAX_REGIONS 100
38 #define NR_NOTE_ENTRIES 2
39   Elf_Ehdr elf_header;
40   Elf_Phdr phdrs[MAX_REGIONS + 1];
41   Elf_Nhdr nhdrs[NR_NOTE_ENTRIES];
42   int phnum;
43 
44   memset(phdrs, 0, sizeof(phdrs));
45 
46   /* Fill in the NOTE Program Header - at phdrs[0] - and
47    * note entries' headers
48    */
49   fill_note_segment_and_entries_hdrs(phdrs, nhdrs);
50 
51   /* Get the memory segments and fill in the Program headers */
52   phnum = get_memory_regions(phdrs) + 1;
53 
54   /* Fill in the ELF header */
55   fill_elf_header(&elf_header, phnum);
56 
57   /* Adjust offsets in program headers - The layout in the ELF core file
58    * is the following: the ELF Header, the Note Program Header,
59    * the rest of Program Headers (memory segments), Note contents,
60    * the program segments' contents
61    */
62   adjust_offsets(phdrs, phnum);
63 
64   /* Write ELF header */
65   dump_elf_header(f, elf_header);
66 
67   /* Write Program headers (Including the NOTE) */
68   dump_program_headers(f, phdrs, phnum);
69 
70   /* Write NOTE contents */
71   dump_notes(f, nhdrs, csig, proc_name);
72 
73   /* Write segments' contents */
74   dump_segments(f, phdrs, phnum);
75 }
76 
77 /*===========================================================================*
78  *				fill_elf_header        			     *
79  *===========================================================================*/
80 static void fill_elf_header (Elf_Ehdr *elf_header, int phnum)
81 {
82   memset((void *) elf_header, 0, sizeof(Elf_Ehdr));
83 
84   elf_header->e_ident[EI_MAG0] = ELFMAG0;
85   elf_header->e_ident[EI_MAG1] = ELFMAG1;
86   elf_header->e_ident[EI_MAG2] = ELFMAG2;
87   elf_header->e_ident[EI_MAG3] = ELFMAG3;
88   elf_header->e_ident[EI_CLASS] = ELF_TARG_CLASS;
89   elf_header->e_ident[EI_DATA] = ELF_TARG_DATA;
90   elf_header->e_ident[EI_VERSION] = EV_CURRENT;
91   elf_header->e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
92   elf_header->e_type = ET_CORE;
93   elf_header->e_machine = ELF_TARG_MACH;
94   elf_header->e_version = EV_CURRENT;
95   elf_header->e_ehsize = sizeof(Elf_Ehdr);
96   elf_header->e_phoff = sizeof(Elf_Ehdr);
97   elf_header->e_phentsize = sizeof(Elf_Phdr);
98   elf_header->e_phnum = phnum;
99 }
100 
101 /*===========================================================================*
102  *				fill_prog_header        		     *
103  *===========================================================================*/
104 static void fill_prog_header (Elf_Phdr *prog_header, Elf_Word p_type,
105 	Elf_Off p_offset, Elf_Addr p_vaddr, Elf_Word p_flags,
106 	Elf_Word p_filesz, Elf_Word p_memsz)
107 {
108 
109   memset((void *) prog_header, 0, sizeof(Elf_Phdr));
110 
111   prog_header->p_type = p_type;
112   prog_header->p_offset = p_offset;
113   prog_header->p_vaddr = p_vaddr;
114   prog_header->p_flags = p_flags;
115   prog_header->p_filesz = p_filesz;
116   prog_header->p_memsz = p_memsz;
117 
118 }
119 
120 #define PADBYTES    4
121 #define PAD_LEN(x)  ((x + (PADBYTES - 1)) & ~(PADBYTES - 1))
122 
123 /*===========================================================================*
124  *			fill_note_segment_and_entries_hdrs     	     	     *
125  *===========================================================================*/
126 static void fill_note_segment_and_entries_hdrs(Elf_Phdr phdrs[],
127 				Elf_Nhdr nhdrs[])
128 {
129   int filesize;
130   const char *note_name = ELF_NOTE_MINIX_ELFCORE_NAME "\0";
131   int name_len, mei_len, gregs_len;
132 
133   /* Size of notes in the core file is rather fixed:
134    * sizeof(minix_elfcore_info_t) +
135    * 2 * sizeof(Elf_Nhdr) + the size of the padded name of the note
136    * - i.e. "MINIX-CORE\0" padded to 4-byte alignment => 2 * 8 bytes
137    */
138 
139   name_len = strlen(note_name) + 1;
140   mei_len = sizeof(minix_elfcore_info_t);
141   gregs_len = sizeof(gregset_t);
142 
143   /* Make sure to also count the padding bytes */
144   filesize = PAD_LEN(mei_len) + PAD_LEN(gregs_len) +
145 	2 * sizeof(Elf_Nhdr) + 2 * PAD_LEN(name_len);
146   fill_prog_header(&phdrs[0], PT_NOTE, 0, 0, PF_R, filesize, 0);
147 
148   /* First note entry header */
149   nhdrs[0].n_namesz = name_len;
150   nhdrs[0].n_descsz = sizeof(minix_elfcore_info_t);
151   nhdrs[0].n_type = NT_MINIX_ELFCORE_INFO;
152 
153   /* Second note entry header */
154   nhdrs[1].n_namesz = name_len;
155   nhdrs[1].n_descsz = sizeof(gregset_t);
156   nhdrs[1].n_type = NT_MINIX_ELFCORE_GREGS;
157 }
158 
159 /*===========================================================================*
160  *				adjust_offset   			     *
161  *===========================================================================*/
162 static void adjust_offsets(Elf_Phdr phdrs[], int phnum)
163 {
164   int i;
165   long offset = sizeof(Elf_Ehdr) + phnum * sizeof(Elf_Phdr);
166 
167   for (i = 0; i < phnum; i++) {
168 	phdrs[i].p_offset = offset;
169 	offset += phdrs[i].p_filesz;
170   }
171 }
172 
173 /*===========================================================================*
174  *				write_buf       			     *
175  *===========================================================================*/
176 static void write_buf(struct filp *f, char *buf, size_t size)
177 {
178   /*
179    * TODO: pass in the proper file descriptor number.  It really doesn't matter
180    * what we pass in, because the write target is a regular file.  As such, the
181    * write call will never be suspended, and suspension is the only case that
182    * read_write() could use the file descriptor.  Still, passing in an invalid
183    * value isn't exactly nice.
184    */
185   read_write(fp, WRITING, -1 /*fd*/, f, (vir_bytes)buf, size, VFS_PROC_NR);
186 }
187 
188 /*===========================================================================*
189  *				get_memory_regions			     *
190  *===========================================================================*/
191 static int get_memory_regions(Elf_Phdr phdrs[])
192 {
193   /* Print the virtual memory regions of a process. */
194 
195   /* The same as dump_regions from procfs/pid.c */
196   struct vm_region_info vri[MAX_VRI_COUNT];
197   vir_bytes next;
198   int i, r, count;
199   Elf_Word pflags;
200 
201   count = 0;
202   next = 0;
203 
204   do {
205 	r = vm_info_region(fp->fp_endpoint, vri, MAX_VRI_COUNT, &next);
206 	if (r < 0) return r;
207 	if (r == 0) break;
208 
209 	for (i = 0; i < r; i++) {
210 		pflags =  (vri[i].vri_prot & PROT_READ ? PF_R : 0)
211 			| (vri[i].vri_prot & PROT_WRITE ? PF_W : 0)
212 			| (vri[i].vri_prot & PROT_EXEC ? PF_X : 0);
213 
214 		fill_prog_header (&phdrs[count + 1], PT_LOAD,
215 				0, vri[i].vri_addr, pflags,
216 				vri[i].vri_length, vri[i].vri_length);
217 		count++;
218 
219 		if (count >= MAX_REGIONS) {
220 			printf("VFS: get_memory_regions Warning: "
221 				"Program has too many regions\n");
222 			return(count);
223 		}
224 	}
225   } while (r == MAX_VRI_COUNT);
226 
227   return(count);
228 }
229 
230 /*===========================================================================*
231  *				dump_notes			             *
232  *===========================================================================*/
233 static void dump_notes(struct filp *f, Elf_Nhdr nhdrs[], int csig,
234 			 char *proc_name)
235 {
236   char *note_name = ELF_NOTE_MINIX_ELFCORE_NAME "\0";
237   char pad[4];
238   minix_elfcore_info_t mei;
239   int mei_len = sizeof(minix_elfcore_info_t);
240   int gregs_len = sizeof(gregset_t);
241   struct stackframe_s regs;
242 
243   /* Dump first note entry */
244   mei.mei_version = MINIX_ELFCORE_VERSION;
245   mei.mei_meisize = mei_len;
246   mei.mei_signo = csig;
247   mei.mei_pid = fp->fp_pid;
248   memcpy(mei.mei_command, proc_name, sizeof(mei.mei_command));
249 
250   write_buf(f, (char *) &nhdrs[0], sizeof(Elf_Nhdr));
251   write_buf(f, note_name, nhdrs[0].n_namesz);
252   write_buf(f, pad, PAD_LEN(nhdrs[0].n_namesz) - nhdrs[0].n_namesz);
253   write_buf(f, (char *) &mei, mei_len);
254   write_buf(f, pad, PAD_LEN(mei_len) - mei_len);
255 
256   /* Get registers */
257   if (sys_getregs(&regs, fp->fp_endpoint) != OK)
258 	printf("VFS: Could not read registers\n");
259 
260   if (sizeof(regs) != gregs_len)
261 	printf("VFS: Wrong core register structure size\n");
262 
263   /* Dump second note entry - the general registers */
264   write_buf(f, (char *) &nhdrs[1], sizeof(Elf_Nhdr));
265 
266   write_buf(f, note_name, nhdrs[1].n_namesz);
267   write_buf(f, pad, PAD_LEN(nhdrs[1].n_namesz) - nhdrs[1].n_namesz);
268   write_buf(f, (char *) &regs, gregs_len);
269   write_buf(f, pad, PAD_LEN(gregs_len) - gregs_len);
270 }
271 
272 /*===========================================================================*
273  *				dump_elf_header			             *
274  *===========================================================================*/
275 static void dump_elf_header(struct filp *f, Elf_Ehdr elf_header)
276 {
277   write_buf(f, (char *) &elf_header, sizeof(Elf_Ehdr));
278 }
279 
280 /*===========================================================================*
281  *			  dump_program_headers			             *
282  *===========================================================================*/
283 static void dump_program_headers(struct filp *f, Elf_Phdr phdrs[], int phnum)
284 {
285   int i;
286 
287   for (i = 0; i < phnum; i++)
288 	write_buf(f, (char *) &phdrs[i], sizeof(Elf_Phdr));
289 }
290 
291 /*===========================================================================*
292  *			      dump_segments 			             *
293  *===========================================================================*/
294 static void dump_segments(struct filp *f, Elf_Phdr phdrs[], int phnum)
295 {
296   int i;
297   vir_bytes len;
298   off_t off, seg_off;
299   int r;
300   static u8_t buf[CLICK_SIZE];
301 
302   for (i = 1; i < phnum; i++) {
303 	len = phdrs[i].p_memsz;
304 	seg_off = phdrs[i].p_vaddr;
305 
306 	if (len > LONG_MAX) {
307 		printf("VFS: segment too large to dump, truncating\n");
308 		len = LONG_MAX;
309 	}
310 
311 	for (off = 0; off < (off_t) len; off += CLICK_SIZE) {
312 		vir_bytes p = (vir_bytes) (seg_off + off);
313 		r = sys_datacopy_try(fp->fp_endpoint, p,
314 			SELF, (vir_bytes) buf,
315 			(phys_bytes) CLICK_SIZE);
316 
317 		if(r != OK) {
318 			/* memory didn't exist; write as zeroes */
319 			memset(buf, 0, sizeof(buf));
320 			continue;
321 		}
322 
323 		write_buf(f, (char *) buf, (off + CLICK_SIZE <= (off_t) len) ?
324 					CLICK_SIZE : (len - off));
325 	}
326   }
327 }
328