int use_blk(ALLOC * a, handle_t h, void *buf, size_t len) { size_t need = len_need(len); size_t padding = len2atom(len) * ATOM_LEN - need; unsigned char *b; if ((b = buf_get(a, need + padding)) == NULL) return -1; if (len <= CTBLK_MAXSHORT) { b[0] = CTBLK_SHORT; b[1] = (unsigned char)len; memcpy(&b[2], buf, len); bzero(&b[2 + len], padding); } else { b[0] = CTBLK_LONG; uint16tob(&b[1], len); memcpy(&b[3], buf, len); bzero(&b[3 + len], padding); } if (writeat(a->fd, b, need + padding, hdl2off(h)) != need + padding) { buf_put(a, b); return -1; } buf_put(a, b); return 0; }
int free_blk(ALLOC * a, handle_t h, handle_t atoms) { if (hdl2off(h + atoms) >= fsize(a->fd)) return fshrink(a->fd, hdl2off(h)); unsigned char buf[16]; handle_t prev = 0, next; int idx; switch (atoms) { case 0: return -1; case 1: buf[0] = buf[15] = FRBLK_SINGLE; break; default: buf[0] = buf[15] = FRBLK_LONG; if (write_handle(a->fd, atoms, hdl2off(h) + 16) != 0) return -1; } if (writeat(a->fd, buf, sizeof(buf), hdl2off(h)) != sizeof(buf)) return -1; idx = flt_idx(atoms); next = a->flt[idx]; a->flt[idx] = h; if (list_setprev(a, h, prev) != 0) return -1; if (list_setnext(a, h, next) != 0) return -1; return list_setprev(a, next, h); }
int _realloc_blk(ALLOC * a, handle_t handle, void *buf, size_t len) { off_t offset; size_t olen; handle_t prev_handle = 0, new_handle, old_atoms, new_atoms; int redirected = 0; unsigned char bytes[16]; Retry: offset = hdl2off(handle); if (readat(a->fd, bytes, 8, offset) != 8) return -1; switch (bytes[0]) { case CTBLK_SHORT: olen = (size_t) bytes[1]; break; case CTBLK_LONG: olen = (size_t) b2uint16(&bytes[1]); break; case REBLK: if (redirected) // allow only once redirect return -1; prev_handle = handle; handle = b2hdl(&bytes[1]); redirected = 1; goto Retry; default: xerrno = FATAL_BLKTAG; return -1; } old_atoms = len2atom(olen); new_atoms = len2atom(len); if (old_atoms == new_atoms) return use_blk(a, handle, buf, len); else if (old_atoms > new_atoms) { handle_t h_free = handle + new_atoms; if (free_blk(a, h_free, old_atoms - new_atoms) != 0) return -1; return use_blk(a, handle, buf, len); } if (prev_handle != 0) { if (dealloc_blk(a, handle) != 0) return -1; } else { if (old_atoms > 1) if (free_blk(a, handle + 1, old_atoms - 1) != 0) return -1; prev_handle = handle; } if ((new_handle = alloc_blk(a, buf, len)) == 0) return -1; bytes[0] = REBLK; hdl2b(&bytes[1], new_handle); bzero(&bytes[8], 8); if (writeat(a->fd, bytes, 16, hdl2off(prev_handle)) != 16) return -1; return 0; }
int flt_remove(ALLOC * a, int idx, handle_t prev, handle_t next) { if (prev == 0) { // head a->flt[idx] = next; if (writeat(a->fd, a->flt, FLT_LEN, 0) != FLT_LEN) return -1; return list_setprev(a, next, 0); } if (list_setprev(a, next, prev) != 0) return -1; return list_setnext(a, prev, next); }
/****************************************************************** * dump_memory_info * * dumps information about the memory of the process (stack of the threads) */ static void dump_memory_info(struct dump_context* dc) { MINIDUMP_MEMORY_LIST mdMemList; MINIDUMP_MEMORY_DESCRIPTOR mdMem; DWORD written; unsigned i, pos, len; RVA rva_base; char tmp[1024]; mdMemList.NumberOfMemoryRanges = dc->num_mem; append(dc, &mdMemList.NumberOfMemoryRanges, sizeof(mdMemList.NumberOfMemoryRanges)); rva_base = dc->rva; dc->rva += mdMemList.NumberOfMemoryRanges * sizeof(mdMem); for (i = 0; i < dc->num_mem; i++) { mdMem.StartOfMemoryRange = dc->mem[i].base; mdMem.Memory.Rva = dc->rva; mdMem.Memory.DataSize = dc->mem[i].size; SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN); for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp)) { len = min(dc->mem[i].size - pos, sizeof(tmp)); if (ReadProcessMemory(dc->hProcess, (void*)(ULONG)(dc->mem[i].base + pos), tmp, len, NULL)) WriteFile(dc->hFile, tmp, len, &written, NULL); } dc->rva += mdMem.Memory.DataSize; writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem)); if (dc->mem[i].rva) { writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva)); } } }
int init_allocator(ALLOC * a, int fd, int oflag) { a->fd = fd; if ((a->lru = calloc(LRU_NSLOT, sizeof(struct lru_node *))) == NULL) { xerrno = FATAL_NOMEM; return -1; } a->lru_head = a->lru_tail = NULL; a->lru_size = 0; bzero(a->flt, FLT_LEN); if ((oflag & O_TRUNC) || fsize(fd) == 0) { if (fshrink(fd, 0) == -1) return -1; if (alloc(fd, 0, FLT_LEN) == -1) return -1; if (writeat(fd, a->flt, FLT_LEN, 0) != FLT_LEN) return -1; return 0; } /* open an existing allocator */ if (readat(fd, a->flt, FLT_LEN, 0) != FLT_LEN) return -1; return 0; }
/****************************************************************** * dump_modules * * Write in File the modules from pcs */ static unsigned dump_modules(struct dump_context* dc, BOOL dump_elf) { MINIDUMP_MODULE mdModule; MINIDUMP_MODULE_LIST mdModuleList; char tmp[1024]; MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp; ULONG i, nmod; RVA rva_base; DWORD flags_out; unsigned sz; for (i = nmod = 0; i < dc->num_modules; i++) { if ((dc->modules[i].is_elf && dump_elf) || (!dc->modules[i].is_elf && !dump_elf)) nmod++; } mdModuleList.NumberOfModules = 0; /* reserve space for mdModuleList * FIXME: since we don't support 0 length arrays, we cannot use the * size of mdModuleList * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file */ /* the stream size is just the size of the module index. It does not include the data for the names of each module. *Technically* the names are supposed to go into the common string table in the minidump file. Since each string is referenced by RVA they can all safely be located anywhere between streams in the file, so the end of this stream is sufficient. */ rva_base = dc->rva; dc->rva += sz = sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod; for (i = 0; i < dc->num_modules; i++) { if ((dc->modules[i].is_elf && !dump_elf) || (!dc->modules[i].is_elf && dump_elf)) continue; flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord; if (dc->type & MiniDumpWithDataSegs) flags_out |= ModuleWriteDataSeg; if (dc->type & MiniDumpWithProcessThreadData) flags_out |= ModuleWriteTlsData; if (dc->type & MiniDumpWithCodeSegs) flags_out |= ModuleWriteCodeSegs; ms->Length = (lstrlenW(dc->modules[i].name) + 1) * sizeof(WCHAR); if (sizeof(ULONG) + ms->Length > sizeof(tmp)) FIXME("Buffer overflow!!!\n"); lstrcpyW(ms->Buffer, dc->modules[i].name); if (dc->cb) { MINIDUMP_CALLBACK_INPUT cbin; MINIDUMP_CALLBACK_OUTPUT cbout; cbin.ProcessId = dc->pid; cbin.ProcessHandle = dc->hProcess; cbin.CallbackType = ModuleCallback; cbin.u.Module.FullPath = ms->Buffer; cbin.u.Module.BaseOfImage = dc->modules[i].base; cbin.u.Module.SizeOfImage = dc->modules[i].size; cbin.u.Module.CheckSum = dc->modules[i].checksum; cbin.u.Module.TimeDateStamp = dc->modules[i].timestamp; memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo)); cbin.u.Module.CvRecord = NULL; cbin.u.Module.SizeOfCvRecord = 0; cbin.u.Module.MiscRecord = NULL; cbin.u.Module.SizeOfMiscRecord = 0; cbout.u.ModuleWriteFlags = flags_out; if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout)) continue; flags_out &= cbout.u.ModuleWriteFlags; } if (flags_out & ModuleWriteModule) { /* fetch CPU dependent module info (like UNWIND_INFO) */ dbghelp_current_cpu->fetch_minidump_module(dc, i, flags_out); mdModule.BaseOfImage = dc->modules[i].base; mdModule.SizeOfImage = dc->modules[i].size; mdModule.CheckSum = dc->modules[i].checksum; mdModule.TimeDateStamp = dc->modules[i].timestamp; mdModule.ModuleNameRva = dc->rva; ms->Length -= sizeof(WCHAR); append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR)); fetch_module_versioninfo(ms->Buffer, &mdModule.VersionInfo); mdModule.CvRecord.DataSize = 0; /* FIXME */ mdModule.CvRecord.Rva = 0; /* FIXME */ mdModule.MiscRecord.DataSize = 0; /* FIXME */ mdModule.MiscRecord.Rva = 0; /* FIXME */ mdModule.Reserved0 = 0; /* FIXME */ mdModule.Reserved1 = 0; /* FIXME */ writeat(dc, rva_base + sizeof(mdModuleList.NumberOfModules) + mdModuleList.NumberOfModules++ * sizeof(mdModule), &mdModule, sizeof(mdModule)); } } writeat(dc, rva_base, &mdModuleList.NumberOfModules, sizeof(mdModuleList.NumberOfModules)); return sz; }
/****************************************************************** * append * * writes a new chunk of data to the minidump, increasing the current * rva in dc */ static void append(struct dump_context* dc, const void* data, unsigned size) { writeat(dc, dc->rva, data, size); dc->rva += size; }
/****************************************************************** * MiniDumpWriteDump (DEBUGHLP.@) * */ BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile, MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, PMINIDUMP_CALLBACK_INFORMATION CallbackParam) { MINIDUMP_HEADER mdHead; MINIDUMP_DIRECTORY mdDir; DWORD i, nStreams, idx_stream; struct dump_context dc; dc.hProcess = hProcess; dc.hFile = hFile; dc.pid = pid; dc.module = NULL; dc.num_module = 0; dc.cb = CallbackParam; dc.type = DumpType; dc.mem = NULL; dc.num_mem = 0; dc.rva = 0; if (!fetch_process_info(&dc)) return FALSE; fetch_module_info(&dc); /* 1) init */ nStreams = 6 + (ExceptionParam ? 1 : 0) + (UserStreamParam ? UserStreamParam->UserStreamCount : 0); if (DumpType & MiniDumpWithDataSegs) FIXME("NIY MiniDumpWithDataSegs\n"); if (DumpType & MiniDumpWithFullMemory) FIXME("NIY MiniDumpWithFullMemory\n"); if (DumpType & MiniDumpWithHandleData) FIXME("NIY MiniDumpWithHandleData\n"); if (DumpType & MiniDumpFilterMemory) FIXME("NIY MiniDumpFilterMemory\n"); if (DumpType & MiniDumpScanMemory) FIXME("NIY MiniDumpScanMemory\n"); /* 2) write header */ mdHead.Signature = MINIDUMP_SIGNATURE; mdHead.Version = MINIDUMP_VERSION; mdHead.NumberOfStreams = nStreams; mdHead.StreamDirectoryRva = sizeof(mdHead); mdHead.u.TimeDateStamp = time(NULL); mdHead.Flags = DumpType; append(&dc, &mdHead, sizeof(mdHead)); /* 3) write stream directories */ dc.rva += nStreams * sizeof(mdDir); idx_stream = 0; /* 3.1) write data stream directories */ mdDir.StreamType = ThreadListStream; mdDir.Location.Rva = dc.rva; dump_threads(&dc); mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = ModuleListStream; mdDir.Location.Rva = dc.rva; dump_modules(&dc, FALSE); mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */ mdDir.Location.Rva = dc.rva; dump_modules(&dc, TRUE); mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = MemoryListStream; mdDir.Location.Rva = dc.rva; dump_memory_info(&dc); mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = SystemInfoStream; mdDir.Location.Rva = dc.rva; dump_system_info(&dc); mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); mdDir.StreamType = MiscInfoStream; mdDir.Location.Rva = dc.rva; dump_misc_info(&dc); mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); /* 3.2) write exception information (if any) */ if (ExceptionParam) { mdDir.StreamType = ExceptionStream; mdDir.Location.Rva = dc.rva; dump_exception_info(&dc, ExceptionParam); mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva; writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); } /* 3.3) write user defined streams (if any) */ if (UserStreamParam) { for (i = 0; i < UserStreamParam->UserStreamCount; i++) { mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type; mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize; mdDir.Location.Rva = dc.rva; writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir), &mdDir, sizeof(mdDir)); append(&dc, UserStreamParam->UserStreamArray[i].Buffer, UserStreamParam->UserStreamArray[i].BufferSize); } } HeapFree(GetProcessHeap(), 0, dc.pcs_buffer); HeapFree(GetProcessHeap(), 0, dc.mem); HeapFree(GetProcessHeap(), 0, dc.module); return TRUE; }
/****************************************************************** * dump_threads * * Dumps into File the information about running threads */ static void dump_threads(struct dump_context* dc) { MINIDUMP_THREAD mdThd; MINIDUMP_THREAD_LIST mdThdList; unsigned i; RVA rva_base; DWORD flags_out; CONTEXT ctx; mdThdList.NumberOfThreads = 0; rva_base = dc->rva; dc->rva += sizeof(mdThdList.NumberOfThreads) + dc->spi->dwThreadCount * sizeof(mdThd); for (i = 0; i < dc->spi->dwThreadCount; i++) { fetch_thread_info(dc, i, &mdThd, &ctx); flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext | ThreadWriteInstructionWindow; if (dc->type & MiniDumpWithProcessThreadData) flags_out |= ThreadWriteThreadData; if (dc->type & MiniDumpWithThreadInfo) flags_out |= ThreadWriteThreadInfo; if (dc->cb) { MINIDUMP_CALLBACK_INPUT cbin; MINIDUMP_CALLBACK_OUTPUT cbout; cbin.ProcessId = dc->pid; cbin.ProcessHandle = dc->hProcess; cbin.CallbackType = ThreadCallback; cbin.u.Thread.ThreadId = dc->spi->ti[i].dwThreadID; cbin.u.Thread.ThreadHandle = 0; /* FIXME */ memcpy(&cbin.u.Thread.Context, &ctx, sizeof(CONTEXT)); cbin.u.Thread.SizeOfContext = sizeof(CONTEXT); cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange; cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange + mdThd.Stack.Memory.DataSize; cbout.u.ThreadWriteFlags = flags_out; if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout)) continue; flags_out &= cbout.u.ThreadWriteFlags; } if (flags_out & ThreadWriteThread) { if (ctx.ContextFlags && (flags_out & ThreadWriteContext)) { mdThd.ThreadContext.Rva = dc->rva; mdThd.ThreadContext.DataSize = sizeof(CONTEXT); append(dc, &ctx, sizeof(CONTEXT)); } if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack)) { add_memory_block(dc, mdThd.Stack.StartOfMemoryRange, mdThd.Stack.Memory.DataSize, rva_base + sizeof(mdThdList.NumberOfThreads) + mdThdList.NumberOfThreads * sizeof(mdThd) + FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva)); } writeat(dc, rva_base + sizeof(mdThdList.NumberOfThreads) + mdThdList.NumberOfThreads * sizeof(mdThd), &mdThd, sizeof(mdThd)); mdThdList.NumberOfThreads++; } if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow)) { /* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP * - also crop values across module boundaries, * - and don't make it i386 dependent */ /* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */ } } writeat(dc, rva_base, &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads)); }
/****************************************************************** * dump_modules * * Write in File the modules from pcs */ static void dump_modules(struct dump_context* dc, BOOL dump_elf) { MINIDUMP_MODULE mdModule; MINIDUMP_MODULE_LIST mdModuleList; char tmp[1024]; MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp; ULONG i, nmod; RVA rva_base; DWORD flags_out; for (i = nmod = 0; i < dc->num_module; i++) { if ((dc->module[i].is_elf && dump_elf) || (!dc->module[i].is_elf && !dump_elf)) nmod++; } mdModuleList.NumberOfModules = 0; /* reserve space for mdModuleList * FIXME: since we don't support 0 length arrays, we cannot use the * size of mdModuleList * FIXME: if we don't ask for all modules in cb, we'll get a hole in the file */ rva_base = dc->rva; dc->rva += sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod; for (i = 0; i < dc->num_module; i++) { if ((dc->module[i].is_elf && !dump_elf) || (!dc->module[i].is_elf && dump_elf)) continue; flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord; if (dc->type & MiniDumpWithDataSegs) flags_out |= ModuleWriteDataSeg; if (dc->type & MiniDumpWithProcessThreadData) flags_out |= ModuleWriteTlsData; if (dc->type & MiniDumpWithCodeSegs) flags_out |= ModuleWriteCodeSegs; ms->Length = MultiByteToWideChar(CP_ACP, 0, dc->module[i].name, -1, NULL, 0) * sizeof(WCHAR); if (sizeof(ULONG) + ms->Length > sizeof(tmp)) FIXME("Buffer overflow!!!\n"); MultiByteToWideChar(CP_ACP, 0, dc->module[i].name, -1, ms->Buffer, ms->Length/sizeof(WCHAR)); if (dc->cb) { MINIDUMP_CALLBACK_INPUT cbin; MINIDUMP_CALLBACK_OUTPUT cbout; cbin.ProcessId = dc->pid; cbin.ProcessHandle = dc->hProcess; cbin.CallbackType = ModuleCallback; cbin.u.Module.FullPath = ms->Buffer; cbin.u.Module.BaseOfImage = dc->module[i].base; cbin.u.Module.SizeOfImage = dc->module[i].size; cbin.u.Module.CheckSum = dc->module[i].checksum; cbin.u.Module.TimeDateStamp = dc->module[i].timestamp; memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo)); cbin.u.Module.CvRecord = NULL; cbin.u.Module.SizeOfCvRecord = 0; cbin.u.Module.MiscRecord = NULL; cbin.u.Module.SizeOfMiscRecord = 0; cbout.u.ModuleWriteFlags = flags_out; if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout)) continue; flags_out &= cbout.u.ModuleWriteFlags; } if (flags_out & ModuleWriteModule) { mdModule.BaseOfImage = dc->module[i].base; mdModule.SizeOfImage = dc->module[i].size; mdModule.CheckSum = dc->module[i].checksum; mdModule.TimeDateStamp = dc->module[i].timestamp; mdModule.ModuleNameRva = dc->rva; ms->Length -= sizeof(WCHAR); append(dc, ms, sizeof(ULONG) + ms->Length); memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo)); /* FIXME */ mdModule.CvRecord.DataSize = 0; /* FIXME */ mdModule.CvRecord.Rva = 0; /* FIXME */ mdModule.MiscRecord.DataSize = 0; /* FIXME */ mdModule.MiscRecord.Rva = 0; /* FIXME */ mdModule.Reserved0 = 0; /* FIXME */ mdModule.Reserved1 = 0; /* FIXME */ writeat(dc, rva_base + sizeof(mdModuleList.NumberOfModules) + mdModuleList.NumberOfModules++ * sizeof(mdModule), &mdModule, sizeof(mdModule)); } } writeat(dc, rva_base, &mdModuleList.NumberOfModules, sizeof(mdModuleList.NumberOfModules)); }