source: trunk/kDbg/kDbgModPE-generic.cpp@ 3528

Last change on this file since 3528 was 3528, checked in by bird, 18 years ago

Some refactoring and OS abstraction by instroducing kDbgHlp.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 13.4 KB
Line 
1/* $Id: kDbgModPE-generic.cpp 3528 2007-08-20 02:43:13Z bird $ */
2/** @file
3 * kDbg - The Debug Info Reader, PE Module (Generic).
4 */
5
6/*
7 * Copyright (c) 2006-2007 knut st. osmundsen <bird-src-spam@anduin.net>
8 *
9 * This file is part of kLIBC.
10 *
11 * kLIBC is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * kLIBC is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with kLIBC; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27/*******************************************************************************
28* Header Files *
29*******************************************************************************/
30#include "kDbg.h"
31#include "kDbgInternal.h"
32#include <kLdrModPE.h>
33
34
35/*******************************************************************************
36* Structures and Typedefs *
37*******************************************************************************/
38/**
39 * A dbghelp based PE debug reader.
40 */
41typedef struct KDBGMODPE
42{
43 /** The common module core. */
44 KDBGMOD Core;
45 /** The image size. */
46 uint32_t cbImage;
47 /** The number of sections. (We've added the implicit header section.) */
48 int32_t cSections;
49 /** The section headers (variable size). The first section is the
50 * implicit header section.*/
51 IMAGE_SECTION_HEADER aSections[1];
52} KDBGMODPE, *PKDBGMODPE;
53
54
55/**
56 * Calcs the RVA for a segment:offset address.
57 *
58 * @returns IPRT status code.
59 *
60 * @param pModPe The PE debug module instance.
61 * @param iSegment The segment number. Special segments are dealt with as well.
62 * @param off The segment offset.
63 * @param puRVA Where to store the RVA on success.
64 */
65static int rtDbgModPeSegOffToRVA(PKDBGMODPE pModPe, int32_t iSegment, KDBGADDR off, uint32_t *puRVA)
66{
67 if (iSegment >= 0)
68 {
69 kDbgAssertMsgReturn(iSegment < pModPe->cSections, ("iSegment=%x cSections=%x\n", iSegment, pModPe->cSections),
70 KDBG_ERR_INVALID_ADDRESS);
71 kDbgAssertMsgReturn(off < pModPe->aSections[iSegment].Misc.VirtualSize,
72 ("off=" PRI_KDBGADDR " VirtualSize=%x\n", off, pModPe->aSections[iSegment].Misc.VirtualSize),
73 KDBG_ERR_INVALID_ADDRESS);
74 *puRVA = pModPe->aSections[iSegment].VirtualAddress + off;
75 return 0;
76 }
77
78 if (iSegment == KDBGSEG_RVA)
79 {
80 kDbgAssertMsgReturn(off < pModPe->cbImage, ("off=" PRI_KDBGADDR ", cbImage=%x\n", off, pModPe->cbImage),
81 KDBG_ERR_INVALID_ADDRESS);
82 *puRVA = off;
83 return 0;
84 }
85 AssertMsgFailedReturn(("iSegment=%RI32\n", iSegment), KDBG_ERR_INVALID_ADDRESS);
86}
87
88
89/**
90 * Calcs the segment:offset address for a RVA.
91 *
92 * @returns IPRT status code.
93 *
94 * @param pModPe The PE debug module instance.
95 * @param uRVA The RVA.
96 * @param piSegment Where to store the segment number.
97 * @param poff Where to store the segment offset.
98 */
99static int rtDbgModPeRVAToSegOff(PKDBGMODPE pModPe, uint32_t uRVA, int32_t *piSegment, KDBGADDR *poff)
100{
101 kDbgAssertMsgReturn(uRVA < pModPe->cbImage, ("uRVA=%x, cbImage=%x\n", uRVA, pModPe->cbImage),
102 KDBG_ERR_INVALID_ADDRESS);
103 for (int32_t iSegment = 0; iSegment < pModPe->cSections; iSegment++)
104 {
105 /** @todo should probably be less strict about address in the alignment gaps. */
106 uint32_t off = uRVA - pModPe->aSections[iSegment].VirtualAddress;
107 if (off < pModPe->aSections[iSegment].Misc.VirtualSize)
108 {
109 *poff = off;
110 *piSegment = iSegment;
111 return 0;
112 }
113 }
114 AssertMsgFailedReturn(("uRVA=%x\n", uRVA), KDBG_ERR_INVALID_ADDRESS);
115}
116
117
118/**
119 * @copydoc KDBGMODOPS::pfnQueryLine
120 */
121static DECLCALLBACK(int) rtDbgModPEQueryLine(PKDBGMOD pMod, int32_t iSegment, KDBGADDR off, PKDBGLINE pLine)
122{
123 PKDBGMODPE pModPe = (PKDBGMODPE)pMod;
124
125 /*
126 * Translate the address to an RVA.
127 */
128 uint32_t uRVA;
129 int rc = rtDbgModPeSegOffToRVA(pModPe, iSegment, off, &uRVA);
130 if (!rc)
131 {
132#if 0
133 DWORD64 off;
134 IMAGEHLP_LINE64 Line;
135 Line.SizeOfStruct = sizeof(Line);
136 if (g_pfnSymGetLineFromAddr64(pModPe->hSymInst, pModPe->ImageBase + uRVA, &off, &Line))
137 {
138 pLine->RVA = (KDBGADDR)(Line.Address - pModPe->ImageBase);
139 rc = rtDbgModPeRVAToSegOff(pModPe, pLine->RVA, &pLine->iSegment, &pLine->offSegment);
140 pLine->iLine = Line.LineNumber;
141 pLine->cchFile = strlen(Line.FileName);
142 if (pLine->cchFile >= sizeof(pLine->szFile))
143 pLine->cchFile = sizeof(pLine->szFile) - 1;
144 memcpy(pLine->szFile, Line.FileName, pLine->cchFile + 1);
145 }
146 else
147 {
148 DWORD Err = GetLastError();
149 rc = RTErrConvertFromWin32(Err);
150 }
151#endif
152 rc = VERR_NOT_IMPLEMENTED;
153 }
154 return rc;
155}
156
157
158/**
159 * @copydoc KDBGMODOPS::pfnQuerySymbol
160 */
161static DECLCALLBACK(int) rtDbgModPEQuerySymbol(PKDBGMOD pMod, int32_t iSegment, KDBGADDR off, PKDBGSYMBOL pSym)
162{
163 PKDBGMODPE pModPe = (PKDBGMODPE)pMod;
164
165 /*
166 * Translate the address to an RVA.
167 */
168 uint32_t uRVA;
169 int rc = rtDbgModPeSegOffToRVA(pModPe, iSegment, off, &uRVA);
170 if (!rc)
171 {
172#if 0
173 DWORD64 off;
174 union
175 {
176 SYMBOL_INFO Sym;
177 char achBuffer[sizeof(SYMBOL_INFO) + RTDBG_SYMBOL_MAX];
178 } Buf;
179 Buf.Sym.SizeOfStruct = sizeof(SYMBOL_INFO);
180 Buf.Sym.MaxNameLen = RTDBG_SYMBOL_MAX;
181 if (g_pfnSymFromAddr(pModPe->hSymInst, pModPe->ImageBase + uRVA, &off, &Buf.Sym))
182 {
183 pSym->cb = Buf.Sym.Size;
184 pSym->fFlags = 0;
185 if (Buf.Sym.Flags & SYMFLAG_FUNCTION)
186 pSym->fFlags |= KDBGSYM_FLAGS_CODE;
187 else if (Buf.Sym.Flags & SYMFLAG_CONSTANT)
188 pSym->fFlags |= KDBGSYM_FLAGS_ABS; /** @todo SYMFLAG_CONSTANT must be tested - documentation is too brief to say what is really meant here.*/
189 else
190 pSym->fFlags |= KDBGSYM_FLAGS_DATA;
191 if (Buf.Sym.Flags & SYMFLAG_EXPORT)
192 pSym->fFlags |= KDBGSYM_FLAGS_EXPORTED;
193 if ((Buf.Sym.Flags & (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT)) == (SYMFLAG_VALUEPRESENT | SYMFLAG_CONSTANT))
194 {
195 pSym->iSegment = RTDBGSEG_ABS;
196 pSym->offSegment = (KDBGADDR)Buf.Sym.Value;
197 pSym->RVA = (KDBGADDR)Buf.Sym.Value;
198 }
199 else
200 {
201 pSym->RVA = (KDBGADDR)(Buf.Sym.Address - pModPe->ImageBase);
202 rc = rtDbgModPeRVAToSegOff(pModPe, pSym->RVA, &pSym->iSegment, &pSym->offSegment);
203 }
204 pSym->cchName = (uint16_t)Buf.Sym.NameLen;
205 if (pSym->cchName >= sizeof(pSym->szName))
206 pSym->cchName = sizeof(pSym->szName) - 1;
207 memcpy(pSym->szName, Buf.Sym.Name, Buf.Sym.NameLen);
208 pSym->szName[Buf.Sym.NameLen] = '\0';
209 }
210 else
211 {
212 DWORD Err = GetLastError();
213 rc = RTErrConvertFromWin32(Err);
214 }
215#endif
216 rc = VERR_NOT_IMPLEMENTED;
217 }
218 return rc;
219}
220
221
222/**
223 * @copydoc KDBGMODOPS::pfnClose
224 */
225static DECLCALLBACK(int) rtDbgModPEClose(PKDBGMOD pMod)
226{
227 PKDBGMODPE pModPe = (PKDBGMODPE)pMod;
228
229 //if (g_pfnSymCleanup(pModPe->hSymInst))
230 // return 0;
231 //
232 //DWORD Err = GetLastError();
233 //int rc = RTErrConvertFromWin32(Err);
234 //AssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc));
235 //return rc;
236 return VERR_NOT_IMPLEMENTED;
237}
238
239
240/**
241 * Methods for a PE module.
242 */
243static const KDBGMODOPS g_rtDbgModPEOps =
244{
245 "PE (dbghelp)",
246 rtDbgModPEClose,
247 rtDbgModPEQuerySymbol,
248 rtDbgModPEQueryLine
249};
250
251
252/**
253 * Opens the debug info for a PE image using the windows dbghelp library.
254 *
255 * @returns IPRT status code.
256 *
257 * @param File The handle to the module.
258 * @param offHdr The offset of the PE header.
259 * @param pszModulePath The path to the module.
260 * @param ppDbgMod Where to store the module handle.
261 *
262 */
263int kdbgModPEOpen(RTFILE File, int64_t offHdr, const char *pszModulePath, PKDBGMOD *ppDbgMod)
264{
265 /*
266 * We need to read the section headers and get the image size.
267 */
268 IMAGE_FILE_HEADER FHdr;
269 int rc = kDbgHlpReadAt(File, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, FileHeader), &FHdr, sizeof(FHdr), NULL);
270 AssertRCReturn(rc, rc);
271
272 uint32_t cbImage;
273 if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32))
274 rc = kDbgHlpReadAt(File, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.SizeOfImage),
275 &cbImage, sizeof(cbImage), NULL);
276 else if (FHdr.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER64))
277 rc = kDbgHlpReadAt(File, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.SizeOfImage),
278 &cbImage, sizeof(cbImage), NULL);
279 else
280 AssertFailedReturn(VERR_BAD_EXE_FORMAT);
281 AssertRCReturn(rc, rc);
282
283 /*
284 * Allocate the module and read/construct the section headers.
285 */
286 PKDBGMODPE pModPe = (PKDBGMODPE)RTMemAlloc(KDBG_OFFSETOF(KDBGMODPE, aSections[FHdr.NumberOfSections + 2]));
287 AssertReturn(pModPe, KDBG_ERR_NO_MEMORY);
288 pModPe->Core.u32Magic = KDBGMOD_MAGIC;
289 pModPe->Core.pOps = &g_rtDbgModPEOps;
290 pModPe->Core.File = File;
291 pModPe->cbImage = cbImage;
292 pModPe->cSections = 1 + FHdr.NumberOfSections;
293 rc = kDbgHlpReadAt(File, offHdr + KDBG_OFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader) + FHdr.SizeOfOptionalHeader,
294 &pModPe->aSections[1], sizeof(pModPe->aSections[0]) * FHdr.NumberOfSections, NULL);
295 if (!rc)
296 {
297 PIMAGE_SECTION_HEADER pSH = &pModPe->aSections[0];
298 memcpy(pSH->Name, "headers", sizeof(pSH->Name));
299 pSH->Misc.VirtualSize = pModPe->aSections[1].VirtualAddress;
300 pSH->VirtualAddress = 0;
301 pSH->SizeOfRawData = pSH->Misc.VirtualSize;
302 pSH->PointerToRawData = 0;
303 pSH->PointerToRelocations = 0;
304 pSH->PointerToLinenumbers = 0;
305 pSH->NumberOfRelocations = 0;
306 pSH->NumberOfLinenumbers = 0;
307 pSH->Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_4BYTES | IMAGE_SCN_MEM_READ;
308
309 uint32_t uTheEnd = pModPe->aSections[FHdr.NumberOfSections].VirtualAddress
310 + pModPe->aSections[FHdr.NumberOfSections].Misc.VirtualSize;
311 if (uTheEnd < cbImage)
312 {
313 pSH = &pModPe->aSections[pModPe->cSections++];
314 memcpy(pSH->Name, "tail\0\0\0", sizeof(pSH->Name));
315 pSH->Misc.VirtualSize = cbImage - uTheEnd;
316 pSH->VirtualAddress = uTheEnd;
317 pSH->SizeOfRawData = pSH->Misc.VirtualSize;
318 pSH->PointerToRawData = 0;
319 pSH->PointerToRelocations = 0;
320 pSH->PointerToLinenumbers = 0;
321 pSH->NumberOfRelocations = 0;
322 pSH->NumberOfLinenumbers = 0;
323 pSH->Characteristics = IMAGE_SCN_CNT_UNINITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ;
324 }
325
326#if 0
327 /*
328 * Find a new dbghelp handle.
329 *
330 * We assume 4GB of handles outlast most debugging sessions, or in anyways that
331 * when we start reusing handles they are no longer in use. :-)
332 */
333 static volatile uint32_t s_u32LastHandle = 1;
334 HANDLE hSymInst = (HANDLE)ASMAtomicIncU32(&s_u32LastHandle);
335 while ( hSymInst == INVALID_HANDLE_VALUE
336 || hSymInst == (HANDLE)0
337 || hSymInst == GetCurrentProcess())
338 hSymInst = (HANDLE)ASMAtomicIncU32(&s_u32LastHandle);
339
340 /*
341 * Initialize dbghelp and try open the specified module.
342 */
343 if (g_pfnSymInitialize(hSymInst, NULL, FALSE))
344 {
345 g_pfnSymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_AUTO_PUBLICS | SYMOPT_ALLOW_ABSOLUTE_SYMBOLS);
346
347 RTFileSeek(File, 0, RTFILE_SEEK_BEGIN, NULL); /* don't know if this is required or not... */
348 DWORD64 ImageBase = g_pfnSymLoadModule64(hSymInst, (HANDLE)File, pszModulePath, NULL, 0x00400000, 0);
349 if (ImageBase)
350 {
351 pModPe->hSymInst = hSymInst;
352 pModPe->ImageBase = ImageBase;
353 *ppDbgMod = &pModPe->Core;
354 return rc;
355 }
356
357 DWORD Err = GetLastError();
358 rc = RTErrConvertFromWin32(Err);
359 AssertMsgFailed(("SymLoadModule64 failed: Err=%d rc=%Rrc\n", Err, rc));
360 g_pfnSymCleanup(hSymInst);
361 }
362 else
363 {
364 DWORD Err = GetLastError();
365 rc = RTErrConvertFromWin32(Err);
366 AssertMsgFailed(("SymInitialize failed: Err=%d rc=%Rrc\n", Err, rc));
367 }
368#endif
369 rc = VERR_NOT_IMPLEMENTED;
370 }
371 else
372 AssertRC(rc);
373
374 kDbgHlpFree(pModPe);
375 return rc;
376}
377
Note: See TracBrowser for help on using the repository browser.