github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/proc/core/minidump/minidump.go (about)

     1  package minidump
     2  
     3  // Package minidump provides a loader for Windows Minidump files.
     4  // Minidump files are the Windows equivalent of unix core dumps.
     5  // They can be created by the kernel when a program crashes (however this is
     6  // disabled for Go programs) or programmatically using either WinDbg or the
     7  // ProcDump utility.
     8  //
     9  // The file format is described on MSDN starting at:
    10  //  https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_header
    11  // which is the structure found at offset 0 on a minidump file.
    12  //
    13  // Further information on the format can be found reading
    14  // chromium-breakpad's minidump loading code, specifically:
    15  //  https://chromium.googlesource.com/breakpad/breakpad/+/master/src/google_breakpad/common/minidump_cpu_amd64.h
    16  // and:
    17  //  https://chromium.googlesource.com/breakpad/breakpad/+/master/src/google_breakpad/common/minidump_format.h
    18  
    19  import (
    20  	"encoding/binary"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"unicode/utf16"
    25  	"unsafe"
    26  
    27  	"github.com/go-delve/delve/pkg/proc/winutil"
    28  )
    29  
    30  type minidumpBuf struct {
    31  	buf  []byte
    32  	kind string
    33  	off  int
    34  	err  error
    35  	ctx  string
    36  }
    37  
    38  func (buf *minidumpBuf) u16() uint16 {
    39  	const stride = 2
    40  	if buf.err != nil {
    41  		return 0
    42  	}
    43  	if buf.off+stride >= len(buf.buf) {
    44  		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
    45  	}
    46  	r := binary.LittleEndian.Uint16(buf.buf[buf.off : buf.off+stride])
    47  	buf.off += stride
    48  	return r
    49  }
    50  
    51  func (buf *minidumpBuf) u32() uint32 {
    52  	const stride = 4
    53  	if buf.err != nil {
    54  		return 0
    55  	}
    56  	if buf.off+stride >= len(buf.buf) {
    57  		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
    58  	}
    59  	r := binary.LittleEndian.Uint32(buf.buf[buf.off : buf.off+stride])
    60  	buf.off += stride
    61  	return r
    62  }
    63  
    64  func (buf *minidumpBuf) u64() uint64 {
    65  	const stride = 8
    66  	if buf.err != nil {
    67  		return 0
    68  	}
    69  	if buf.off+stride >= len(buf.buf) {
    70  		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
    71  	}
    72  	r := binary.LittleEndian.Uint64(buf.buf[buf.off : buf.off+stride])
    73  	buf.off += stride
    74  	return r
    75  }
    76  
    77  func streamBuf(stream *Stream, buf *minidumpBuf, name string) *minidumpBuf {
    78  	return &minidumpBuf{
    79  		buf:  buf.buf,
    80  		kind: "stream",
    81  		off:  stream.Offset,
    82  		err:  nil,
    83  		ctx:  fmt.Sprintf("reading %s stream at %#x", name, stream.Offset),
    84  	}
    85  }
    86  
    87  // ErrNotAMinidump is the error returned when the file being loaded is not a
    88  // minidump file.
    89  type ErrNotAMinidump struct {
    90  	what string
    91  	got  uint32
    92  }
    93  
    94  func (err ErrNotAMinidump) Error() string {
    95  	return fmt.Sprintf("not a minidump, invalid %s %#x", err.what, err.got)
    96  }
    97  
    98  const (
    99  	minidumpSignature = 0x504d444d // 'MDMP'
   100  	minidumpVersion   = 0xa793
   101  )
   102  
   103  // Minidump represents a minidump file
   104  type Minidump struct {
   105  	Timestamp uint32
   106  	Flags     FileFlags
   107  
   108  	Streams []Stream
   109  
   110  	Threads []Thread
   111  	Modules []Module
   112  
   113  	Pid uint32
   114  
   115  	MemoryRanges []MemoryRange
   116  	MemoryInfo   []MemoryInfo
   117  
   118  	streamNum uint32
   119  	streamOff uint32
   120  }
   121  
   122  // Stream represents one (uninterpreted) stream in a minidump file.
   123  // See: https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_directory
   124  type Stream struct {
   125  	Type    StreamType
   126  	Offset  int
   127  	RawData []byte
   128  }
   129  
   130  // Thread represents an entry in the ThreadList stream.
   131  // See: https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_thread
   132  type Thread struct {
   133  	ID            uint32
   134  	SuspendCount  uint32
   135  	PriorityClass uint32
   136  	Priority      uint32
   137  	TEB           uint64
   138  	Context       winutil.AMD64CONTEXT
   139  }
   140  
   141  // Module represents an entry in the ModuleList stream.
   142  // See: https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_module
   143  type Module struct {
   144  	BaseOfImage   uint64
   145  	SizeOfImage   uint32
   146  	Checksum      uint32
   147  	TimeDateStamp uint32
   148  	Name          string
   149  	VersionInfo   VSFixedFileInfo
   150  
   151  	// CVRecord stores a CodeView record and is populated when a module's debug information resides in a PDB file.  It identifies the PDB file.
   152  	CVRecord []byte
   153  
   154  	// MiscRecord is populated when a module's debug information resides in a DBG file.  It identifies the DBG file.  This field is effectively obsolete with modules built by recent toolchains.
   155  	MiscRecord []byte
   156  }
   157  
   158  // VSFixedFileInfo  Visual Studio Fixed File Info.
   159  // See: https://docs.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
   160  type VSFixedFileInfo struct {
   161  	Signature        uint32
   162  	StructVersion    uint32
   163  	FileVersionHi    uint32
   164  	FileVersionLo    uint32
   165  	ProductVersionHi uint32
   166  	ProductVersionLo uint32
   167  	FileFlagsMask    uint32
   168  	FileFlags        uint32
   169  	FileOS           uint32
   170  	FileType         uint32
   171  	FileSubtype      uint32
   172  	FileDateHi       uint32
   173  	FileDateLo       uint32
   174  }
   175  
   176  // MemoryRange represents a region of memory saved to the core file, it's constructed after either:
   177  // 1. parsing an entry in the Memory64List stream.
   178  // 2. parsing the stack field of an entry in the ThreadList stream.
   179  type MemoryRange struct {
   180  	Addr uint64
   181  	Data []byte
   182  }
   183  
   184  // ReadMemory reads len(buf) bytes of memory starting at addr into buf from this memory region.
   185  func (m *MemoryRange) ReadMemory(buf []byte, addr uint64) (int, error) {
   186  	if len(buf) == 0 {
   187  		return 0, nil
   188  	}
   189  	if (uint64(addr) < m.Addr) || (uint64(addr)+uint64(len(buf)) > m.Addr+uint64(len(m.Data))) {
   190  		return 0, io.EOF
   191  	}
   192  	copy(buf, m.Data[uint64(addr)-m.Addr:])
   193  	return len(buf), nil
   194  }
   195  
   196  // MemoryInfo represents an entry in the MemoryInfoList stream.
   197  // See: https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory_info_list
   198  type MemoryInfo struct {
   199  	Addr       uint64
   200  	Size       uint64
   201  	State      MemoryState
   202  	Protection MemoryProtection
   203  	Type       MemoryType
   204  }
   205  
   206  //go:generate stringer -type FileFlags,StreamType,Arch,MemoryState,MemoryType,MemoryProtection
   207  
   208  // MemoryState is the type of the State field of MINIDUMP_MEMORY_INFO
   209  type MemoryState uint32
   210  
   211  const (
   212  	MemoryStateCommit  MemoryState = 0x1000
   213  	MemoryStateReserve MemoryState = 0x2000
   214  	MemoryStateFree    MemoryState = 0x10000
   215  )
   216  
   217  // MemoryType is the type of the Type field of MINIDUMP_MEMORY_INFO
   218  type MemoryType uint32
   219  
   220  const (
   221  	MemoryTypePrivate MemoryType = 0x20000
   222  	MemoryTypeMapped  MemoryType = 0x40000
   223  	MemoryTypeImage   MemoryType = 0x1000000
   224  )
   225  
   226  // MemoryProtection is the type of the Protection field of MINIDUMP_MEMORY_INFO
   227  type MemoryProtection uint32
   228  
   229  const (
   230  	MemoryProtectNoAccess         MemoryProtection = 0x01 // PAGE_NOACCESS
   231  	MemoryProtectReadOnly         MemoryProtection = 0x02 // PAGE_READONLY
   232  	MemoryProtectReadWrite        MemoryProtection = 0x04 // PAGE_READWRITE
   233  	MemoryProtectWriteCopy        MemoryProtection = 0x08 // PAGE_WRITECOPY
   234  	MemoryProtectExecute          MemoryProtection = 0x10 // PAGE_EXECUTE
   235  	MemoryProtectExecuteRead      MemoryProtection = 0x20 // PAGE_EXECUTE_READ
   236  	MemoryProtectExecuteReadWrite MemoryProtection = 0x40 // PAGE_EXECUTE_READWRITE
   237  	MemoryProtectExecuteWriteCopy MemoryProtection = 0x80 // PAGE_EXECUTE_WRITECOPY
   238  	// These options can be combined with the previous flags
   239  	MemoryProtectPageGuard    MemoryProtection = 0x100 // PAGE_GUARD
   240  	MemoryProtectNoCache      MemoryProtection = 0x200 // PAGE_NOCACHE
   241  	MemoryProtectWriteCombine MemoryProtection = 0x400 // PAGE_WRITECOMBINE
   242  
   243  )
   244  
   245  // FileFlags is the type of the Flags field of MINIDUMP_HEADER
   246  type FileFlags uint64
   247  
   248  const (
   249  	FileNormal                          FileFlags = 0x00000000
   250  	FileWithDataSegs                    FileFlags = 0x00000001
   251  	FileWithFullMemory                  FileFlags = 0x00000002
   252  	FileWithHandleData                  FileFlags = 0x00000004
   253  	FileFilterMemory                    FileFlags = 0x00000008
   254  	FileScanMemory                      FileFlags = 0x00000010
   255  	FileWithUnloadedModules             FileFlags = 0x00000020
   256  	FileWithIncorrectlyReferencedMemory FileFlags = 0x00000040
   257  	FileFilterModulePaths               FileFlags = 0x00000080
   258  	FileWithProcessThreadData           FileFlags = 0x00000100
   259  	FileWithPrivateReadWriteMemory      FileFlags = 0x00000200
   260  	FileWithoutOptionalData             FileFlags = 0x00000400
   261  	FileWithFullMemoryInfo              FileFlags = 0x00000800
   262  	FileWithThreadInfo                  FileFlags = 0x00001000
   263  	FileWithCodeSegs                    FileFlags = 0x00002000
   264  	FileWithoutAuxilliarySegs           FileFlags = 0x00004000
   265  	FileWithFullAuxilliaryState         FileFlags = 0x00008000
   266  	FileWithPrivateCopyMemory           FileFlags = 0x00010000
   267  	FileIgnoreInaccessibleMemory        FileFlags = 0x00020000
   268  	FileWithTokenInformation            FileFlags = 0x00040000
   269  )
   270  
   271  // StreamType is the type of the StreamType field of MINIDUMP_DIRECTORY
   272  type StreamType uint32
   273  
   274  const (
   275  	UnusedStream              StreamType = 0
   276  	ReservedStream0           StreamType = 1
   277  	ReservedStream1           StreamType = 2
   278  	ThreadListStream          StreamType = 3
   279  	ModuleListStream          StreamType = 4
   280  	MemoryListStream          StreamType = 5
   281  	ExceptionStream           StreamType = 6
   282  	SystemInfoStream          StreamType = 7
   283  	ThreadExListStream        StreamType = 8
   284  	Memory64ListStream        StreamType = 9
   285  	CommentStreamA            StreamType = 10
   286  	CommentStreamW            StreamType = 11
   287  	HandleDataStream          StreamType = 12
   288  	FunctionTableStream       StreamType = 13
   289  	UnloadedModuleStream      StreamType = 14
   290  	MiscInfoStream            StreamType = 15
   291  	MemoryInfoListStream      StreamType = 16
   292  	ThreadInfoListStream      StreamType = 17
   293  	HandleOperationListStream StreamType = 18
   294  	TokenStream               StreamType = 19
   295  	JavascriptDataStream      StreamType = 20
   296  	SystemMemoryInfoStream    StreamType = 21
   297  	ProcessVMCounterStream    StreamType = 22
   298  )
   299  
   300  // Arch is the type of the ProcessorArchitecture field of MINIDUMP_SYSTEM_INFO.
   301  type Arch uint16
   302  
   303  const (
   304  	CpuArchitectureX86     Arch = 0
   305  	CpuArchitectureMips    Arch = 1
   306  	CpuArchitectureAlpha   Arch = 2
   307  	CpuArchitecturePPC     Arch = 3
   308  	CpuArchitectureSHX     Arch = 4 // Super-H
   309  	CpuArchitectureARM     Arch = 5
   310  	CpuArchitectureIA64    Arch = 6
   311  	CpuArchitectureAlpha64 Arch = 7
   312  	CpuArchitectureMSIL    Arch = 8 // Microsoft Intermediate Language
   313  	CpuArchitectureAMD64   Arch = 9
   314  	CpuArchitectureWoW64   Arch = 10
   315  	CpuArchitectureARM64   Arch = 12
   316  	CpuArchitectureUnknown Arch = 0xffff
   317  )
   318  
   319  // Open reads the minidump file at path and returns it as a Minidump structure.
   320  func Open(path string, logfn func(fmt string, args ...interface{})) (*Minidump, error) {
   321  	rawbuf, err := ioutil.ReadFile(path) //TODO(aarzilli): mmap?
   322  	if err != nil {
   323  		return nil, err
   324  	}
   325  
   326  	buf := &minidumpBuf{buf: rawbuf, kind: "file"}
   327  
   328  	var mdmp Minidump
   329  
   330  	readMinidumpHeader(&mdmp, buf)
   331  	if buf.err != nil {
   332  		return nil, buf.err
   333  	}
   334  
   335  	if logfn != nil {
   336  		logfn("Minidump Header\n")
   337  		logfn("Num Streams: %d\n", mdmp.streamNum)
   338  		logfn("Streams offset: %#x\n", mdmp.streamOff)
   339  		logfn("File flags: %s\n", fileFlagsToString(mdmp.Flags))
   340  		logfn("Offset after header %#x\n", buf.off)
   341  	}
   342  
   343  	readDirectory(&mdmp, buf)
   344  	if buf.err != nil {
   345  		return nil, buf.err
   346  	}
   347  
   348  	for i := range mdmp.Streams {
   349  		stream := &mdmp.Streams[i]
   350  		if stream.Type != SystemInfoStream {
   351  			continue
   352  		}
   353  
   354  		sb := streamBuf(stream, buf, "system info")
   355  		if buf.err != nil {
   356  			return nil, buf.err
   357  		}
   358  
   359  		arch := Arch(sb.u16())
   360  
   361  		if logfn != nil {
   362  			logfn("Found processor architecture %s\n", arch.String())
   363  		}
   364  
   365  		if arch != CpuArchitectureAMD64 {
   366  			return nil, fmt.Errorf("unsupported architecture %s", arch.String())
   367  		}
   368  	}
   369  
   370  	for i := range mdmp.Streams {
   371  		stream := &mdmp.Streams[i]
   372  		if logfn != nil {
   373  			logfn("Stream %d: type:%s off:%#x size:%#x\n", i, stream.Type, stream.Offset, len(stream.RawData))
   374  		}
   375  		switch stream.Type {
   376  		case ThreadListStream:
   377  			readThreadList(&mdmp, streamBuf(stream, buf, "thread list"))
   378  			if logfn != nil {
   379  				for i := range mdmp.Threads {
   380  					logfn("\tID:%#x TEB:%#x\n", mdmp.Threads[i].ID, mdmp.Threads[i].TEB)
   381  				}
   382  			}
   383  		case ModuleListStream:
   384  			readModuleList(&mdmp, streamBuf(stream, buf, "module list"))
   385  			if logfn != nil {
   386  				for i := range mdmp.Modules {
   387  					logfn("\tName:%q BaseOfImage:%#x SizeOfImage:%#x\n", mdmp.Modules[i].Name, mdmp.Modules[i].BaseOfImage, mdmp.Modules[i].SizeOfImage)
   388  				}
   389  			}
   390  		case ExceptionStream:
   391  			//TODO(aarzilli): this stream contains the exception that made the
   392  			//process stop and caused the minidump to be taken. If we ever start
   393  			//caring about this we should parse this.
   394  		case Memory64ListStream:
   395  			readMemory64List(&mdmp, streamBuf(stream, buf, "memory64 list"), logfn)
   396  		case MemoryInfoListStream:
   397  			readMemoryInfoList(&mdmp, streamBuf(stream, buf, "memory info list"), logfn)
   398  		case MiscInfoStream:
   399  			readMiscInfo(&mdmp, streamBuf(stream, buf, "misc info"))
   400  			if logfn != nil {
   401  				logfn("\tPid: %#x\n", mdmp.Pid)
   402  			}
   403  		case CommentStreamW:
   404  			if logfn != nil {
   405  				logfn("\t%q\n", decodeUTF16(stream.RawData))
   406  			}
   407  		case CommentStreamA:
   408  			if logfn != nil {
   409  				logfn("\t%s\n", string(stream.RawData))
   410  			}
   411  		}
   412  		if buf.err != nil {
   413  			return nil, buf.err
   414  		}
   415  	}
   416  
   417  	return &mdmp, nil
   418  }
   419  
   420  // decodeUTF16 converts a NUL-terminated UTF16LE string to (non NUL-terminated) UTF8.
   421  func decodeUTF16(in []byte) string {
   422  	utf16encoded := []uint16{}
   423  	for i := 0; i+1 < len(in); i += 2 {
   424  		var ch uint16
   425  		ch = uint16(in[i]) + uint16(in[i+1])<<8
   426  		utf16encoded = append(utf16encoded, ch)
   427  	}
   428  	s := string(utf16.Decode(utf16encoded))
   429  	if len(s) > 0 && s[len(s)-1] == 0 {
   430  		s = s[:len(s)-1]
   431  	}
   432  	return s
   433  }
   434  
   435  func fileFlagsToString(flags FileFlags) string {
   436  	out := []byte{}
   437  	for i, name := range _FileFlags_map {
   438  		if i == 0 {
   439  			continue
   440  		}
   441  		if flags&i != 0 {
   442  			if len(out) > 0 {
   443  				out = append(out, '|')
   444  			}
   445  			out = append(out, name...)
   446  		}
   447  	}
   448  	if len(out) == 0 {
   449  		return flags.String()
   450  	}
   451  	return string(out)
   452  }
   453  
   454  // readMinidumpHeader reads the minidump file header
   455  func readMinidumpHeader(mdmp *Minidump, buf *minidumpBuf) {
   456  	buf.ctx = "reading minidump header"
   457  
   458  	if sig := buf.u32(); sig != minidumpSignature {
   459  		buf.err = ErrNotAMinidump{"signature", sig}
   460  		return
   461  	}
   462  
   463  	if ver := buf.u16(); ver != minidumpVersion {
   464  		buf.err = ErrNotAMinidump{"version", uint32(ver)}
   465  		return
   466  	}
   467  
   468  	buf.u16() // implementation specific version
   469  	mdmp.streamNum = buf.u32()
   470  	mdmp.streamOff = buf.u32()
   471  	buf.u32() // checksum, but it's always 0
   472  	mdmp.Timestamp = buf.u32()
   473  	mdmp.Flags = FileFlags(buf.u64())
   474  }
   475  
   476  // readDirectory reads the list of streams (i.e. the minidump "directory")
   477  func readDirectory(mdmp *Minidump, buf *minidumpBuf) {
   478  	buf.off = int(mdmp.streamOff)
   479  
   480  	mdmp.Streams = make([]Stream, mdmp.streamNum)
   481  	for i := range mdmp.Streams {
   482  		buf.ctx = fmt.Sprintf("reading stream directory entry %d", i)
   483  		stream := &mdmp.Streams[i]
   484  		stream.Type = StreamType(buf.u32())
   485  		stream.Offset, stream.RawData = readLocationDescriptor(buf)
   486  		if buf.err != nil {
   487  			return
   488  		}
   489  	}
   490  }
   491  
   492  // readLocationDescriptor reads a location descriptor structure (a structure
   493  // which describes a subregion of the file), and returns the destination
   494  // offset and a slice into the minidump file's buffer.
   495  func readLocationDescriptor(buf *minidumpBuf) (off int, rawData []byte) {
   496  	sz := buf.u32()
   497  	off = int(buf.u32())
   498  	if buf.err != nil {
   499  		return off, nil
   500  	}
   501  	end := off + int(sz)
   502  	if off >= len(buf.buf) || end > len(buf.buf) {
   503  		buf.err = fmt.Errorf("location starting at %#x of size %#x is past the end of file, while %s", off, sz, buf.ctx)
   504  		return 0, nil
   505  	}
   506  	rawData = buf.buf[off:end]
   507  	return
   508  }
   509  
   510  func readString(buf *minidumpBuf) string {
   511  	startOff := buf.off
   512  	sz := buf.u32()
   513  	if buf.err != nil {
   514  		return ""
   515  	}
   516  	end := buf.off + int(sz)
   517  	if buf.off >= len(buf.buf) || end > len(buf.buf) {
   518  		buf.err = fmt.Errorf("string starting at %#x of size %#x is past the end of file, while %s", startOff, sz, buf.ctx)
   519  		return ""
   520  	}
   521  	return decodeUTF16(buf.buf[buf.off:end])
   522  }
   523  
   524  // readThreadList reads a thread list stream and adds the threads to the minidump.
   525  func readThreadList(mdmp *Minidump, buf *minidumpBuf) {
   526  	threadNum := buf.u32()
   527  	if buf.err != nil {
   528  		return
   529  	}
   530  
   531  	mdmp.Threads = make([]Thread, threadNum)
   532  
   533  	for i := range mdmp.Threads {
   534  		buf.ctx = fmt.Sprintf("reading thread list entry %d", i)
   535  		thread := &mdmp.Threads[i]
   536  
   537  		thread.ID = buf.u32()
   538  		thread.SuspendCount = buf.u32()
   539  		thread.PriorityClass = buf.u32()
   540  		thread.Priority = buf.u32()
   541  		thread.TEB = buf.u64()
   542  		if buf.err != nil {
   543  			return
   544  		}
   545  
   546  		readMemoryDescriptor(mdmp, buf)                    // thread stack
   547  		_, rawThreadContext := readLocationDescriptor(buf) // thread context
   548  		thread.Context = *((*winutil.AMD64CONTEXT)(unsafe.Pointer(&rawThreadContext[0])))
   549  		if buf.err != nil {
   550  			return
   551  		}
   552  	}
   553  }
   554  
   555  // readModuleList reads a module list stream and adds the modules to the minidump.
   556  func readModuleList(mdmp *Minidump, buf *minidumpBuf) {
   557  	moduleNum := buf.u32()
   558  	if buf.err != nil {
   559  		return
   560  	}
   561  
   562  	mdmp.Modules = make([]Module, moduleNum)
   563  
   564  	for i := range mdmp.Modules {
   565  		buf.ctx = fmt.Sprintf("reading module list entry %d", i)
   566  		module := &mdmp.Modules[i]
   567  
   568  		module.BaseOfImage = buf.u64()
   569  		module.SizeOfImage = buf.u32()
   570  		module.Checksum = buf.u32()
   571  		module.TimeDateStamp = buf.u32()
   572  		nameOff := int(buf.u32())
   573  
   574  		versionInfoVec := make([]uint32, unsafe.Sizeof(VSFixedFileInfo{})/unsafe.Sizeof(uint32(0)))
   575  		for j := range versionInfoVec {
   576  			versionInfoVec[j] = buf.u32()
   577  		}
   578  
   579  		module.VersionInfo = *(*VSFixedFileInfo)(unsafe.Pointer(&versionInfoVec[0]))
   580  
   581  		_, module.CVRecord = readLocationDescriptor(buf)
   582  		_, module.MiscRecord = readLocationDescriptor(buf)
   583  
   584  		if buf.err != nil {
   585  			return
   586  		}
   587  
   588  		nameBuf := minidumpBuf{buf: buf.buf, kind: "file", off: nameOff, err: nil, ctx: buf.ctx}
   589  		module.Name = readString(&nameBuf)
   590  		if nameBuf.err != nil {
   591  			buf.err = nameBuf.err
   592  			return
   593  		}
   594  	}
   595  }
   596  
   597  // readMemory64List reads a _MINIDUMP_MEMORY64_LIST structure, containing
   598  // the description of the process memory.
   599  // See: https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory64_list
   600  // And: https://docs.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory_descriptor
   601  func readMemory64List(mdmp *Minidump, buf *minidumpBuf, logfn func(fmt string, args ...interface{})) {
   602  	rangesNum := buf.u64()
   603  	baseOff := int(buf.u64())
   604  	if buf.err != nil {
   605  		return
   606  	}
   607  
   608  	for i := uint64(0); i < rangesNum; i++ {
   609  		addr := buf.u64()
   610  		sz := buf.u64()
   611  
   612  		end := baseOff + int(sz)
   613  		if baseOff >= len(buf.buf) || end > len(buf.buf) {
   614  			buf.err = fmt.Errorf("memory range at %#x of size %#x is past the end of file, while %s", baseOff, sz, buf.ctx)
   615  			return
   616  		}
   617  
   618  		mdmp.addMemory(addr, buf.buf[baseOff:end])
   619  
   620  		if logfn != nil {
   621  			logfn("\tMemory %d addr:%#x size:%#x FileOffset:%#x\n", i, addr, sz, baseOff)
   622  		}
   623  
   624  		baseOff = end
   625  	}
   626  }
   627  
   628  func readMemoryInfoList(mdmp *Minidump, buf *minidumpBuf, logfn func(fmt string, args ...interface{})) {
   629  	startOff := buf.off
   630  	sizeOfHeader := int(buf.u32())
   631  	sizeOfEntry := int(buf.u32())
   632  	numEntries := buf.u64()
   633  
   634  	buf.off = startOff + sizeOfHeader
   635  
   636  	mdmp.MemoryInfo = make([]MemoryInfo, numEntries)
   637  
   638  	for i := range mdmp.MemoryInfo {
   639  		memInfo := &mdmp.MemoryInfo[i]
   640  		startOff := buf.off
   641  
   642  		memInfo.Addr = buf.u64()
   643  		buf.u64() // allocation_base
   644  
   645  		buf.u32() // allocation_protection
   646  		buf.u32() // alignment
   647  
   648  		memInfo.Size = buf.u64()
   649  
   650  		memInfo.State = MemoryState(buf.u32())
   651  		memInfo.Protection = MemoryProtection(buf.u32())
   652  		memInfo.Type = MemoryType(buf.u32())
   653  
   654  		if logfn != nil {
   655  			logfn("\tMemoryInfo %d Addr:%#x Size:%#x %s %s %s\n", i, memInfo.Addr, memInfo.Size, memInfo.State, memInfo.Protection, memInfo.Type)
   656  		}
   657  
   658  		buf.off = startOff + sizeOfEntry
   659  	}
   660  }
   661  
   662  // readMiscInfo reads the process_id from a MiscInfo stream.
   663  func readMiscInfo(mdmp *Minidump, buf *minidumpBuf) {
   664  	buf.u32() // size of info
   665  	buf.u32() // flags1
   666  
   667  	mdmp.Pid = buf.u32() // process_id
   668  	// there are more fields here, but we don't care about them
   669  }
   670  
   671  // readMemoryDescriptor reads a memory descriptor struct and adds it to the memory map of the minidump.
   672  func readMemoryDescriptor(mdmp *Minidump, buf *minidumpBuf) {
   673  	addr := buf.u64()
   674  	if buf.err != nil {
   675  		return
   676  	}
   677  	_, rawData := readLocationDescriptor(buf)
   678  	if buf.err != nil {
   679  		return
   680  	}
   681  	mdmp.addMemory(addr, rawData)
   682  }
   683  
   684  func (mdmp *Minidump) addMemory(addr uint64, data []byte) {
   685  	mdmp.MemoryRanges = append(mdmp.MemoryRanges, MemoryRange{addr, data})
   686  }