Cygwin: Use MEMORY_WORKING_SET_EX_INFORMATION in dumper

Use the (undocumented) MEMORY_WORKING_SET_EX_INFORMATION in dumper to
determine if a MEM_IMAGE region is unsharable, and hence has been
modified.

After this, we will end up dumping memory regions where:

- state is MEM_COMMIT (i.e. is not MEM_RESERVE or MEM_FREE), and
-- type is MEM_PRIVATE and protection allows reads (i.e. not a guardpage), or
-- type is MEM_IMAGE and attribute is non-sharable (i.e. it was WC, got
   written to, and is now a RW copy)
This commit is contained in:
Jon Turney 2020-07-07 20:54:27 +01:00
parent 35227fec97
commit b245014abd
No known key found for this signature in database
GPG Key ID: C7C86F0370285C81
3 changed files with 65 additions and 8 deletions

View File

@ -524,11 +524,11 @@ error_start=x:\path\to\dumper.exe
<command>dumper</command> exits, the target process is terminated too. </para>
<para> To save space in the core dump, <command>dumper</command> doesn't
write those portions of target process' memory space that are loaded from
executable and dll files and are unchangeable, such as program code and
debug info. Instead, <command>dumper</command> saves paths to files which
write those portions of the target process's memory space that are loaded
from executable and dll files and are unchanged (e.g. program code).
Instead, <command>dumper</command> saves paths to the files which
contain that data. When a core dump is loaded into gdb, it uses these
paths to load appropriate files. That means that if you create a core
paths to load the appropriate files. That means that if you create a core
dump on one machine and try to debug it on another, you'll need to place
identical copies of the executable and dlls in the same directories as on
the machine where the core dump was created. </para>

View File

@ -116,7 +116,7 @@ CYGWIN_BINS += dumper.exe
dumper.o module_info.o: CXXFLAGS += -I$(top_srcdir)/include
dumper.o: dumper.h
dumper.exe: module_info.o
dumper.exe: CYGWIN_LDFLAGS += -lpsapi -lbfd -lintl -liconv -liberty ${ZLIB}
dumper.exe: CYGWIN_LDFLAGS += -lpsapi -lbfd -lintl -liconv -liberty ${ZLIB} -lntdll
else
all: warn_dumper
endif

View File

@ -266,6 +266,46 @@ void protect_dump(DWORD protect, char *buf)
strcat (buf, pt[i]);
}
typedef enum _MEMORY_INFORMATION_CLASS
{
MemoryWorkingSetExInformation = 4, // MEMORY_WORKING_SET_EX_INFORMATION
} MEMORY_INFORMATION_CLASS;
extern "C"
NTSTATUS
NtQueryVirtualMemory(HANDLE ProcessHandle,
LPVOID BaseAddress,
MEMORY_INFORMATION_CLASS MemoryInformationClass,
LPVOID MemoryInformation,
SIZE_T MemoryInformationLength,
SIZE_T *ReturnLength);
typedef struct _MEMORY_WORKING_SET_EX_INFORMATION
{
LPVOID VirtualAddress;
ULONG_PTR Long;
} MEMORY_WORKING_SET_EX_INFORMATION;
#define MWSEI_ATTRIB_SHARED (0x1 << 15)
static BOOL
getRegionAttributes(HANDLE hProcess, LPVOID address, DWORD &attribs)
{
MEMORY_WORKING_SET_EX_INFORMATION mwsei = { address };
NTSTATUS status = NtQueryVirtualMemory(hProcess, 0,
MemoryWorkingSetExInformation,
&mwsei, sizeof(mwsei), 0);
if (!status)
{
attribs = mwsei.Long;
return TRUE;
}
deb_printf("MemoryWorkingSetExInformation failed status %08x\n", status);
return FALSE;
}
int
dumper::collect_memory_sections ()
{
@ -292,10 +332,27 @@ dumper::collect_memory_sections ()
int skip_region_p = 0;
const char *disposition = "dumped";
if ((mbi.Type & MEM_IMAGE) && !(mbi.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE)))
if (mbi.Type & MEM_IMAGE)
{
skip_region_p = 1;
disposition = "skipped due to non-writeable MEM_IMAGE";
DWORD attribs = 0;
if (getRegionAttributes(hProcess, current_page_address, attribs))
{
if (attribs & MWSEI_ATTRIB_SHARED)
{
skip_region_p = 1;
disposition = "skipped due to shared MEM_IMAGE";
}
}
/*
The undocumented MemoryWorkingSetExInformation is allegedly
supported since XP, so should always succeed, but if it fails,
fallback to looking at region protection.
*/
else if (!(mbi.Protect & (PAGE_EXECUTE_READWRITE | PAGE_READWRITE)))
{
skip_region_p = 1;
disposition = "skipped due to non-writeable MEM_IMAGE";
}
}
if (mbi.Protect & PAGE_NOACCESS)