1 |
|
#include <hurd.h> |
2 |
|
#include <elf.h> |
3 |
|
#include <link.h> |
4 |
|
#include <string.h> |
5 |
|
#include <sys/mman.h> |
6 |
|
#include <sys/utsname.h> |
7 |
|
|
8 |
|
|
9 |
|
#define ELF_MACHINE EM_386 /* XXX */ |
10 |
|
|
11 |
|
#define ELF_CLASS PASTE (ELFCLASS, __ELF_NATIVE_CLASS) |
12 |
|
#define PASTE(a, b) PASTE_1 (a, b) |
13 |
|
#define PASTE_1(a, b) a##b |
14 |
|
|
15 |
|
#include <endian.h> |
16 |
|
#if BYTE_ORDER == BIG_ENDIAN |
17 |
|
#define ELF_DATA ELFDATA2MSB |
18 |
|
#elif BYTE_ORDER == LITTLE_ENDIAN |
19 |
|
#define ELF_DATA ELFDATA2LSB |
20 |
|
#endif |
21 |
|
|
22 |
|
|
23 |
|
error_t |
24 |
|
dump_core (task_t task, file_t file, off_t corelimit, |
25 |
|
int signo, long int sigcode, int sigerror) |
26 |
{ |
{ |
27 |
processor_set_name_t pset; |
error_t err; |
28 |
host_t host; |
ElfW(Phdr) *phdrs, *ph; |
29 |
processor_set_basic_info_data_t pinfo; |
ElfW(Ehdr) hdr = /* ELF header for the core file. */ |
30 |
|
{ |
31 |
|
e_ident: |
32 |
|
{ |
33 |
|
[EI_MAG0] = ELFMAG0, |
34 |
|
[EI_MAG1] = ELFMAG1, |
35 |
|
[EI_MAG2] = ELFMAG2, |
36 |
|
[EI_MAG3] = ELFMAG3, |
37 |
|
[EI_CLASS] = ELF_CLASS, |
38 |
|
[EI_DATA] = ELF_DATA, |
39 |
|
[EI_VERSION] = EV_CURRENT, |
40 |
|
[EI_OSABI] = ELFOSABI_SYSV, |
41 |
|
[EI_ABIVERSION] = 0 |
42 |
|
}, |
43 |
|
e_type: ET_CORE, |
44 |
|
e_version: EV_CURRENT, |
45 |
|
e_machine: ELF_MACHINE, |
46 |
|
e_ehsize: sizeof hdr, |
47 |
|
e_phentsize: sizeof phdrs[0], |
48 |
|
e_phoff: sizeof hdr, /* Fill in e_phnum later. */ |
49 |
|
}; |
50 |
|
off_t offset; |
51 |
|
size_t wrote; |
52 |
|
|
53 |
thread_t *threads; |
thread_t *threads; |
54 |
size_t nthreads; |
size_t nthreads, i; |
55 |
|
off_t notestart; |
56 |
|
|
57 |
vm_address_t addr; |
/* Helper macros for writing notes. */ |
58 |
vm_size_t size; |
#define DEFINE_NOTE(typename) struct { struct note_header hdr; typename data; } |
59 |
vm_prot_t prot, maxprot; |
#define WRITE_NOTE(type, var) ({ \ |
60 |
vm_inherit_t inherit; |
(var).hdr = NOTE_HEADER ((type), sizeof (var)); \ |
61 |
boolean_t shared; |
write_note (&(var).hdr); \ |
62 |
memory_object_name_t objname; |
}) |
63 |
vm_offset_t offset; |
struct note_header |
64 |
|
{ |
65 |
/* Figure out what flavor of machine the task is on. */ |
ElfW(Nhdr) note; |
66 |
if (err = task_get_assignment (task, &pset)) |
char name[4]; |
67 |
goto lose; |
}; |
68 |
err = processor_set_info (pset, PROCESSOR_SET_BASIC_INFO, &host, |
#define NOTE_HEADER(type, size) \ |
69 |
&pinfo, PROCESSOR_SET_BASIC_INFO_COUNT); |
((struct note_header) { { 4, (size), (type) }, "CORE" }) |
70 |
mach_port_deallocate (mach_task_self (), pset); |
inline error_t write_note (struct note_header *hdr) |
71 |
if (err) |
{ |
72 |
goto lose; |
error_t err = 0; |
73 |
err = bfd_mach_host_arch_mach (host, &arch, &machine, &e_machine); |
char *data = (char *) hdr; |
74 |
mach_port_deallocate (mach_task_self (), host); |
size_t size = sizeof *hdr + hdr->note.n_descsz; |
75 |
|
if (corelimit >= 0 && offset + size > corelimit) |
76 |
|
size = corelimit - offset; |
77 |
|
while (size > 0) |
78 |
|
{ |
79 |
|
err = io_write (file, data, size, offset, &wrote); |
80 |
|
if (err) |
81 |
|
return err; |
82 |
|
offset = (offset + wrote + 3) &~ 3; /* Pad it to word alignment. */ |
83 |
|
if (wrote > size) |
84 |
|
break; |
85 |
|
data += wrote; |
86 |
|
size -= wrote; |
87 |
|
} |
88 |
|
return err; |
89 |
|
} |
90 |
|
|
91 |
|
struct vm_region_list |
92 |
|
{ |
93 |
|
struct vm_region_list *next; |
94 |
|
vm_prot_t protection; |
95 |
|
vm_address_t start; |
96 |
|
vm_size_t length; |
97 |
|
}; |
98 |
|
struct vm_region_list *regions = NULL, **tailp = ®ions, *r; |
99 |
|
unsigned int nregions = 0; |
100 |
|
|
101 |
|
if (corelimit >= 0 && corelimit < sizeof hdr) |
102 |
|
return EFBIG; |
103 |
|
|
104 |
|
{ |
105 |
|
/* Examine the task and record the locations of contiguous memory |
106 |
|
segments that we will dump into the core file. */ |
107 |
|
|
108 |
|
vm_address_t region_address, last_region_address, last_region_end; |
109 |
|
vm_prot_t last_protection; |
110 |
|
inline void record_last_region (void) |
111 |
|
{ |
112 |
|
if (last_region_end > last_region_address |
113 |
|
&& last_protection != VM_PROT_NONE) |
114 |
|
{ |
115 |
|
struct vm_region_list *region = alloca (sizeof *region); |
116 |
|
*tailp = region; |
117 |
|
tailp = ®ion->next; |
118 |
|
region->next = NULL; |
119 |
|
region->start = last_region_address; |
120 |
|
region->length = last_region_end - last_region_address; |
121 |
|
region->protection = last_protection; |
122 |
|
++nregions; |
123 |
|
} |
124 |
|
} |
125 |
|
|
126 |
|
region_address = last_region_address = last_region_end = VM_MIN_ADDRESS; |
127 |
|
last_protection = VM_PROT_NONE; |
128 |
|
while (region_address < VM_MAX_ADDRESS) |
129 |
|
{ |
130 |
|
vm_prot_t protection; |
131 |
|
vm_prot_t max_protection; |
132 |
|
vm_inherit_t inheritance; |
133 |
|
boolean_t shared; |
134 |
|
mach_port_t object_name; |
135 |
|
vm_offset_t offset; |
136 |
|
vm_size_t region_length = VM_MAX_ADDRESS - region_address; |
137 |
|
|
138 |
|
err = vm_region (task, |
139 |
|
®ion_address, |
140 |
|
®ion_length, |
141 |
|
&protection, |
142 |
|
&max_protection, |
143 |
|
&inheritance, |
144 |
|
&shared, |
145 |
|
&object_name, |
146 |
|
&offset); |
147 |
|
if (err == KERN_NO_SPACE) |
148 |
|
break; |
149 |
|
if (err != KERN_SUCCESS) |
150 |
|
return err; |
151 |
|
|
152 |
|
if (protection == last_protection && region_address == last_region_end) |
153 |
|
/* This region is contiguous with and indistinguishable from |
154 |
|
the previous one, so we just extend that one. */ |
155 |
|
last_region_end = region_address += region_length; |
156 |
|
else |
157 |
|
{ |
158 |
|
/* This region is distinct from the last one we saw, |
159 |
|
so record that previous one. */ |
160 |
|
record_last_region (); |
161 |
|
last_region_address = region_address; |
162 |
|
last_region_end = region_address += region_length; |
163 |
|
last_protection = protection; |
164 |
|
} |
165 |
|
} |
166 |
|
/* Record the final region. */ |
167 |
|
record_last_region (); |
168 |
|
} |
169 |
|
|
170 |
|
/* Now we start laying out the file. */ |
171 |
|
offset = round_page (sizeof hdr + ((nregions + 1) * sizeof *phdrs)); |
172 |
|
|
173 |
|
/* Final check for tiny core limit. From now on, we will simply truncate |
174 |
|
the file at CORELIMIT but not change the contents of what we write. */ |
175 |
|
if (corelimit >= 0 && corelimit < offset) |
176 |
|
return EFBIG; |
177 |
|
|
178 |
|
/* Now we can complete the file header and write it. */ |
179 |
|
hdr.e_phnum = nregions + 1; |
180 |
|
err = io_write (file, (char *) &hdr, sizeof hdr, 0, &wrote); |
181 |
if (err) |
if (err) |
182 |
goto lose; |
return err; |
183 |
|
if (wrote < sizeof hdr) |
184 |
|
return EGRATUITOUS; /* XXX */ |
185 |
|
|
186 |
if (err = task_threads (task, &threads, &nthreads)) |
/* Now we can write the various notes. */ |
187 |
goto lose; |
notestart = offset; |
188 |
|
|
189 |
/* Create a BFD section to describe each contiguous chunk |
/* First a dull note containing the results of `uname', a la Solaris. */ |
190 |
of the task's address space with the same stats. */ |
{ |
191 |
sec = NULL; |
DEFINE_NOTE (struct utsname) note; |
192 |
addr = 0; |
if (uname (¬e.data) == 0) /* XXX Use proc_uname on task's proc port? */ |
193 |
while (!vm_region (task, &addr, &size, &prot, &maxprot, |
err = WRITE_NOTE (NT_UTSNAME, note); |
194 |
&inherit, &shared, &objname, &offset)) |
} |
195 |
{ |
if (err || (corelimit >= 0 && corelimit <= offset)) |
196 |
mach_port_deallocate (mach_task_self (), objname); |
return err; |
197 |
|
|
198 |
if (prot != VM_PROT_NONE) |
#if 0 |
199 |
{ |
/* The pstatus_t note should contain the death info and some process-global |
200 |
flagword flags = SEC_NO_FLAGS; |
info we should get from the proc server, but no thread-specific info |
201 |
|
like register state. We need to define this type. */ |
202 |
|
{ |
203 |
|
DEFINE_NOTE (pstatus_t) note; |
204 |
|
note.data.pr_info.si_signo = signo; |
205 |
|
note.data.pr_info.si_code = sigcode; |
206 |
|
note.data.pr_info.si_errno = sigerror; |
207 |
|
err = WRITE_NOTE (NT_PSTATUS, note); |
208 |
|
} |
209 |
|
if (err || (corelimit >= 0 && corelimit <= offset)) |
210 |
|
return err; |
211 |
|
#endif |
212 |
|
|
213 |
if (!(prot & VM_PROT_WRITE)) |
/* Now examine all the threads in the task. |
214 |
flags |= SEC_READONLY; |
For each thread we produce one or more notes. */ |
215 |
if (!(prot & VM_PROT_EXECUTE)) |
err = task_threads (task, &threads, &nthreads); |
216 |
flags |= SEC_DATA; |
if (err) |
217 |
|
return err; |
218 |
if (sec != NULL && |
for (i = 0; i < nthreads; ++i) |
219 |
(vm_address_t) (bfd_section_vma (bfd, sec) + |
{ |
220 |
bfd_section_size (bfd, sec)) == addr && |
{ |
221 |
flags == (bfd_get_section_flags (bfd, sec) & |
/* lwpinfo_t a la Solaris gives thread's CPU time and such. */ |
222 |
(SEC_READONLY|SEC_DATA))) |
DEFINE_NOTE (struct thread_basic_info) note; |
223 |
/* Coalesce with the previous section. */ |
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; |
224 |
bfd_set_section_size (bfd, sec, |
err = thread_info (threads[i], THREAD_BASIC_INFO, |
225 |
bfd_section_size (bfd, sec) + size); |
(thread_info_t)¬e.data, &count); |
226 |
else |
if (err == 0) |
227 |
{ |
err = WRITE_NOTE (NT_LWPSINFO, note); |
228 |
/* Make a new section (which might grow by |
else /* Just skip it if we can't get the info. */ |
229 |
the next region being coalesced onto it). */ |
err = 0; |
230 |
char *name = bfd_intuit_section_name (addr, size, &flags); |
} |
231 |
if (name == NULL) |
if (err || (corelimit >= 0 && corelimit <= offset)) |
232 |
{ |
break; |
233 |
/* No guess from BFD. */ |
|
234 |
if (asprintf (&name, "[%p,%p) %c%c%c", |
#ifdef WRITE_THREAD_NOTES |
235 |
(void *) addr, (void *) (addr + size), |
/* XXX Here would go the note flavors for machine thread states. */ |
236 |
(prot & VM_PROT_READ) ? 'r' : '-', |
err = WRITE_THREAD_NOTES (i, threads[i]); |
237 |
(prot & VM_PROT_WRITE) ? 'w' : '-', |
#endif |
238 |
(prot & VM_PROT_EXECUTE) ? 'x' : '-') == -1) |
if (err || (corelimit >= 0 && corelimit <= offset)) |
239 |
goto lose; |
break; |
240 |
} |
mach_port_deallocate (mach_task_self (), threads[i]); |
|
sec = bfd_make_section (name); |
|
|
bfd_set_section_flags (bfd, sec, flags); |
|
|
bfd_set_section_vma (bfd, sec, addr); |
|
|
bfd_set_section_size (bfd, sec, size); |
|
|
} |
|
|
} |
|
241 |
} |
} |
242 |
|
while (i < nthreads) |
243 |
|
mach_port_deallocate (mach_task_self (), threads[i++]); |
244 |
|
munmap (threads, nthreads * sizeof *threads); |
245 |
|
if (err || (corelimit >= 0 && corelimit <= offset)) |
246 |
|
return err; |
247 |
|
|
248 |
|
/* Make an array of program headers and fill them in. |
249 |
|
The first one describes the note segment. */ |
250 |
|
ph = phdrs = alloca ((nregions + 1) * sizeof *phdrs); |
251 |
|
|
252 |
/* Write all the sections' data. */ |
memset (ph, 0, sizeof *ph); |
253 |
for (sec = bfd->sections; sec != NULL; sec = sec->next) |
ph->p_type = PT_NOTE; |
254 |
|
ph->p_offset = notestart; |
255 |
|
ph->p_filesz = offset - notestart; |
256 |
|
++ph; |
257 |
|
|
258 |
|
/* Now make ELF program headers for each of the record memory regions. |
259 |
|
Consistent with the Linux kernel, we create PT_LOAD headers with |
260 |
|
p_filesz = 0 for the read-only segments that we are not dumping |
261 |
|
into the file. */ |
262 |
|
for (r = regions; r != NULL; r = r->next) |
263 |
{ |
{ |
264 |
void *data; |
memset (ph, 0, sizeof *ph); |
265 |
err = vm_read (task, bfd_section_vma (bfd, sec), |
ph->p_type = PT_LOAD; |
266 |
bfd_section_size (bfd, sec), &data); |
ph->p_align = vm_page_size; |
267 |
if (err) |
ph->p_flags = (((r->protection & VM_PROT_READ) ? PF_R : 0) |
268 |
/* XXX What to do? |
| ((r->protection & VM_PROT_WRITE) ? PF_W : 0) |
269 |
1. lose |
| ((r->protection & VM_PROT_EXECUTE) ? PF_X : 0)); |
270 |
2. remove this section |
ph->p_vaddr = r->start; |
271 |
3. mark this section as having ungettable contents (how?) |
ph->p_memsz = r->length; |
272 |
*/ |
ph->p_filesz = (r->protection & VM_PROT_WRITE) ? ph->p_memsz : 0; |
273 |
goto lose; |
ph->p_offset = offset; |
274 |
err = bfd_set_section_contents (bfd, sec, data, 0, |
offset += ph->p_filesz; |
275 |
bfd_section_size (bfd, sec)); |
++ph; |
|
munmap ((caddr_t) data, bfd_section_size (bfd, sec)); |
|
|
if (err) |
|
|
goto bfdlose; |
|
276 |
} |
} |
277 |
|
|
278 |
|
/* Now write the memory segment data. */ |
279 |
|
for (ph = phdrs; ph < &phdrs[nregions]; ++ph) |
280 |
|
if (ph->p_filesz > 0) |
281 |
|
{ |
282 |
|
vm_address_t va = ph->p_vaddr; |
283 |
|
vm_size_t sz = ph->p_memsz; |
284 |
|
off_t ofs = ph->p_offset; |
285 |
|
int wrote_any = 0; |
286 |
|
do |
287 |
|
{ |
288 |
|
pointer_t copied; |
289 |
|
int copy_count; |
290 |
|
err = vm_read (task, va, sz, &copied, ©_count); |
291 |
|
if (err == 0) |
292 |
|
{ |
293 |
|
char *data = (void *) copied; |
294 |
|
size_t left = copy_count, wrote; |
295 |
|
|
296 |
|
va += copy_count; |
297 |
|
sz -= copy_count; |
298 |
|
|
299 |
|
do |
300 |
|
{ |
301 |
|
if (corelimit >= 0 && ofs + left > corelimit) |
302 |
|
left = corelimit - ofs; |
303 |
|
err = io_write (file, data, left, ofs, &wrote); |
304 |
|
if (err) |
305 |
|
break; |
306 |
|
ofs += wrote; |
307 |
|
data += wrote; |
308 |
|
left -= wrote; |
309 |
|
if (ofs >= corelimit) |
310 |
|
break; |
311 |
|
} while (left > 0); |
312 |
|
|
313 |
|
munmap ((void *) copied, copy_count); |
314 |
|
|
315 |
|
if (left < copy_count) |
316 |
|
wrote_any = 1; |
317 |
|
} |
318 |
|
else |
319 |
|
{ |
320 |
|
/* Leave a hole in the file for pages we can't read. */ |
321 |
|
va += vm_page_size; |
322 |
|
sz -= vm_page_size; |
323 |
|
ofs += vm_page_size; |
324 |
|
} |
325 |
|
} while (sz > 0 && (corelimit < 0 || ofs < corelimit)); |
326 |
|
|
327 |
|
if (! wrote_any) |
328 |
|
/* If we failed to write any contents at all, |
329 |
|
don't claim the big hole as the contents. */ |
330 |
|
ph->p_filesz = 0; |
331 |
|
} |
332 |
|
|
333 |
|
/* Finally, we go back and write the program headers. */ |
334 |
|
err = io_write (file, (char *) phdrs, (nregions + 1) * sizeof phdrs[0], |
335 |
|
sizeof hdr, &wrote); |
336 |
|
|
337 |
|
return err; |
338 |
|
} |