blob: 0f1bea9027904c0da681c18e093a631dd1d16fa4 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2013-2014 Google, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of Google, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include "share.h"
#ifndef WINDOWS
# error WINDOWS-only
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>
#include "config.h"
#include "share.h"
#include "dr_frontend_private.h" /* for debuglevel/abortlevel */
#include <string.h>
#include <errno.h>
#include "utils.h"
#include <assert.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include "dr_frontend.h"
#include <DbgHelp.h>
/* We're using dynamic loading of dbghelp.dll library here.
* So that only a frontend who called these symbol routines ends up needing it.
*/
static HMODULE hlib;
typedef BOOL (WINAPI *dbghelp_SymInitializeW_t)(IN HANDLE hProcess,
IN PCTSTR UserSearchPath,
IN BOOL fInvadeProcess);
typedef BOOL (WINAPI *dbghelp_SymCleanupW_t)(IN HANDLE hProcess);
typedef BOOL (WINAPI *dbghelp_SymSetSearchPathW_t)(IN HANDLE hProcess,
IN PCTSTR SearchPath);
typedef BOOL (WINAPI *dbghelp_SymLoadModuleExW_t)(IN HANDLE hProcess,
IN HANDLE hFile,
IN PCTSTR ImageName,
IN PCTSTR ModuleName,
IN DWORD64 BaseOfDll,
IN DWORD DllSize,
IN PMODLOAD_DATA Data,
IN DWORD Flags);
typedef BOOL (WINAPI *dbghelp_SymUnloadModule64_t)(IN HANDLE hProcess,
IN DWORD64 BaseOfDll);
typedef BOOL (WINAPI *dbghelp_SymGetModuleInfoW64_t)(IN HANDLE hProcess,
IN DWORD64 dwAddr,
OUT PIMAGEHLP_MODULEW64 ModuleInfo);
static dbghelp_SymInitializeW_t sym_init_func;
static dbghelp_SymCleanupW_t sym_cleanup_func;
static dbghelp_SymSetSearchPathW_t sym_set_path_func;
static dbghelp_SymLoadModuleExW_t sym_load_module_func;
static dbghelp_SymUnloadModule64_t sym_unload_module_func;
static dbghelp_SymGetModuleInfoW64_t sym_get_module_info_func;
#define MAX_SYMSRV_PATH MAX_PATH
/* Semi-compatibility with the Windows CRT _access function.
*/
drfront_status_t
drfront_access(const char *fname, drfront_access_mode_t mode, OUT bool *ret)
{
int r;
int msdn_mode = 00;
TCHAR wfname[MAX_PATH];
drfront_status_t status_check = DRFRONT_ERROR;
if (ret == NULL)
return DRFRONT_ERROR_INVALID_PARAMETER;
status_check = drfront_char_to_tchar(fname, wfname, MAX_PATH);
if (status_check != DRFRONT_SUCCESS) {
*ret = false;
return status_check;
}
/* Translate drfront_access_mode_t to msdn _waccess mode */
if (TEST(mode, DRFRONT_WRITE))
msdn_mode |= 02;
if (TEST(mode, DRFRONT_READ))
msdn_mode |= 04;
r = _waccess(wfname, msdn_mode);
if (r == -1) {
*ret = false;
if (GetLastError() == EACCES)
return DRFRONT_SUCCESS;
return DRFRONT_ERROR;
}
*ret = true;
return DRFRONT_SUCCESS;
}
/* Implements a normal path search for fname on the paths in env_var. Resolves symlinks,
* which is needed to get the right config filename (i#1062).
*/
drfront_status_t
drfront_searchenv(const char *fname, const char *env_var, OUT char *full_path,
const size_t full_path_size, OUT bool *ret)
{
drfront_status_t status_check = DRFRONT_ERROR;
size_t size_needed = 0;
TCHAR wfname[MAX_PATH];
/* XXX: Not sure what the size for environment variable names should be.
* Perhaps we want a drfront_char_to_tchar_size_needed
*/
TCHAR wenv_var[MAX_PATH];
TCHAR wfull_path[MAX_PATH];
if (full_path == NULL && ret == NULL)
return DRFRONT_ERROR_INVALID_PARAMETER;
status_check = drfront_char_to_tchar(fname, wfname, BUFFER_SIZE_ELEMENTS(wfname));
if (status_check != DRFRONT_SUCCESS) {
*ret = false;
return status_check;
}
status_check = drfront_char_to_tchar(env_var, wenv_var,
BUFFER_SIZE_ELEMENTS(wenv_var));
if (status_check != DRFRONT_SUCCESS) {
*ret = false;
return status_check;
}
_wsearchenv(wfname, wenv_var, wfull_path);
if (wfull_path[0] == L'\0') {
*ret = false;
return DRFRONT_ERROR;
}
status_check = drfront_tchar_to_char_size_needed(wfull_path, &size_needed);
if (status_check != DRFRONT_SUCCESS) {
*ret = false;
return status_check;
} else if (full_path_size < size_needed) {
*ret = true;
return DRFRONT_ERROR_INVALID_SIZE;
}
status_check = drfront_tchar_to_char(wfull_path, full_path, full_path_size);
if (status_check != DRFRONT_SUCCESS) {
*ret = false;
return status_check;
}
full_path[full_path_size - 1] = '\0';
*ret = true;
return DRFRONT_SUCCESS;
}
/* always null-terminates */
drfront_status_t
drfront_tchar_to_char(const TCHAR *wstr, OUT char *buf, size_t buflen/*# elements*/)
{
int res = WideCharToMultiByte(CP_UTF8, 0, wstr, -1/*null-term*/,
buf, (int)buflen, NULL, NULL);
if (res <= 0)
return DRFRONT_ERROR;
buf[buflen - 1] = '\0';
return DRFRONT_SUCCESS;
}
/* includes the terminating null */
drfront_status_t
drfront_tchar_to_char_size_needed(const TCHAR *wstr, OUT size_t *needed)
{
if (needed == NULL)
return DRFRONT_ERROR_INVALID_PARAMETER;
*needed = WideCharToMultiByte(CP_UTF8, 0, wstr, -1/*null-term*/, NULL, 0, NULL,
NULL);
return DRFRONT_SUCCESS;
}
/* always null-terminates */
drfront_status_t
drfront_char_to_tchar(const char *str, OUT TCHAR *wbuf, size_t wbuflen/*# elements*/)
{
int res = MultiByteToWideChar(CP_UTF8, 0/*=>MB_PRECOMPOSED*/, str, -1/*null-term*/,
wbuf, (int)wbuflen);
if (res <= 0)
return DRFRONT_ERROR;
wbuf[wbuflen - 1] = L'\0';
return DRFRONT_SUCCESS;
}
drfront_status_t
drfront_get_env_var(const char *name, OUT char *buf, size_t buflen/*# elements*/)
{
TCHAR wbuf[MAX_PATH];
/* XXX: Not sure what the size for environment variable names should be. */
TCHAR wname[MAX_PATH];
drfront_status_t res =
drfront_char_to_tchar(name, wname, BUFFER_SIZE_ELEMENTS(wname));
if (res != DRFRONT_SUCCESS)
return res;
if (GetEnvironmentVariable(wname, wbuf, BUFFER_SIZE_ELEMENTS(wbuf)) > 0) {
return drfront_tchar_to_char(wbuf, buf, buflen);
}
return DRFRONT_ERROR;
}
drfront_status_t
drfront_get_absolute_path(const char *src, OUT char *buf, size_t buflen/*# elements*/)
{
TCHAR wsrc[MAX_PATH];
TCHAR wdst[MAX_PATH];
drfront_status_t status_check = DRFRONT_ERROR;
int res;
status_check = drfront_char_to_tchar(src, wsrc, BUFFER_SIZE_ELEMENTS(wsrc));
if (status_check != DRFRONT_SUCCESS)
return status_check;
res = GetFullPathName(wsrc, BUFFER_SIZE_ELEMENTS(wdst), wdst, NULL);
if (res <= 0)
return DRFRONT_ERROR;
NULL_TERMINATE_BUFFER(wdst);
status_check = drfront_tchar_to_char(wdst, buf, buflen);
return status_check;
}
drfront_status_t
drfront_get_app_full_path(const char *app, OUT char *buf, size_t buflen/*# elements*/)
{
TCHAR wbuf[MAX_PATH];
TCHAR wapp[MAX_PATH];
drfront_status_t status_check = DRFRONT_ERROR;
bool is_dir = false;
status_check = drfront_char_to_tchar(app, wapp, BUFFER_SIZE_ELEMENTS(wapp));
if (status_check != DRFRONT_SUCCESS)
return status_check;
_tsearchenv(wapp, _T("PATH"), wbuf);
NULL_TERMINATE_BUFFER(wbuf);
status_check = drfront_tchar_to_char(wbuf, buf, buflen);
if (status_check != DRFRONT_SUCCESS)
return status_check;
if (wbuf[0] == _T('\0') ||
/* DrM-i#1617: we might have a same-name directory on the path */
(drfront_dir_exists(buf, &is_dir) == DRFRONT_SUCCESS && is_dir)) {
/* may need to append .exe, FIXME : other executable types */
TCHAR tmp_buf[MAX_PATH];
_sntprintf(tmp_buf, BUFFER_SIZE_ELEMENTS(tmp_buf), _T("%s%s"), wapp, _T(".exe"));
NULL_TERMINATE_BUFFER(wbuf);
_tsearchenv(tmp_buf, _T("PATH"), wbuf);
}
if (wbuf[0] == _T('\0')) {
/* last try: expand w/ cur dir */
GetFullPathName(wapp, BUFFER_SIZE_ELEMENTS(wbuf), wbuf, NULL);
NULL_TERMINATE_BUFFER(wbuf);
}
status_check = drfront_tchar_to_char(wbuf, buf, buflen);
return status_check;
}
drfront_status_t
drfront_set_client_symbol_search_path(const char *symdir, bool ignore_env,
OUT char *symsrv_path, size_t symsrv_path_sz)
{
char app_symsrv_path[MAX_SYMSRV_PATH];
TCHAR wapp_symsrv_path[MAX_SYMSRV_PATH];
char tmp_srv_path[MAX_SYMSRV_PATH];
char tmp_symsrv_path[MAX_SYMSRV_PATH];
char *cur;
char *end;
size_t sofar;
ssize_t len;
bool has_srv;
bool dir_exists;
bool has_ms_symsrv;
drfront_status_t sc;
static const char ms_symsrv[] = "http://msdl.microsoft.com/download/symbols";
if (sym_set_path_func == NULL)
return DRFRONT_ERROR_LIB_UNSUPPORTED;
/* If the user set a non-empty _NT_SYMBOL_PATH, then we use that.
* If not, we use symdir/symbols path and make sure it exists.
*/
if (ignore_env ||
drfront_get_env_var("_NT_SYMBOL_PATH", tmp_symsrv_path,
BUFFER_SIZE_ELEMENTS(tmp_symsrv_path)) != DRFRONT_SUCCESS ||
strlen(tmp_symsrv_path) == 0) {
char pdb_dir[MAXIMUM_PATH];
_snprintf(pdb_dir, BUFFER_SIZE_ELEMENTS(pdb_dir), "%s/symbols", symdir);
NULL_TERMINATE_BUFFER(pdb_dir);
drfront_string_replace_character(pdb_dir, '/', '\\'); /* canonicalize */
sc = drfront_create_dir(pdb_dir);
if ((sc != DRFRONT_SUCCESS && sc != DRFRONT_ERROR_FILE_EXISTS) ||
drfront_access(pdb_dir, DRFRONT_READ, &dir_exists) != DRFRONT_SUCCESS ||
!dir_exists) {
DO_DEBUG(DL_WARN,
printf("Failed to create directory for symbols: %s\n", pdb_dir);
);
return DRFRONT_ERROR_INVALID_PATH;
}
strncpy(tmp_symsrv_path, pdb_dir, BUFFER_SIZE_ELEMENTS(tmp_symsrv_path));
NULL_TERMINATE_BUFFER(tmp_symsrv_path);
}
/* Prepend "srv*" if it isn't there, and append the MS symbol server if it
* isn't there.
*/
has_srv = (_strnicmp("srv*", tmp_symsrv_path, 4) == 0);
has_ms_symsrv = (strstr(tmp_symsrv_path, ms_symsrv) != NULL);
_snprintf(tmp_srv_path, BUFFER_SIZE_ELEMENTS(tmp_srv_path),
"%s%s%s%s",
(has_srv ? "" : "srv*"),
tmp_symsrv_path,
(has_ms_symsrv ? "" : "*"),
(has_ms_symsrv ? "" : ms_symsrv));
NULL_TERMINATE_BUFFER(tmp_srv_path);
strncpy(tmp_symsrv_path, tmp_srv_path, BUFFER_SIZE_ELEMENTS(tmp_symsrv_path));
NULL_TERMINATE_BUFFER(tmp_symsrv_path);
/* For app_symsrv_path, split tmp_symsrv_path on '*' and filter out all the
* non-directory elements.
*/
strncpy(tmp_srv_path, tmp_symsrv_path, BUFFER_SIZE_ELEMENTS(tmp_srv_path));
NULL_TERMINATE_BUFFER(tmp_srv_path);
cur = tmp_srv_path;
end = strchr(tmp_srv_path, '\0');
drfront_string_replace_character(tmp_srv_path, '*', '\0');
sofar = 0;
app_symsrv_path[0] = '\0';
while (cur < end) {
char *next = strchr(cur, '\0');
if (drfront_access(cur, DRFRONT_READ, &dir_exists) == DRFRONT_SUCCESS &&
dir_exists) {
drfront_bufprint(app_symsrv_path, BUFFER_SIZE_ELEMENTS(app_symsrv_path),
&sofar, &len, "%s*", cur);
}
cur = next + 1;
}
if (sofar > 0)
app_symsrv_path[sofar-1] = '\0'; /* Cut trailing '*'. */
if (app_symsrv_path[0] == '\0') {
if (!ignore_env) {
/* Easiest to recurse. Bool prevents 2nd recursion. */
DO_DEBUG(DL_WARN,
printf("No _NT_SYMBOL_PATH dir exists. Trying to"\
"use user-provided path.\n");
);
return drfront_set_client_symbol_search_path(symdir, true, symsrv_path,
symsrv_path_sz);
} else {
DO_DEBUG(DL_WARN,
printf("Error parsing _NT_SYMBOL_PATH: may fail to fetch syms\n");
);
return DRFRONT_ERROR;
}
}
DO_DEBUG(DL_INFO,
printf("Using symbol path %s as the local store\n", app_symsrv_path);
);
DO_DEBUG(DL_INFO,
printf("Using symbol path %s to fetch symbols\n", tmp_symsrv_path);
);
/* Set _NT_SYMBOL_PATH for dbghelp in the app. */
drfront_char_to_tchar(app_symsrv_path, wapp_symsrv_path,
BUFFER_SIZE_ELEMENTS(wapp_symsrv_path));
if (!SetEnvironmentVariable(_T("_NT_SYMBOL_PATH"), wapp_symsrv_path)) {
DO_DEBUG(DL_INFO,
printf("SetEnvironmentVariable failed: %d\n", GetLastError());
);
return DRFRONT_ERROR;
}
/* Set it for our own use as well (dbghelp cached _NT_SYMBOL_PATH when it
* initialized (if NULL was passed to drfront_sym_init) but we validated
* and potentially changed it here). It's up to the caller to use
* the MS symbol server path we're returning to them.
* This allows the caller to test for local pdbs before incurring the
* cost of a network query.
*/
if (!sym_set_path_func(GetCurrentProcess(), wapp_symsrv_path)) {
DO_DEBUG(DL_WARN,
printf("SymSetSearchPathW failed %d\n", GetLastError());
);
return DRFRONT_ERROR;
}
if (symsrv_path != NULL) {
strncpy(symsrv_path, tmp_symsrv_path, symsrv_path_sz);
symsrv_path[symsrv_path_sz - 1] = '\0';
}
return DRFRONT_SUCCESS;
}
drfront_status_t
drfront_set_symbol_search_path(const char *symsrv_path)
{
TCHAR wsymsrv_path[MAX_SYMSRV_PATH];
if (sym_set_path_func == NULL)
return DRFRONT_ERROR_LIB_UNSUPPORTED;
drfront_char_to_tchar(symsrv_path, wsymsrv_path,
BUFFER_SIZE_ELEMENTS(wsymsrv_path));
if (!sym_set_path_func(GetCurrentProcess(), wsymsrv_path)) {
DO_DEBUG(DL_WARN,
printf("SymSetSearchPathW failed %d\n", GetLastError());
);
return DRFRONT_ERROR;
}
return DRFRONT_SUCCESS;
}
drfront_status_t
drfront_sym_init(const char *symsrv_path, const char *dbghelp_path)
{
HANDLE proc_handle = GetCurrentProcess();
TCHAR wdbghelp_path[MAX_PATH];
TCHAR wsymsrv_path[MAX_SYMSRV_PATH];
/* check that it's first call */
if (hlib != NULL)
return DRFRONT_ERROR;
if (dbghelp_path == NULL)
return DRFRONT_ERROR_INVALID_PARAMETER;
drfront_char_to_tchar(dbghelp_path, wdbghelp_path,
BUFFER_SIZE_ELEMENTS(wdbghelp_path));
hlib = LoadLibraryW(wdbghelp_path);
if (hlib == NULL) {
DO_DEBUG(DL_WARN,
printf("dbghelp.dll load failed %d\n", GetLastError());
);
return DRFRONT_ERROR;
}
sym_init_func = (dbghelp_SymInitializeW_t) GetProcAddress(hlib, "SymInitializeW");
if (sym_init_func == NULL) {
DO_DEBUG(DL_WARN,
printf("SymInitializeW load failed %d\n", GetLastError());
);
return DRFRONT_ERROR_LIB_UNSUPPORTED;
}
sym_cleanup_func = (dbghelp_SymCleanupW_t) GetProcAddress(hlib, "SymCleanup");
if (sym_cleanup_func == NULL) {
DO_DEBUG(DL_WARN,
printf("SymCleanup load failed %d\n", GetLastError());
);
return DRFRONT_ERROR_LIB_UNSUPPORTED;
}
sym_set_path_func = (dbghelp_SymSetSearchPathW_t)
GetProcAddress(hlib, "SymSetSearchPathW");
if (sym_set_path_func == NULL) {
DO_DEBUG(DL_WARN,
printf("SymSetSearchPathW load failed %d\n", GetLastError());
);
return DRFRONT_ERROR_LIB_UNSUPPORTED;
}
sym_load_module_func = (dbghelp_SymLoadModuleExW_t)
GetProcAddress(hlib, "SymLoadModuleExW");
if (sym_load_module_func == NULL) {
DO_DEBUG(DL_WARN,
printf("SymLoadModuleExW load failed %d\n", GetLastError());
);
return DRFRONT_ERROR_LIB_UNSUPPORTED;
}
sym_unload_module_func = (dbghelp_SymUnloadModule64_t)
GetProcAddress(hlib, "SymUnloadModule64");
if (sym_unload_module_func == NULL) {
DO_DEBUG(DL_WARN,
printf("SymUnloadModule64 load failed %d\n", GetLastError());
);
return DRFRONT_ERROR_LIB_UNSUPPORTED;
}
sym_get_module_info_func = (dbghelp_SymGetModuleInfoW64_t)
GetProcAddress(hlib, "SymGetModuleInfoW64");
if (sym_get_module_info_func == NULL) {
DO_DEBUG(DL_WARN,
printf("SymGetModuleInfoW64 load failed %d\n", GetLastError());
);
return DRFRONT_ERROR_LIB_UNSUPPORTED;
}
if (symsrv_path != NULL) {
drfront_char_to_tchar(symsrv_path, wsymsrv_path,
BUFFER_SIZE_ELEMENTS(wsymsrv_path));
} else
wsymsrv_path[0] = '\0';
if (!sym_init_func(proc_handle, symsrv_path == NULL ? NULL : wsymsrv_path, FALSE)) {
DO_DEBUG(DL_WARN,
printf("SymInitializeW failed %d\n", GetLastError());
);
return DRFRONT_ERROR;
}
return DRFRONT_SUCCESS;
}
drfront_status_t
drfront_sym_exit(void)
{
HANDLE proc = GetCurrentProcess();
if (sym_cleanup_func == NULL)
return DRFRONT_ERROR_LIB_UNSUPPORTED;
if (!sym_cleanup_func(proc))
return DRFRONT_ERROR;
if (!FreeLibrary(hlib))
return DRFRONT_ERROR;
return DRFRONT_SUCCESS;
}
drfront_status_t
drfront_fetch_module_symbols(const char *modpath, OUT char *symbol_path,
size_t symbol_path_sz)
{
DWORD64 base;
IMAGEHLP_MODULEW64 mod_info;
bool got_pdbs = FALSE;
TCHAR wmodpath[MAXIMUM_PATH];
HANDLE proc = GetCurrentProcess();
if (sym_load_module_func == NULL ||
sym_get_module_info_func == NULL ||
sym_unload_module_func == NULL)
return DRFRONT_ERROR_LIB_UNSUPPORTED;
drfront_char_to_tchar(modpath, wmodpath, BUFFER_SIZE_ELEMENTS(wmodpath));
/* The SymSrv* API calls are complicated. It's easier to set the symbol
* path to point at a server and rely on SymLoadModuleEx to fetch symbols.
*/
/* We must use SymLoadModuleEx as there's no wide version of SymLoadModule64 */
base = sym_load_module_func(proc, NULL, wmodpath, NULL, 0, 0, NULL, 0);
if (base == 0) {
DO_DEBUG(DL_WARN,
printf("SymLoadModuleEx %S error: %d\n", wmodpath, GetLastError());
);
return DRFRONT_ERROR;
}
/* Check that we actually got pdbs. */
memset(&mod_info, 0, sizeof(mod_info));
mod_info.SizeOfStruct = sizeof(mod_info);
if (sym_get_module_info_func(proc, base, &mod_info)) {
switch (mod_info.SymType) {
case SymPdb:
case SymDeferred:
DO_DEBUG(DL_INFO,
printf(" pdb for %s stored at %S\n",
modpath, mod_info.LoadedPdbName);
);
got_pdbs = TRUE;
break;
case SymExport:
DO_DEBUG(DL_WARN,
printf(" failed to fetch pdb for %s, exports only\n",
modpath);
);
break;
default:
DO_DEBUG(DL_WARN,
printf(" failed to fetch pdb for %s, got SymType %d\n",
modpath, mod_info.SymType);
);
break;
}
} else {
DO_DEBUG(DL_WARN,
printf("SymGetModuleInfoEx failed: %d\n", GetLastError());
);
}
if (symbol_path_sz != 0)
drfront_tchar_to_char(mod_info.LoadedPdbName, symbol_path, symbol_path_sz);
/* Unload it. */
if (!sym_unload_module_func(proc, base)) {
DO_DEBUG(DL_WARN,
printf("SymUnloadModule64 error %d\n", GetLastError());
);
}
if (!got_pdbs)
return DRFRONT_ERROR;
return DRFRONT_SUCCESS;
}
/* On failure returns INVALID_HANDLE_VALUE.
* On success returns a file handle which must be closed via CloseHandle()
* by the caller.
*/
static HANDLE
read_nt_headers(const char *exe, IMAGE_NT_HEADERS *nt)
{
HANDLE f;
DWORD offs;
DWORD read;
IMAGE_DOS_HEADER dos;
TCHAR wexe[MAX_PATH];
drfront_status_t status_check = DRFRONT_ERROR;
status_check = drfront_char_to_tchar(exe, wexe, BUFFER_SIZE_ELEMENTS(wexe));
if (status_check != DRFRONT_SUCCESS)
return INVALID_HANDLE_VALUE;
f = CreateFile(wexe, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (f == INVALID_HANDLE_VALUE)
goto read_nt_headers_error;
if (!ReadFile(f, &dos, sizeof(dos), &read, NULL) ||
read != sizeof(dos) ||
dos.e_magic != IMAGE_DOS_SIGNATURE)
goto read_nt_headers_error;
offs = SetFilePointer(f, dos.e_lfanew, NULL, FILE_BEGIN);
if (offs == INVALID_SET_FILE_POINTER)
goto read_nt_headers_error;
if (!ReadFile(f, nt, sizeof(*nt), &read, NULL) ||
read != sizeof(*nt) ||
nt->Signature != IMAGE_NT_SIGNATURE)
goto read_nt_headers_error;
return f;
read_nt_headers_error:
if (f != INVALID_HANDLE_VALUE)
CloseHandle(f);
return INVALID_HANDLE_VALUE;
}
drfront_status_t
drfront_is_64bit_app(const char *exe, OUT bool *is_64)
{
bool res = false;
IMAGE_NT_HEADERS nt;
HANDLE f = NULL;
if (is_64 == NULL)
return DRFRONT_ERROR_INVALID_PARAMETER;
f = read_nt_headers(exe, &nt);
if (f == INVALID_HANDLE_VALUE) {
*is_64 = res;
return DRFRONT_ERROR_INVALID_PARAMETER;
}
res = (nt.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC);
CloseHandle(f);
*is_64 = res;
return DRFRONT_SUCCESS;
}
drfront_status_t
drfront_is_graphical_app(const char *exe, OUT bool *is_graphical)
{
/* reads the PE headers to see whether the given image is a graphical app */
bool res = false; /* err on side of console */
IMAGE_NT_HEADERS nt;
HANDLE f = NULL;
if (is_graphical == NULL)
return DRFRONT_ERROR_INVALID_PARAMETER;
f = read_nt_headers(exe, &nt);
if (f == INVALID_HANDLE_VALUE) {
*is_graphical = res;
return DRFRONT_ERROR_INVALID_PARAMETER;
}
res = (nt.OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI);
CloseHandle(f);
*is_graphical = res;
return DRFRONT_SUCCESS;
}
drfront_status_t
drfront_dir_exists(const char *path, OUT bool *is_dir)
{
TCHAR wpath[MAX_PATH];
DWORD file_attrs = INVALID_FILE_ATTRIBUTES;
drfront_status_t status_check = DRFRONT_ERROR;
if (is_dir == NULL)
return DRFRONT_ERROR_INVALID_PARAMETER;
status_check = drfront_char_to_tchar(path, wpath, BUFFER_SIZE_ELEMENTS(wpath));
if (status_check != DRFRONT_SUCCESS) {
*is_dir = false;
return status_check;
}
/* check if path is a file or directory */
file_attrs = GetFileAttributes(wpath);
if (file_attrs == INVALID_FILE_ATTRIBUTES) {
*is_dir = false;
return DRFRONT_ERROR_INVALID_PATH;
} else {
if (TEST(file_attrs, FILE_ATTRIBUTE_DIRECTORY))
*is_dir = true;
else
*is_dir = false;
}
return DRFRONT_SUCCESS;
}