Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for legacy Windows versions #203

Merged
merged 11 commits into from
Mar 30, 2020
68 changes: 42 additions & 26 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ endif()

option(SENTRY_ENABLE_INSTALL "Enable sentry installation" "${SENTRY_MAIN_PROJECT}")

if(APPLE OR WIN32)
if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$")
message(WARNING "Crashpad is not supported for MSVC with XP toolset. Default backend was switched to 'none'")
set(SENTRY_DEFAULT_BACKEND "none")
elseif(APPLE OR WIN32)
set(SENTRY_DEFAULT_BACKEND "crashpad")
elseif(LINUX)
set(SENTRY_DEFAULT_BACKEND "breakpad")
Expand Down Expand Up @@ -112,12 +115,11 @@ if(NOT CMAKE_BUILD_TYPE)
endif()

# use -O3 when doing `RelWithDebInfo` builds
foreach(lang ASM C CXX)
# unix-like syntax
string(REPLACE "-O2" "-O3" CMAKE_${lang}_FLAGS_RELWITHDEBINFO "${CMAKE_${lang}_FLAGS_RELWITHDEBINFO}")
# windows-like syntax
string(REPLACE "/O2" "/O3" CMAKE_${lang}_FLAGS_RELWITHDEBINFO "${CMAKE_${lang}_FLAGS_RELWITHDEBINFO}")
endforeach()
if(NOT MSVC)
foreach(lang ASM C CXX)
string(REPLACE "-O2" "-O3" CMAKE_${lang}_FLAGS_RELWITHDEBINFO "${CMAKE_${lang}_FLAGS_RELWITHDEBINFO}")
endforeach()
endif()

# https://gitlab.kitware.com/cmake/cmake/issues/20256
if(APPLE)
Expand Down Expand Up @@ -149,23 +151,6 @@ function(sentry_target_sources_cwd TARGET)
endforeach()
endfunction()

#respect CMAKE_SYSTEM_VERSION
if(WIN32)
if(${CMAKE_SYSTEM_VERSION} EQUAL 10)
add_definitions(-D_WIN32_WINNT=0x0A00)
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.3)
add_definitions(-D_WIN32_WINNT=0x0603)
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.2)
add_definitions(-D_WIN32_WINNT=0x0602)
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.1)
add_definitions(-D_WIN32_WINNT=0x0601)
elseif(${CMAKE_SYSTEM_VERSION} EQUAL 6.0)
add_definitions(-D_WIN32_WINNT=0x0600)
else()
add_definitions(-D_WIN32_WINNT=0x0501)
endif()
endif()

# ===== sentry library =====

add_library(sentry "${PROJECT_SOURCE_DIR}/vendor/mpack.c")
Expand Down Expand Up @@ -223,12 +208,43 @@ target_include_directories(sentry PUBLIC
target_link_libraries(sentry INTERFACE
"$<$<OR:$<PLATFORM_ID:Linux>,$<PLATFORM_ID:Android>>:-Wl,--build-id=sha1>")

#respect CMAKE_SYSTEM_VERSION
if(WIN32)
if(${CMAKE_SYSTEM_VERSION} MATCHES "^10")
target_compile_definitions(sentry PRIVATE "-D_WIN32_WINNT=0x0A00")
target_link_libraries(sentry PRIVATE pathcch)
elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^6.3")
target_compile_definitions(sentry PRIVATE "-D_WIN32_WINNT=0x0603")
target_link_libraries(sentry PRIVATE pathcch)
elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^6.2")
target_compile_definitions(sentry PRIVATE "-D_WIN32_WINNT=0x0602")
target_link_libraries(sentry PRIVATE pathcch)
elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^6.1")
target_compile_definitions(sentry PRIVATE "-D_WIN32_WINNT=0x0601")
target_link_libraries(sentry PRIVATE shlwapi)
elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^6.0")
target_compile_definitions(sentry PRIVATE "-D_WIN32_WINNT=0x0600")
target_link_libraries(sentry PRIVATE shlwapi)
elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^5.2")
target_compile_definitions(sentry PRIVATE "-D_WIN32_WINNT=0x0502")
target_link_libraries(sentry PRIVATE shlwapi)
elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^5.1")
target_compile_definitions(sentry PRIVATE "-D_WIN32_WINNT=0x0501")
target_link_libraries(sentry PRIVATE shlwapi)
endif()

#crashpad does not support Windows XP toolset
if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$" AND SENTRY_BACKEND_CRASHPAD)
message(FATAL_ERROR "MSVC XP toolset does not support Crashpad")
endif()
endif()

if(LINUX)
target_link_libraries(sentry PRIVATE pthread dl)
elseif(MSVC)
target_link_libraries(sentry PRIVATE dbghelp pathcch shlwapi)
target_link_libraries(sentry PRIVATE dbghelp)
elseif(MINGW)
target_link_libraries(sentry PRIVATE dbghelp shlwapi) #unimplemented pathcch
target_link_libraries(sentry PRIVATE dbghelp)
target_compile_options(sentry PRIVATE
-Wno-unused-variable
-Wno-unused-parameter
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,21 @@ using `cmake -D BUILD_SHARED_LIBS=OFF ..`.
- `BUILD_SHARED_LIBS` (Default: ON):
By default, `sentry` is built as a shared library. Setting this option to
`OFF` will build `sentry` as a static library instead.

- `CMAKE_SYSTEM_VERSION`: (Default: depending on Windows SDK version):
Sets up a minimal version of Windows where sentry-native can be guaranteed to run.
Possible values:
- `5.1` (Windows XP)
- `5.2` (Windows XP 64-bit / Server 2003 / Server 2003 R2)
- `6.0` (Windows Vista / Server 2008)
- `6.1` (Windows 7 / Server 2008 R2)
- `6.2` (Windows 8.0 / Server 2012)
- `6.3` (Windows 8.1 / Server 2012 R2)
- `10` (Windows 10 / Server 2016 / Server 2019)

​ For Windows versions below than `6.0` it is also necessary to use XP toolchain in case of MSVC compiler (pass `-T v141_xp` to CMake command line).
Also, you are not able to use Crashpad with XP toolchains, no crashes will be handled at all.

- `SENTRY_TRANSPORT` (Default: depending on platform):
Sentry can use different http libraries to send reports to the server.
- **curl**: This uses the `curl` library for HTTP handling. This requires
Expand Down
2 changes: 2 additions & 0 deletions src/path/sentry_path_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
#include "sentry_string.h"
#include "sentry_utils.h"

#if _WIN32_WINNT >= 0x0602
#include <pathcch.h>
#endif
#include <shellapi.h>
#include <shlobj.h>
#include <shlwapi.h>
Expand Down
145 changes: 128 additions & 17 deletions src/sentry_sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,107 @@

// define a recursive mutex for all platforms
#ifdef SENTRY_PLATFORM_WINDOWS
# include <synchapi.h>
# if _WIN32_WINNT >= 0x0600
# include <synchapi.h>
# endif
# include <winnt.h>

#if _WIN32_WINNT < 0x0600

#define INIT_ONCE_STATIC_INIT {0}

typedef union {
PVOID Ptr;
} INIT_ONCE, *PINIT_ONCE;

typedef struct {
HANDLE Semaphore;
HANDLE ContinueEvent;
LONG Waiters;
LONG Target;
} CONDITION_VARIABLE_PREVISTA, *PCONDITION_VARIABLE_PREVISTA;

typedef BOOL(WINAPI *PINIT_ONCE_FN)(
PINIT_ONCE InitOnce, PVOID Parameter, PVOID *Context);

inline BOOL
InitOnceExecuteOnce(
PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter, LPVOID *Context)
{
for (;;) {
switch ((ULONG_PTR)InitOnce->Ptr) {
case 0: // not started
if (InterlockedCompareExchangePointer(&InitOnce->Ptr, (PVOID)1, (PVOID)0) != 0) {
break;
}
if (InitFn(InitOnce, Parameter, Context)) {
InitOnce->Ptr = (PVOID)2;
return TRUE;
}
InitOnce->Ptr = 0;
return FALSE;
case 1: // in progress
Sleep(1);
break;
case 2: // completed
return TRUE;
default: // unexpecterd value
return FALSE;
}
}
}

inline void
InitializeConditionVariable_PREVISTA(PCONDITION_VARIABLE_PREVISTA ConditionVariable)
{
ConditionVariable->Target = 0;
ConditionVariable->Waiters = 0;
ConditionVariable->Semaphore = CreateSemaphoreW(NULL, 0, MAXLONG, NULL);
ConditionVariable->ContinueEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
}


inline BOOL
SleepConditionVariableCS_PREVISTA(
PCONDITION_VARIABLE_PREVISTA cv, PCRITICAL_SECTION cs, DWORD timeout)
{
DWORD result = 0;

LeaveCriticalSection(cs);

InterlockedIncrement((LONG *)&cv->Waiters);
result = WaitForSingleObject(cv->Semaphore, timeout);

// send event only on target
if (InterlockedDecrement((LONG *)&cv->Waiters) == cv->Target
&& result == WAIT_OBJECT_0) {
SetEvent(cv->ContinueEvent);
}

EnterCriticalSection(cs);

return result;
}

inline void
WakeConditionVariable_PREVISTA(PCONDITION_VARIABLE_PREVISTA ConditionVariable)
{
if (!ConditionVariable) {
return;
}

if (ConditionVariable->Waiters == 0) {
return;
}

//set target for continue event, alert it on first occurance
ConditionVariable->Target = ConditionVariable->Waiters - 1;

SignalObjectAndWait(ConditionVariable->Semaphore, ConditionVariable->ContinueEvent, INFINITE, FALSE);
}

#endif /* _WIN32_WINNT < 0x0600 */

struct sentry__winmutex_s {
INIT_ONCE init_once;
CRITICAL_SECTION critical_section;
Expand All @@ -32,28 +131,41 @@ sentry__winmutex_lock(struct sentry__winmutex_s *mutex)

typedef HANDLE sentry_threadid_t;
typedef struct sentry__winmutex_s sentry_mutex_t;
typedef CONDITION_VARIABLE sentry_cond_t;
# define SENTRY__MUTEX_INIT \
{ \
INIT_ONCE_STATIC_INIT, { 0 } \
}
# define sentry__mutex_lock(Lock) sentry__winmutex_lock(Lock)
# define sentry__mutex_unlock(Lock) \
LeaveCriticalSection(&(Lock)->critical_section)
# define SENTRY__COND_INIT \
{ \
0 \
}
# define sentry__cond_wait_timeout(CondVar, Lock, Timeout) \
SleepConditionVariableCS(CondVar, &(Lock)->critical_section, Timeout)
# define sentry__cond_wait(CondVar, Lock) \
sentry__cond_wait_timeout(CondVar, Lock, INFINITE)
# define sentry__cond_wake WakeConditionVariable

# define sentry__thread_spawn(ThreadId, Func, Data) \
(*ThreadId = CreateThread(NULL, 0, Func, Data, 0, NULL), \
*ThreadId == INVALID_HANDLE_VALUE ? 1 : 0)
# define sentry__thread_join(ThreadId) \
WaitForSingleObject(ThreadId, INFINITE)

# if _WIN32_WINNT < 0x0600
typedef CONDITION_VARIABLE_PREVISTA sentry_cond_t;
# define sentry__cond_init(CondVar) \
InitializeConditionVariable_PREVISTA(CondVar)
# define sentry__cond_wake WakeConditionVariable_PREVISTA
# define sentry__cond_wait_timeout(CondVar, Lock, Timeout) \
SleepConditionVariableCS_PREVISTA( \
CondVar, &(Lock)->critical_section, Timeout)
# else
typedef CONDITION_VARIABLE sentry_cond_t;
# define sentry__cond_init(CondVar) \
InitializeConditionVariable(CondVar)
# define sentry__cond_wake WakeConditionVariable
# define sentry__cond_wait_timeout(CondVar, Lock, Timeout) \
SleepConditionVariableCS( \
CondVar, &(Lock)->critical_section, Timeout)
# endif
# define sentry__cond_wait(CondVar, Lock) \
sentry__cond_wait_timeout(CondVar, Lock, INFINITE)


#else
# include <errno.h>
# include <pthread.h>
Expand Down Expand Up @@ -97,7 +209,11 @@ typedef pthread_cond_t sentry_cond_t;
pthread_mutex_unlock(Mutex); \
} \
} while (0)
# define SENTRY__COND_INIT PTHREAD_COND_INITIALIZER
# define sentry__cond_init(CondVar) \
do { \
sentry_cond_t tmp = PTHREAD_COND_INITIALIZER; \
*(CondVar) = tmp; \
} while (0)
# define sentry__cond_wait(Cond, Mutex) \
do { \
if (sentry__block_for_signal_handler()) { \
Expand Down Expand Up @@ -133,11 +249,6 @@ sentry__cond_wait_timeout(
sentry_mutex_t tmp = SENTRY__MUTEX_INIT; \
*(Mutex) = tmp; \
} while (0)
#define sentry__cond_init(CondVar) \
do { \
sentry_cond_t tmp = SENTRY__COND_INIT; \
*(CondVar) = tmp; \
} while (0)

static inline int
sentry__atomic_fetch_and_add(volatile int *val, int diff)
Expand Down
9 changes: 5 additions & 4 deletions src/transports/sentry_transport_winhttp.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,16 @@ winhttp_transport_start(sentry_transport_t *transport)
= WinHttpOpen(state->user_agent, WINHTTP_ACCESS_TYPE_NAMED_PROXY,
state->proxy, WINHTTP_NO_PROXY_BYPASS, 0);
} else {
#if _WIN32_WINNT >= 0x0603
state->session = WinHttpOpen(state->user_agent,
WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS, 0);
// On windows 7, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY does
// not work on error we fallback to
// WINHTTP_ACCESS_TYPE_NO_PROXY
#endif
// On windows 8.0 or lower, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY does
// not work on error we fallback to WINHTTP_ACCESS_TYPE_DEFAULT_PROXY
if (!state->session) {
state->session
= WinHttpOpen(state->user_agent, WINHTTP_ACCESS_TYPE_NO_PROXY,
= WinHttpOpen(state->user_agent, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
}
}
Expand Down
24 changes: 24 additions & 0 deletions src/unwinder/sentry_unwinder_dbghelp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,36 @@

#include <dbghelp.h>

#if _WIN32_WINNT && _WIN32_WINNT < 0x0600
typedef WORD(NTAPI *RtlCaptureStackBackTraceProc)(DWORD FramesToSkip,
DWORD FramesToCapture, PVOID *BackTrace, PDWORD BackTraceHash);
#endif

size_t
sentry__unwind_stack_dbghelp(
void *addr, const sentry_ucontext_t *uctx, void **ptrs, size_t max_frames)
{
if (!uctx && !addr) {
#if _WIN32_WINNT && _WIN32_WINNT < 0x0600
HMODULE ntdll = NULL;
RtlCaptureStackBackTraceProc proc = NULL;

if (!(ntdll = LoadLibraryW(L"ntdll.dll"))) {
return 0;
}
if (!(proc = (RtlCaptureStackBackTraceProc)GetProcAddress(ntdll, "RtlCaptureStackBackTrace"))) {
return 0;
}

//sum of frames to skip and frames to captures must be less than 63 for XP/2003
if (max_frames > 61) {
max_frames = 61;
}

return (size_t)proc(1, (DWORD)max_frames, ptrs, 0);
#else
return (size_t)CaptureStackBackTrace(1, (ULONG)max_frames, ptrs, 0);
#endif
}

sentry__init_dbghelp();
Expand Down