📄 elfcore.c
字号:
#endif } } /* Align all following segments to multiples of page size */ if (note_align) { char scratch[note_align]; memset(scratch, 0, sizeof(scratch)); if (writer(handle, scratch, sizeof(scratch)) != sizeof(scratch)) { goto done; } } /* Write all memory segments */ for (i = 0; i < num_mappings; i++) { if (mappings[i].flags & (PF_ANONYMOUS|PF_W) && writer(handle, (void *)mappings[i].start_address, mappings[i].end_address - mappings[i].start_address) != mappings[i].end_address - mappings[i].start_address) { goto done; } } rc = 0; } } }done: if (is_done(handle)) { rc = 0; } if (loopback[0] >= 0) NO_INTR(sys_close(loopback[0])); if (loopback[1] >= 0) NO_INTR(sys_close(loopback[1])); return rc;}struct CreateArgs { int *fds; int openmax; const char *PATH; const struct CoredumperCompressor* compressors; int zip_in[2]; int zip_out[2];};static int CreatePipelineChild(void *void_arg) { int my_errno; /* scope */ { /* Define a private copy of syscall macros, which does not modify the * global copy of errno. */ #define SYS_ERRNO my_errno #define SYS_INLINE inline #undef SYS_LINUX_SYSCALL_SUPPORT_H #define SYS_PREFIX 0 #include "linux_syscall_support.h" struct CreateArgs *args = (struct CreateArgs *)void_arg; int i; /* Use pipe to tell parent about the compressor that we chose. * Make sure the file handle for the write-end of the pipe is * bigger than 2, so that it does not interfere with the * stdin/stdout/stderr file handles which must be 0-2. */ MY_NO_INTR(sys0_close(args->fds[0])); while (args->fds[1] <= 2) { MY_NO_INTR(args->fds[1] = sys0_dup(args->fds[1])); } sys0_fcntl(args->fds[1], F_SETFD, FD_CLOEXEC); /* Move the filehandles for stdin/stdout/stderr, so that they * map to handles 0-2. stdin/stdout are connected to pipes, and * stderr points to "/dev/null". */ while (args->zip_in[0] <= 2) { MY_NO_INTR(args->zip_in[0] = sys0_dup(args->zip_in[0])); } while (args->zip_out[1] <= 2) { MY_NO_INTR(args->zip_out[1] = sys0_dup(args->zip_out[1])); } MY_NO_INTR(sys0_dup2(args->zip_in[0], 0)); MY_NO_INTR(sys0_dup2(args->zip_out[1], 1)); MY_NO_INTR(sys0_close(2)); MY_NO_INTR(sys0_dup2(sys0_open("/dev/null", O_WRONLY, 0), 2)); /* Close all handles other than stdin/stdout/stderr and the * pipe to the parent. This also takes care of all the filehandles * that we temporarily created by calling sys_dup(). */ for (i = 3; i < args->openmax; i++) if (i != args->fds[1]) MY_NO_INTR(sys0_close(i)); while (args->compressors->compressor != NULL && *args->compressors->compressor) { extern char **environ; const char *compressor = args->compressors->compressor; const char *const *cmd_args = args->compressors->args; /* Try next compressor description. If the compressor exists, * the fds[1] file handle will get closed on exec(). The * parent detects this, and eventually updates * selected_compressor with the compressor that is now running. * * Please note, the caller does not need to call wait() for any * compressor that gets launched, because our parent process is * going to die soon; thus, the compressor will be reaped by "init". */ c_write(args->fds[1], &args->compressors, sizeof(&args->compressors), &my_errno); if (strchr(compressor, '/')) { /* Absolute or relative path precedes name of executable */ sys0_execve(compressor, cmd_args, (const char * const *)environ); } else { /* Search for executable along PATH variable */ const char *ptr = args->PATH; if (ptr != NULL) { for (;;) { const char *end = ptr; while (*end && *end != ':') end++; if (ptr == end) { /* Found current directory in PATH */ sys0_execve(compressor, cmd_args, (const char * const *)environ); } else { /* Compute new file name */ char executable[strlen(compressor) + (end - ptr) + 2]; memcpy(executable, ptr, end-ptr); executable[end - ptr] = '/'; strcpy(executable + (end - ptr + 1), compressor); sys0_execve(executable, cmd_args, (const char * const *)environ); } if (!*end) break; ptr = end + 1; } } } ++args->compressors; } /* No suitable compressor found. Tell parent about it. */ c_write(args->fds[1], &args->compressors, sizeof(&args->compressors), &my_errno); MY_NO_INTR(sys0_close(args->fds[1])); sys__exit(0); return 0; }}/* Create a pipeline for sending the core file from the child process back to * the caller. Optionally include a compressor program in the loop. The * "compressors" variable will be updated to point to the compressor that was * actually used. */static int CreatePipeline(int *fds, int openmax, const char *PATH, const struct CoredumperCompressor** compressors) { /* Create a pipe for communicating between processes */ if (sys_pipe(fds) < 0) return -1; /* Find a suitable compressor program, if necessary */ if (*compressors != NULL && (*compressors)->compressor != NULL) { char stack[4096]; struct CreateArgs args; pid_t comp_pid; args.fds = fds; args.openmax = openmax; args.PATH = PATH; args.compressors = *compressors; if (sys_pipe(args.zip_in) < 0) { fail0: { int saved_errno = errno; NO_INTR(sys_close(fds[0])); NO_INTR(sys_close(fds[1])); errno = saved_errno; return -1; } } else if (sys_pipe(args.zip_out) < 0) { fail1: { int saved_errno = errno; NO_INTR(sys_close(args.zip_in[0])); NO_INTR(sys_close(args.zip_in[1])); errno = saved_errno; goto fail0; } } /* We use clone() here, instead of the more common fork(). This ensures * that the WriteCoreDump() code path never results in making a COW * instance of the processes' address space. This increases the likelihood * that we can dump core files even if we are using a lot of memory and * the kernel disallows overcomitting of memory. * After cloning, both the parent and the child share the same instance * of errno. We must make sure that at least one of these processes * (in our case, the child) uses modified syscall macros that update * a local copy of errno, instead. */ comp_pid = sys_clone(CreatePipelineChild, stack + sizeof(stack) - 16, CLONE_VM|CLONE_UNTRACED|SIGCHLD, &args, 0, 0, 0); if (comp_pid < 0) { int clone_errno = errno; NO_INTR(sys_close(args.zip_out[0])); NO_INTR(sys_close(args.zip_out[1])); errno = clone_errno; goto fail1; } /* Close write-end of pipe, and read from read-end until child closes * its reference to the pipe. */ NO_INTR(sys_close(fds[1])); *compressors = NULL; while (c_read(fds[0], compressors, sizeof(*compressors), &errno)) { } NO_INTR(sys_close(fds[0])); /* Fail if either the child never even executed (unlikely), or * did not find any compressor that could be executed. */ if (*compressors == NULL || (*compressors)->compressor == NULL) { int saved_errno1 = errno; NO_INTR(sys_close(args.zip_out[0])); NO_INTR(sys_close(args.zip_out[1])); errno = saved_errno1; fail2: { int saved_errno2 = errno; NO_INTR(sys_close(args.zip_in[0])); NO_INTR(sys_close(args.zip_in[1])); errno = saved_errno2; return -1; } } if (*(*compressors)->compressor) { /* Found a good compressor program, which is now connected to * zip_in/zip_out. */ fds[0] = args.zip_out[0]; fds[1] = args.zip_in[1]; NO_INTR(sys_close(args.zip_in[0])); NO_INTR(sys_close(args.zip_out[1])); } else { /* No suitable compressor found, but the caller allowed * uncompressed core files. So, just close unneeded file handles, * and reap the child's exit code. */ int status; fds[0] = -1; fds[1] = -1; NO_INTR(sys_close(args.zip_in[0]));NO_INTR(sys_close(args.zip_out[0])); NO_INTR(sys_close(args.zip_in[1]));NO_INTR(sys_close(args.zip_out[1])); while (sys_waitpid(comp_pid, &status, 0) < 0) { if (errno != EINTR) { goto fail2; } } } } return 0;}/* If this code is being built without support for multi-threaded core files, * some of our basic assumptions are not quite right. Most noticably, the * fake thread lister ends up calling InternalGetCoreDump() from the main * (i.e. only) thread in the application, which cannot be ptrace()'d at this * time. This prevents us from retrieving CPU registers. * * We work around this problem by delaying the call to ptrace() until we * have forked. We also need to double-fork here, in order to make sure that * the core writer process can get reaped by "init" after it reaches EOF. */static inline int GetParentRegs(void *frame, regs *cpu, fpregs *fp, fpxregs *fpx, int *hasSSE) {#ifdef THREADS return 1;#else int rc = 0; char scratch[4096]; pid_t pid = getppid(); if (sys_ptrace(PTRACE_ATTACH, pid, (void *)0, (void *)0) == 0 && waitpid(pid, (void *)0, __WALL) >= 0) { memset(scratch, 0xFF, sizeof(scratch)); if (sys_ptrace(PTRACE_GETREGS, pid, scratch, scratch) == 0) { memcpy(cpu, scratch, sizeof(struct regs)); SET_FRAME(*(Frame *)frame, *cpu); memset(scratch, 0xFF, sizeof(scratch)); if (sys_ptrace(PTRACE_GETFPREGS, pid, scratch, scratch) == 0) { memcpy(fp, scratch, sizeof(struct fpregs)); memset(scratch, 0xFF, sizeof(scratch)); #if defined(__i386__) && !defined( __x86_64__) /* Linux on x86-64 stores all FPU registers in the SSE structure */ if (sys_ptrace(PTRACE_GETFPXREGS, pid, scratch, scratch) == 0) { memcpy(fpx, scratch, sizeof(struct fpxregs)); } else { *hasSSE = 0; } #else *hasSSE = 0; #endif rc = 1; } } } sys_ptrace_detach(pid); /* Need to double-fork, so that "init" can reap the core writer upon EOF. */ switch (sys_fork()) { case -1: return 0; case 0: return rc; default: sys__exit(0); }#endif}/* Internal function for generating a core file. This function works for * both single- and multi-threaded core files. It assumes that all threads * are already suspended, and will resume them before returning. * * The caller must make sure that prctl(PR_SET_DUMPABLE, 1) has been called, * or this function might fail. */int InternalGetCoreDump(void *frame, int num_threads, pid_t *pids, va_list ap /* const char *file_name, size_t max_length, const char *PATH, const struct CoredumperCompressor *compressors, const struct CoredumperCompressor **selected_comp */) { long i; int rc = -1, fd = -1, threads = num_threads, hasSSE = 1; user user; prpsinfo prpsinfo; prstatus prstatus; regs thread_regs[threads]; fpregs thread_fpregs[threads]; fpxregs thread_fpxregs[threads]; int pair[2]; int main_pid = ((Frame *)frame)->tid; /* Get thread status */ memset(thread_regs, 0, threads * sizeof(struct regs)); memset(thread_fpregs, 0, threads * sizeof(struct fpregs)); memset(thread_fpxregs, 0, threads * sizeof(struct fpxregs)); /* Threads are already attached, read their registers now */#ifdef THREADS for (i = 0; i < threads; i++) { char scratch[4096]; memset(scratch, 0xFF, sizeof(scratch)); if (sys_ptrace(PTRACE_GETREGS, pids[i], scratch, scratch) == 0) { memcpy(thread_regs + i, scratch, sizeof(struct regs)); if (main_pid == pids[i]) SET_FRAME(*(Frame *)frame, thread_regs[i]); memset(scratch, 0xFF, sizeof(scratch)); if (sys_ptrace(PTRACE_GETFPREGS, pids[i], scratch, scratch) == 0) { memcpy(thread_fpregs + i, scratch, sizeof(struct fpregs)); memset(scratch, 0xFF, sizeof(scratch)); #if defined(__i386__) && !defined( __x86_64__) /* Linux on x86-64 stores all FPU registers in the SSE structure */ if (sys_ptrace(PTRACE_GETFPXREGS, pids[i], scratch, scratch) == 0) { memcpy(thread_fpxregs + i, scratch, sizeof(struct fpxregs)); } else { hasSSE = 0; } #else hasSSE = 0; #endif } else goto ptrace; } else { ptrace: /* Oh, well, undo everything and get out of here */ ResumeAllProcessThreads(threads, pids); goto error; } } /* Get parent's CPU registers, and user data structure */ for (i = 0; i < sizeof(struct user); i += sizeof(int)) sys_ptrace(PTRACE_PEEKUSER, pids[0], (void *)i, ((int *)&user) + i/sizeof(int)); memcpy(&user.regs, thread_regs, sizeof(struct regs));#endif /* Build the PRPSINFO data structure */ memset(&prpsinfo, 0, sizeof(struct prpsinfo)); prpsinfo.pr_sname = 'R'; prpsinfo.pr_nice = sys_getpriority(PRIO_PROCESS, 0);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -