1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

Squash: Fix bugs

This commit is contained in:
winlin 2021-12-26 17:30:51 +08:00
parent 10d188faab
commit 716e578a19
382 changed files with 170096 additions and 220 deletions

View file

@ -0,0 +1,86 @@
* Get heap-profile-table.cc using DeleteMatchingFiles
* Get heap-profile-table.cc using FillProcSelfMaps, DumpProcSelfMaps
* Play around with ExperimentalGetStackTrace
* Support the windows-level memory-allocation functions? See
/home/build/googleclient/earth/client/tools/memorytracking/client/memorytrace/src/memorytrace.cpp
/home/build/googleclient/total_recall/common/sitestep/*
http://www.internals.com/articles/apispy/apispy.htm
http://www.wheaty.net/APISPY32.zip
* Verify /proc/xxx/maps:
http://www.geocities.com/wah_java_dotnet/procmap/index.html
* Figure out how to edit the executable IAT so tcmalloc.dll is loaded first
* Use QueryPerformanceCounter instead of GetTickCount() (also for sparsehash)
----
More info on windows-level memory-allocation functions:
C runtime malloc
LocalAlloc
GlobalAlloc
HeapAlloc
VirtualAlloc
mmap stuff
malloc, LocalAlloc and GlobalAlloc call HeapAlloc, which calls
VirtualAlloc when needed, which calls VirtualAllocEx (the __sbrk equiv?)
siggi sez: If you want to do a generic job, you probably need to
preserve the semantics of all of these Win32 calls:
Heap32First
Heap32ListFirst
Heap32ListNext
Heap32Next
HeapAlloc
HeapCompact
HeapCreate
HeapCreateTagsW
HeapDestroy
HeapExtend
HeapFree
HeapLock
HeapQueryInformation
HeapQueryTagW
HeapReAlloc
HeapSetInformation
HeapSize
HeapSummary
HeapUnlock
HeapUsage
HeapValidate
HeapWalk
kernel32.dll export functions and nt.dll export functions:
http://www.shorthike.com/svn/trunk/tools_win32/dm/lib/kernel32.def
http://undocumented.ntinternals.net/
You can edit the executable IAT to have the patching DLL be the
first one loaded.
Most complete way to intercept system calls is patch the functions
(not the IAT).
Microsoft has somee built-in routines for heap-checking:
http://support.microsoft.com/kb/268343
----
Itimer replacement:
http://msdn2.microsoft.com/en-us/library/ms712713.aspx
----
Changes I've had to make to the project file:
0) When creating the project file, click on "no autogenerated files"
--- For each project:
1) Alt-F7 -> General -> [pulldown "all configurations" ] -> Output Directory -> $(SolutionDir)$(ConfigurationName)
2) Alt-F7 -> General -> [pulldown "all configurations" ] -> Intermediate Directory -> $(ConfigurationName)
--- For each .cc file:
1) Alt-F7 -> C/C++ -> General -> [pulldown "all configurations"] -> Additional Include Directives --> src/windows + src/
2) Alt-F7 -> C/C++ -> Code Generation -> Runtime Library -> Multi-threaded, debug/release, DLL or not
--- For DLL:
3) Alt-F7 -> Linker -> Input -> [pulldown "all configurations" ] -> Module Definition File -> src\windows\vc7and8.def
--- For binaries depending on a DLL:
3) Right-click on project -> Project Dependencies -> [add dll]
--- For static binaries (not depending on a DLL)
3) Alt-F7 -> C/C++ -> Command Line -> [pulldown "all configurations"] -> /D PERFTOOLS_DLL_DECL=

View file

@ -0,0 +1,183 @@
/* -*- Mode: c; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2008, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: David Vitek
*
* Dump function addresses using Microsoft debug symbols. This works
* on PDB files. Note that this program will download symbols to
* c:\websymbols without asking.
*/
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#ifndef _CRT_SECURE_NO_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <dbghelp.h>
#define SEARCH_CAP (1024*1024)
#define WEBSYM "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols"
void usage() {
fprintf(stderr, "usage: addr2line-pdb "
"[-f|--functions] [-C|--demangle] [-e|--exe filename]\n");
fprintf(stderr, "(Then list the hex addresses on stdin, one per line)\n");
}
int main(int argc, char *argv[]) {
DWORD error;
HANDLE process;
ULONG64 module_base;
int i;
char* search;
char buf[256]; /* Enough to hold one hex address, I trust! */
int rv = 0;
/* We may add SYMOPT_UNDNAME if --demangle is specified: */
DWORD symopts = SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG | SYMOPT_LOAD_LINES;
char* filename = "a.out"; /* The default if -e isn't specified */
int print_function_name = 0; /* Set to 1 if -f is specified */
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "--functions") == 0 || strcmp(argv[i], "-f") == 0) {
print_function_name = 1;
} else if (strcmp(argv[i], "--demangle") == 0 ||
strcmp(argv[i], "-C") == 0) {
symopts |= SYMOPT_UNDNAME;
} else if (strcmp(argv[i], "--exe") == 0 ||
strcmp(argv[i], "-e") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "FATAL ERROR: -e must be followed by a filename\n");
return 1;
}
filename = argv[i+1];
i++; /* to skip over filename too */
} else if (strcmp(argv[i], "--help") == 0) {
usage();
exit(0);
} else {
usage();
exit(1);
}
}
process = GetCurrentProcess();
if (!SymInitialize(process, NULL, FALSE)) {
error = GetLastError();
fprintf(stderr, "SymInitialize returned error : %lu\n", error);
return 1;
}
search = malloc(SEARCH_CAP);
if (SymGetSearchPath(process, search, SEARCH_CAP)) {
if (strlen(search) + sizeof(";" WEBSYM) > SEARCH_CAP) {
fprintf(stderr, "Search path too long\n");
SymCleanup(process);
return 1;
}
strcat(search, ";" WEBSYM);
} else {
error = GetLastError();
fprintf(stderr, "SymGetSearchPath returned error : %lu\n", error);
rv = 1; /* An error, but not a fatal one */
strcpy(search, WEBSYM); /* Use a default value */
}
if (!SymSetSearchPath(process, search)) {
error = GetLastError();
fprintf(stderr, "SymSetSearchPath returned error : %lu\n", error);
rv = 1; /* An error, but not a fatal one */
}
SymSetOptions(symopts);
module_base = SymLoadModuleEx(process, NULL, filename, NULL, 0, 0, NULL, 0);
if (!module_base) {
/* SymLoadModuleEx failed */
error = GetLastError();
fprintf(stderr, "SymLoadModuleEx returned error : %lu for %s\n",
error, filename);
SymCleanup(process);
return 1;
}
buf[sizeof(buf)-1] = '\0'; /* Just to be safe */
while (fgets(buf, sizeof(buf)-1, stdin)) {
/* GNU addr2line seems to just do a strtol and ignore any
* weird characters it gets, so we will too.
*/
unsigned __int64 reladdr = _strtoui64(buf, NULL, 16);
ULONG64 buffer[(sizeof(SYMBOL_INFO) +
MAX_SYM_NAME*sizeof(TCHAR) +
sizeof(ULONG64) - 1)
/ sizeof(ULONG64)];
memset(buffer, 0, sizeof(buffer));
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
IMAGEHLP_LINE64 line;
DWORD dummy;
// Just ignore overflow. In an overflow scenario, the resulting address
// will be lower than module_base which hasn't been mapped by any prior
// SymLoadModuleEx() command. This will cause SymFromAddr() and
// SymGetLineFromAddr64() both to return failures and print the correct
// ?? and ??:0 message variant.
ULONG64 absaddr = reladdr + module_base;
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
// The length of the name is not including the null-terminating character.
pSymbol->MaxNameLen = MAX_SYM_NAME - 1;
if (print_function_name) {
if (SymFromAddr(process, (DWORD64)absaddr, NULL, pSymbol)) {
printf("%s\n", pSymbol->Name);
} else {
printf("??\n");
}
}
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (SymGetLineFromAddr64(process, (DWORD64)absaddr, &dummy, &line)) {
printf("%s:%d\n", line.FileName, (int)line.LineNumber);
} else {
printf("??:0\n");
}
}
SymUnloadModule64(process, module_base);
SymCleanup(process);
return rv;
}

View file

@ -0,0 +1,156 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2010 The Chromium Authors. 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 THE COPYRIGHT
// OWNER 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.
//
// Utility for using SideStep with unit tests.
#ifndef CEEE_TESTING_SIDESTEP_AUTO_TESTING_HOOK_H_
#define CEEE_TESTING_SIDESTEP_AUTO_TESTING_HOOK_H_
#include "base/basictypes.h"
#include "base/logging.h"
#include "preamble_patcher.h"
#define SIDESTEP_CHK(x) CHECK(x)
#define SIDESTEP_EXPECT_TRUE(x) SIDESTEP_CHK(x)
namespace sidestep {
// Same trick as common/scope_cleanup.h ScopeGuardImplBase
class AutoTestingHookBase {
public:
virtual ~AutoTestingHookBase() {}
};
// This is the typedef you normally use for the class, e.g.
//
// AutoTestingHook hook = MakeTestingHook(TargetFunc, HookTargetFunc);
//
// The 'hook' variable will then be destroyed when it goes out of scope.
//
// NOTE: You must not hold this type as a member of another class. Its
// destructor will not get called.
typedef const AutoTestingHookBase& AutoTestingHook;
// This is the class you must use when holding a hook as a member of another
// class, e.g.
//
// public:
// AutoTestingHookHolder holder_;
// MyClass() : my_hook_holder(MakeTestingHookHolder(Target, Hook)) {}
class AutoTestingHookHolder {
public:
explicit AutoTestingHookHolder(AutoTestingHookBase* hook) : hook_(hook) {}
~AutoTestingHookHolder() { delete hook_; }
private:
AutoTestingHookHolder() {} // disallow
AutoTestingHookBase* hook_;
};
// This class helps patch a function, then unpatch it when the object exits
// scope, and also maintains the pointer to the original function stub.
//
// To enable use of the class without having to explicitly provide the
// type of the function pointers (and instead only providing it
// implicitly) we use the same trick as ScopeGuard (see
// common/scope_cleanup.h) uses, so to create a hook you use the MakeHook
// function rather than a constructor.
//
// NOTE: This function is only safe for e.g. unit tests and _not_ for
// production code. See PreamblePatcher class for details.
template <typename T>
class AutoTestingHookImpl : public AutoTestingHookBase {
public:
static AutoTestingHookImpl<T> MakeTestingHook(T target_function,
T replacement_function,
bool do_it) {
return AutoTestingHookImpl<T>(target_function, replacement_function, do_it);
}
static AutoTestingHookImpl<T>* MakeTestingHookHolder(T target_function,
T replacement_function,
bool do_it) {
return new AutoTestingHookImpl<T>(target_function,
replacement_function, do_it);
}
~AutoTestingHookImpl() {
if (did_it_) {
SIDESTEP_CHK(SIDESTEP_SUCCESS == PreamblePatcher::Unpatch(
(void*)target_function_, (void*)replacement_function_,
(void*)original_function_));
}
}
// Returns a pointer to the original function. To use this method you will
// have to explicitly create an AutoTestingHookImpl of the specific
// function pointer type (i.e. not use the AutoTestingHook typedef).
T original_function() {
return original_function_;
}
private:
AutoTestingHookImpl(T target_function, T replacement_function, bool do_it)
: target_function_(target_function),
original_function_(NULL),
replacement_function_(replacement_function),
did_it_(do_it) {
if (do_it) {
SIDESTEP_CHK(SIDESTEP_SUCCESS == PreamblePatcher::Patch(target_function,
replacement_function,
&original_function_));
}
}
T target_function_; // always valid
T original_function_; // always valid
T replacement_function_; // always valid
bool did_it_; // Remember if we did it or not...
};
template <typename T>
inline AutoTestingHookImpl<T> MakeTestingHook(T target,
T replacement,
bool do_it) {
return AutoTestingHookImpl<T>::MakeTestingHook(target, replacement, do_it);
}
template <typename T>
inline AutoTestingHookImpl<T> MakeTestingHook(T target, T replacement) {
return AutoTestingHookImpl<T>::MakeTestingHook(target, replacement, true);
}
template <typename T>
inline AutoTestingHookImpl<T>* MakeTestingHookHolder(T target, T replacement) {
return AutoTestingHookImpl<T>::MakeTestingHookHolder(target, replacement,
true);
}
}; // namespace sidestep
#endif // CEEE_TESTING_SIDESTEP_AUTO_TESTING_HOOK_H_

View file

@ -0,0 +1,306 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* A manual version of config.h fit for windows machines.
*
* Use of this source code is governed by a BSD-style license that can
* be found in the LICENSE file.
*/
/* Sometimes we accidentally #include this config.h instead of the one
in .. -- this is particularly true for msys/mingw, which uses the
unix config.h but also runs code in the windows directory.
*/
#ifdef __MINGW32__
#include "../config.h"
#define GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_
#endif
#ifndef GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_
#define GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_
/* used by tcmalloc.h */
#define GPERFTOOLS_CONFIG_H_
/* Enable aggressive decommit by default */
/* #undef ENABLE_AGGRESSIVE_DECOMMIT_BY_DEFAULT */
/* Build new/delete operators for overaligned types */
/* #undef ENABLE_ALIGNED_NEW_DELETE */
/* Build runtime detection for sized delete */
/* #undef ENABLE_DYNAMIC_SIZED_DELETE */
/* Report large allocation */
/* #undef ENABLE_LARGE_ALLOC_REPORT */
/* Build sized deletion operators */
/* #undef ENABLE_SIZED_DELETE */
/* Define to 1 if you have the <asm/ptrace.h> header file. */
/* #undef HAVE_ASM_PTRACE_H */
/* Define to 1 if you have the <cygwin/signal.h> header file. */
/* #undef HAVE_CYGWIN_SIGNAL_H */
/* Define to 1 if you have the declaration of `backtrace', and to 0 if you
don't. */
/* #undef HAVE_DECL_BACKTRACE */
/* Define to 1 if you have the declaration of `cfree', and to 0 if you don't.
*/
#define HAVE_DECL_CFREE 0
/* Define to 1 if you have the declaration of `memalign', and to 0 if you
don't. */
#define HAVE_DECL_MEMALIGN 0
/* Define to 1 if you have the declaration of `nanosleep', and to 0 if you
don't. */
#define HAVE_DECL_NANOSLEEP 0
/* Define to 1 if you have the declaration of `posix_memalign', and to 0 if
you don't. */
#define HAVE_DECL_POSIX_MEMALIGN 0
/* Define to 1 if you have the declaration of `pvalloc', and to 0 if you
don't. */
#define HAVE_DECL_PVALLOC 0
/* Define to 1 if you have the declaration of `sleep', and to 0 if you don't.
*/
#define HAVE_DECL_SLEEP 0
/* Define to 1 if you have the declaration of `valloc', and to 0 if you don't.
*/
#define HAVE_DECL_VALLOC 0
/* Define to 1 if you have the <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */
/* Define to 1 if the system has the type `Elf32_Versym'. */
/* #undef HAVE_ELF32_VERSYM */
/* Define to 1 if you have the <execinfo.h> header file. */
/* #undef HAVE_EXECINFO_H */
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define to 1 if you have the <features.h> header file. */
/* #undef HAVE_FEATURES_H */
/* Define to 1 if you have the `fork' function. */
/* #undef HAVE_FORK */
/* Define to 1 if you have the `geteuid' function. */
/* #undef HAVE_GETEUID */
/* Define to 1 if you have the <glob.h> header file. */
/* #undef HAVE_GLOB_H */
/* Define to 1 if you have the <grp.h> header file. */
/* #undef HAVE_GRP_H */
/* Define to 1 if you have the <inttypes.h> header file. */
#if defined(_MSC_VER) && _MSC_VER >= 1900
#define HAVE_INTTYPES_H 1
#endif
/* Define to 1 if you have the <libunwind.h> header file. */
/* #undef HAVE_LIBUNWIND_H */
/* Define to 1 if you have the <linux/ptrace.h> header file. */
/* #undef HAVE_LINUX_PTRACE_H */
/* Define if this is Linux that has SIGEV_THREAD_ID */
/* #undef HAVE_LINUX_SIGEV_THREAD_ID */
/* Define to 1 if you have the <malloc.h> header file. */
#define HAVE_MALLOC_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have a working `mmap' system call. */
/* #undef HAVE_MMAP */
/* Define to 1 if you have the <poll.h> header file. */
/* #undef HAVE_POLL_H */
/* define if libc has program_invocation_name */
/* #undef HAVE_PROGRAM_INVOCATION_NAME */
/* Define if you have POSIX threads libraries and header files. */
/* #undef HAVE_PTHREAD */
/* defined to 1 if pthread symbols are exposed even without include pthread.h
*/
/* #undef HAVE_PTHREAD_DESPITE_ASKING_FOR */
/* Define to 1 if you have the <pwd.h> header file. */
/* #undef HAVE_PWD_H */
/* Define to 1 if you have the `sbrk' function. */
/* #undef HAVE_SBRK */
/* Define to 1 if you have the <sched.h> header file. */
/* #undef HAVE_SCHED_H */
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
/* #undef HAVE_STRINGS_H */
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if the system has the type `struct mallinfo'. */
/* #undef HAVE_STRUCT_MALLINFO */
/* Define to 1 if you have the <sys/cdefs.h> header file. */
/* #undef HAVE_SYS_CDEFS_H */
/* Define to 1 if you have the <sys/prctl.h> header file. */
/* #undef HAVE_SYS_PRCTL_H */
/* Define to 1 if you have the <sys/resource.h> header file. */
/* #undef HAVE_SYS_RESOURCE_H */
/* Define to 1 if you have the <sys/socket.h> header file. */
/* #undef HAVE_SYS_SOCKET_H */
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/syscall.h> header file. */
/* #undef HAVE_SYS_SYSCALL_H */
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/ucontext.h> header file. */
/* #undef HAVE_SYS_UCONTEXT_H */
/* Define to 1 if you have the <sys/wait.h> header file. */
/* #undef HAVE_SYS_WAIT_H */
/* Define to 1 if compiler supports __thread */
#define HAVE_TLS 1
/* Define to 1 if you have the <ucontext.h> header file. */
/* #undef HAVE_UCONTEXT_H */
/* Define to 1 if you have the <unistd.h> header file. */
/* #undef HAVE_UNISTD_H */
/* Whether <unwind.h> contains _Unwind_Backtrace */
/* #undef HAVE_UNWIND_BACKTRACE */
/* Define to 1 if you have the <unwind.h> header file. */
/* #undef HAVE_UNWIND_H */
/* define if your compiler has __attribute__ */
/* #undef HAVE___ATTRIBUTE__ */
/* define if your compiler supports alignment of functions */
/* #undef HAVE___ATTRIBUTE__ALIGNED_FN */
/* Define to 1 if compiler supports __environ */
/* #undef HAVE___ENVIRON */
/* Define to 1 if you have the `__sbrk' function. */
/* #undef HAVE___SBRK */
/* prefix where we look for installed files */
/* #undef INSTALL_PREFIX */
/* Define to 1 if int32_t is equivalent to intptr_t */
#ifndef _WIN64
#define INT32_EQUALS_INTPTR 1
#endif
/* Define to the sub-directory where libtool stores uninstalled libraries. */
/* #undef LT_OBJDIR */
/* Name of package */
#define PACKAGE "gperftools"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "gperftools@googlegroups.com"
/* Define to the full name of this package. */
#define PACKAGE_NAME "gperftools"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "gperftools 2.9.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "gperftools"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "2.9.1"
/* How to access the PC from a struct ucontext */
/* #undef PC_FROM_UCONTEXT */
/* Always the empty-string on non-windows systems. On windows, should be
"__declspec(dllexport)". This way, when we compile the dll, we export our
functions/classes. It's safe to define this here because config.h is only
used internally, to compile the DLL, and every DLL source file #includes
"config.h" before anything else. */
#ifndef PERFTOOLS_DLL_DECL
# define PERFTOOLS_IS_A_DLL 1 /* not set if you're statically linking */
# define PERFTOOLS_DLL_DECL __declspec(dllexport)
# define PERFTOOLS_DLL_DECL_FOR_UNITTESTS __declspec(dllimport)
#endif
/* Mark the systems where we know it's bad if pthreads runs too
early before main (before threads are initialized, presumably). */
#ifdef __FreeBSD__
#define PTHREADS_CRASHES_IF_RUN_TOO_EARLY 1
#endif
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define 8 bytes of allocation alignment for tcmalloc */
/* #undef TCMALLOC_ALIGN_8BYTES */
/* Define internal page size for tcmalloc as number of left bitshift */
/* #undef TCMALLOC_PAGE_SIZE_SHIFT */
/* Version number of package */
#define VERSION "2.9.1"
/* C99 says: define this to get the PRI... macros from stdint.h */
#ifndef __STDC_FORMAT_MACROS
# define __STDC_FORMAT_MACROS 1
#endif
// ---------------------------------------------------------------------
// Extra stuff not found in config.h.in
// This must be defined before the windows.h is included. We need at
// least 0x0400 for mutex.h to have access to TryLock, and at least
// 0x0501 for patch_functions.cc to have access to GetModuleHandleEx.
// (This latter is an optimization we could take out if need be.)
#ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0501
#endif
// We want to make sure not to ever try to #include heap-checker.h
#define NO_HEAP_CHECK 1
// TODO(csilvers): include windows/port.h in every relevant source file instead?
#include "windows/port.h"
#endif /* GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_ */

View file

@ -0,0 +1,65 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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 THE COPYRIGHT
// OWNER 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.
//
// ---
// Author: Craig Silverstein (opensource@google.com)
// When you are porting perftools to a new compiler or architecture
// (win64 vs win32) for instance, you'll need to change the mangled
// symbol names for operator new and friends at the top of
// patch_functions.cc. This file helps you do that.
//
// It does this by defining these functions with the proper signature.
// All you need to do is compile this file and the run dumpbin on it.
// (See http://msdn.microsoft.com/en-us/library/5x49w699.aspx for more
// on dumpbin). To do this in MSVC, use the MSVC commandline shell:
// http://msdn.microsoft.com/en-us/library/ms235639(VS.80).aspx)
//
// The run:
// cl /c get_mangled_names.cc
// dumpbin /symbols get_mangled_names.obj
//
// It will print out the mangled (and associated unmangled) names of
// the 8 symbols you need to put at the top of patch_functions.cc
#include <sys/types.h> // for size_t
#include <new> // for nothrow_t
static char m; // some dummy memory so new doesn't return NULL.
void* operator new(size_t size) { return &m; }
void operator delete(void* p) throw() { }
void* operator new[](size_t size) { return &m; }
void operator delete[](void* p) throw() { }
void* operator new(size_t size, const std::nothrow_t&) throw() { return &m; }
void operator delete(void* p, const std::nothrow_t&) throw() { }
void* operator new[](size_t size, const std::nothrow_t&) throw() { return &m; }
void operator delete[](void* p, const std::nothrow_t&) throw() { }

View file

@ -0,0 +1,155 @@
// -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2003, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Sanjay Ghemawat <opensource@google.com>
* .h file by Craig Silverstein <opensource@google.com>
*/
#ifndef TCMALLOC_TCMALLOC_H_
#define TCMALLOC_TCMALLOC_H_
#include <stddef.h> /* for size_t */
#ifdef __cplusplus
#include <new> /* for std::nothrow_t, std::align_val_t */
#endif
/* Define the version number so folks can check against it */
#define TC_VERSION_MAJOR 2
#define TC_VERSION_MINOR 9
#define TC_VERSION_PATCH ".1"
#define TC_VERSION_STRING "gperftools 2.9.1"
#ifndef PERFTOOLS_NOTHROW
#if __cplusplus >= 201103L
#define PERFTOOLS_NOTHROW noexcept
#elif defined(__cplusplus)
#define PERFTOOLS_NOTHROW throw()
#else
# ifdef __GNUC__
# define PERFTOOLS_NOTHROW __attribute__((__nothrow__))
# else
# define PERFTOOLS_NOTHROW
# endif
#endif
#endif
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*
* Returns a human-readable version string. If major, minor,
* and/or patch are not NULL, they are set to the major version,
* minor version, and patch-code (a string, usually "").
*/
PERFTOOLS_DLL_DECL const char* tc_version(int* major, int* minor,
const char** patch) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_free(void* ptr) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_free_sized(void *ptr, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_memalign(size_t __alignment,
size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL int tc_posix_memalign(void** ptr,
size_t align, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_malloc_stats(void) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) PERFTOOLS_NOTHROW;
/*
* This is an alias for MallocExtension::instance()->GetAllocatedSize().
* It is equivalent to
* OS X: malloc_size()
* glibc: malloc_usable_size()
* Windows: _msize()
*/
PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) PERFTOOLS_NOTHROW;
#ifdef __cplusplus
PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_new(size_t size);
PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete(void* p) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);
PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray(void* p) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
#if defined(__cpp_aligned_new) || (defined(_MSVC_LANG) && _MSVC_LANG > 201402L)
PERFTOOLS_DLL_DECL void* tc_new_aligned(size_t size, std::align_val_t al);
PERFTOOLS_DLL_DECL void* tc_new_aligned_nothrow(size_t size, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_aligned_nothrow(void* p, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_newarray_aligned(size_t size, std::align_val_t al);
PERFTOOLS_DLL_DECL void* tc_newarray_aligned_nothrow(size_t size, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_aligned_nothrow(void* p, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
#endif
}
#endif
/* We're only un-defining for public */
#if !defined(GPERFTOOLS_CONFIG_H_)
#undef PERFTOOLS_NOTHROW
#endif /* GPERFTOOLS_CONFIG_H_ */
#endif /* #ifndef TCMALLOC_TCMALLOC_H_ */

View file

@ -0,0 +1,155 @@
// -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2003, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Sanjay Ghemawat <opensource@google.com>
* .h file by Craig Silverstein <opensource@google.com>
*/
#ifndef TCMALLOC_TCMALLOC_H_
#define TCMALLOC_TCMALLOC_H_
#include <stddef.h> /* for size_t */
#ifdef __cplusplus
#include <new> /* for std::nothrow_t, std::align_val_t */
#endif
/* Define the version number so folks can check against it */
#define TC_VERSION_MAJOR @TC_VERSION_MAJOR@
#define TC_VERSION_MINOR @TC_VERSION_MINOR@
#define TC_VERSION_PATCH "@TC_VERSION_PATCH@"
#define TC_VERSION_STRING "gperftools @TC_VERSION_MAJOR@.@TC_VERSION_MINOR@@TC_VERSION_PATCH@"
#ifndef PERFTOOLS_NOTHROW
#if __cplusplus >= 201103L
#define PERFTOOLS_NOTHROW noexcept
#elif defined(__cplusplus)
#define PERFTOOLS_NOTHROW throw()
#else
# ifdef __GNUC__
# define PERFTOOLS_NOTHROW __attribute__((__nothrow__))
# else
# define PERFTOOLS_NOTHROW
# endif
#endif
#endif
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*
* Returns a human-readable version string. If major, minor,
* and/or patch are not NULL, they are set to the major version,
* minor version, and patch-code (a string, usually "").
*/
PERFTOOLS_DLL_DECL const char* tc_version(int* major, int* minor,
const char** patch) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_free(void* ptr) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_free_sized(void *ptr, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_memalign(size_t __alignment,
size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL int tc_posix_memalign(void** ptr,
size_t align, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_malloc_stats(void) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) PERFTOOLS_NOTHROW;
/*
* This is an alias for MallocExtension::instance()->GetAllocatedSize().
* It is equivalent to
* OS X: malloc_size()
* glibc: malloc_usable_size()
* Windows: _msize()
*/
PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) PERFTOOLS_NOTHROW;
#ifdef __cplusplus
PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_new(size_t size);
PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete(void* p) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);
PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray(void* p) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
#if defined(__cpp_aligned_new) || (defined(_MSVC_LANG) && _MSVC_LANG > 201402L)
PERFTOOLS_DLL_DECL void* tc_new_aligned(size_t size, std::align_val_t al);
PERFTOOLS_DLL_DECL void* tc_new_aligned_nothrow(size_t size, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_aligned_nothrow(void* p, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_newarray_aligned(size_t size, std::align_val_t al);
PERFTOOLS_DLL_DECL void* tc_newarray_aligned_nothrow(size_t size, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_aligned_nothrow(void* p, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
#endif
}
#endif
/* We're only un-defining for public */
#if !defined(GPERFTOOLS_CONFIG_H_)
#undef PERFTOOLS_NOTHROW
#endif /* GPERFTOOLS_CONFIG_H_ */
#endif /* #ifndef TCMALLOC_TCMALLOC_H_ */

View file

@ -0,0 +1,122 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Joi Sigurdsson
*
* Table of relevant information about how to decode the ModR/M byte.
* Based on information in the IA-32 Intel® Architecture
* Software Developer's Manual Volume 2: Instruction Set Reference.
*/
#include "mini_disassembler.h"
#include "mini_disassembler_types.h"
namespace sidestep {
const ModrmEntry MiniDisassembler::s_ia16_modrm_map_[] = {
// mod == 00
/* r/m == 000 */ { false, false, OS_ZERO },
/* r/m == 001 */ { false, false, OS_ZERO },
/* r/m == 010 */ { false, false, OS_ZERO },
/* r/m == 011 */ { false, false, OS_ZERO },
/* r/m == 100 */ { false, false, OS_ZERO },
/* r/m == 101 */ { false, false, OS_ZERO },
/* r/m == 110 */ { true, false, OS_WORD },
/* r/m == 111 */ { false, false, OS_ZERO },
// mod == 01
/* r/m == 000 */ { true, false, OS_BYTE },
/* r/m == 001 */ { true, false, OS_BYTE },
/* r/m == 010 */ { true, false, OS_BYTE },
/* r/m == 011 */ { true, false, OS_BYTE },
/* r/m == 100 */ { true, false, OS_BYTE },
/* r/m == 101 */ { true, false, OS_BYTE },
/* r/m == 110 */ { true, false, OS_BYTE },
/* r/m == 111 */ { true, false, OS_BYTE },
// mod == 10
/* r/m == 000 */ { true, false, OS_WORD },
/* r/m == 001 */ { true, false, OS_WORD },
/* r/m == 010 */ { true, false, OS_WORD },
/* r/m == 011 */ { true, false, OS_WORD },
/* r/m == 100 */ { true, false, OS_WORD },
/* r/m == 101 */ { true, false, OS_WORD },
/* r/m == 110 */ { true, false, OS_WORD },
/* r/m == 111 */ { true, false, OS_WORD },
// mod == 11
/* r/m == 000 */ { false, false, OS_ZERO },
/* r/m == 001 */ { false, false, OS_ZERO },
/* r/m == 010 */ { false, false, OS_ZERO },
/* r/m == 011 */ { false, false, OS_ZERO },
/* r/m == 100 */ { false, false, OS_ZERO },
/* r/m == 101 */ { false, false, OS_ZERO },
/* r/m == 110 */ { false, false, OS_ZERO },
/* r/m == 111 */ { false, false, OS_ZERO }
};
const ModrmEntry MiniDisassembler::s_ia32_modrm_map_[] = {
// mod == 00
/* r/m == 000 */ { false, false, OS_ZERO },
/* r/m == 001 */ { false, false, OS_ZERO },
/* r/m == 010 */ { false, false, OS_ZERO },
/* r/m == 011 */ { false, false, OS_ZERO },
/* r/m == 100 */ { false, true, OS_ZERO },
/* r/m == 101 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 110 */ { false, false, OS_ZERO },
/* r/m == 111 */ { false, false, OS_ZERO },
// mod == 01
/* r/m == 000 */ { true, false, OS_BYTE },
/* r/m == 001 */ { true, false, OS_BYTE },
/* r/m == 010 */ { true, false, OS_BYTE },
/* r/m == 011 */ { true, false, OS_BYTE },
/* r/m == 100 */ { true, true, OS_BYTE },
/* r/m == 101 */ { true, false, OS_BYTE },
/* r/m == 110 */ { true, false, OS_BYTE },
/* r/m == 111 */ { true, false, OS_BYTE },
// mod == 10
/* r/m == 000 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 001 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 010 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 011 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 100 */ { true, true, OS_DOUBLE_WORD },
/* r/m == 101 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 110 */ { true, false, OS_DOUBLE_WORD },
/* r/m == 111 */ { true, false, OS_DOUBLE_WORD },
// mod == 11
/* r/m == 000 */ { false, false, OS_ZERO },
/* r/m == 001 */ { false, false, OS_ZERO },
/* r/m == 010 */ { false, false, OS_ZERO },
/* r/m == 011 */ { false, false, OS_ZERO },
/* r/m == 100 */ { false, false, OS_ZERO },
/* r/m == 101 */ { false, false, OS_ZERO },
/* r/m == 110 */ { false, false, OS_ZERO },
/* r/m == 111 */ { false, false, OS_ZERO },
};
}; // namespace sidestep

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,72 @@
/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Craig Silverstein
*
* MinGW is an interesting mix of unix and windows. We use a normal
* configure script, but still need the windows port.h to define some
* stuff that MinGW doesn't support, like pthreads.
*/
#ifndef GOOGLE_PERFTOOLS_WINDOWS_MINGW_H_
#define GOOGLE_PERFTOOLS_WINDOWS_MINGW_H_
#ifdef __MINGW32__
// Older version of the mingw msvcrt don't define _aligned_malloc
#if __MSVCRT_VERSION__ < 0x0700
# define PERFTOOLS_NO_ALIGNED_MALLOC 1
#endif
// This must be defined before the windows.h is included. We need at
// least 0x0400 for mutex.h to have access to TryLock, and at least
// 0x0501 for patch_functions.cc to have access to GetModuleHandleEx.
// (This latter is an optimization we could take out if need be.)
#ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0501
#endif
// Some mingw distributions have a pthreads wrapper, but it doesn't
// work as well as native windows spinlocks (at least for us). So
// pretend the pthreads wrapper doesn't exist, even when it does.
#ifndef HAVE_PTHREAD_DESPITE_ASKING_FOR
#undef HAVE_PTHREAD
#endif
#undef HAVE_FORK
#define HAVE_PID_T
#include "windows/port.h"
#endif /* __MINGW32__ */
#endif /* GOOGLE_PERFTOOLS_WINDOWS_MINGW_H_ */

View file

@ -0,0 +1,432 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Joi Sigurdsson
*
* Implementation of MiniDisassembler.
*/
#include "mini_disassembler.h"
namespace sidestep {
MiniDisassembler::MiniDisassembler(bool operand_default_is_32_bits,
bool address_default_is_32_bits)
: operand_default_is_32_bits_(operand_default_is_32_bits),
address_default_is_32_bits_(address_default_is_32_bits) {
Initialize();
}
MiniDisassembler::MiniDisassembler()
: operand_default_is_32_bits_(true),
address_default_is_32_bits_(true) {
Initialize();
}
InstructionType MiniDisassembler::Disassemble(
unsigned char* start_byte,
unsigned int& instruction_bytes) {
// Clean up any state from previous invocations.
Initialize();
// Start by processing any prefixes.
unsigned char* current_byte = start_byte;
unsigned int size = 0;
InstructionType instruction_type = ProcessPrefixes(current_byte, size);
if (IT_UNKNOWN == instruction_type)
return instruction_type;
current_byte += size;
size = 0;
// Invariant: We have stripped all prefixes, and the operand_is_32_bits_
// and address_is_32_bits_ flags are correctly set.
instruction_type = ProcessOpcode(current_byte, 0, size);
// Check for error processing instruction
if ((IT_UNKNOWN == instruction_type_) || (IT_UNUSED == instruction_type_)) {
return IT_UNKNOWN;
}
current_byte += size;
// Invariant: operand_bytes_ indicates the total size of operands
// specified by the opcode and/or ModR/M byte and/or SIB byte.
// pCurrentByte points to the first byte after the ModR/M byte, or after
// the SIB byte if it is present (i.e. the first byte of any operands
// encoded in the instruction).
// We get the total length of any prefixes, the opcode, and the ModR/M and
// SIB bytes if present, by taking the difference of the original starting
// address and the current byte (which points to the first byte of the
// operands if present, or to the first byte of the next instruction if
// they are not). Adding the count of bytes in the operands encoded in
// the instruction gives us the full length of the instruction in bytes.
instruction_bytes += operand_bytes_ + (current_byte - start_byte);
// Return the instruction type, which was set by ProcessOpcode().
return instruction_type_;
}
void MiniDisassembler::Initialize() {
operand_is_32_bits_ = operand_default_is_32_bits_;
address_is_32_bits_ = address_default_is_32_bits_;
#ifdef _M_X64
operand_default_support_64_bits_ = true;
#else
operand_default_support_64_bits_ = false;
#endif
operand_is_64_bits_ = false;
operand_bytes_ = 0;
have_modrm_ = false;
should_decode_modrm_ = false;
instruction_type_ = IT_UNKNOWN;
got_f2_prefix_ = false;
got_f3_prefix_ = false;
got_66_prefix_ = false;
}
InstructionType MiniDisassembler::ProcessPrefixes(unsigned char* start_byte,
unsigned int& size) {
InstructionType instruction_type = IT_GENERIC;
const Opcode& opcode = s_ia32_opcode_map_[0].table_[*start_byte];
switch (opcode.type_) {
case IT_PREFIX_ADDRESS:
address_is_32_bits_ = !address_default_is_32_bits_;
goto nochangeoperand;
case IT_PREFIX_OPERAND:
operand_is_32_bits_ = !operand_default_is_32_bits_;
nochangeoperand:
case IT_PREFIX:
if (0xF2 == (*start_byte))
got_f2_prefix_ = true;
else if (0xF3 == (*start_byte))
got_f3_prefix_ = true;
else if (0x66 == (*start_byte))
got_66_prefix_ = true;
else if (operand_default_support_64_bits_ && (*start_byte) & 0x48)
operand_is_64_bits_ = true;
instruction_type = opcode.type_;
size ++;
// we got a prefix, so add one and check next byte
ProcessPrefixes(start_byte + 1, size);
default:
break; // not a prefix byte
}
return instruction_type;
}
InstructionType MiniDisassembler::ProcessOpcode(unsigned char* start_byte,
unsigned int table_index,
unsigned int& size) {
const OpcodeTable& table = s_ia32_opcode_map_[table_index]; // Get our table
unsigned char current_byte = (*start_byte) >> table.shift_;
current_byte = current_byte & table.mask_; // Mask out the bits we will use
// Check whether the byte we have is inside the table we have.
if (current_byte < table.min_lim_ || current_byte > table.max_lim_) {
instruction_type_ = IT_UNKNOWN;
return instruction_type_;
}
const Opcode& opcode = table.table_[current_byte];
if (IT_UNUSED == opcode.type_) {
// This instruction is not used by the IA-32 ISA, so we indicate
// this to the user. Probably means that we were pointed to
// a byte in memory that was not the start of an instruction.
instruction_type_ = IT_UNUSED;
return instruction_type_;
} else if (IT_REFERENCE == opcode.type_) {
// We are looking at an opcode that has more bytes (or is continued
// in the ModR/M byte). Recursively find the opcode definition in
// the table for the opcode's next byte.
size++;
ProcessOpcode(start_byte + 1, opcode.table_index_, size);
return instruction_type_;
}
const SpecificOpcode* specific_opcode = (SpecificOpcode*)&opcode;
if (opcode.is_prefix_dependent_) {
if (got_f2_prefix_ && opcode.opcode_if_f2_prefix_.mnemonic_ != 0) {
specific_opcode = &opcode.opcode_if_f2_prefix_;
} else if (got_f3_prefix_ && opcode.opcode_if_f3_prefix_.mnemonic_ != 0) {
specific_opcode = &opcode.opcode_if_f3_prefix_;
} else if (got_66_prefix_ && opcode.opcode_if_66_prefix_.mnemonic_ != 0) {
specific_opcode = &opcode.opcode_if_66_prefix_;
}
}
// Inv: The opcode type is known.
instruction_type_ = specific_opcode->type_;
// Let's process the operand types to see if we have any immediate
// operands, and/or a ModR/M byte.
ProcessOperand(specific_opcode->flag_dest_);
ProcessOperand(specific_opcode->flag_source_);
ProcessOperand(specific_opcode->flag_aux_);
// Inv: We have processed the opcode and incremented operand_bytes_
// by the number of bytes of any operands specified by the opcode
// that are stored in the instruction (not registers etc.). Now
// we need to return the total number of bytes for the opcode and
// for the ModR/M or SIB bytes if they are present.
if (table.mask_ != 0xff) {
if (have_modrm_) {
// we're looking at a ModR/M byte so we're not going to
// count that into the opcode size
ProcessModrm(start_byte, size);
return IT_GENERIC;
} else {
// need to count the ModR/M byte even if it's just being
// used for opcode extension
size++;
return IT_GENERIC;
}
} else {
if (have_modrm_) {
// The ModR/M byte is the next byte.
size++;
ProcessModrm(start_byte + 1, size);
return IT_GENERIC;
} else {
size++;
return IT_GENERIC;
}
}
}
bool MiniDisassembler::ProcessOperand(int flag_operand) {
bool succeeded = true;
if (AM_NOT_USED == flag_operand)
return succeeded;
// Decide what to do based on the addressing mode.
switch (flag_operand & AM_MASK) {
// No ModR/M byte indicated by these addressing modes, and no
// additional (e.g. immediate) parameters.
case AM_A: // Direct address
case AM_F: // EFLAGS register
case AM_X: // Memory addressed by the DS:SI register pair
case AM_Y: // Memory addressed by the ES:DI register pair
case AM_IMPLICIT: // Parameter is implicit, occupies no space in
// instruction
break;
// There is a ModR/M byte but it does not necessarily need
// to be decoded.
case AM_C: // reg field of ModR/M selects a control register
case AM_D: // reg field of ModR/M selects a debug register
case AM_G: // reg field of ModR/M selects a general register
case AM_P: // reg field of ModR/M selects an MMX register
case AM_R: // mod field of ModR/M may refer only to a general register
case AM_S: // reg field of ModR/M selects a segment register
case AM_T: // reg field of ModR/M selects a test register
case AM_V: // reg field of ModR/M selects a 128-bit XMM register
have_modrm_ = true;
break;
// In these addressing modes, there is a ModR/M byte and it needs to be
// decoded. No other (e.g. immediate) params than indicated in ModR/M.
case AM_E: // Operand is either a general-purpose register or memory,
// specified by ModR/M byte
case AM_M: // ModR/M byte will refer only to memory
case AM_Q: // Operand is either an MMX register or memory (complex
// evaluation), specified by ModR/M byte
case AM_W: // Operand is either a 128-bit XMM register or memory (complex
// eval), specified by ModR/M byte
have_modrm_ = true;
should_decode_modrm_ = true;
break;
// These addressing modes specify an immediate or an offset value
// directly, so we need to look at the operand type to see how many
// bytes.
case AM_I: // Immediate data.
case AM_J: // Jump to offset.
case AM_O: // Operand is at offset.
switch (flag_operand & OT_MASK) {
case OT_B: // Byte regardless of operand-size attribute.
operand_bytes_ += OS_BYTE;
break;
case OT_C: // Byte or word, depending on operand-size attribute.
if (operand_is_32_bits_)
operand_bytes_ += OS_WORD;
else
operand_bytes_ += OS_BYTE;
break;
case OT_D: // Doubleword, regardless of operand-size attribute.
operand_bytes_ += OS_DOUBLE_WORD;
break;
case OT_DQ: // Double-quadword, regardless of operand-size attribute.
operand_bytes_ += OS_DOUBLE_QUAD_WORD;
break;
case OT_P: // 32-bit or 48-bit pointer, depending on operand-size
// attribute.
if (operand_is_32_bits_)
operand_bytes_ += OS_48_BIT_POINTER;
else
operand_bytes_ += OS_32_BIT_POINTER;
break;
case OT_PS: // 128-bit packed single-precision floating-point data.
operand_bytes_ += OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING;
break;
case OT_Q: // Quadword, regardless of operand-size attribute.
operand_bytes_ += OS_QUAD_WORD;
break;
case OT_S: // 6-byte pseudo-descriptor.
operand_bytes_ += OS_PSEUDO_DESCRIPTOR;
break;
case OT_SD: // Scalar Double-Precision Floating-Point Value
case OT_PD: // Unaligned packed double-precision floating point value
operand_bytes_ += OS_DOUBLE_PRECISION_FLOATING;
break;
case OT_SS:
// Scalar element of a 128-bit packed single-precision
// floating data.
// We simply return enItUnknown since we don't have to support
// floating point
succeeded = false;
break;
case OT_V: // Word, doubleword or quadword, depending on operand-size
// attribute.
if (operand_is_64_bits_ && flag_operand & AM_I &&
flag_operand & IOS_64)
operand_bytes_ += OS_QUAD_WORD;
else if (operand_is_32_bits_)
operand_bytes_ += OS_DOUBLE_WORD;
else
operand_bytes_ += OS_WORD;
break;
case OT_W: // Word, regardless of operand-size attribute.
operand_bytes_ += OS_WORD;
break;
// Can safely ignore these.
case OT_A: // Two one-word operands in memory or two double-word
// operands in memory
case OT_PI: // Quadword MMX technology register (e.g. mm0)
case OT_SI: // Doubleword integer register (e.g., eax)
break;
default:
break;
}
break;
default:
break;
}
return succeeded;
}
bool MiniDisassembler::ProcessModrm(unsigned char* start_byte,
unsigned int& size) {
// If we don't need to decode, we just return the size of the ModR/M
// byte (there is never a SIB byte in this case).
if (!should_decode_modrm_) {
size++;
return true;
}
// We never care about the reg field, only the combination of the mod
// and r/m fields, so let's start by packing those fields together into
// 5 bits.
unsigned char modrm = (*start_byte);
unsigned char mod = modrm & 0xC0; // mask out top two bits to get mod field
modrm = modrm & 0x07; // mask out bottom 3 bits to get r/m field
mod = mod >> 3; // shift the mod field to the right place
modrm = mod | modrm; // combine the r/m and mod fields as discussed
mod = mod >> 3; // shift the mod field to bits 2..0
// Invariant: modrm contains the mod field in bits 4..3 and the r/m field
// in bits 2..0, and mod contains the mod field in bits 2..0
const ModrmEntry* modrm_entry = 0;
if (address_is_32_bits_)
modrm_entry = &s_ia32_modrm_map_[modrm];
else
modrm_entry = &s_ia16_modrm_map_[modrm];
// Invariant: modrm_entry points to information that we need to decode
// the ModR/M byte.
// Add to the count of operand bytes, if the ModR/M byte indicates
// that some operands are encoded in the instruction.
if (modrm_entry->is_encoded_in_instruction_)
operand_bytes_ += modrm_entry->operand_size_;
// Process the SIB byte if necessary, and return the count
// of ModR/M and SIB bytes.
if (modrm_entry->use_sib_byte_) {
size++;
return ProcessSib(start_byte + 1, mod, size);
} else {
size++;
return true;
}
}
bool MiniDisassembler::ProcessSib(unsigned char* start_byte,
unsigned char mod,
unsigned int& size) {
// get the mod field from the 2..0 bits of the SIB byte
unsigned char sib_base = (*start_byte) & 0x07;
if (0x05 == sib_base) {
switch (mod) {
case 0x00: // mod == 00
case 0x02: // mod == 10
operand_bytes_ += OS_DOUBLE_WORD;
break;
case 0x01: // mod == 01
operand_bytes_ += OS_BYTE;
break;
case 0x03: // mod == 11
// According to the IA-32 docs, there does not seem to be a disp
// value for this value of mod
default:
break;
}
}
size++;
return true;
}
}; // namespace sidestep

View file

@ -0,0 +1,198 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Joi Sigurdsson
*
* Definition of MiniDisassembler.
*/
#ifndef GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_H_
#define GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_H_
#include "config.h"
#include <windows.h>
#include "mini_disassembler_types.h"
// compatibility shim
#include "base/logging.h"
#define SIDESTEP_ASSERT(cond) RAW_DCHECK(cond, #cond)
#define SIDESTEP_LOG(msg) RAW_VLOG(1, msg)
namespace sidestep {
// This small disassembler is very limited
// in its functionality, and in fact does only the bare minimum required by the
// preamble patching utility. It may be useful for other purposes, however.
//
// The limitations include at least the following:
// -# No support for coprocessor opcodes, MMX, etc.
// -# No machine-readable identification of opcodes or decoding of
// assembly parameters. The name of the opcode (as a string) is given,
// however, to aid debugging.
//
// You may ask what this little disassembler actually does, then? The answer is
// that it does the following, which is exactly what the patching utility needs:
// -# Indicates if opcode is a jump (any kind) or a return (any kind)
// because this is important for the patching utility to determine if
// a function is too short or there are jumps too early in it for it
// to be preamble patched.
// -# The opcode length is always calculated, so that the patching utility
// can figure out where the next instruction starts, and whether it
// already has enough instructions to replace with the absolute jump
// to the patching code.
//
// The usage is quite simple; just create a MiniDisassembler and use its
// Disassemble() method.
//
// If you would like to extend this disassembler, please refer to the
// IA-32 Intel® Architecture Software Developer's Manual Volume 2:
// Instruction Set Reference for information about operand decoding
// etc.
class PERFTOOLS_DLL_DECL MiniDisassembler {
public:
// Creates a new instance and sets defaults.
//
// @param operand_default_32_bits If true, the default operand size is
// set to 32 bits, which is the default under Win32. Otherwise it is 16 bits.
// @param address_default_32_bits If true, the default address size is
// set to 32 bits, which is the default under Win32. Otherwise it is 16 bits.
MiniDisassembler(bool operand_default_32_bits,
bool address_default_32_bits);
// Equivalent to MiniDisassembler(true, true);
MiniDisassembler();
// Attempts to disassemble a single instruction starting from the
// address in memory it is pointed to.
//
// @param start Address where disassembly should start.
// @param instruction_bytes Variable that will be <b>incremented</b> by
// the length in bytes of the instruction.
// @return enItJump, enItReturn or enItGeneric on success. enItUnknown
// if unable to disassemble, enItUnused if this seems to be an unused
// opcode. In the last two (error) cases, cbInstruction will be set
// to 0xffffffff.
//
// @post This instance of the disassembler is ready to be used again,
// with unchanged defaults from creation time.
InstructionType Disassemble(unsigned char* start, unsigned int& instruction_bytes);
private:
// Makes the disassembler ready for reuse.
void Initialize();
// Sets the flags for address and operand sizes.
// @return Number of prefix bytes.
InstructionType ProcessPrefixes(unsigned char* start, unsigned int& size);
// Sets the flag for whether we have ModR/M, and increments
// operand_bytes_ if any are specifies by the opcode directly.
// @return Number of opcode bytes.
InstructionType ProcessOpcode(unsigned char* start,
unsigned int table,
unsigned int& size);
// Checks the type of the supplied operand. Increments
// operand_bytes_ if it directly indicates an immediate etc.
// operand. Asserts have_modrm_ if the operand specifies
// a ModR/M byte.
bool ProcessOperand(int flag_operand);
// Increments operand_bytes_ by size specified by ModR/M and
// by SIB if present.
// @return 0 in case of error, 1 if there is just a ModR/M byte,
// 2 if there is a ModR/M byte and a SIB byte.
bool ProcessModrm(unsigned char* start, unsigned int& size);
// Processes the SIB byte that it is pointed to.
// @param start Pointer to the SIB byte.
// @param mod The mod field from the ModR/M byte.
// @return 1 to indicate success (indicates 1 SIB byte)
bool ProcessSib(unsigned char* start, unsigned char mod, unsigned int& size);
// The instruction type we have decoded from the opcode.
InstructionType instruction_type_;
// Counts the number of bytes that is occupied by operands in
// the current instruction (note: we don't care about how large
// operands stored in registers etc. are).
unsigned int operand_bytes_;
// True iff there is a ModR/M byte in this instruction.
bool have_modrm_;
// True iff we need to decode the ModR/M byte (sometimes it just
// points to a register, we can tell by the addressing mode).
bool should_decode_modrm_;
// Current operand size is 32 bits if true, 16 bits if false.
bool operand_is_32_bits_;
// Default operand size is 32 bits if true, 16 bits if false.
bool operand_default_is_32_bits_;
// Current address size is 32 bits if true, 16 bits if false.
bool address_is_32_bits_;
// Default address size is 32 bits if true, 16 bits if false.
bool address_default_is_32_bits_;
// Determines if 64 bit operands are supported (x64).
bool operand_default_support_64_bits_;
// Current operand size is 64 bits if true, 32 bits if false.
bool operand_is_64_bits_;
// Huge big opcode table based on the IA-32 manual, defined
// in Ia32OpcodeMap.cc
static const OpcodeTable s_ia32_opcode_map_[];
// Somewhat smaller table to help with decoding ModR/M bytes
// when 16-bit addressing mode is being used. Defined in
// Ia32ModrmMap.cc
static const ModrmEntry s_ia16_modrm_map_[];
// Somewhat smaller table to help with decoding ModR/M bytes
// when 32-bit addressing mode is being used. Defined in
// Ia32ModrmMap.cc
static const ModrmEntry s_ia32_modrm_map_[];
// Indicators of whether we got certain prefixes that certain
// silly Intel instructions depend on in nonstandard ways for
// their behaviors.
bool got_f2_prefix_, got_f3_prefix_, got_66_prefix_;
};
}; // namespace sidestep
#endif // GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_H_

View file

@ -0,0 +1,237 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Joi Sigurdsson
*
* Several simple types used by the disassembler and some of the patching
* mechanisms.
*/
#ifndef GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_TYPES_H_
#define GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_TYPES_H_
namespace sidestep {
// Categories of instructions that we care about
enum InstructionType {
// This opcode is not used
IT_UNUSED,
// This disassembler does not recognize this opcode (error)
IT_UNKNOWN,
// This is not an instruction but a reference to another table
IT_REFERENCE,
// This byte is a prefix byte that we can ignore
IT_PREFIX,
// This is a prefix byte that switches to the nondefault address size
IT_PREFIX_ADDRESS,
// This is a prefix byte that switches to the nondefault operand size
IT_PREFIX_OPERAND,
// A jump or call instruction
IT_JUMP,
// A return instruction
IT_RETURN,
// Any other type of instruction (in this case we don't care what it is)
IT_GENERIC,
};
// Lists IA-32 operand sizes in multiples of 8 bits
enum OperandSize {
OS_ZERO = 0,
OS_BYTE = 1,
OS_WORD = 2,
OS_DOUBLE_WORD = 4,
OS_QUAD_WORD = 8,
OS_DOUBLE_QUAD_WORD = 16,
OS_32_BIT_POINTER = 32/8,
OS_48_BIT_POINTER = 48/8,
OS_SINGLE_PRECISION_FLOATING = 32/8,
OS_DOUBLE_PRECISION_FLOATING = 64/8,
OS_DOUBLE_EXTENDED_PRECISION_FLOATING = 80/8,
OS_128_BIT_PACKED_SINGLE_PRECISION_FLOATING = 128/8,
OS_PSEUDO_DESCRIPTOR = 6
};
// Operand addressing methods from the IA-32 manual. The enAmMask value
// is a mask for the rest. The other enumeration values are named for the
// names given to the addressing methods in the manual, e.g. enAm_D is for
// the D addressing method.
//
// The reason we use a full 4 bytes and a mask, is that we need to combine
// these flags with the enOperandType to store the details
// on the operand in a single integer.
enum AddressingMethod {
AM_NOT_USED = 0, // This operand is not used for this instruction
AM_MASK = 0x00FF0000, // Mask for the rest of the values in this enumeration
AM_A = 0x00010000, // A addressing type
AM_C = 0x00020000, // C addressing type
AM_D = 0x00030000, // D addressing type
AM_E = 0x00040000, // E addressing type
AM_F = 0x00050000, // F addressing type
AM_G = 0x00060000, // G addressing type
AM_I = 0x00070000, // I addressing type
AM_J = 0x00080000, // J addressing type
AM_M = 0x00090000, // M addressing type
AM_O = 0x000A0000, // O addressing type
AM_P = 0x000B0000, // P addressing type
AM_Q = 0x000C0000, // Q addressing type
AM_R = 0x000D0000, // R addressing type
AM_S = 0x000E0000, // S addressing type
AM_T = 0x000F0000, // T addressing type
AM_V = 0x00100000, // V addressing type
AM_W = 0x00110000, // W addressing type
AM_X = 0x00120000, // X addressing type
AM_Y = 0x00130000, // Y addressing type
AM_REGISTER = 0x00140000, // Specific register is always used as this op
AM_IMPLICIT = 0x00150000, // An implicit, fixed value is used
};
// Operand types from the IA-32 manual. The enOtMask value is
// a mask for the rest. The rest of the values are named for the
// names given to these operand types in the manual, e.g. enOt_ps
// is for the ps operand type in the manual.
//
// The reason we use a full 4 bytes and a mask, is that we need
// to combine these flags with the enAddressingMethod to store the details
// on the operand in a single integer.
enum OperandType {
OT_MASK = 0xFF000000,
OT_A = 0x01000000,
OT_B = 0x02000000,
OT_C = 0x03000000,
OT_D = 0x04000000,
OT_DQ = 0x05000000,
OT_P = 0x06000000,
OT_PI = 0x07000000,
OT_PS = 0x08000000, // actually unsupported for (we don't know its size)
OT_Q = 0x09000000,
OT_S = 0x0A000000,
OT_SS = 0x0B000000,
OT_SI = 0x0C000000,
OT_V = 0x0D000000,
OT_W = 0x0E000000,
OT_SD = 0x0F000000, // scalar double-precision floating-point value
OT_PD = 0x10000000, // double-precision floating point
// dummy "operand type" for address mode M - which doesn't specify
// operand type
OT_ADDRESS_MODE_M = 0x80000000
};
// Flag that indicates if an immediate operand is 64-bits.
//
// The Intel 64 and IA-32 Architecture Software Developer's Manual currently
// defines MOV as the only instruction supporting a 64-bit immediate operand.
enum ImmediateOperandSize {
IOS_MASK = 0x0000F000,
IOS_DEFAULT = 0x0,
IOS_64 = 0x00001000
};
// Everything that's in an Opcode (see below) except the three
// alternative opcode structs for different prefixes.
struct SpecificOpcode {
// Index to continuation table, or 0 if this is the last
// byte in the opcode.
int table_index_;
// The opcode type
InstructionType type_;
// Description of the type of the dest, src and aux operands,
// put together from enOperandType, enAddressingMethod and
// enImmediateOperandSize flags.
int flag_dest_;
int flag_source_;
int flag_aux_;
// We indicate the mnemonic for debugging purposes
const char* mnemonic_;
};
// The information we keep in our tables about each of the different
// valid instructions recognized by the IA-32 architecture.
struct Opcode {
// Index to continuation table, or 0 if this is the last
// byte in the opcode.
int table_index_;
// The opcode type
InstructionType type_;
// Description of the type of the dest, src and aux operands,
// put together from an enOperandType flag and an enAddressingMethod
// flag.
unsigned flag_dest_;
unsigned flag_source_;
unsigned flag_aux_;
// We indicate the mnemonic for debugging purposes
const char* mnemonic_;
// Alternative opcode info if certain prefixes are specified.
// In most cases, all of these are zeroed-out. Only used if
// bPrefixDependent is true.
bool is_prefix_dependent_;
SpecificOpcode opcode_if_f2_prefix_;
SpecificOpcode opcode_if_f3_prefix_;
SpecificOpcode opcode_if_66_prefix_;
};
// Information about each table entry.
struct OpcodeTable {
// Table of instruction entries
const Opcode* table_;
// How many bytes left to shift ModR/M byte <b>before</b> applying mask
unsigned char shift_;
// Mask to apply to byte being looked at before comparing to table
unsigned char mask_;
// Minimum/maximum indexes in table.
unsigned char min_lim_;
unsigned char max_lim_;
};
// Information about each entry in table used to decode ModR/M byte.
struct ModrmEntry {
// Is the operand encoded as bytes in the instruction (rather than
// if it's e.g. a register in which case it's just encoded in the
// ModR/M byte)
bool is_encoded_in_instruction_;
// Is there a SIB byte? In this case we always need to decode it.
bool use_sib_byte_;
// What is the size of the operand (only important if it's encoded
// in the instruction)?
OperandSize operand_size_;
};
}; // namespace sidestep
#endif // GOOGLE_PERFTOOLS_MINI_DISASSEMBLER_TYPES_H_

View file

@ -0,0 +1,274 @@
/* -*- Mode: c; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2008, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: David Vitek
*
* Dump function addresses using Microsoft debug symbols. This works
* on PDB files. Note that this program will download symbols to
* c:\websymbols without asking.
*/
#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // for _strdup
#include <windows.h>
#include <dbghelp.h>
// Unfortunately, there is no versioning info in dbghelp.h so I can
// tell whether it has an old-style (circa VC7.1) IMAGEHLP_MODULE64
// struct, with only a few fields, or a new-style (circa VC8)
// IMAGEHLP_MODULE64, with lots of fields. These fields are just used
// for debugging, so it's fine to just assume the smaller struct, but
// for most people, using a modern MSVC, the full struct is available.
// If you are one of those people and would like this extra debugging
// info, you can uncomment the line below.
//#define VC8_OR_ABOVE
#define SEARCH_CAP (1024*1024)
#define WEBSYM "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols"
typedef struct {
char *name;
ULONG64 addr;
ULONG flags;
} SYM;
typedef struct {
ULONG64 module_base;
SYM *syms;
DWORD syms_len;
DWORD syms_cap;
} SYM_CONTEXT;
static int sym_cmp(const void *_s1, const void *_s2) {
const SYM *s1 = (const SYM *)_s1;
const SYM *s2 = (const SYM *)_s2;
if (s1->addr < s2->addr)
return -1;
if (s1->addr > s2->addr)
return 1;
return 0;
}
static BOOL CALLBACK EnumSymProc(PSYMBOL_INFO symbol_info,
ULONG symbol_size,
PVOID user_context) {
SYM_CONTEXT *ctx = (SYM_CONTEXT*)user_context;
if (symbol_info->Address < ctx->module_base ||
(symbol_info->Flags & SYMFLAG_TLSREL)) {
return TRUE;
}
if (ctx->syms_len == ctx->syms_cap) {
if (!ctx->syms_cap)
ctx->syms_cap++;
ctx->syms_cap *= 2;
ctx->syms = realloc(ctx->syms, sizeof(ctx->syms[0]) * ctx->syms_cap);
}
ctx->syms[ctx->syms_len].name = _strdup(symbol_info->Name);
ctx->syms[ctx->syms_len].addr = symbol_info->Address;
ctx->syms[ctx->syms_len].flags = symbol_info->Flags;
ctx->syms_len++;
return TRUE;
}
static void MaybePrint(const char* var, const char* description) {
if (var[0])
printf("%s: %s\n", description, var);
}
static void PrintAvailability(BOOL var, const char *description) {
printf("%s: %s\n", description, (var ? "Available" : "Not available"));
}
static void ShowSymbolInfo(HANDLE process, ULONG64 module_base) {
/* Get module information. */
IMAGEHLP_MODULE64 module_info;
BOOL getmoduleinfo_rv;
printf("Load Address: %I64x\n", module_base);
memset(&module_info, 0, sizeof(module_info));
module_info.SizeOfStruct = sizeof(module_info);
getmoduleinfo_rv = SymGetModuleInfo64(process, module_base, &module_info);
if (!getmoduleinfo_rv) {
printf("Error: SymGetModuleInfo64() failed. Error code: %u\n",
GetLastError());
return;
}
/* Display information about symbols, based on kind of symbol. */
switch (module_info.SymType) {
case SymNone:
printf(("No symbols available for the module.\n"));
break;
case SymExport:
printf(("Loaded symbols: Exports\n"));
break;
case SymCoff:
printf(("Loaded symbols: COFF\n"));
break;
case SymCv:
printf(("Loaded symbols: CodeView\n"));
break;
case SymSym:
printf(("Loaded symbols: SYM\n"));
break;
case SymVirtual:
printf(("Loaded symbols: Virtual\n"));
break;
case SymPdb:
printf(("Loaded symbols: PDB\n"));
break;
case SymDia:
printf(("Loaded symbols: DIA\n"));
break;
case SymDeferred:
printf(("Loaded symbols: Deferred\n")); /* not actually loaded */
break;
default:
printf(("Loaded symbols: Unknown format.\n"));
break;
}
MaybePrint("Image name", module_info.ImageName);
MaybePrint("Loaded image name", module_info.LoadedImageName);
#ifdef VC8_OR_ABOVE /* TODO(csilvers): figure out how to tell */
MaybePrint("PDB file name", module_info.LoadedPdbName);
if (module_info.PdbUnmatched || module_info.DbgUnmatched) {
/* This can only happen if the debug information is contained in a
* separate file (.DBG or .PDB)
*/
printf(("Warning: Unmatched symbols.\n"));
}
#endif
/* Contents */
#ifdef VC8_OR_ABOVE /* TODO(csilvers): figure out how to tell */
PrintAvailability("Line numbers", module_info.LineNumbers);
PrintAvailability("Global symbols", module_info.GlobalSymbols);
PrintAvailability("Type information", module_info.TypeInfo);
#endif
}
void usage() {
fprintf(stderr, "usage: nm-pdb [-C|--demangle] <module or filename>\n");
}
int main(int argc, char *argv[]) {
DWORD error;
HANDLE process;
ULONG64 module_base;
SYM_CONTEXT ctx;
int i;
char* search;
char* filename = NULL;
int rv = 0;
/* We may add SYMOPT_UNDNAME if --demangle is specified: */
DWORD symopts = SYMOPT_DEFERRED_LOADS | SYMOPT_DEBUG;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "--demangle") == 0 || strcmp(argv[i], "-C") == 0) {
symopts |= SYMOPT_UNDNAME;
} else if (strcmp(argv[i], "--help") == 0) {
usage();
exit(0);
} else {
break;
}
}
if (i != argc - 1) {
usage();
exit(1);
}
filename = argv[i];
process = GetCurrentProcess();
if (!SymInitialize(process, NULL, FALSE)) {
error = GetLastError();
fprintf(stderr, "SymInitialize returned error : %d\n", error);
return 1;
}
search = malloc(SEARCH_CAP);
if (SymGetSearchPath(process, search, SEARCH_CAP)) {
if (strlen(search) + sizeof(";" WEBSYM) > SEARCH_CAP) {
fprintf(stderr, "Search path too long\n");
SymCleanup(process);
return 1;
}
strcat(search, ";" WEBSYM);
} else {
error = GetLastError();
fprintf(stderr, "SymGetSearchPath returned error : %d\n", error);
rv = 1; /* An error, but not a fatal one */
strcpy(search, WEBSYM); /* Use a default value */
}
if (!SymSetSearchPath(process, search)) {
error = GetLastError();
fprintf(stderr, "SymSetSearchPath returned error : %d\n", error);
rv = 1; /* An error, but not a fatal one */
}
SymSetOptions(symopts);
module_base = SymLoadModuleEx(process, NULL, filename, NULL, 0, 0, NULL, 0);
if (!module_base) {
/* SymLoadModuleEx failed */
error = GetLastError();
fprintf(stderr, "SymLoadModuleEx returned error : %d for %s\n",
error, filename);
SymCleanup(process);
return 1;
}
ShowSymbolInfo(process, module_base);
memset(&ctx, 0, sizeof(ctx));
ctx.module_base = module_base;
if (!SymEnumSymbols(process, module_base, NULL, EnumSymProc, &ctx)) {
error = GetLastError();
fprintf(stderr, "SymEnumSymbols returned error: %d\n", error);
rv = 1;
} else {
DWORD j;
qsort(ctx.syms, ctx.syms_len, sizeof(ctx.syms[0]), sym_cmp);
for (j = 0; j < ctx.syms_len; j++) {
printf("%016I64x X %s\n", ctx.syms[j].addr, ctx.syms[j].name);
}
/* In a perfect world, maybe we'd clean up ctx's memory? */
}
SymUnloadModule64(process, module_base);
SymCleanup(process);
return rv;
}

View file

@ -0,0 +1,173 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2007, 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 THE COPYRIGHT
// OWNER 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.
//
// ---
// Author: Mike Belshe
//
// To link tcmalloc into a EXE or DLL statically without using the patching
// facility, we can take a stock libcmt and remove all the allocator functions.
// When we relink the EXE/DLL with the modified libcmt and tcmalloc, a few
// functions are missing. This file contains the additional overrides which
// are required in the VS2005 libcmt in order to link the modified libcmt.
//
// See also
// http://groups.google.com/group/google-perftools/browse_thread/thread/41cd3710af85e57b
#include <config.h>
#ifndef _WIN32
# error You should only be including this file in a windows environment!
#endif
#ifndef WIN32_OVERRIDE_ALLOCATORS
# error This file is intended for use when overriding allocators
#endif
#include "tcmalloc.cc"
extern "C" {
void* _malloc_base(size_t size) {
return malloc(size);
}
void _free_base(void* p) {
free(p);
}
void* _calloc_base(size_t n, size_t size) {
return calloc(n, size);
}
void* _recalloc(void* old_ptr, size_t n, size_t size) {
// Ensure that (n * size) does not overflow
if (!(n == 0 || (std::numeric_limits<size_t>::max)() / n >= size)) {
errno = ENOMEM;
return NULL;
}
const size_t old_size = tc_malloc_size(old_ptr);
const size_t new_size = n * size;
void* new_ptr = realloc(old_ptr, new_size);
// If the reallocation succeeded and the new block is larger, zero-fill the
// new bytes:
if (new_ptr != NULL && new_size > old_size) {
memset(static_cast<char*>(new_ptr) + old_size, 0, tc_nallocx(new_size, 0) - old_size);
}
return new_ptr;
}
void* _calloc_impl(size_t n, size_t size) {
return calloc(n, size);
}
size_t _msize(void* p) {
return MallocExtension::instance()->GetAllocatedSize(p);
}
HANDLE __acrt_heap = nullptr;
bool __acrt_initialize_heap() {
new TCMallocGuard();
return true;
}
bool __acrt_uninitialize_heap(bool) {
return true;
}
intptr_t _get_heap_handle() {
return 0;
}
HANDLE __acrt_getheap() {
return __acrt_heap;
}
// The CRT heap initialization stub.
int _heap_init() {
// We intentionally leak this object. It lasts for the process
// lifetime. Trying to teardown at _heap_term() is so late that
// you can't do anything useful anyway.
new TCMallocGuard();
return 1;
}
// The CRT heap cleanup stub.
void _heap_term() {
}
// We set this to 1 because part of the CRT uses a check of _crtheap != 0
// to test whether the CRT has been initialized. Once we've ripped out
// the allocators from libcmt, we need to provide this definition so that
// the rest of the CRT is still usable.
void* _crtheap = reinterpret_cast<void*>(1);
int _set_new_mode(int flag) {
return tc_set_new_mode(flag);
}
int _query_new_mode() {
return tc_query_new_mode();
}
} // extern "C"
#ifndef NDEBUG
#undef malloc
#undef free
#undef calloc
int _CrtDbgReport(int, const char*, int, const char*, const char*, ...) {
return 0;
}
int _CrtDbgReportW(int, const wchar_t*, int, const wchar_t*, const wchar_t*, ...) {
return 0;
}
int _CrtSetReportMode(int, int) {
return 0;
}
extern "C" void* _malloc_dbg(size_t size, int , const char*, int) {
return malloc(size);
}
extern "C" void _free_dbg(void* ptr, int) {
free(ptr);
}
extern "C" void* _calloc_dbg(size_t n, size_t size, int, const char*, int) {
return calloc(n, size);
}
#endif // NDEBUG

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,249 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Craig Silverstein
*/
#ifndef _WIN32
# error You should only be including windows/port.cc in a windows environment!
#endif
#define NOMINMAX // so std::max, below, compiles correctly
#include <config.h>
#include <string.h> // for strlen(), memset(), memcmp()
#include <assert.h>
#include <stdarg.h> // for va_list, va_start, va_end
#include <algorithm> // for std:{min,max}
#include <windows.h>
#include "port.h"
#include "base/logging.h"
#include "base/spinlock.h"
#include "internal_logging.h"
// -----------------------------------------------------------------------
// Basic libraries
PERFTOOLS_DLL_DECL
int getpagesize() {
static int pagesize = 0;
if (pagesize == 0) {
SYSTEM_INFO system_info;
GetSystemInfo(&system_info);
pagesize = std::max(system_info.dwPageSize,
system_info.dwAllocationGranularity);
}
return pagesize;
}
extern "C" PERFTOOLS_DLL_DECL void* __sbrk(ptrdiff_t increment) {
LOG(FATAL, "Windows doesn't implement sbrk!\n");
return NULL;
}
// We need to write to 'stderr' without having windows allocate memory.
// The safest way is via a low-level call like WriteConsoleA(). But
// even then we need to be sure to print in small bursts so as to not
// require memory allocation.
extern "C" PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len) {
// Looks like windows allocates for writes of >80 bytes
for (int i = 0; i < len; i += 80) {
write(STDERR_FILENO, buf + i, std::min(80, len - i));
}
}
// -----------------------------------------------------------------------
// Threads code
// Windows doesn't support pthread_key_create's destr_function, and in
// fact it's a bit tricky to get code to run when a thread exits. This
// is cargo-cult magic from https://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
// and http://lallouslab.net/2017/05/30/using-cc-tls-callbacks-in-visual-studio-with-your-32-or-64bits-programs/.
// This code is for VC++ 7.1 and later; VC++ 6.0 support is possible
// but more busy-work -- see the webpage for how to do it. If all
// this fails, we could use DllMain instead. The big problem with
// DllMain is it doesn't run if this code is statically linked into a
// binary (it also doesn't run if the thread is terminated via
// TerminateThread, which if we're lucky this routine does).
// Force a reference to _tls_used to make the linker create the TLS directory
// if it's not already there (that is, even if __declspec(thread) is not used).
// Force a reference to p_thread_callback_tcmalloc and p_process_term_tcmalloc
// to prevent whole program optimization from discarding the variables.
#ifdef _MSC_VER
#if defined(_M_IX86)
#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma comment(linker, "/INCLUDE:_p_thread_callback_tcmalloc")
#pragma comment(linker, "/INCLUDE:_p_process_term_tcmalloc")
#elif defined(_M_X64)
#pragma comment(linker, "/INCLUDE:_tls_used")
#pragma comment(linker, "/INCLUDE:p_thread_callback_tcmalloc")
#pragma comment(linker, "/INCLUDE:p_process_term_tcmalloc")
#endif
#endif
// When destr_fn eventually runs, it's supposed to take as its
// argument the tls-value associated with key that pthread_key_create
// creates. (Yeah, it sounds confusing but it's really not.) We
// store the destr_fn/key pair in this data structure. Because we
// store this in a single var, this implies we can only have one
// destr_fn in a program! That's enough in practice. If asserts
// trigger because we end up needing more, we'll have to turn this
// into an array.
struct DestrFnClosure {
void (*destr_fn)(void*);
pthread_key_t key_for_destr_fn_arg;
};
static DestrFnClosure destr_fn_info; // initted to all NULL/0.
static int on_process_term(void) {
if (destr_fn_info.destr_fn) {
void *ptr = TlsGetValue(destr_fn_info.key_for_destr_fn_arg);
// This shouldn't be necessary, but in Release mode, Windows
// sometimes trashes the pointer in the TLS slot, so we need to
// remove the pointer from the TLS slot before the thread dies.
TlsSetValue(destr_fn_info.key_for_destr_fn_arg, NULL);
if (ptr) // pthread semantics say not to call if ptr is NULL
(*destr_fn_info.destr_fn)(ptr);
}
return 0;
}
static void NTAPI on_tls_callback(HINSTANCE h, DWORD dwReason, PVOID pv) {
if (dwReason == DLL_THREAD_DETACH) { // thread is being destroyed!
on_process_term();
}
}
#ifdef _MSC_VER
// extern "C" suppresses C++ name mangling so we know the symbol names
// for the linker /INCLUDE:symbol pragmas above.
// Note that for some unknown reason, the extern "C" {} construct is ignored
// by the MSVC VS2017 compiler (at least) when a const modifier is used
#if defined(_M_IX86)
extern "C" {
// In x86, the PE loader looks for callbacks in a data segment
#pragma data_seg(push, old_seg)
#pragma data_seg(".CRT$XLB")
void (NTAPI *p_thread_callback_tcmalloc)(
HINSTANCE h, DWORD dwReason, PVOID pv) = on_tls_callback;
#pragma data_seg(".CRT$XTU")
int (*p_process_term_tcmalloc)(void) = on_process_term;
#pragma data_seg(pop, old_seg)
} // extern "C"
#elif defined(_M_X64)
// In x64, the PE loader looks for callbacks in a constant segment
#pragma const_seg(push, oldseg)
#pragma const_seg(".CRT$XLB")
extern "C" void (NTAPI * const p_thread_callback_tcmalloc)(
HINSTANCE h, DWORD dwReason, PVOID pv) = on_tls_callback;
#pragma const_seg(".CRT$XTU")
extern "C" int (NTAPI * const p_process_term_tcmalloc)(void) = on_process_term;
#pragma const_seg(pop, oldseg)
#endif
#else // #ifdef _MSC_VER [probably msys/mingw]
// We have to try the DllMain solution here, because we can't use the
// msvc-specific pragmas.
BOOL WINAPI DllMain(HINSTANCE h, DWORD dwReason, PVOID pv) {
if (dwReason == DLL_THREAD_DETACH)
on_tls_callback(h, dwReason, pv);
else if (dwReason == DLL_PROCESS_DETACH)
on_process_term();
return TRUE;
}
#endif // #ifdef _MSC_VER
extern "C" pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)) {
// Semantics are: we create a new key, and then promise to call
// destr_fn with TlsGetValue(key) when the thread is destroyed
// (as long as TlsGetValue(key) is not NULL).
pthread_key_t key = TlsAlloc();
if (destr_fn) { // register it
// If this assert fails, we'll need to support an array of destr_fn_infos
assert(destr_fn_info.destr_fn == NULL);
destr_fn_info.destr_fn = destr_fn;
destr_fn_info.key_for_destr_fn_arg = key;
}
return key;
}
// NOTE: this is Win2K and later. For Win98 we could use a CRITICAL_SECTION...
extern "C" int perftools_pthread_once(pthread_once_t *once_control,
void (*init_routine)(void)) {
// Try for a fast path first. Note: this should be an acquire semantics read.
// It is on x86 and x64, where Windows runs.
if (*once_control != 1) {
while (true) {
switch (InterlockedCompareExchange(once_control, 2, 0)) {
case 0:
init_routine();
InterlockedExchange(once_control, 1);
return 0;
case 1:
// The initializer has already been executed
return 0;
default:
// The initializer is being processed by another thread
SwitchToThread();
}
}
}
return 0;
}
// -----------------------------------------------------------------------
// These functions rework existing functions of the same name in the
// Google codebase.
// A replacement for HeapProfiler::CleanupOldProfiles.
void DeleteMatchingFiles(const char* prefix, const char* full_glob) {
WIN32_FIND_DATAA found; // that final A is for Ansi (as opposed to Unicode)
HANDLE hFind = FindFirstFileA(full_glob, &found); // A is for Ansi
if (hFind != INVALID_HANDLE_VALUE) {
const int prefix_length = strlen(prefix);
do {
const char *fname = found.cFileName;
if ((strlen(fname) >= prefix_length) &&
(memcmp(fname, prefix, prefix_length) == 0)) {
RAW_VLOG(0, "Removing old heap profile %s\n", fname);
// TODO(csilvers): we really need to unlink dirname + fname
_unlink(fname);
}
} while (FindNextFileA(hFind, &found) != FALSE); // A is for Ansi
FindClose(hFind);
}
}

View file

@ -0,0 +1,477 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Craig Silverstein
*
* These are some portability typedefs and defines to make it a bit
* easier to compile this code under VC++.
*
* Several of these are taken from glib:
* http://developer.gnome.org/doc/API/glib/glib-windows-compatability-functions.html
*/
#ifndef GOOGLE_BASE_WINDOWS_H_
#define GOOGLE_BASE_WINDOWS_H_
/* You should never include this file directly, but always include it
from either config.h (MSVC) or mingw.h (MinGW/msys). */
#if !defined(GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_) && \
!defined(GOOGLE_PERFTOOLS_WINDOWS_MINGW_H_)
# error "port.h should only be included from config.h or mingw.h"
#endif
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN /* We always want minimal includes */
#endif
#include <windows.h>
#include <io.h> /* because we so often use open/close/etc */
#include <direct.h> /* for _getcwd */
#include <process.h> /* for _getpid */
#include <limits.h> /* for PATH_MAX */
#include <stdarg.h> /* for va_list */
#include <stdio.h> /* need this to override stdio's (v)snprintf */
#include <sys/types.h> /* for _off_t */
#include <assert.h>
#include <stdlib.h> /* for rand, srand, _strtoxxx */
#if defined(_MSC_VER) && _MSC_VER >= 1900
#define _TIMESPEC_DEFINED
#include <time.h>
#endif
/*
* 4018: signed/unsigned mismatch is common (and ok for signed_i < unsigned_i)
* 4244: otherwise we get problems when subtracting two size_t's to an int
* 4288: VC++7 gets confused when a var is defined in a loop and then after it
* 4267: too many false positives for "conversion gives possible data loss"
* 4290: it's ok windows ignores the "throw" directive
* 4996: Yes, we're ok using "unsafe" functions like vsnprintf and getenv()
* 4146: internal_logging.cc intentionally negates an unsigned value
*/
#ifdef _MSC_VER
#pragma warning(disable:4018 4244 4288 4267 4290 4996 4146)
#endif
#ifndef __cplusplus
/* MSVC does not support C99 */
# if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
# ifdef _MSC_VER
# define inline __inline
# else
# define inline static
# endif
# endif
#endif
#ifdef __cplusplus
# define EXTERN_C extern "C"
#else
# define EXTERN_C extern
#endif
/* ----------------------------------- BASIC TYPES */
#ifndef HAVE_STDINT_H
# error Do not know how to set up type aliases. Edit port.h for your system.
#endif
/* I guess MSVC's <types.h> doesn't include ssize_t by default? */
#ifdef _MSC_VER
typedef intptr_t ssize_t;
#endif
/* ----------------------------------- THREADS */
#ifndef HAVE_PTHREAD /* not true for MSVC, but may be true for MSYS */
typedef DWORD pthread_t;
typedef DWORD pthread_key_t;
typedef LONG pthread_once_t;
enum { PTHREAD_ONCE_INIT = 0 }; /* important that this be 0! for SpinLock */
inline pthread_t pthread_self(void) {
return GetCurrentThreadId();
}
#ifdef __cplusplus
inline bool pthread_equal(pthread_t left, pthread_t right) {
return left == right;
}
/*
* windows/port.h defines compatibility APIs for several .h files, which
* we therefore shouldn't be #including directly. This hack keeps us from
* doing so. TODO(csilvers): do something more principled.
*/
#define GOOGLE_MAYBE_THREADS_H_ 1
/* This replaces maybe_threads.{h,cc} */
EXTERN_C pthread_key_t PthreadKeyCreate(void (*destr_fn)(void*)); /* port.cc */
inline int perftools_pthread_key_create(pthread_key_t *pkey,
void (*destructor)(void*)) {
pthread_key_t key = PthreadKeyCreate(destructor);
if (key != TLS_OUT_OF_INDEXES) {
*(pkey) = key;
return 0;
} else {
return GetLastError();
}
}
inline void* perftools_pthread_getspecific(DWORD key) {
DWORD err = GetLastError();
void* rv = TlsGetValue(key);
if (err) SetLastError(err);
return rv;
}
inline int perftools_pthread_setspecific(pthread_key_t key, const void *value) {
if (TlsSetValue(key, (LPVOID)value))
return 0;
else
return GetLastError();
}
EXTERN_C int perftools_pthread_once(pthread_once_t *once_control,
void (*init_routine)(void));
#endif /* __cplusplus */
inline void sched_yield(void) {
Sleep(0);
}
#endif /* HAVE_PTHREAD */
/*
* __declspec(thread) isn't usable in a dll opened via LoadLibrary().
* But it doesn't work to LoadLibrary() us anyway, because of all the
* things we need to do before main()! So this kind of TLS is safe for us.
*/
#define __thread __declspec(thread)
/*
* This code is obsolete, but I keep it around in case we are ever in
* an environment where we can't or don't want to use google spinlocks
* (from base/spinlock.{h,cc}). In that case, uncommenting this out,
* and removing spinlock.cc from the build, should be enough to revert
* back to using native spinlocks.
*/
#if 0
// Windows uses a spinlock internally for its mutexes, making our life easy!
// However, the Windows spinlock must always be initialized, making life hard,
// since we want LINKER_INITIALIZED. We work around this by having the
// linker initialize a bool to 0, and check that before accessing the mutex.
// This replaces spinlock.{h,cc}, and all the stuff it depends on (atomicops)
#ifdef __cplusplus
class SpinLock {
public:
SpinLock() : initialize_token_(PTHREAD_ONCE_INIT) {}
// Used for global SpinLock vars (see base/spinlock.h for more details).
enum StaticInitializer { LINKER_INITIALIZED };
explicit SpinLock(StaticInitializer) : initialize_token_(PTHREAD_ONCE_INIT) {
perftools_pthread_once(&initialize_token_, InitializeMutex);
}
// It's important SpinLock not have a destructor: otherwise we run
// into problems when the main thread has exited, but other threads
// are still running and try to access a main-thread spinlock. This
// means we leak mutex_ (we should call DeleteCriticalSection()
// here). However, I've verified that all SpinLocks used in
// perftools have program-long scope anyway, so the leak is
// perfectly fine. But be aware of this for the future!
void Lock() {
// You'd thionk this would be unnecessary, since we call
// InitializeMutex() in our constructor. But sometimes Lock() can
// be called before our constructor is! This can only happen in
// global constructors, when this is a global. If we live in
// bar.cc, and some global constructor in foo.cc calls a routine
// in bar.cc that calls this->Lock(), then Lock() may well run
// before our global constructor does. To protect against that,
// we do this check. For SpinLock objects created after main()
// has started, this pthread_once call will always be a noop.
perftools_pthread_once(&initialize_token_, InitializeMutex);
EnterCriticalSection(&mutex_);
}
void Unlock() {
LeaveCriticalSection(&mutex_);
}
// Used in assertion checks: assert(lock.IsHeld()) (see base/spinlock.h).
inline bool IsHeld() const {
// This works, but probes undocumented internals, so I've commented it out.
// c.f. http://msdn.microsoft.com/msdnmag/issues/03/12/CriticalSections/
//return mutex_.LockCount>=0 && mutex_.OwningThread==GetCurrentThreadId();
return true;
}
private:
void InitializeMutex() { InitializeCriticalSection(&mutex_); }
pthread_once_t initialize_token_;
CRITICAL_SECTION mutex_;
};
class SpinLockHolder { // Acquires a spinlock for as long as the scope lasts
private:
SpinLock* lock_;
public:
inline explicit SpinLockHolder(SpinLock* l) : lock_(l) { l->Lock(); }
inline ~SpinLockHolder() { lock_->Unlock(); }
};
#endif // #ifdef __cplusplus
// This keeps us from using base/spinlock.h's implementation of SpinLock.
#define BASE_SPINLOCK_H_ 1
#endif /* #if 0 */
/* ----------------------------------- MMAP and other memory allocation */
#ifndef HAVE_MMAP /* not true for MSVC, but may be true for msys */
#define MAP_FAILED 0
#define MREMAP_FIXED 2 /* the value in linux, though it doesn't really matter */
/* These, when combined with the mmap invariants below, yield the proper action */
#define PROT_READ PAGE_READWRITE
#define PROT_WRITE PAGE_READWRITE
#define MAP_ANONYMOUS MEM_RESERVE
#define MAP_PRIVATE MEM_COMMIT
#define MAP_SHARED MEM_RESERVE /* value of this #define is 100% arbitrary */
#if __STDC__ && !defined(__MINGW32__)
typedef _off_t off_t;
#endif
/* VirtualAlloc only replaces for mmap when certain invariants are kept. */
inline void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset) {
if (addr == NULL && fd == -1 && offset == 0 &&
prot == (PROT_READ|PROT_WRITE) && flags == (MAP_PRIVATE|MAP_ANONYMOUS)) {
return VirtualAlloc(0, length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
} else {
return NULL;
}
}
inline int munmap(void *addr, size_t length) {
return VirtualFree(addr, 0, MEM_RELEASE) ? 0 : -1;
}
#endif /* HAVE_MMAP */
/* We could maybe use VirtualAlloc for sbrk as well, but no need */
inline void *sbrk(intptr_t increment) {
// sbrk returns -1 on failure
return (void*)-1;
}
/* ----------------------------------- STRING ROUTINES */
/*
* We can't just use _vsnprintf and _snprintf as drop-in-replacements,
* because they don't always NUL-terminate. :-( We also can't use the
* name vsnprintf, since windows defines that (but not snprintf (!)).
*/
#if defined(_MSC_VER) && _MSC_VER >= 1400
/* We can use safe CRT functions, which the required functionality */
inline int perftools_vsnprintf(char *str, size_t size, const char *format,
va_list ap) {
return vsnprintf_s(str, size, _TRUNCATE, format, ap);
}
#else
inline int perftools_vsnprintf(char *str, size_t size, const char *format,
va_list ap) {
if (size == 0) /* not even room for a \0? */
return -1; /* not what C99 says to do, but what windows does */
str[size-1] = '\0';
return _vsnprintf(str, size-1, format, ap);
}
#endif
#ifndef HAVE_INTTYPES_H
#define PRIx64 "I64x"
#define SCNx64 "I64x"
#define PRId64 "I64d"
#define SCNd64 "I64d"
#define PRIu64 "I64u"
#ifdef _WIN64
# define PRIuPTR "llu"
# define PRIxPTR "llx"
#else
# define PRIuPTR "lu"
# define PRIxPTR "lx"
#endif
#endif
/* ----------------------------------- FILE IO */
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#ifndef __MINGW32__
enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2 };
#endif
#ifndef O_RDONLY
#define O_RDONLY _O_RDONLY
#endif
#if __STDC__ && !defined(__MINGW32__)
/* These functions are considered non-standard */
inline int access(const char *pathname, int mode) {
return _access(pathname, mode);
}
inline int open(const char *pathname, int flags, int mode = 0) {
return _open(pathname, flags, mode);
}
inline int close(int fd) {
return _close(fd);
}
inline ssize_t read(int fd, void *buf, size_t count) {
return _read(fd, buf, count);
}
inline ssize_t write(int fd, const void *buf, size_t count) {
return _write(fd, buf, count);
}
inline off_t lseek(int fd, off_t offset, int whence) {
return _lseek(fd, offset, whence);
}
inline char *getcwd(char *buf, size_t size) {
return _getcwd(buf, size);
}
inline int mkdir(const char *pathname, int) {
return _mkdir(pathname);
}
inline FILE *popen(const char *command, const char *type) {
return _popen(command, type);
}
inline int pclose(FILE *stream) {
return _pclose(stream);
}
#endif
EXTERN_C PERFTOOLS_DLL_DECL void WriteToStderr(const char* buf, int len);
/* ----------------------------------- SYSTEM/PROCESS */
#ifndef HAVE_PID_T
typedef int pid_t;
#endif
#if __STDC__ && !defined(__MINGW32__)
inline pid_t getpid(void) { return _getpid(); }
#endif
inline pid_t getppid(void) { return 0; }
/* Handle case when poll is used to simulate sleep. */
inline int poll(struct pollfd* fds, int nfds, int timeout) {
assert(fds == NULL);
assert(nfds == 0);
Sleep(timeout);
return 0;
}
EXTERN_C PERFTOOLS_DLL_DECL int getpagesize(); /* in port.cc */
/* ----------------------------------- OTHER */
inline void srandom(unsigned int seed) { srand(seed); }
inline long random(void) { return rand(); }
#ifndef HAVE_DECL_SLEEP
#define HAVE_DECL_SLEEP 0
#endif
#if !HAVE_DECL_SLEEP
inline unsigned int sleep(unsigned int seconds) {
Sleep(seconds * 1000);
return 0;
}
#endif
// mingw64 seems to define timespec (though mingw.org mingw doesn't),
// protected by the _TIMESPEC_DEFINED macro.
#ifndef _TIMESPEC_DEFINED
struct timespec {
int tv_sec;
int tv_nsec;
};
#endif
#ifndef HAVE_DECL_NANOSLEEP
#define HAVE_DECL_NANOSLEEP 0
#endif
// latest mingw64 has nanosleep. Earlier mingw and MSVC do not
#if !HAVE_DECL_NANOSLEEP
inline int nanosleep(const struct timespec *req, struct timespec *rem) {
Sleep(req->tv_sec * 1000 + req->tv_nsec / 1000000);
return 0;
}
#endif
#ifndef __MINGW32__
#if defined(_MSC_VER) && _MSC_VER < 1800
inline long long int strtoll(const char *nptr, char **endptr, int base) {
return _strtoi64(nptr, endptr, base);
}
inline unsigned long long int strtoull(const char *nptr, char **endptr,
int base) {
return _strtoui64(nptr, endptr, base);
}
inline long long int strtoq(const char *nptr, char **endptr, int base) {
return _strtoi64(nptr, endptr, base);
}
#endif
inline unsigned long long int strtouq(const char *nptr, char **endptr,
int base) {
return _strtoui64(nptr, endptr, base);
}
inline long long atoll(const char *nptr) {
return _atoi64(nptr);
}
#endif
#define __THROW throw()
/* ----------------------------------- TCMALLOC-SPECIFIC */
/* tcmalloc.cc calls this so we can patch VirtualAlloc() et al. */
extern void PatchWindowsFunctions();
#endif /* _WIN32 */
#undef inline
#undef EXTERN_C
#endif /* GOOGLE_BASE_WINDOWS_H_ */

View file

@ -0,0 +1,736 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Joi Sigurdsson
* Author: Scott Francis
*
* Implementation of PreamblePatcher
*/
#include "preamble_patcher.h"
#include "mini_disassembler.h"
// compatibility shims
#include "base/logging.h"
// Definitions of assembly statements we need
#define ASM_JMP32REL 0xE9
#define ASM_INT3 0xCC
#define ASM_JMP32ABS_0 0xFF
#define ASM_JMP32ABS_1 0x25
#define ASM_JMP8REL 0xEB
#define ASM_JCC32REL_0 0x0F
#define ASM_JCC32REL_1_MASK 0x80
#define ASM_NOP 0x90
// X64 opcodes
#define ASM_REXW 0x48
#define ASM_MOVRAX_IMM 0xB8
#define ASM_JMP 0xFF
#define ASM_JMP_RAX 0xE0
namespace sidestep {
PreamblePatcher::PreamblePage* PreamblePatcher::preamble_pages_ = NULL;
long PreamblePatcher::granularity_ = 0;
long PreamblePatcher::pagesize_ = 0;
bool PreamblePatcher::initialized_ = false;
static const unsigned int kPreamblePageMagic = 0x4347414D; // "MAGC"
// Handle a special case that we see with functions that point into an
// IAT table (including functions linked statically into the
// application): these function already starts with ASM_JMP32*. For
// instance, malloc() might be implemented as a JMP to __malloc().
// This function follows the initial JMPs for us, until we get to the
// place where the actual code is defined. If we get to STOP_BEFORE,
// we return the address before stop_before. The stop_before_trampoline
// flag is used in 64-bit mode. If true, we will return the address
// before a trampoline is detected. Trampolines are defined as:
//
// nop
// mov rax, <replacement_function>
// jmp rax
//
// See PreamblePatcher::RawPatchWithStub for more information.
void* PreamblePatcher::ResolveTargetImpl(unsigned char* target,
unsigned char* stop_before,
bool stop_before_trampoline) {
if (target == NULL)
return NULL;
while (1) {
unsigned char* new_target;
if (target[0] == ASM_JMP32REL) {
// target[1-4] holds the place the jmp goes to, but it's
// relative to the next instruction.
int relative_offset; // Windows guarantees int is 4 bytes
SIDESTEP_ASSERT(sizeof(relative_offset) == 4);
memcpy(reinterpret_cast<void*>(&relative_offset),
reinterpret_cast<void*>(target + 1), 4);
new_target = target + 5 + relative_offset;
} else if (target[0] == ASM_JMP8REL) {
// Visual Studio 7.1 implements new[] as an 8 bit jump to new
signed char relative_offset;
memcpy(reinterpret_cast<void*>(&relative_offset),
reinterpret_cast<void*>(target + 1), 1);
new_target = target + 2 + relative_offset;
} else if (target[0] == ASM_JMP32ABS_0 &&
target[1] == ASM_JMP32ABS_1) {
jmp32rel:
// Visual studio seems to sometimes do it this way instead of the
// previous way. Not sure what the rules are, but it was happening
// with operator new in some binaries.
void** new_target_v;
if (kIs64BitBinary) {
// In 64-bit mode JMPs are RIP-relative, not absolute
int target_offset;
memcpy(reinterpret_cast<void*>(&target_offset),
reinterpret_cast<void*>(target + 2), 4);
new_target_v = reinterpret_cast<void**>(target + target_offset + 6);
} else {
SIDESTEP_ASSERT(sizeof(new_target) == 4);
memcpy(&new_target_v, reinterpret_cast<void*>(target + 2), 4);
}
new_target = reinterpret_cast<unsigned char*>(*new_target_v);
} else if (kIs64BitBinary && target[0] == ASM_REXW
&& target[1] == ASM_JMP32ABS_0
&& target[2] == ASM_JMP32ABS_1) {
// in Visual Studio 2012 we're seeing jump like that:
// rex.W jmpq *0x11d019(%rip)
//
// according to docs I have, rex prefix is actually unneeded and
// can be ignored. I.e. docs say for jumps like that operand
// already defaults to 64-bit. But clearly it breaks abs. jump
// detection above and we just skip rex
target++;
goto jmp32rel;
} else {
break;
}
if (new_target == stop_before)
break;
if (stop_before_trampoline && *new_target == ASM_NOP
&& new_target[1] == ASM_REXW && new_target[2] == ASM_MOVRAX_IMM)
break;
target = new_target;
}
return target;
}
// Special case scoped_ptr to avoid dependency on scoped_ptr below.
class DeleteUnsignedCharArray {
public:
DeleteUnsignedCharArray(unsigned char* array) : array_(array) {
}
~DeleteUnsignedCharArray() {
if (array_) {
PreamblePatcher::FreePreambleBlock(array_);
}
}
unsigned char* Release() {
unsigned char* temp = array_;
array_ = NULL;
return temp;
}
private:
unsigned char* array_;
};
SideStepError PreamblePatcher::RawPatchWithStubAndProtections(
void* target_function, void *replacement_function,
unsigned char* preamble_stub, unsigned long stub_size,
unsigned long* bytes_needed) {
// We need to be able to write to a process-local copy of the first
// MAX_PREAMBLE_STUB_SIZE bytes of target_function
DWORD old_target_function_protect = 0;
BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function),
MAX_PREAMBLE_STUB_SIZE,
PAGE_EXECUTE_READWRITE,
&old_target_function_protect);
if (!succeeded) {
SIDESTEP_ASSERT(false && "Failed to make page containing target function "
"copy-on-write.");
return SIDESTEP_ACCESS_DENIED;
}
SideStepError error_code = RawPatchWithStub(target_function,
replacement_function,
preamble_stub,
stub_size,
bytes_needed);
// Restore the protection of the first MAX_PREAMBLE_STUB_SIZE bytes of
// pTargetFunction to what they were before we started goofing around.
// We do this regardless of whether the patch succeeded or not.
succeeded = ::VirtualProtect(reinterpret_cast<void*>(target_function),
MAX_PREAMBLE_STUB_SIZE,
old_target_function_protect,
&old_target_function_protect);
if (!succeeded) {
SIDESTEP_ASSERT(false &&
"Failed to restore protection to target function.");
// We must not return an error here because the function has
// likely actually been patched, and returning an error might
// cause our client code not to unpatch it. So we just keep
// going.
}
if (SIDESTEP_SUCCESS != error_code) { // Testing RawPatchWithStub, above
SIDESTEP_ASSERT(false);
return error_code;
}
// Flush the instruction cache to make sure the processor doesn't execute the
// old version of the instructions (before our patch).
//
// FlushInstructionCache is actually a no-op at least on
// single-processor XP machines. I'm not sure why this is so, but
// it is, yet I want to keep the call to the API here for
// correctness in case there is a difference in some variants of
// Windows/hardware.
succeeded = ::FlushInstructionCache(::GetCurrentProcess(),
target_function,
MAX_PREAMBLE_STUB_SIZE);
if (!succeeded) {
SIDESTEP_ASSERT(false && "Failed to flush instruction cache.");
// We must not return an error here because the function has actually
// been patched, and returning an error would likely cause our client
// code not to unpatch it. So we just keep going.
}
return SIDESTEP_SUCCESS;
}
SideStepError PreamblePatcher::RawPatch(void* target_function,
void* replacement_function,
void** original_function_stub) {
if (!target_function || !replacement_function || !original_function_stub ||
(*original_function_stub) || target_function == replacement_function) {
SIDESTEP_ASSERT(false && "Preconditions not met");
return SIDESTEP_INVALID_PARAMETER;
}
BOOL succeeded = FALSE;
// First, deal with a special case that we see with functions that
// point into an IAT table (including functions linked statically
// into the application): these function already starts with
// ASM_JMP32REL. For instance, malloc() might be implemented as a
// JMP to __malloc(). In that case, we replace the destination of
// the JMP (__malloc), rather than the JMP itself (malloc). This
// way we get the correct behavior no matter how malloc gets called.
void* new_target = ResolveTarget(target_function);
if (new_target != target_function) {
target_function = new_target;
}
// In 64-bit mode, preamble_stub must be within 2GB of target function
// so that if target contains a jump, we can translate it.
unsigned char* preamble_stub = AllocPreambleBlockNear(target_function);
if (!preamble_stub) {
SIDESTEP_ASSERT(false && "Unable to allocate preamble-stub.");
return SIDESTEP_INSUFFICIENT_BUFFER;
}
// Frees the array at end of scope.
DeleteUnsignedCharArray guard_preamble_stub(preamble_stub);
SideStepError error_code = RawPatchWithStubAndProtections(
target_function, replacement_function, preamble_stub,
MAX_PREAMBLE_STUB_SIZE, NULL);
if (SIDESTEP_SUCCESS != error_code) {
SIDESTEP_ASSERT(false);
return error_code;
}
// Flush the instruction cache to make sure the processor doesn't execute the
// old version of the instructions (before our patch).
//
// FlushInstructionCache is actually a no-op at least on
// single-processor XP machines. I'm not sure why this is so, but
// it is, yet I want to keep the call to the API here for
// correctness in case there is a difference in some variants of
// Windows/hardware.
succeeded = ::FlushInstructionCache(::GetCurrentProcess(),
target_function,
MAX_PREAMBLE_STUB_SIZE);
if (!succeeded) {
SIDESTEP_ASSERT(false && "Failed to flush instruction cache.");
// We must not return an error here because the function has actually
// been patched, and returning an error would likely cause our client
// code not to unpatch it. So we just keep going.
}
SIDESTEP_LOG("PreamblePatcher::RawPatch successfully patched.");
// detach the scoped pointer so the memory is not freed
*original_function_stub =
reinterpret_cast<void*>(guard_preamble_stub.Release());
return SIDESTEP_SUCCESS;
}
SideStepError PreamblePatcher::Unpatch(void* target_function,
void* replacement_function,
void* original_function_stub) {
SIDESTEP_ASSERT(target_function && replacement_function &&
original_function_stub);
if (!target_function || !replacement_function ||
!original_function_stub) {
return SIDESTEP_INVALID_PARAMETER;
}
// Before unpatching, target_function should be a JMP to
// replacement_function. If it's not, then either it's an error, or
// we're falling into the case where the original instruction was a
// JMP, and we patched the jumped_to address rather than the JMP
// itself. (For instance, if malloc() is just a JMP to __malloc(),
// we patched __malloc() and not malloc().)
unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
target = reinterpret_cast<unsigned char*>(
ResolveTargetImpl(
target, reinterpret_cast<unsigned char*>(replacement_function),
true));
// We should end at the function we patched. When we patch, we insert
// a ASM_JMP32REL instruction, so look for that as a sanity check.
if (target[0] != ASM_JMP32REL) {
SIDESTEP_ASSERT(false &&
"target_function does not look like it was patched.");
return SIDESTEP_INVALID_PARAMETER;
}
const unsigned int kRequiredTargetPatchBytes = 5;
// We need to be able to write to a process-local copy of the first
// kRequiredTargetPatchBytes bytes of target_function
DWORD old_target_function_protect = 0;
BOOL succeeded = ::VirtualProtect(reinterpret_cast<void*>(target),
kRequiredTargetPatchBytes,
PAGE_EXECUTE_READWRITE,
&old_target_function_protect);
if (!succeeded) {
SIDESTEP_ASSERT(false && "Failed to make page containing target function "
"copy-on-write.");
return SIDESTEP_ACCESS_DENIED;
}
unsigned char* preamble_stub = reinterpret_cast<unsigned char*>(
original_function_stub);
// Disassemble the preamble of stub and copy the bytes back to target.
// If we've done any conditional jumps in the preamble we need to convert
// them back to the original REL8 jumps in the target.
MiniDisassembler disassembler;
unsigned int preamble_bytes = 0;
unsigned int target_bytes = 0;
while (target_bytes < kRequiredTargetPatchBytes) {
unsigned int cur_bytes = 0;
InstructionType instruction_type =
disassembler.Disassemble(preamble_stub + preamble_bytes, cur_bytes);
if (IT_JUMP == instruction_type) {
unsigned int jump_bytes = 0;
SideStepError jump_ret = SIDESTEP_JUMP_INSTRUCTION;
if (IsNearConditionalJump(preamble_stub + preamble_bytes, cur_bytes) ||
IsNearRelativeJump(preamble_stub + preamble_bytes, cur_bytes) ||
IsNearAbsoluteCall(preamble_stub + preamble_bytes, cur_bytes) ||
IsNearRelativeCall(preamble_stub + preamble_bytes, cur_bytes)) {
jump_ret = PatchNearJumpOrCall(preamble_stub + preamble_bytes,
cur_bytes, target + target_bytes,
&jump_bytes, MAX_PREAMBLE_STUB_SIZE);
}
if (jump_ret == SIDESTEP_JUMP_INSTRUCTION) {
SIDESTEP_ASSERT(false &&
"Found unsupported jump instruction in stub!!");
return SIDESTEP_UNSUPPORTED_INSTRUCTION;
}
target_bytes += jump_bytes;
} else if (IT_GENERIC == instruction_type) {
if (IsMovWithDisplacement(preamble_stub + preamble_bytes, cur_bytes)) {
unsigned int mov_bytes = 0;
if (PatchMovWithDisplacement(preamble_stub + preamble_bytes, cur_bytes,
target + target_bytes, &mov_bytes,
MAX_PREAMBLE_STUB_SIZE)
!= SIDESTEP_SUCCESS) {
SIDESTEP_ASSERT(false &&
"Found unsupported generic instruction in stub!!");
return SIDESTEP_UNSUPPORTED_INSTRUCTION;
}
} else {
memcpy(reinterpret_cast<void*>(target + target_bytes),
reinterpret_cast<void*>(reinterpret_cast<unsigned char*>(
original_function_stub) + preamble_bytes), cur_bytes);
target_bytes += cur_bytes;
}
} else {
SIDESTEP_ASSERT(false &&
"Found unsupported instruction in stub!!");
return SIDESTEP_UNSUPPORTED_INSTRUCTION;
}
preamble_bytes += cur_bytes;
}
FreePreambleBlock(reinterpret_cast<unsigned char*>(original_function_stub));
// Restore the protection of the first kRequiredTargetPatchBytes bytes of
// target to what they were before we started goofing around.
succeeded = ::VirtualProtect(reinterpret_cast<void*>(target),
kRequiredTargetPatchBytes,
old_target_function_protect,
&old_target_function_protect);
// Flush the instruction cache to make sure the processor doesn't execute the
// old version of the instructions (before our patch).
//
// See comment on FlushInstructionCache elsewhere in this file.
succeeded = ::FlushInstructionCache(::GetCurrentProcess(),
target,
MAX_PREAMBLE_STUB_SIZE);
if (!succeeded) {
SIDESTEP_ASSERT(false && "Failed to flush instruction cache.");
return SIDESTEP_UNEXPECTED;
}
SIDESTEP_LOG("PreamblePatcher::Unpatch successfully unpatched.");
return SIDESTEP_SUCCESS;
}
void PreamblePatcher::Initialize() {
if (!initialized_) {
SYSTEM_INFO si = { 0 };
::GetSystemInfo(&si);
granularity_ = si.dwAllocationGranularity;
pagesize_ = si.dwPageSize;
initialized_ = true;
}
}
unsigned char* PreamblePatcher::AllocPreambleBlockNear(void* target) {
PreamblePage* preamble_page = preamble_pages_;
while (preamble_page != NULL) {
if (preamble_page->free_ != NULL) {
__int64 val = reinterpret_cast<__int64>(preamble_page) -
reinterpret_cast<__int64>(target);
if ((val > 0 && val + pagesize_ <= INT_MAX) ||
(val < 0 && val >= INT_MIN)) {
break;
}
}
preamble_page = preamble_page->next_;
}
// The free_ member of the page is used to store the next available block
// of memory to use or NULL if there are no chunks available, in which case
// we'll allocate a new page.
if (preamble_page == NULL || preamble_page->free_ == NULL) {
// Create a new preamble page and initialize the free list
preamble_page = reinterpret_cast<PreamblePage*>(AllocPageNear(target));
SIDESTEP_ASSERT(preamble_page != NULL && "Could not allocate page!");
void** pp = &preamble_page->free_;
unsigned char* ptr = reinterpret_cast<unsigned char*>(preamble_page) +
MAX_PREAMBLE_STUB_SIZE;
unsigned char* limit = reinterpret_cast<unsigned char*>(preamble_page) +
pagesize_;
while (ptr < limit) {
*pp = ptr;
pp = reinterpret_cast<void**>(ptr);
ptr += MAX_PREAMBLE_STUB_SIZE;
}
*pp = NULL;
// Insert the new page into the list
preamble_page->magic_ = kPreamblePageMagic;
preamble_page->next_ = preamble_pages_;
preamble_pages_ = preamble_page;
}
unsigned char* ret = reinterpret_cast<unsigned char*>(preamble_page->free_);
preamble_page->free_ = *(reinterpret_cast<void**>(preamble_page->free_));
return ret;
}
void PreamblePatcher::FreePreambleBlock(unsigned char* block) {
SIDESTEP_ASSERT(block != NULL);
SIDESTEP_ASSERT(granularity_ != 0);
uintptr_t ptr = reinterpret_cast<uintptr_t>(block);
ptr -= ptr & (granularity_ - 1);
PreamblePage* preamble_page = reinterpret_cast<PreamblePage*>(ptr);
SIDESTEP_ASSERT(preamble_page->magic_ == kPreamblePageMagic);
*(reinterpret_cast<void**>(block)) = preamble_page->free_;
preamble_page->free_ = block;
}
void* PreamblePatcher::AllocPageNear(void* target) {
MEMORY_BASIC_INFORMATION mbi = { 0 };
if (!::VirtualQuery(target, &mbi, sizeof(mbi))) {
SIDESTEP_ASSERT(false && "VirtualQuery failed on target address");
return 0;
}
if (initialized_ == false) {
PreamblePatcher::Initialize();
SIDESTEP_ASSERT(initialized_);
}
void* pv = NULL;
unsigned char* allocation_base = reinterpret_cast<unsigned char*>(
mbi.AllocationBase);
__int64 i = 1;
bool high_target = reinterpret_cast<__int64>(target) > UINT_MAX;
while (pv == NULL) {
__int64 val = reinterpret_cast<__int64>(allocation_base) -
(i * granularity_);
if (high_target &&
reinterpret_cast<__int64>(target) - val > INT_MAX) {
// We're further than 2GB from the target
break;
} else if (val <= 0) {
// Less than 0
break;
}
pv = ::VirtualAlloc(reinterpret_cast<void*>(allocation_base -
(i++ * granularity_)),
pagesize_, MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
}
// We couldn't allocate low, try to allocate high
if (pv == NULL) {
i = 1;
// Round up to the next multiple of page granularity
allocation_base = reinterpret_cast<unsigned char*>(
(reinterpret_cast<__int64>(target) &
(~(granularity_ - 1))) + granularity_);
while (pv == NULL) {
__int64 val = reinterpret_cast<__int64>(allocation_base) +
(i * granularity_) - reinterpret_cast<__int64>(target);
if (val > INT_MAX || val < 0) {
// We're too far or we overflowed
break;
}
pv = ::VirtualAlloc(reinterpret_cast<void*>(allocation_base +
(i++ * granularity_)),
pagesize_, MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
}
}
return pv;
}
bool PreamblePatcher::IsShortConditionalJump(
unsigned char* target,
unsigned int instruction_size) {
return (*(target) & 0x70) == 0x70 && instruction_size == 2;
}
bool PreamblePatcher::IsShortJump(
unsigned char* target,
unsigned int instruction_size) {
return target[0] == 0xeb && instruction_size == 2;
}
bool PreamblePatcher::IsNearConditionalJump(
unsigned char* target,
unsigned int instruction_size) {
return *(target) == 0xf && (*(target + 1) & 0x80) == 0x80 &&
instruction_size == 6;
}
bool PreamblePatcher::IsNearRelativeJump(
unsigned char* target,
unsigned int instruction_size) {
return *(target) == 0xe9 && instruction_size == 5;
}
bool PreamblePatcher::IsNearAbsoluteCall(
unsigned char* target,
unsigned int instruction_size) {
return *(target) == 0xff && (*(target + 1) & 0x10) == 0x10 &&
instruction_size == 6;
}
bool PreamblePatcher::IsNearRelativeCall(
unsigned char* target,
unsigned int instruction_size) {
return *(target) == 0xe8 && instruction_size == 5;
}
bool PreamblePatcher::IsMovWithDisplacement(
unsigned char* target,
unsigned int instruction_size) {
// In this case, the ModRM byte's mod field will be 0 and r/m will be 101b (5)
return instruction_size == 7 && *target == 0x48 && *(target + 1) == 0x8b &&
(*(target + 2) >> 6) == 0 && (*(target + 2) & 0x7) == 5;
}
SideStepError PreamblePatcher::PatchShortConditionalJump(
unsigned char* source,
unsigned int instruction_size,
unsigned char* target,
unsigned int* target_bytes,
unsigned int target_size) {
// note: rel8 offset is signed. Thus we need to ask for signed char
// to negative offsets right
unsigned char* original_jump_dest = (source + 2) + static_cast<signed char>(source[1]);
unsigned char* stub_jump_from = target + 6;
__int64 fixup_jump_offset = original_jump_dest - stub_jump_from;
if (fixup_jump_offset > INT_MAX || fixup_jump_offset < INT_MIN) {
SIDESTEP_ASSERT(false &&
"Unable to fix up short jump because target"
" is too far away.");
return SIDESTEP_JUMP_INSTRUCTION;
}
*target_bytes = 6;
if (target_size > *target_bytes) {
// Convert the short jump to a near jump.
//
// 0f 8x xx xx xx xx = Jcc rel32off
unsigned short jmpcode = ((0x80 | (source[0] & 0xf)) << 8) | 0x0f;
memcpy(reinterpret_cast<void*>(target),
reinterpret_cast<void*>(&jmpcode), 2);
memcpy(reinterpret_cast<void*>(target + 2),
reinterpret_cast<void*>(&fixup_jump_offset), 4);
}
return SIDESTEP_SUCCESS;
}
SideStepError PreamblePatcher::PatchShortJump(
unsigned char* source,
unsigned int instruction_size,
unsigned char* target,
unsigned int* target_bytes,
unsigned int target_size) {
// note: rel8 offset is _signed_. Thus we need signed char here.
unsigned char* original_jump_dest = (source + 2) + static_cast<signed char>(source[1]);
unsigned char* stub_jump_from = target + 5;
__int64 fixup_jump_offset = original_jump_dest - stub_jump_from;
if (fixup_jump_offset > INT_MAX || fixup_jump_offset < INT_MIN) {
SIDESTEP_ASSERT(false &&
"Unable to fix up short jump because target"
" is too far away.");
return SIDESTEP_JUMP_INSTRUCTION;
}
*target_bytes = 5;
if (target_size > *target_bytes) {
// Convert the short jump to a near jump.
//
// e9 xx xx xx xx = jmp rel32off
target[0] = 0xe9;
memcpy(reinterpret_cast<void*>(target + 1),
reinterpret_cast<void*>(&fixup_jump_offset), 4);
}
return SIDESTEP_SUCCESS;
}
SideStepError PreamblePatcher::PatchNearJumpOrCall(
unsigned char* source,
unsigned int instruction_size,
unsigned char* target,
unsigned int* target_bytes,
unsigned int target_size) {
SIDESTEP_ASSERT(instruction_size == 5 || instruction_size == 6);
unsigned int jmp_offset_in_instruction = instruction_size == 5 ? 1 : 2;
unsigned char* original_jump_dest = reinterpret_cast<unsigned char *>(
reinterpret_cast<__int64>(source + instruction_size) +
*(reinterpret_cast<int*>(source + jmp_offset_in_instruction)));
unsigned char* stub_jump_from = target + instruction_size;
__int64 fixup_jump_offset = original_jump_dest - stub_jump_from;
if (fixup_jump_offset > INT_MAX || fixup_jump_offset < INT_MIN) {
SIDESTEP_ASSERT(false &&
"Unable to fix up near jump because target"
" is too far away.");
return SIDESTEP_JUMP_INSTRUCTION;
}
if ((fixup_jump_offset < SCHAR_MAX && fixup_jump_offset > SCHAR_MIN)) {
*target_bytes = 2;
if (target_size > *target_bytes) {
// If the new offset is in range, use a short jump instead of a near jump.
if (source[0] == ASM_JCC32REL_0 &&
(source[1] & ASM_JCC32REL_1_MASK) == ASM_JCC32REL_1_MASK) {
unsigned short jmpcode = (static_cast<unsigned char>(
fixup_jump_offset) << 8) | (0x70 | (source[1] & 0xf));
memcpy(reinterpret_cast<void*>(target),
reinterpret_cast<void*>(&jmpcode),
2);
} else {
target[0] = ASM_JMP8REL;
target[1] = static_cast<unsigned char>(fixup_jump_offset);
}
}
} else {
*target_bytes = instruction_size;
if (target_size > *target_bytes) {
memcpy(reinterpret_cast<void*>(target),
reinterpret_cast<void*>(source),
jmp_offset_in_instruction);
memcpy(reinterpret_cast<void*>(target + jmp_offset_in_instruction),
reinterpret_cast<void*>(&fixup_jump_offset),
4);
}
}
return SIDESTEP_SUCCESS;
}
SideStepError PreamblePatcher::PatchMovWithDisplacement(
unsigned char* source,
unsigned int instruction_size,
unsigned char* target,
unsigned int* target_bytes,
unsigned int target_size) {
SIDESTEP_ASSERT(instruction_size == 7);
const int mov_offset_in_instruction = 3; // 0x48 0x8b 0x0d <offset>
unsigned char* original_mov_dest = reinterpret_cast<unsigned char*>(
reinterpret_cast<__int64>(source + instruction_size) +
*(reinterpret_cast<int*>(source + mov_offset_in_instruction)));
unsigned char* stub_mov_from = target + instruction_size;
__int64 fixup_mov_offset = original_mov_dest - stub_mov_from;
if (fixup_mov_offset > INT_MAX || fixup_mov_offset < INT_MIN) {
SIDESTEP_ASSERT(false &&
"Unable to fix up near MOV because target is too far away.");
return SIDESTEP_UNEXPECTED;
}
*target_bytes = instruction_size;
if (target_size > *target_bytes) {
memcpy(reinterpret_cast<void*>(target),
reinterpret_cast<void*>(source),
mov_offset_in_instruction);
memcpy(reinterpret_cast<void*>(target + mov_offset_in_instruction),
reinterpret_cast<void*>(&fixup_mov_offset),
4);
}
return SIDESTEP_SUCCESS;
}
}; // namespace sidestep

View file

@ -0,0 +1,620 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Joi Sigurdsson
* Author: Scott Francis
*
* Definition of PreamblePatcher
*/
#ifndef GOOGLE_PERFTOOLS_PREAMBLE_PATCHER_H_
#define GOOGLE_PERFTOOLS_PREAMBLE_PATCHER_H_
#include "config.h"
#include <windows.h>
// compatibility shim
#include "base/logging.h"
#define SIDESTEP_ASSERT(cond) RAW_DCHECK(cond, #cond)
#define SIDESTEP_LOG(msg) RAW_VLOG(1, msg)
// Maximum size of the preamble stub. We overwrite at least the first 5
// bytes of the function. Considering the worst case scenario, we need 4
// bytes + the max instruction size + 5 more bytes for our jump back to
// the original code. With that in mind, 32 is a good number :)
#ifdef _M_X64
// In 64-bit mode we may need more room. In 64-bit mode all jumps must be
// within +/-2GB of RIP. Because of this limitation we may need to use a
// trampoline to jump to the replacement function if it is further than 2GB
// away from the target. The trampoline is 14 bytes.
//
// So 4 bytes + max instruction size (17 bytes) + 5 bytes to jump back to the
// original code + trampoline size. 64 bytes is a nice number :-)
#define MAX_PREAMBLE_STUB_SIZE (64)
#else
#define MAX_PREAMBLE_STUB_SIZE (32)
#endif
// Determines if this is a 64-bit binary.
#ifdef _M_X64
static const bool kIs64BitBinary = true;
#else
static const bool kIs64BitBinary = false;
#endif
namespace sidestep {
// Possible results of patching/unpatching
enum SideStepError {
SIDESTEP_SUCCESS = 0,
SIDESTEP_INVALID_PARAMETER,
SIDESTEP_INSUFFICIENT_BUFFER,
SIDESTEP_JUMP_INSTRUCTION,
SIDESTEP_FUNCTION_TOO_SMALL,
SIDESTEP_UNSUPPORTED_INSTRUCTION,
SIDESTEP_NO_SUCH_MODULE,
SIDESTEP_NO_SUCH_FUNCTION,
SIDESTEP_ACCESS_DENIED,
SIDESTEP_UNEXPECTED,
};
#define SIDESTEP_TO_HRESULT(error) \
MAKE_HRESULT(SEVERITY_ERROR, FACILITY_NULL, error)
class DeleteUnsignedCharArray;
// Implements a patching mechanism that overwrites the first few bytes of
// a function preamble with a jump to our hook function, which is then
// able to call the original function via a specially-made preamble-stub
// that imitates the action of the original preamble.
//
// NOTE: This patching mechanism should currently only be used for
// non-production code, e.g. unit tests, because it is not threadsafe.
// See the TODO in preamble_patcher_with_stub.cc for instructions on what
// we need to do before using it in production code; it's fairly simple
// but unnecessary for now since we only intend to use it in unit tests.
//
// To patch a function, use either of the typesafe Patch() methods. You
// can unpatch a function using Unpatch().
//
// Typical usage goes something like this:
// @code
// typedef int (*MyTypesafeFuncPtr)(int x);
// MyTypesafeFuncPtr original_func_stub;
// int MyTypesafeFunc(int x) { return x + 1; }
// int HookMyTypesafeFunc(int x) { return 1 + original_func_stub(x); }
//
// void MyPatchInitializingFunction() {
// original_func_stub = PreamblePatcher::Patch(
// MyTypesafeFunc, HookMyTypesafeFunc);
// if (!original_func_stub) {
// // ... error handling ...
// }
//
// // ... continue - you have patched the function successfully ...
// }
// @endcode
//
// Note that there are a number of ways that this method of patching can
// fail. The most common are:
// - If there is a jump (jxx) instruction in the first 5 bytes of
// the function being patched, we cannot patch it because in the
// current implementation we do not know how to rewrite relative
// jumps after relocating them to the preamble-stub. Note that
// if you really really need to patch a function like this, it
// would be possible to add this functionality (but at some cost).
// - If there is a return (ret) instruction in the first 5 bytes
// we cannot patch the function because it may not be long enough
// for the jmp instruction we use to inject our patch.
// - If there is another thread currently executing within the bytes
// that are copied to the preamble stub, it will crash in an undefined
// way.
//
// If you get any other error than the above, you're either pointing the
// patcher at an invalid instruction (e.g. into the middle of a multi-
// byte instruction, or not at memory containing executable instructions)
// or, there may be a bug in the disassembler we use to find
// instruction boundaries.
//
// NOTE: In optimized builds, when you have very trivial functions that
// the compiler can reason do not have side effects, the compiler may
// reuse the result of calling the function with a given parameter, which
// may mean if you patch the function in between your patch will never get
// invoked. See preamble_patcher_test.cc for an example.
class PERFTOOLS_DLL_DECL PreamblePatcher {
public:
// This is a typesafe version of RawPatch(), identical in all other
// ways than it takes a template parameter indicating the type of the
// function being patched.
//
// @param T The type of the function you are patching. Usually
// you will establish this type using a typedef, as in the following
// example:
// @code
// typedef BOOL (WINAPI *MessageBoxPtr)(HWND, LPCTSTR, LPCTSTR, UINT);
// MessageBoxPtr original = NULL;
// PreamblePatcher::Patch(MessageBox, Hook_MessageBox, &original);
// @endcode
template <class T>
static SideStepError Patch(T target_function,
T replacement_function,
T* original_function_stub) {
// NOTE: casting from a function to a pointer is contra the C++
// spec. It's not safe on IA64, but is on i386. We use
// a C-style cast here to emphasize this is not legal C++.
return RawPatch((void*)(target_function),
(void*)(replacement_function),
(void**)(original_function_stub));
}
// Patches a named function imported from the named module using
// preamble patching. Uses RawPatch() to do the actual patching
// work.
//
// @param T The type of the function you are patching. Must
// exactly match the function you specify using module_name and
// function_name.
//
// @param module_name The name of the module from which the function
// is being imported. Note that the patch will fail if this module
// has not already been loaded into the current process.
//
// @param function_name The name of the function you wish to patch.
//
// @param replacement_function Your replacement function which
// will be called whenever code tries to call the original function.
//
// @param original_function_stub Pointer to memory that should receive a
// pointer that can be used (e.g. in the replacement function) to call the
// original function, or NULL to indicate failure.
//
// @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS
// indicates success.
template <class T>
static SideStepError Patch(LPCTSTR module_name,
LPCSTR function_name,
T replacement_function,
T* original_function_stub) {
SIDESTEP_ASSERT(module_name && function_name);
if (!module_name || !function_name) {
SIDESTEP_ASSERT(false &&
"You must specify a module name and function name.");
return SIDESTEP_INVALID_PARAMETER;
}
HMODULE module = ::GetModuleHandle(module_name);
SIDESTEP_ASSERT(module != NULL);
if (!module) {
SIDESTEP_ASSERT(false && "Invalid module name.");
return SIDESTEP_NO_SUCH_MODULE;
}
FARPROC existing_function = ::GetProcAddress(module, function_name);
if (!existing_function) {
SIDESTEP_ASSERT(
false && "Did not find any function with that name in the module.");
return SIDESTEP_NO_SUCH_FUNCTION;
}
// NOTE: casting from a function to a pointer is contra the C++
// spec. It's not safe on IA64, but is on i386. We use
// a C-style cast here to emphasize this is not legal C++.
return RawPatch((void*)existing_function, (void*)replacement_function,
(void**)(original_function_stub));
}
// Patches a function by overwriting its first few bytes with
// a jump to a different function. This is the "worker" function
// for each of the typesafe Patch() functions. In most cases,
// it is preferable to use the Patch() functions rather than
// this one as they do more checking at compile time.
//
// @param target_function A pointer to the function that should be
// patched.
//
// @param replacement_function A pointer to the function that should
// replace the target function. The replacement function must have
// exactly the same calling convention and parameters as the original
// function.
//
// @param original_function_stub Pointer to memory that should receive a
// pointer that can be used (e.g. in the replacement function) to call the
// original function, or NULL to indicate failure.
//
// @param original_function_stub Pointer to memory that should receive a
// pointer that can be used (e.g. in the replacement function) to call the
// original function, or NULL to indicate failure.
//
// @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS
// indicates success.
//
// @note The preamble-stub (the memory pointed to by
// *original_function_stub) is allocated on the heap, and (in
// production binaries) never destroyed, resulting in a memory leak. This
// will be the case until we implement safe unpatching of a method.
// However, it is quite difficult to unpatch a method (because other
// threads in the process may be using it) so we are leaving it for now.
// See however UnsafeUnpatch, which can be used for binaries where you
// know only one thread is running, e.g. unit tests.
static SideStepError RawPatch(void* target_function,
void* replacement_function,
void** original_function_stub);
// Unpatches target_function and deletes the stub that previously could be
// used to call the original version of the function.
//
// DELETES the stub that is passed to the function.
//
// @param target_function Pointer to the target function which was
// previously patched, i.e. a pointer which value should match the value
// of the symbol prior to patching it.
//
// @param replacement_function Pointer to the function target_function
// was patched to.
//
// @param original_function_stub Pointer to the stub returned when
// patching, that could be used to call the original version of the
// patched function. This function will also delete the stub, which after
// unpatching is useless.
//
// If your original call was
// Patch(VirtualAlloc, MyVirtualAlloc, &origptr)
// then to undo it you would call
// Unpatch(VirtualAlloc, MyVirtualAlloc, origptr);
//
// @return One of the EnSideStepError error codes; only SIDESTEP_SUCCESS
// indicates success.
static SideStepError Unpatch(void* target_function,
void* replacement_function,
void* original_function_stub);
// A helper routine when patching, which follows jmp instructions at
// function addresses, to get to the "actual" function contents.
// This allows us to identify two functions that are at different
// addresses but actually resolve to the same code.
//
// @param target_function Pointer to a function.
//
// @return Either target_function (the input parameter), or if
// target_function's body consists entirely of a JMP instruction,
// the address it JMPs to (or more precisely, the address at the end
// of a chain of JMPs).
template <class T>
static T ResolveTarget(T target_function) {
return (T)ResolveTargetImpl((unsigned char*)target_function, NULL);
}
// Allocates a block of memory of size MAX_PREAMBLE_STUB_SIZE that is as
// close (within 2GB) as possible to target. This is done to ensure that
// we can perform a relative jump from target to a trampoline if the
// replacement function is > +-2GB from target. This means that we only need
// to patch 5 bytes in the target function.
//
// @param target Pointer to target function.
//
// @return Returns a block of memory of size MAX_PREAMBLE_STUB_SIZE that can
// be used to store a function preamble block.
static unsigned char* AllocPreambleBlockNear(void* target);
// Frees a block allocated by AllocPreambleBlockNear.
//
// @param block Block that was returned by AllocPreambleBlockNear.
static void FreePreambleBlock(unsigned char* block);
private:
friend class DeleteUnsignedCharArray;
// Used to store data allocated for preamble stubs
struct PreamblePage {
unsigned int magic_;
PreamblePage* next_;
// This member points to a linked list of free blocks within the page
// or NULL if at the end
void* free_;
};
// In 64-bit mode, the replacement function must be within 2GB of the original
// target in order to only require 5 bytes for the function patch. To meet
// this requirement we're creating an allocator within this class to
// allocate blocks that are within 2GB of a given target. This member is the
// head of a linked list of pages used to allocate blocks that are within
// 2GB of the target.
static PreamblePage* preamble_pages_;
// Page granularity
static long granularity_;
// Page size
static long pagesize_;
// Determines if the patcher has been initialized.
static bool initialized_;
// Used to initialize static members.
static void Initialize();
// Patches a function by overwriting its first few bytes with
// a jump to a different function. This is similar to the RawPatch
// function except that it uses the stub allocated by the caller
// instead of allocating it.
//
// We call VirtualProtect to make the
// target function writable at least for the duration of the call.
//
// @param target_function A pointer to the function that should be
// patched.
//
// @param replacement_function A pointer to the function that should
// replace the target function. The replacement function must have
// exactly the same calling convention and parameters as the original
// function.
//
// @param preamble_stub A pointer to a buffer where the preamble stub
// should be copied. The size of the buffer should be sufficient to
// hold the preamble bytes.
//
// @param stub_size Size in bytes of the buffer allocated for the
// preamble_stub
//
// @param bytes_needed Pointer to a variable that receives the minimum
// number of bytes required for the stub. Can be set to NULL if you're
// not interested.
//
// @return An error code indicating the result of patching.
static SideStepError RawPatchWithStubAndProtections(
void* target_function,
void* replacement_function,
unsigned char* preamble_stub,
unsigned long stub_size,
unsigned long* bytes_needed);
// A helper function used by RawPatchWithStubAndProtections -- it
// does everything but the VirtualProtect work. Defined in
// preamble_patcher_with_stub.cc.
//
// @param target_function A pointer to the function that should be
// patched.
//
// @param replacement_function A pointer to the function that should
// replace the target function. The replacement function must have
// exactly the same calling convention and parameters as the original
// function.
//
// @param preamble_stub A pointer to a buffer where the preamble stub
// should be copied. The size of the buffer should be sufficient to
// hold the preamble bytes.
//
// @param stub_size Size in bytes of the buffer allocated for the
// preamble_stub
//
// @param bytes_needed Pointer to a variable that receives the minimum
// number of bytes required for the stub. Can be set to NULL if you're
// not interested.
//
// @return An error code indicating the result of patching.
static SideStepError RawPatchWithStub(void* target_function,
void* replacement_function,
unsigned char* preamble_stub,
unsigned long stub_size,
unsigned long* bytes_needed);
// A helper routine when patching, which follows jmp instructions at
// function addresses, to get to the "actual" function contents.
// This allows us to identify two functions that are at different
// addresses but actually resolve to the same code.
//
// @param target_function Pointer to a function.
//
// @param stop_before If, when following JMP instructions from
// target_function, we get to the address stop, we return
// immediately, the address that jumps to stop_before.
//
// @param stop_before_trampoline When following JMP instructions from
// target_function, stop before a trampoline is detected. See comment in
// PreamblePatcher::RawPatchWithStub for more information. This parameter
// has no effect in 32-bit mode.
//
// @return Either target_function (the input parameter), or if
// target_function's body consists entirely of a JMP instruction,
// the address it JMPs to (or more precisely, the address at the end
// of a chain of JMPs).
static void* ResolveTargetImpl(unsigned char* target_function,
unsigned char* stop_before,
bool stop_before_trampoline = false);
// Helper routine that attempts to allocate a page as close (within 2GB)
// as possible to target.
//
// @param target Pointer to target function.
//
// @return Returns an address that is within 2GB of target.
static void* AllocPageNear(void* target);
// Helper routine that determines if a target instruction is a short
// conditional jump.
//
// @param target Pointer to instruction.
//
// @param instruction_size Size of the instruction in bytes.
//
// @return Returns true if the instruction is a short conditional jump.
static bool IsShortConditionalJump(unsigned char* target,
unsigned int instruction_size);
static bool IsShortJump(unsigned char *target, unsigned int instruction_size);
// Helper routine that determines if a target instruction is a near
// conditional jump.
//
// @param target Pointer to instruction.
//
// @param instruction_size Size of the instruction in bytes.
//
// @return Returns true if the instruction is a near conditional jump.
static bool IsNearConditionalJump(unsigned char* target,
unsigned int instruction_size);
// Helper routine that determines if a target instruction is a near
// relative jump.
//
// @param target Pointer to instruction.
//
// @param instruction_size Size of the instruction in bytes.
//
// @return Returns true if the instruction is a near absolute jump.
static bool IsNearRelativeJump(unsigned char* target,
unsigned int instruction_size);
// Helper routine that determines if a target instruction is a near
// absolute call.
//
// @param target Pointer to instruction.
//
// @param instruction_size Size of the instruction in bytes.
//
// @return Returns true if the instruction is a near absolute call.
static bool IsNearAbsoluteCall(unsigned char* target,
unsigned int instruction_size);
// Helper routine that determines if a target instruction is a near
// absolute call.
//
// @param target Pointer to instruction.
//
// @param instruction_size Size of the instruction in bytes.
//
// @return Returns true if the instruction is a near absolute call.
static bool IsNearRelativeCall(unsigned char* target,
unsigned int instruction_size);
// Helper routine that determines if a target instruction is a 64-bit MOV
// that uses a RIP-relative displacement.
//
// @param target Pointer to instruction.
//
// @param instruction_size Size of the instruction in bytes.
//
// @return Returns true if the instruction is a MOV with displacement.
static bool IsMovWithDisplacement(unsigned char* target,
unsigned int instruction_size);
// Helper routine that converts a short conditional jump instruction
// to a near conditional jump in a target buffer. Note that the target
// buffer must be within 2GB of the source for the near jump to work.
//
// A short conditional jump instruction is in the format:
// 7x xx = Jcc rel8off
//
// @param source Pointer to instruction.
//
// @param instruction_size Size of the instruction.
//
// @param target Target buffer to write the new instruction.
//
// @param target_bytes Pointer to a buffer that contains the size
// of the target instruction, in bytes.
//
// @param target_size Size of the target buffer.
//
// @return Returns SIDESTEP_SUCCESS if successful, otherwise an error.
static SideStepError PatchShortConditionalJump(unsigned char* source,
unsigned int instruction_size,
unsigned char* target,
unsigned int* target_bytes,
unsigned int target_size);
static SideStepError PatchShortJump(unsigned char* source,
unsigned int instruction_size,
unsigned char* target,
unsigned int* target_bytes,
unsigned int target_size);
// Helper routine that converts an instruction that will convert various
// jump-like instructions to corresponding instructions in the target buffer.
// What this routine does is fix up the relative offsets contained in jump
// instructions to point back to the original target routine. Like with
// PatchShortConditionalJump, the target buffer must be within 2GB of the
// source.
//
// We currently handle the following instructions:
//
// E9 xx xx xx xx = JMP rel32off
// 0F 8x xx xx xx xx = Jcc rel32off
// FF /2 xx xx xx xx = CALL reg/mem32/mem64
// E8 xx xx xx xx = CALL rel32off
//
// It should not be hard to update this function to support other
// instructions that jump to relative targets.
//
// @param source Pointer to instruction.
//
// @param instruction_size Size of the instruction.
//
// @param target Target buffer to write the new instruction.
//
// @param target_bytes Pointer to a buffer that contains the size
// of the target instruction, in bytes.
//
// @param target_size Size of the target buffer.
//
// @return Returns SIDESTEP_SUCCESS if successful, otherwise an error.
static SideStepError PatchNearJumpOrCall(unsigned char* source,
unsigned int instruction_size,
unsigned char* target,
unsigned int* target_bytes,
unsigned int target_size);
// Helper routine that patches a 64-bit MOV instruction with a RIP-relative
// displacement. The target buffer must be within 2GB of the source.
//
// 48 8B 0D XX XX XX XX = MOV rel32off
//
// @param source Pointer to instruction.
//
// @param instruction_size Size of the instruction.
//
// @param target Target buffer to write the new instruction.
//
// @param target_bytes Pointer to a buffer that contains the size
// of the target instruction, in bytes.
//
// @param target_size Size of the target buffer.
//
// @return Returns SIDESTEP_SUCCESS if successful, otherwise an error.
static SideStepError PatchMovWithDisplacement(unsigned char* source,
unsigned int instruction_size,
unsigned char* target,
unsigned int* target_bytes,
unsigned int target_size);
};
}; // namespace sidestep
#endif // GOOGLE_PERFTOOLS_PREAMBLE_PATCHER_H_

View file

@ -0,0 +1,368 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2011, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Joi Sigurdsson
* Author: Scott Francis
*
* Unit tests for PreamblePatcher
*/
#include "config_for_unittests.h"
#include "preamble_patcher.h"
#include "mini_disassembler.h"
#pragma warning(push)
#pragma warning(disable:4553)
#include "auto_testing_hook.h"
#pragma warning(pop)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <tchar.h>
// Turning off all optimizations for this file, since the official build's
// "Whole program optimization" seems to cause the TestPatchUsingDynamicStub
// test to crash with an access violation. We debugged this and found
// that the optimized access a register that is changed by a call to the hook
// function.
#pragma optimize("", off)
// A convenience macro to avoid a lot of casting in the tests.
// I tried to make this a templated function, but windows complained:
// error C2782: 'sidestep::SideStepError `anonymous-namespace'::Unpatch(T,T,T *)' : template parameter 'T' is ambiguous
// could be 'int (int)'
// or 'int (__cdecl *)(int)'
// My life isn't long enough to try to figure out how to fix this.
#define UNPATCH(target_function, replacement_function, original_function_stub) \
sidestep::PreamblePatcher::Unpatch((void*)(target_function), \
(void*)(replacement_function), \
(void*)(original_function))
namespace {
// Function for testing - this is what we patch
//
// NOTE: Because of the way the compiler optimizes this function in
// release builds, we need to use a different input value every time we
// call it within a function, otherwise the compiler will just reuse the
// last calculated incremented value.
int __declspec(noinline) IncrementNumber(int i) {
#ifdef _M_X64
__int64 i2 = i + 1;
return (int) i2;
#else
return i + 1;
#endif
}
extern "C" int TooShortFunction(int);
extern "C" int JumpShortCondFunction(int);
extern "C" int JumpNearCondFunction(int);
extern "C" int JumpAbsoluteFunction(int);
extern "C" int CallNearRelativeFunction(int);
typedef int (*IncrementingFunc)(int);
IncrementingFunc original_function = NULL;
int HookIncrementNumber(int i) {
SIDESTEP_ASSERT(original_function != NULL);
int incremented_once = original_function(i);
return incremented_once + 1;
}
// For the AutoTestingHook test, we can't use original_function, because
// all that is encapsulated.
// This function "increments" by 10, just to set it apart from the other
// functions.
int __declspec(noinline) AutoHookIncrementNumber(int i) {
return i + 10;
}
}; // namespace
namespace sidestep {
bool TestDisassembler() {
unsigned int instruction_size = 0;
sidestep::MiniDisassembler disassembler;
void * target = reinterpret_cast<unsigned char *>(IncrementNumber);
void * new_target = PreamblePatcher::ResolveTarget(target);
if (target != new_target)
target = new_target;
while (1) {
sidestep::InstructionType instructionType = disassembler.Disassemble(
reinterpret_cast<unsigned char *>(target) + instruction_size,
instruction_size);
if (sidestep::IT_RETURN == instructionType) {
return true;
}
}
}
bool TestPatchWithLongJump() {
original_function = NULL;
void *p = ::VirtualAlloc(reinterpret_cast<void *>(0x0000020000000000), 4096,
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
SIDESTEP_EXPECT_TRUE(p != NULL);
memset(p, 0xcc, 4096);
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(IncrementNumber,
(IncrementingFunc) p,
&original_function));
SIDESTEP_ASSERT((*original_function)(1) == 2);
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
UNPATCH(IncrementNumber,
(IncrementingFunc)p,
original_function));
::VirtualFree(p, 0, MEM_RELEASE);
return true;
}
bool TestPatchWithPreambleShortCondJump() {
original_function = NULL;
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(JumpShortCondFunction,
HookIncrementNumber,
&original_function));
(*original_function)(1);
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
UNPATCH(JumpShortCondFunction,
(void*)HookIncrementNumber,
original_function));
return true;
}
bool TestPatchWithPreambleNearRelativeCondJump() {
original_function = NULL;
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(JumpNearCondFunction,
HookIncrementNumber,
&original_function));
(*original_function)(0);
(*original_function)(1);
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
UNPATCH(JumpNearCondFunction,
HookIncrementNumber,
original_function));
return true;
}
bool TestPatchWithPreambleAbsoluteJump() {
original_function = NULL;
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(JumpAbsoluteFunction,
HookIncrementNumber,
&original_function));
(*original_function)(0);
(*original_function)(1);
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
UNPATCH(JumpAbsoluteFunction,
HookIncrementNumber,
original_function));
return true;
}
bool TestPatchWithPreambleNearRelativeCall() {
original_function = NULL;
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(
CallNearRelativeFunction,
HookIncrementNumber,
&original_function));
(*original_function)(0);
(*original_function)(1);
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
UNPATCH(CallNearRelativeFunction,
HookIncrementNumber,
original_function));
return true;
}
bool TestPatchUsingDynamicStub() {
original_function = NULL;
SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(IncrementNumber,
HookIncrementNumber,
&original_function));
SIDESTEP_EXPECT_TRUE(original_function);
SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 4);
SIDESTEP_EXPECT_TRUE(original_function(3) == 4);
// Clearbox test to see that the function has been patched.
sidestep::MiniDisassembler disassembler;
unsigned int instruction_size = 0;
SIDESTEP_EXPECT_TRUE(sidestep::IT_JUMP == disassembler.Disassemble(
reinterpret_cast<unsigned char*>(IncrementNumber),
instruction_size));
// Since we patched IncrementNumber, its first statement is a
// jmp to the hook function. So verify that we now can not patch
// IncrementNumber because it starts with a jump.
#if 0
IncrementingFunc dummy = NULL;
// TODO(joi@chromium.org): restore this test once flag is added to
// disable JMP following
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_JUMP_INSTRUCTION ==
sidestep::PreamblePatcher::Patch(IncrementNumber,
HookIncrementNumber,
&dummy));
// This test disabled because code in preamble_patcher_with_stub.cc
// asserts before returning the error code -- so there is no way
// to get an error code here, in debug build.
dummy = NULL;
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_FUNCTION_TOO_SMALL ==
sidestep::PreamblePatcher::Patch(TooShortFunction,
HookIncrementNumber,
&dummy));
#endif
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
UNPATCH(IncrementNumber,
HookIncrementNumber,
original_function));
return true;
}
bool PatchThenUnpatch() {
original_function = NULL;
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
sidestep::PreamblePatcher::Patch(IncrementNumber,
HookIncrementNumber,
&original_function));
SIDESTEP_EXPECT_TRUE(original_function);
SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 3);
SIDESTEP_EXPECT_TRUE(original_function(2) == 3);
SIDESTEP_EXPECT_TRUE(sidestep::SIDESTEP_SUCCESS ==
UNPATCH(IncrementNumber,
HookIncrementNumber,
original_function));
original_function = NULL;
SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
return true;
}
bool AutoTestingHookTest() {
SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
// Inner scope, so we can test what happens when the AutoTestingHook
// goes out of scope
{
AutoTestingHook hook = MakeTestingHook(IncrementNumber,
AutoHookIncrementNumber);
(void) hook;
SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12);
}
SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
return true;
}
bool AutoTestingHookInContainerTest() {
SIDESTEP_EXPECT_TRUE(IncrementNumber(1) == 2);
// Inner scope, so we can test what happens when the AutoTestingHook
// goes out of scope
{
AutoTestingHookHolder hook(MakeTestingHookHolder(IncrementNumber,
AutoHookIncrementNumber));
(void) hook;
SIDESTEP_EXPECT_TRUE(IncrementNumber(2) == 12);
}
SIDESTEP_EXPECT_TRUE(IncrementNumber(3) == 4);
return true;
}
bool TestPreambleAllocation() {
__int64 diff = 0;
void* p1 = reinterpret_cast<void*>(0x110000000);
void* p2 = reinterpret_cast<void*>(0x810000000);
unsigned char* b1 = PreamblePatcher::AllocPreambleBlockNear(p1);
SIDESTEP_EXPECT_TRUE(b1 != NULL);
diff = reinterpret_cast<__int64>(p1) - reinterpret_cast<__int64>(b1);
// Ensure blocks are within 2GB
SIDESTEP_EXPECT_TRUE(diff <= INT_MAX && diff >= INT_MIN);
unsigned char* b2 = PreamblePatcher::AllocPreambleBlockNear(p2);
SIDESTEP_EXPECT_TRUE(b2 != NULL);
diff = reinterpret_cast<__int64>(p2) - reinterpret_cast<__int64>(b2);
SIDESTEP_EXPECT_TRUE(diff <= INT_MAX && diff >= INT_MIN);
// Ensure we're reusing free blocks
unsigned char* b3 = b1;
unsigned char* b4 = b2;
PreamblePatcher::FreePreambleBlock(b1);
PreamblePatcher::FreePreambleBlock(b2);
b1 = PreamblePatcher::AllocPreambleBlockNear(p1);
SIDESTEP_EXPECT_TRUE(b1 == b3);
b2 = PreamblePatcher::AllocPreambleBlockNear(p2);
SIDESTEP_EXPECT_TRUE(b2 == b4);
PreamblePatcher::FreePreambleBlock(b1);
PreamblePatcher::FreePreambleBlock(b2);
return true;
}
bool UnitTests() {
return TestPatchWithPreambleNearRelativeCall() &&
TestPatchWithPreambleAbsoluteJump() &&
TestPatchWithPreambleNearRelativeCondJump() &&
TestPatchWithPreambleShortCondJump() &&
TestDisassembler() && TestPatchWithLongJump() &&
TestPatchUsingDynamicStub() && PatchThenUnpatch() &&
AutoTestingHookTest() && AutoTestingHookInContainerTest() &&
TestPreambleAllocation();
}
}; // namespace sidestep
int safe_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
if (size == 0) // not even room for a \0?
return -1; // not what C99 says to do, but what windows does
str[size-1] = '\0';
return _vsnprintf(str, size-1, format, ap);
}
int _tmain(int argc, _TCHAR* argv[])
{
bool ret = sidestep::UnitTests();
printf("%s\n", ret ? "PASS" : "FAIL");
return ret ? 0 : -1;
}
#pragma optimize("", on)

View file

@ -0,0 +1,302 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2007, 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 THE COPYRIGHT
* OWNER 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.
*
* ---
* Author: Joi Sigurdsson
* Author: Scott Francis
*
* Implementation of PreamblePatcher
*/
#include "preamble_patcher.h"
#include "mini_disassembler.h"
// Definitions of assembly statements we need
#define ASM_JMP32REL 0xE9
#define ASM_INT3 0xCC
#define ASM_NOP 0x90
// X64 opcodes
#define ASM_MOVRAX_IMM 0xB8
#define ASM_REXW 0x48
#define ASM_JMP 0xFF
#define ASM_JMP_RAX 0xE0
#define ASM_PUSH 0x68
#define ASM_RET 0xC3
namespace sidestep {
SideStepError PreamblePatcher::RawPatchWithStub(
void* target_function,
void* replacement_function,
unsigned char* preamble_stub,
unsigned long stub_size,
unsigned long* bytes_needed) {
if ((NULL == target_function) ||
(NULL == replacement_function) ||
(NULL == preamble_stub)) {
SIDESTEP_ASSERT(false &&
"Invalid parameters - either pTargetFunction or "
"pReplacementFunction or pPreambleStub were NULL.");
return SIDESTEP_INVALID_PARAMETER;
}
// TODO(V7:joi) Siggi and I just had a discussion and decided that both
// patching and unpatching are actually unsafe. We also discussed a
// method of making it safe, which is to freeze all other threads in the
// process, check their thread context to see if their eip is currently
// inside the block of instructions we need to copy to the stub, and if so
// wait a bit and try again, then unfreeze all threads once we've patched.
// Not implementing this for now since we're only using SideStep for unit
// testing, but if we ever use it for production code this is what we
// should do.
//
// NOTE: Stoyan suggests we can write 8 or even 10 bytes atomically using
// FPU instructions, and on newer processors we could use cmpxchg8b or
// cmpxchg16b. So it might be possible to do the patching/unpatching
// atomically and avoid having to freeze other threads. Note though, that
// doing it atomically does not help if one of the other threads happens
// to have its eip in the middle of the bytes you change while you change
// them.
unsigned char* target = reinterpret_cast<unsigned char*>(target_function);
unsigned int required_trampoline_bytes = 0;
const unsigned int kRequiredStubJumpBytes = 5;
const unsigned int kRequiredTargetPatchBytes = 5;
// Initialize the stub with INT3's just in case.
if (stub_size) {
memset(preamble_stub, 0xcc, stub_size);
}
if (kIs64BitBinary) {
// In 64-bit mode JMP instructions are always relative to RIP. If the
// replacement - target offset is > 2GB, we can't JMP to the replacement
// function. In this case, we're going to use a trampoline - that is,
// we're going to do a relative jump to a small chunk of code in the stub
// that will then do the absolute jump to the replacement function. By
// doing this, we only need to patch 5 bytes in the target function, as
// opposed to patching 12 bytes if we were to do an absolute jump.
//
// Note that the first byte of the trampoline is a NOP instruction. This
// is used as a trampoline signature that will be detected when unpatching
// the function.
//
// jmp <trampoline>
//
// trampoline:
// nop
// mov rax, <replacement_function>
// jmp rax
//
__int64 replacement_target_offset = reinterpret_cast<__int64>(
replacement_function) - reinterpret_cast<__int64>(target) - 5;
if (replacement_target_offset > INT_MAX
|| replacement_target_offset < INT_MIN) {
// The stub needs to be within 2GB of the target for the trampoline to
// work!
__int64 trampoline_offset = reinterpret_cast<__int64>(preamble_stub)
- reinterpret_cast<__int64>(target) - 5;
if (trampoline_offset > INT_MAX || trampoline_offset < INT_MIN) {
// We're screwed.
SIDESTEP_ASSERT(false
&& "Preamble stub is too far from target to patch.");
return SIDESTEP_UNEXPECTED;
}
required_trampoline_bytes = 13;
}
}
// Let's disassemble the preamble of the target function to see if we can
// patch, and to see how much of the preamble we need to take. We need 5
// bytes for our jmp instruction, so let's find the minimum number of
// instructions to get 5 bytes.
MiniDisassembler disassembler;
unsigned int preamble_bytes = 0;
unsigned int stub_bytes = 0;
while (preamble_bytes < kRequiredTargetPatchBytes) {
unsigned int cur_bytes = 0;
InstructionType instruction_type =
disassembler.Disassemble(target + preamble_bytes, cur_bytes);
if (IT_JUMP == instruction_type) {
unsigned int jump_bytes = 0;
SideStepError jump_ret = SIDESTEP_JUMP_INSTRUCTION;
if (IsShortConditionalJump(target + preamble_bytes, cur_bytes)) {
jump_ret = PatchShortConditionalJump(target + preamble_bytes, cur_bytes,
preamble_stub + stub_bytes,
&jump_bytes,
stub_size - stub_bytes);
} else if (IsShortJump(target + preamble_bytes, cur_bytes)) {
jump_ret = PatchShortJump(target + preamble_bytes, cur_bytes,
preamble_stub + stub_bytes,
&jump_bytes,
stub_size - stub_bytes);
} else if (IsNearConditionalJump(target + preamble_bytes, cur_bytes) ||
IsNearRelativeJump(target + preamble_bytes, cur_bytes) ||
IsNearAbsoluteCall(target + preamble_bytes, cur_bytes) ||
IsNearRelativeCall(target + preamble_bytes, cur_bytes)) {
jump_ret = PatchNearJumpOrCall(target + preamble_bytes, cur_bytes,
preamble_stub + stub_bytes, &jump_bytes,
stub_size - stub_bytes);
}
if (jump_ret != SIDESTEP_SUCCESS) {
SIDESTEP_ASSERT(false &&
"Unable to patch because there is an unhandled branch "
"instruction in the initial preamble bytes.");
return SIDESTEP_JUMP_INSTRUCTION;
}
stub_bytes += jump_bytes;
} else if (IT_RETURN == instruction_type) {
SIDESTEP_ASSERT(false &&
"Unable to patch because function is too short");
return SIDESTEP_FUNCTION_TOO_SMALL;
} else if (IT_GENERIC == instruction_type) {
if (IsMovWithDisplacement(target + preamble_bytes, cur_bytes)) {
unsigned int mov_bytes = 0;
if (PatchMovWithDisplacement(target + preamble_bytes, cur_bytes,
preamble_stub + stub_bytes, &mov_bytes,
stub_size - stub_bytes)
!= SIDESTEP_SUCCESS) {
return SIDESTEP_UNSUPPORTED_INSTRUCTION;
}
stub_bytes += mov_bytes;
} else {
memcpy(reinterpret_cast<void*>(preamble_stub + stub_bytes),
reinterpret_cast<void*>(target + preamble_bytes), cur_bytes);
stub_bytes += cur_bytes;
}
} else {
SIDESTEP_ASSERT(false &&
"Disassembler encountered unsupported instruction "
"(either unused or unknown");
return SIDESTEP_UNSUPPORTED_INSTRUCTION;
}
preamble_bytes += cur_bytes;
}
if (NULL != bytes_needed)
*bytes_needed = stub_bytes + kRequiredStubJumpBytes
+ required_trampoline_bytes;
// Inv: cbPreamble is the number of bytes (at least 5) that we need to take
// from the preamble to have whole instructions that are 5 bytes or more
// in size total. The size of the stub required is cbPreamble +
// kRequiredStubJumpBytes (5) + required_trampoline_bytes (0 or 13)
if (stub_bytes + kRequiredStubJumpBytes + required_trampoline_bytes
> stub_size) {
SIDESTEP_ASSERT(false);
return SIDESTEP_INSUFFICIENT_BUFFER;
}
// Now, make a jmp instruction to the rest of the target function (minus the
// preamble bytes we moved into the stub) and copy it into our preamble-stub.
// find address to jump to, relative to next address after jmp instruction
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#endif
int relative_offset_to_target_rest
= ((reinterpret_cast<unsigned char*>(target) + preamble_bytes) -
(preamble_stub + stub_bytes + kRequiredStubJumpBytes));
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// jmp (Jump near, relative, displacement relative to next instruction)
preamble_stub[stub_bytes] = ASM_JMP32REL;
// copy the address
memcpy(reinterpret_cast<void*>(preamble_stub + stub_bytes + 1),
reinterpret_cast<void*>(&relative_offset_to_target_rest), 4);
if (kIs64BitBinary && required_trampoline_bytes != 0) {
// Construct the trampoline
unsigned int trampoline_pos = stub_bytes + kRequiredStubJumpBytes;
preamble_stub[trampoline_pos] = ASM_NOP;
preamble_stub[trampoline_pos + 1] = ASM_REXW;
preamble_stub[trampoline_pos + 2] = ASM_MOVRAX_IMM;
memcpy(reinterpret_cast<void*>(preamble_stub + trampoline_pos + 3),
reinterpret_cast<void*>(&replacement_function),
sizeof(void *));
preamble_stub[trampoline_pos + 11] = ASM_JMP;
preamble_stub[trampoline_pos + 12] = ASM_JMP_RAX;
// Now update replacement_function to point to the trampoline
replacement_function = preamble_stub + trampoline_pos;
}
// Inv: preamble_stub points to assembly code that will execute the
// original function by first executing the first cbPreamble bytes of the
// preamble, then jumping to the rest of the function.
// Overwrite the first 5 bytes of the target function with a jump to our
// replacement function.
// (Jump near, relative, displacement relative to next instruction)
target[0] = ASM_JMP32REL;
// Find offset from instruction after jmp, to the replacement function.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
#endif
int offset_to_replacement_function =
reinterpret_cast<unsigned char*>(replacement_function) -
reinterpret_cast<unsigned char*>(target) - 5;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
// complete the jmp instruction
memcpy(reinterpret_cast<void*>(target + 1),
reinterpret_cast<void*>(&offset_to_replacement_function), 4);
// Set any remaining bytes that were moved to the preamble-stub to INT3 so
// as not to cause confusion (otherwise you might see some strange
// instructions if you look at the disassembly, or even invalid
// instructions). Also, by doing this, we will break into the debugger if
// some code calls into this portion of the code. If this happens, it
// means that this function cannot be patched using this patcher without
// further thought.
if (preamble_bytes > kRequiredTargetPatchBytes) {
memset(reinterpret_cast<void*>(target + kRequiredTargetPatchBytes),
ASM_INT3, preamble_bytes - kRequiredTargetPatchBytes);
}
// Inv: The memory pointed to by target_function now points to a relative
// jump instruction that jumps over to the preamble_stub. The preamble
// stub contains the first stub_size bytes of the original target
// function's preamble code, followed by a relative jump back to the next
// instruction after the first cbPreamble bytes.
//
// In 64-bit mode the memory pointed to by target_function *may* point to a
// relative jump instruction that jumps to a trampoline which will then
// perform an absolute jump to the replacement function. The preamble stub
// still contains the original target function's preamble code, followed by a
// jump back to the instructions after the first preamble bytes.
//
return SIDESTEP_SUCCESS;
}
}; // namespace sidestep

View file

@ -0,0 +1,169 @@
; Copyright (c) 2011, 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 THE COPYRIGHT
; OWNER 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.
;
; ---
; Author: Scott Francis
;
; Unit tests for PreamblePatcher
.MODEL small
.CODE
TooShortFunction PROC
ret
TooShortFunction ENDP
JumpShortCondFunction PROC
test cl, 1
jnz jumpspot
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
int 3
jumpspot:
nop
nop
nop
nop
mov rax, 1
ret
JumpShortCondFunction ENDP
JumpNearCondFunction PROC
test cl, 1
jnz jumpspot
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
jumpspot:
nop
nop
mov rax, 1
ret
JumpNearCondFunction ENDP
JumpAbsoluteFunction PROC
test cl, 1
jmp jumpspot
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
jumpspot:
nop
nop
mov rax, 1
ret
JumpAbsoluteFunction ENDP
CallNearRelativeFunction PROC
test cl, 1
call TooShortFunction
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
mov rdx, 0ffff1111H
nop
nop
nop
ret
CallNearRelativeFunction ENDP
END

View file

@ -0,0 +1,205 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2013, 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 THE COPYRIGHT
// OWNER 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.
// ---
// Author: Petr Hosek
#ifndef _WIN32
# error You should only be including windows/system-alloc.cc in a windows environment!
#endif
#include <config.h>
#include <windows.h>
#include <algorithm> // std::min
#include <gperftools/malloc_extension.h>
#include "base/logging.h"
#include "base/spinlock.h"
#include "internal_logging.h"
#include "system-alloc.h"
static SpinLock spinlock(SpinLock::LINKER_INITIALIZED);
// The current system allocator declaration
SysAllocator* tcmalloc_sys_alloc = NULL;
// Number of bytes taken from system.
size_t TCMalloc_SystemTaken = 0;
class VirtualSysAllocator : public SysAllocator {
public:
VirtualSysAllocator() : SysAllocator() {
}
void* Alloc(size_t size, size_t *actual_size, size_t alignment);
};
static char virtual_space[sizeof(VirtualSysAllocator)];
// This is mostly like MmapSysAllocator::Alloc, except it does these weird
// munmap's in the middle of the page, which is forbidden in windows.
void* VirtualSysAllocator::Alloc(size_t size, size_t *actual_size,
size_t alignment) {
// Align on the pagesize boundary
const int pagesize = getpagesize();
if (alignment < pagesize) alignment = pagesize;
size = ((size + alignment - 1) / alignment) * alignment;
// Report the total number of bytes the OS actually delivered. This might be
// greater than |size| because of alignment concerns. The full size is
// necessary so that adjacent spans can be coalesced.
// TODO(antonm): proper processing of alignments
// in actual_size and decommitting.
if (actual_size) {
*actual_size = size;
}
// We currently do not support alignments larger than the pagesize or
// alignments that are not multiples of the pagesize after being floored.
// If this ability is needed it can be done by the caller (assuming it knows
// the page size).
assert(alignment <= pagesize);
void* result = VirtualAlloc(0, size,
MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
if (result == NULL)
return NULL;
// If the result is not aligned memory fragmentation will result which can
// lead to pathological memory use.
assert((reinterpret_cast<uintptr_t>(result) & (alignment - 1)) == 0);
return result;
}
#ifdef _MSC_VER
extern "C" SysAllocator* tc_get_sysalloc_override(SysAllocator *def);
extern "C" SysAllocator* tc_get_sysalloc_default(SysAllocator *def)
{
return def;
}
#if defined(_M_IX86)
#pragma comment(linker, "/alternatename:_tc_get_sysalloc_override=_tc_get_sysalloc_default")
#elif defined(_M_X64)
#pragma comment(linker, "/alternatename:tc_get_sysalloc_override=tc_get_sysalloc_default")
#endif
#else // !_MSC_VER
extern "C" ATTRIBUTE_NOINLINE
SysAllocator* tc_get_sysalloc_override(SysAllocator *def)
{
return def;
}
#endif
static bool system_alloc_inited = false;
void InitSystemAllocators(void) {
VirtualSysAllocator *alloc = new (virtual_space) VirtualSysAllocator();
tcmalloc_sys_alloc = tc_get_sysalloc_override(alloc);
}
extern PERFTOOLS_DLL_DECL
void* TCMalloc_SystemAlloc(size_t size, size_t *actual_size,
size_t alignment) {
SpinLockHolder lock_holder(&spinlock);
if (!system_alloc_inited) {
InitSystemAllocators();
system_alloc_inited = true;
}
void* result = tcmalloc_sys_alloc->Alloc(size, actual_size, alignment);
if (result != NULL) {
if (actual_size) {
TCMalloc_SystemTaken += *actual_size;
} else {
TCMalloc_SystemTaken += size;
}
}
return result;
}
extern PERFTOOLS_DLL_DECL
bool TCMalloc_SystemRelease(void* start, size_t length) {
if (VirtualFree(start, length, MEM_DECOMMIT))
return true;
// The decommit may fail if the memory region consists of allocations
// from more than one call to VirtualAlloc. In this case, fall back to
// using VirtualQuery to retrieve the allocation boundaries and decommit
// them each individually.
char* ptr = static_cast<char*>(start);
char* end = ptr + length;
MEMORY_BASIC_INFORMATION info;
while (ptr < end) {
size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
assert(resultSize == sizeof(info));
size_t decommitSize = std::min<size_t>(info.RegionSize, end - ptr);
BOOL success = VirtualFree(ptr, decommitSize, MEM_DECOMMIT);
assert(success == TRUE);
ptr += decommitSize;
}
return true;
}
extern PERFTOOLS_DLL_DECL
void TCMalloc_SystemCommit(void* start, size_t length) {
if (VirtualAlloc(start, length, MEM_COMMIT, PAGE_READWRITE) == start)
return;
// The commit may fail if the memory region consists of allocations
// from more than one call to VirtualAlloc. In this case, fall back to
// using VirtualQuery to retrieve the allocation boundaries and commit them
// each individually.
char* ptr = static_cast<char*>(start);
char* end = ptr + length;
MEMORY_BASIC_INFORMATION info;
while (ptr < end) {
size_t resultSize = VirtualQuery(ptr, &info, sizeof(info));
assert(resultSize == sizeof(info));
size_t commitSize = std::min<size_t>(info.RegionSize, end - ptr);
void* newAddress = VirtualAlloc(ptr, commitSize, MEM_COMMIT,
PAGE_READWRITE);
assert(newAddress == ptr);
ptr += commitSize;
}
}
bool RegisterSystemAllocator(SysAllocator *allocator, int priority) {
return false; // we don't allow registration on windows, right now
}
void DumpSystemAllocatorStats(TCMalloc_Printer* printer) {
// We don't dump stats on windows, right now
}