windowsgowinapisdkwdk

MiniDumpWriteDump with Callback in Go


I'm trying to implement MiniDumpWriteDump with a callback in Go.

Call of MiniDumpWriteDump:

    callback := syscall.NewCallback(miniDumpCallback)
    var newCallbackRoutine MINIDUMP_CALLBACK_INFORMATION
    newCallbackRoutine.CallbackParam = 0
    newCallbackRoutine.CallbackRoutine = callback
    ret, _, err := miniDumpWriteDump.Call(
        uintptr(processHandle),
        uintptr(processId),
        uintptr(dumpFile),
        uintptr(options),
        0,
        0,
        uintptr(unsafe.Pointer(&newCallbackRoutine)),
    )

Callback function itself:

func miniDumpCallback(_ uintptr, CallbackInput *MINIDUMP_CALLBACK_INPUT, _ uintptr) uintptr {
    fmt.Println(CallbackInput.ProcessId, CallbackInput.CallbackType)
    return 1
}

Type definitions:

type MINIDUMP_CALLBACK_INPUT struct {
    ProcessId     win32.ULONG
    ProcessHandle win32.HANDLE
    CallbackType  win32.ULONG
    CallbackInfo  uintptr
}

type MINIDUMP_CALLBACK_INFORMATION struct {
    CallbackRoutine uintptr
    CallbackParam   uintptr
}

Callback is called and some fields recieve the correct data, but some get nonsensical values.

For example, the callback above recieves the ProcessId field of CallbackInput correctly, but recieves random integers as CallbackType, when it shoud be recieving MINIDUMP_CALLBACK_TYPE enumeration.

Output:

12544 0
12544 1133445120
12544 12548
12544 13028
12544 1114112
12544 1023344640
12544 999620608
12544 990117888
12544 992542720
12544 1005518848
12544 1994850304
12544 1114112
12544 1994915840

Solution

  • As comments suggested, the problem was with structure alignment.

    As @IInspectable explained, minidumpapiset.h, which exports MiniDumpWriteDump function and MINIDUMP_CALLBACK_INPUT struct, uses 4-byte alignment for both, 32bit and 64bit architectures, while Go uses 8-byte alignment by default for 64bit and offers no automatic way of changing it.

    The solution is to manually read the struct. Here's a worknig example:

    type MINIDUMP_CALLBACK_INPUT struct {
    ProcessId     uint32
    ProcessHandle uintptr
    CallbackType  uint32
    CallbackInfo uintptr}
    
    func ptrToMinidumpCallbackInput(ptrCallbackInput uintptr) MINIDUMP_CALLBACK_INPUT{
        var input MINIDUMP_CALLBACK_INPUT
        input.ProcessId = *(*uint32)(unsafe.Pointer(ptrCallbackInput))
        input.ProcessHandle = *(*uintptr)(unsafe.Pointer(ptrCallbackInput + unsafe.Sizeof(uint32(0))))
        input.CallbackType = *(*uint32)(unsafe.Pointer(ptrCallbackInput + unsafe.Sizeof(uint32(0)) + unsafe.Sizeof(uintptr(0))))
        input.CallbackInfo = *(*uintptr)(unsafe.Pointer(ptrCallbackInput + unsafe.Sizeof(uint32(0)) + unsafe.Sizeof(uintptr(0)) + unsafe.Sizeof(uint32(0))))
        return input}
    

    The original code should work fine on 32bit architectures, since it's padding(4-byte) matches with the padding minidumpapiset.h uses.