Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -103,7 +103,6 @@ sanitizer_symbolizer_internal.h sanitizer_symbolizer_libbacktrace.h sanitizer_symbolizer_mac.h - sanitizer_symbolizer_win.h sanitizer_syscall_generic.inc sanitizer_syscall_linux_x86_64.inc sanitizer_thread_registry.h) Index: lib/sanitizer_common/sanitizer_common.cc =================================================================== --- lib/sanitizer_common/sanitizer_common.cc +++ lib/sanitizer_common/sanitizer_common.cc @@ -344,6 +344,30 @@ return true; } +char *FindPathToBinary(const char *name) { + const char *path = GetEnv("PATH"); + if (!path) + return 0; + uptr name_len = internal_strlen(name); + InternalScopedBuffer buffer(kMaxPathLength); + const char *beg = path; + while (true) { + const char *end = internal_strchrnul(beg, SANITIZER_WINDOWS ? ';' : ':'); + uptr prefix_len = end - beg; + if (prefix_len + name_len + 2 <= kMaxPathLength) { + internal_memcpy(buffer.data(), beg, prefix_len); + buffer[prefix_len] = '/'; + internal_memcpy(&buffer[prefix_len + 1], name, name_len); + buffer[prefix_len + 1 + name_len] = '\0'; + if (FileExists(buffer.data())) + return internal_strdup(buffer.data()); + } + if (*end == '\0') break; + beg = end + 1; + } + return nullptr; +} + static char binary_name_cache_str[kMaxPathLength]; static char process_name_cache_str[kMaxPathLength]; Index: lib/sanitizer_common/sanitizer_posix.cc =================================================================== --- lib/sanitizer_common/sanitizer_posix.cc +++ lib/sanitizer_common/sanitizer_posix.cc @@ -296,30 +296,6 @@ return GetEnv("PWD"); } -char *FindPathToBinary(const char *name) { - const char *path = GetEnv("PATH"); - if (!path) - return 0; - uptr name_len = internal_strlen(name); - InternalScopedBuffer buffer(kMaxPathLength); - const char *beg = path; - while (true) { - const char *end = internal_strchrnul(beg, ':'); - uptr prefix_len = end - beg; - if (prefix_len + name_len + 2 <= kMaxPathLength) { - internal_memcpy(buffer.data(), beg, prefix_len); - buffer[prefix_len] = '/'; - internal_memcpy(&buffer[prefix_len + 1], name, name_len); - buffer[prefix_len + 1 + name_len] = '\0'; - if (FileExists(buffer.data())) - return internal_strdup(buffer.data()); - } - if (*end == '\0') break; - beg = end + 1; - } - return 0; -} - bool IsPathSeparator(const char c) { return c == '/'; } Index: lib/sanitizer_common/sanitizer_symbolizer.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer.cc +++ lib/sanitizer_common/sanitizer_symbolizer.cc @@ -110,4 +110,246 @@ sym_->end_hook_(); } +// For now we assume the following protocol: +// For each request of the form +// +// passed to STDIN, external symbolizer prints to STDOUT response: +// +// :: +// +// :: +// ... +// +class LLVMSymbolizerProcess : public SymbolizerProcess { + public: + explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {} + + private: + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { + // Empty line marks the end of llvm-symbolizer output. + return length >= 2 && buffer[length - 1] == '\n' && + buffer[length - 2] == '\n'; + } + + void GetArgV(const char *path_to_binary, + const char *(&argv)[kArgVMax]) const override { +#if defined(__x86_64h__) + const char* const kSymbolizerArch = "--default-arch=x86_64h"; +#elif defined(__x86_64__) + const char* const kSymbolizerArch = "--default-arch=x86_64"; +#elif defined(__i386__) + const char* const kSymbolizerArch = "--default-arch=i386"; +#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__) + const char* const kSymbolizerArch = "--default-arch=powerpc64"; +#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) + const char* const kSymbolizerArch = "--default-arch=powerpc64le"; +#else + const char* const kSymbolizerArch = "--default-arch=unknown"; +#endif + + const char *const inline_flag = common_flags()->symbolize_inline_frames + ? "--inlining=true" + : "--inlining=false"; + int i = 0; + argv[i++] = path_to_binary; + argv[i++] = inline_flag; + argv[i++] = kSymbolizerArch; + argv[i++] = nullptr; + } +}; + +LLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator) + : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {} + +// Parses one or more two-line strings in the following format: +// +// :[:] +// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of +// them use the same output format. +bool ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) { + bool top_frame = true; + SymbolizedStack *last = res; + while (true) { + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + InternalFree(function_name); + break; + } + SymbolizedStack *cur; + if (top_frame) { + cur = res; + top_frame = false; + } else { + cur = SymbolizedStack::New(res->info.address); + cur->info.FillModuleInfo(res->info.module, res->info.module_offset); + last->next = cur; + last = cur; + } + + AddressInfo *info = &cur->info; + info->function = function_name; + // Parse :: buffer. The file path may contain colons on + // Windows, so extract tokens from the right hand side first. + char *file_line_info = 0; + str = ExtractToken(str, "\n", &file_line_info); + CHECK(file_line_info); + // Parse the last :, which must be there. + char *last_colon = internal_strrchr(file_line_info, ':'); + CHECK(last_colon); + int line_or_column; + ExtractInt(last_colon + 1, "", &line_or_column); + *last_colon = '\0'; + last_colon = internal_strrchr(file_line_info, ':'); + if (last_colon && IsDigit(last_colon[1])) { + // If the colon before this one is followed by a digit, it must be the line + // number, and the previous parsed number was a column. + info->line = internal_atoll(last_colon + 1); + info->column = line_or_column; + *last_colon = '\0'; + } else { + // Otherwise, we have line info but no column info. + info->line = line_or_column; + info->column = 0; + } + ExtractToken(file_line_info, "", &info->file); + InternalFree(file_line_info); + + // Functions and filenames can be "??", in which case we write 0 + // to address info to mark that names are unknown. + if (0 == internal_strcmp(info->function, "??")) { + InternalFree(info->function); + info->function = 0; + } + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; + } + } + + // If we got a function name, we probably had debug info. + return last->info.function != nullptr; +} + +// Parses a two-line string in the following format: +// +// +// Used by LLVMSymbolizer and InternalSymbolizer. +void ParseSymbolizeDataOutput(const char *str, DataInfo *info) { + str = ExtractToken(str, "\n", &info->name); + str = ExtractUptr(str, " ", &info->start); + str = ExtractUptr(str, "\n", &info->size); +} + +bool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module, + stack->info.module_offset)) { + return ParseSymbolizePCOutput(buf, stack); + } + return false; +} + +bool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + if (const char *buf = + SendCommand(/*is_data*/ true, info->module, info->module_offset)) { + ParseSymbolizeDataOutput(buf, info); + info->start += (addr - info->module_offset); // Add the base address. + return true; + } + return false; +} + +const char *LLVMSymbolizer::SendCommand(bool is_data, const char *module_name, + uptr module_offset) { + CHECK(module_name); + internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", + is_data ? "DATA " : "", module_name, module_offset); + return symbolizer_process_->SendCommand(buffer_); +} + +SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty) + : path_(path), + input_fd_(kInvalidFd), + output_fd_(kInvalidFd), + times_restarted_(0), + failed_to_start_(false), + reported_invalid_path_(false), + use_forkpty_(use_forkpty) { + CHECK(path_); + CHECK_NE(path_[0], '\0'); +} + +const char *SymbolizerProcess::SendCommand(const char *command) { + for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { + // Start or restart symbolizer if we failed to send command to it. + if (const char *res = SendCommandImpl(command)) + return res; + Restart(); + } + if (!failed_to_start_) { + Report("WARNING: Failed to use and restart external symbolizer!\n"); + failed_to_start_ = true; + } + return 0; +} + +const char *SymbolizerProcess::SendCommandImpl(const char *command) { + if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) + return 0; + if (!WriteToSymbolizer(command, internal_strlen(command))) + return 0; + if (!ReadFromSymbolizer(buffer_, kBufferSize)) + return 0; + return buffer_; +} + +bool SymbolizerProcess::Restart() { + if (input_fd_ != kInvalidFd) + CloseFile(input_fd_); + if (output_fd_ != kInvalidFd) + CloseFile(output_fd_); + return StartSymbolizerSubprocess(); +} + +bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) { + if (max_length == 0) + return true; + uptr read_len = 0; + while (true) { + uptr just_read; + error_t err = 0; + bool success = ReadFromFile(input_fd_, buffer + read_len, + max_length - read_len - 1, &just_read, &err); + // We can't read 0 bytes, as we don't expect external symbolizer to close + // its stdout. + if (!success || just_read == 0 || just_read == (uptr)-1) { + Report("WARNING: Can't read from symbolizer at fd %d (error %d)\n", + input_fd_, err); + return false; + } + read_len += just_read; + if (ReachedEndOfOutput(buffer, read_len)) + break; + } + buffer[read_len] = '\0'; + return true; +} + +bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) { + if (length == 0) + return true; + uptr write_len = 0; + error_t err = 0; + bool success = WriteToFile(output_fd_, buffer, length, &write_len, &err); + if (!success || write_len == 0 || write_len == (uptr)-1) { + Report("WARNING: Can't write to symbolizer at fd %d (error %d)\n", + output_fd_, err); + Report("WARNING: success %d length %d write_len %d\n", success, length, write_len); + return false; + } + return true; +} + } // namespace __sanitizer Index: lib/sanitizer_common/sanitizer_symbolizer_internal.h =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_internal.h +++ lib/sanitizer_common/sanitizer_symbolizer_internal.h @@ -74,24 +74,30 @@ explicit SymbolizerProcess(const char *path, bool use_forkpty = false); const char *SendCommand(const char *command); - private: - bool Restart(); - const char *SendCommandImpl(const char *command); - bool ReadFromSymbolizer(char *buffer, uptr max_length); - bool WriteToSymbolizer(const char *buffer, uptr length); - bool StartSymbolizerSubprocess(); - + protected: virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const { UNIMPLEMENTED(); } - virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const { + /// The maximum number of arguments required to invoke a tool process. + enum { kArgVMax = 6 }; + + /// Fill in an argv array to invoke the child process. + virtual void GetArgV(const char *path_to_binary, + const char *(&argv)[kArgVMax]) const { UNIMPLEMENTED(); } + private: + bool Restart(); + const char *SendCommandImpl(const char *command); + bool ReadFromSymbolizer(char *buffer, uptr max_length); + bool WriteToSymbolizer(const char *buffer, uptr length); + bool StartSymbolizerSubprocess(); + const char *path_; - int input_fd_; - int output_fd_; + fd_t input_fd_; + fd_t output_fd_; static const uptr kBufferSize = 16 * 1024; char buffer_[kBufferSize]; @@ -104,6 +110,41 @@ bool use_forkpty_; }; +class LLVMSymbolizerProcess; + +// This tool invokes llvm-symbolizer in a subprocess. It should be as portable +// as the llvm-symbolizer tool is. +class LLVMSymbolizer : public SymbolizerTool { + public: + explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator); + + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + + bool SymbolizeData(uptr addr, DataInfo *info) override; + + private: + const char *SendCommand(bool is_data, const char *module_name, + uptr module_offset); + + LLVMSymbolizerProcess *symbolizer_process_; + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; +}; + +// Parses one or more two-line strings in the following format: +// +// :[:] +// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of +// them use the same output format. Returns true if any useful debug +// information was found. +bool ParseSymbolizePCOutput(const char *str, SymbolizedStack *res); + +// Parses a two-line string in the following format: +// +// +// Used by LLVMSymbolizer and InternalSymbolizer. +void ParseSymbolizeDataOutput(const char *str, DataInfo *info); + } // namespace __sanitizer #endif // SANITIZER_SYMBOLIZER_INTERNAL_H Index: lib/sanitizer_common/sanitizer_symbolizer_mac.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_mac.cc +++ lib/sanitizer_common/sanitizer_symbolizer_mac.cc @@ -44,28 +44,33 @@ class AtosSymbolizerProcess : public SymbolizerProcess { public: explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid) - : SymbolizerProcess(path, /*use_forkpty*/ true), - parent_pid_(parent_pid) {} + : SymbolizerProcess(path, /*use_forkpty*/ true) { + // Put the string command line argument in the object so that it outlives + // the call to GetArgV. + internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid_); + } private: bool ReachedEndOfOutput(const char *buffer, uptr length) const override { return (length >= 1 && buffer[length - 1] == '\n'); } - void ExecuteWithDefaultArgs(const char *path_to_binary) const override { - char pid_str[16]; - internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_); + void GetArgV(const char *path_to_binary, + const char *(&argv)[kArgVMax]) const override { + int i = 0; + argv[i++] = path_to_binary; + argv[i++] = "-p"; + argv[i++] = &pid_str_[0]; if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) { // On Mavericks atos prints a deprecation warning which we suppress by // passing -d. The warning isn't present on other OSX versions, even the // newer ones. - execl(path_to_binary, path_to_binary, "-p", pid_str, "-d", (char *)0); - } else { - execl(path_to_binary, path_to_binary, "-p", pid_str, (char *)0); + argv[i++] = "-d"; } + argv[i++] = nullptr; } - pid_t parent_pid_; + char pid_str_[16]; }; static const char *kAtosErrorMessages[] = { Index: lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -53,150 +53,6 @@ return name; } -// Parses one or more two-line strings in the following format: -// -// :[:] -// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of -// them use the same output format. -static void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) { - bool top_frame = true; - SymbolizedStack *last = res; - while (true) { - char *function_name = 0; - str = ExtractToken(str, "\n", &function_name); - CHECK(function_name); - if (function_name[0] == '\0') { - // There are no more frames. - InternalFree(function_name); - break; - } - SymbolizedStack *cur; - if (top_frame) { - cur = res; - top_frame = false; - } else { - cur = SymbolizedStack::New(res->info.address); - cur->info.FillModuleInfo(res->info.module, res->info.module_offset); - last->next = cur; - last = cur; - } - - AddressInfo *info = &cur->info; - info->function = function_name; - // Parse :: buffer. - char *file_line_info = 0; - str = ExtractToken(str, "\n", &file_line_info); - CHECK(file_line_info); - const char *line_info = ExtractToken(file_line_info, ":", &info->file); - line_info = ExtractInt(line_info, ":", &info->line); - line_info = ExtractInt(line_info, "", &info->column); - InternalFree(file_line_info); - - // Functions and filenames can be "??", in which case we write 0 - // to address info to mark that names are unknown. - if (0 == internal_strcmp(info->function, "??")) { - InternalFree(info->function); - info->function = 0; - } - if (0 == internal_strcmp(info->file, "??")) { - InternalFree(info->file); - info->file = 0; - } - } -} - -// Parses a two-line string in the following format: -// -// -// Used by LLVMSymbolizer and InternalSymbolizer. -static void ParseSymbolizeDataOutput(const char *str, DataInfo *info) { - str = ExtractToken(str, "\n", &info->name); - str = ExtractUptr(str, " ", &info->start); - str = ExtractUptr(str, "\n", &info->size); -} - -// For now we assume the following protocol: -// For each request of the form -// -// passed to STDIN, external symbolizer prints to STDOUT response: -// -// :: -// -// :: -// ... -// -class LLVMSymbolizerProcess : public SymbolizerProcess { - public: - explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {} - - private: - bool ReachedEndOfOutput(const char *buffer, uptr length) const override { - // Empty line marks the end of llvm-symbolizer output. - return length >= 2 && buffer[length - 1] == '\n' && - buffer[length - 2] == '\n'; - } - - void ExecuteWithDefaultArgs(const char *path_to_binary) const override { -#if defined(__x86_64h__) - const char* const kSymbolizerArch = "--default-arch=x86_64h"; -#elif defined(__x86_64__) - const char* const kSymbolizerArch = "--default-arch=x86_64"; -#elif defined(__i386__) - const char* const kSymbolizerArch = "--default-arch=i386"; -#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__) - const char* const kSymbolizerArch = "--default-arch=powerpc64"; -#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__) - const char* const kSymbolizerArch = "--default-arch=powerpc64le"; -#else - const char* const kSymbolizerArch = "--default-arch=unknown"; -#endif - - const char *const inline_flag = common_flags()->symbolize_inline_frames - ? "--inlining=true" - : "--inlining=false"; - execl(path_to_binary, path_to_binary, inline_flag, kSymbolizerArch, - (char *)0); - } -}; - -class LLVMSymbolizer : public SymbolizerTool { - public: - explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator) - : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {} - - bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { - if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module, - stack->info.module_offset)) { - ParseSymbolizePCOutput(buf, stack); - return true; - } - return false; - } - - bool SymbolizeData(uptr addr, DataInfo *info) override { - if (const char *buf = - SendCommand(/*is_data*/ true, info->module, info->module_offset)) { - ParseSymbolizeDataOutput(buf, info); - info->start += (addr - info->module_offset); // Add the base address. - return true; - } - return false; - } - - private: - const char *SendCommand(bool is_data, const char *module_name, - uptr module_offset) { - CHECK(module_name); - internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", - is_data ? "DATA " : "", module_name, module_offset); - return symbolizer_process_->SendCommand(buffer_); - } - - LLVMSymbolizerProcess *symbolizer_process_; - static const uptr kBufferSize = 16 * 1024; - char buffer_[kBufferSize]; -}; - class Addr2LineProcess : public SymbolizerProcess { public: Addr2LineProcess(const char *path, const char *module_name) @@ -217,8 +73,13 @@ return false; } - void ExecuteWithDefaultArgs(const char *path_to_binary) const override { - execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0); + void GetArgV(const char *path_to_binary, + const char *(&argv)[kArgVMax]) const override { + int i = 0; + argv[i++] = path_to_binary; + argv[i++] = "-Cfe"; + argv[i++] = module_name_; + argv[i++] = nullptr; } const char *module_name_; // Owned, leaked. Index: lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc +++ lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc @@ -27,82 +27,6 @@ namespace __sanitizer { -SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty) - : path_(path), - input_fd_(kInvalidFd), - output_fd_(kInvalidFd), - times_restarted_(0), - failed_to_start_(false), - reported_invalid_path_(false), - use_forkpty_(use_forkpty) { - CHECK(path_); - CHECK_NE(path_[0], '\0'); -} - -const char *SymbolizerProcess::SendCommand(const char *command) { - for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { - // Start or restart symbolizer if we failed to send command to it. - if (const char *res = SendCommandImpl(command)) - return res; - Restart(); - } - if (!failed_to_start_) { - Report("WARNING: Failed to use and restart external symbolizer!\n"); - failed_to_start_ = true; - } - return 0; -} - -bool SymbolizerProcess::Restart() { - if (input_fd_ != kInvalidFd) - internal_close(input_fd_); - if (output_fd_ != kInvalidFd) - internal_close(output_fd_); - return StartSymbolizerSubprocess(); -} - -const char *SymbolizerProcess::SendCommandImpl(const char *command) { - if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) - return 0; - if (!WriteToSymbolizer(command, internal_strlen(command))) - return 0; - if (!ReadFromSymbolizer(buffer_, kBufferSize)) - return 0; - return buffer_; -} - -bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) { - if (max_length == 0) - return true; - uptr read_len = 0; - while (true) { - uptr just_read = internal_read(input_fd_, buffer + read_len, - max_length - read_len - 1); - // We can't read 0 bytes, as we don't expect external symbolizer to close - // its stdout. - if (just_read == 0 || just_read == (uptr)-1) { - Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); - return false; - } - read_len += just_read; - if (ReachedEndOfOutput(buffer, read_len)) - break; - } - buffer[read_len] = '\0'; - return true; -} - -bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) { - if (length == 0) - return true; - uptr write_len = internal_write(output_fd_, buffer, length); - if (write_len == 0 || write_len == (uptr)-1) { - Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); - return false; - } - return true; -} - bool SymbolizerProcess::StartSymbolizerSubprocess() { if (!FileExists(path_)) { if (!reported_invalid_path_) { @@ -125,7 +49,9 @@ return false; } else if (pid == 0) { // Child subprocess. - ExecuteWithDefaultArgs(path_); + const char *argv[kArgVMax]; + GetArgV(path_, argv); + execv(path_, const_cast(&argv[0])); internal__exit(1); } @@ -200,7 +126,9 @@ internal_close(infd[1]); for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd); - ExecuteWithDefaultArgs(path_); + const char *argv[kArgVMax]; + GetArgV(path_, argv); + execv(path_, const_cast(&argv[0])); internal__exit(1); } Index: lib/sanitizer_common/sanitizer_symbolizer_win.h =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_win.h +++ /dev/null @@ -1,31 +0,0 @@ -//===-- sanitizer_symbolizer_win.h ------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Header file for the Windows symbolizer tool. -// -//===----------------------------------------------------------------------===// -#ifndef SANITIZER_SYMBOLIZER_WIN_H -#define SANITIZER_SYMBOLIZER_WIN_H - -#include "sanitizer_symbolizer_internal.h" - -namespace __sanitizer { - -class WinSymbolizerTool : public SymbolizerTool { - public: - bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; - bool SymbolizeData(uptr addr, DataInfo *info) override { - return false; - } - const char *Demangle(const char *name) override; -}; - -} // namespace __sanitizer - -#endif // SANITIZER_SYMBOLIZER_WIN_H Index: lib/sanitizer_common/sanitizer_symbolizer_win.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_win.cc +++ lib/sanitizer_common/sanitizer_symbolizer_win.cc @@ -18,13 +18,21 @@ #include #pragma comment(lib, "dbghelp.lib") -#include "sanitizer_symbolizer_win.h" #include "sanitizer_symbolizer_internal.h" namespace __sanitizer { namespace { +class WinSymbolizerTool : public SymbolizerTool { + public: + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override { + return false; + } + const char *Demangle(const char *name) override; +}; + bool is_dbghelp_initialized = false; bool TrySymInitialize() { @@ -115,7 +123,9 @@ frame->info.file = internal_strdup(line_info.FileName); frame->info.line = line_info.LineNumber; } - return true; + // Only consider this a successful symbolization attempt if we got file info. + // Otherwise, try llvm-symbolizer. + return got_fileline; } const char *WinSymbolizerTool::Demangle(const char *name) { @@ -137,10 +147,118 @@ // Do nothing. } +namespace { +struct ScopedHandle { + ScopedHandle() : h_(nullptr) {} + explicit ScopedHandle(HANDLE h) : h_(h) {} + ~ScopedHandle() { + if (h_) + ::CloseHandle(h_); + } + HANDLE get() { return h_; } + HANDLE *receive() { return &h_; } + HANDLE release() { + HANDLE h = h_; + h_ = nullptr; + return h; + } + HANDLE h_; +}; +} + +bool SymbolizerProcess::StartSymbolizerSubprocess() { + // Create inherited pipes for stdin and stdout. + ScopedHandle stdin_read, stdin_write; + ScopedHandle stdout_read, stdout_write; + SECURITY_ATTRIBUTES attrs; + attrs.nLength = sizeof(SECURITY_ATTRIBUTES); + attrs.bInheritHandle = TRUE; + attrs.lpSecurityDescriptor = nullptr; + if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) || + !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) + return false; + + // Don't inherit the writing end of stdin or the reading end of stdout. + if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) || + !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) + return false; + + // Compute the command line. Wrap double quotes around everything. + const char *argv[kArgVMax]; + GetArgV(path_, argv); + InternalScopedString command_line(kMaxPathLength * 3); + for (int i = 0; argv[i]; i++) { + const char *arg = argv[i]; + int arglen = internal_strlen(arg); + // Check that tool command lines are simple and that complete escaping is + // unnecessary. + CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported"); + CHECK(arglen > 0 && arg[arglen - 1] && + "args ending in backslash and empty args unsupported"); + command_line.append("\"%s\" ", arg); + } + VReport(3, "Launching symbolizer command: %s\n", command_line.data()); + + // Launch llvm-symbolizer with stdin and stdout redirected. + STARTUPINFOA si; + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdInput = stdin_read.get(); + si.hStdOutput = stdout_write.get(); + PROCESS_INFORMATION pi; + memset(&pi, 0, sizeof(pi)); + if (!CreateProcessA(path_, // Executable + command_line.data(), // Command line + nullptr, // Process handle not inheritable + nullptr, // Thread handle not inheritable + TRUE, // Set handle inheritance to TRUE + 0, // Creation flags + nullptr, // Use parent's environment block + nullptr, // Use parent's starting directory + &si, &pi)) { + VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n", + SanitizerToolName, path_, GetLastError()); + return false; + } + + // Process creation succeeded, so transfer handle ownership into the fields. + input_fd_ = stdout_read.release(); + output_fd_ = stdin_write.release(); + + // The llvm-symbolizer process is responsible for quitting itself when the + // stdin pipe is closed, so we don't need these handles. Close them to prevent + // leaks. If we ever want to try to kill the symbolizer process from the + // parent, we'll want to hang on to these handles. + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return true; +} + Symbolizer *Symbolizer::PlatformInit() { IntrusiveList list; list.clear(); + + // Add llvm-symbolizer in case the binary has dwarf. + const char *user_path = common_flags()->external_symbolizer_path; + const char *path = + user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe"); + if (path) { + VReport(2, "Using llvm-symbolizer at %spath: %s\n", + user_path ? "user-specified " : "", path); + list.push_back(new(symbolizer_allocator_) + LLVMSymbolizer(path, &symbolizer_allocator_)); + } else { + if (user_path && user_path[0] == '\0') { + VReport(2, "External symbolizer is explicitly disabled.\n"); + } else { + VReport(2, "External symbolizer is not present.\n"); + } + } + + // Add the dbghelp based symbolizer. list.push_back(new(symbolizer_allocator_) WinSymbolizerTool()); + return new(symbolizer_allocator_) Symbolizer(list); } Index: lib/sanitizer_common/sanitizer_win.cc =================================================================== --- lib/sanitizer_common/sanitizer_win.cc +++ lib/sanitizer_common/sanitizer_win.cc @@ -51,7 +51,7 @@ } bool FileExists(const char *filename) { - UNIMPLEMENTED(); + return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES; } uptr internal_getpid() { @@ -292,11 +292,6 @@ UNIMPLEMENTED(); } -char *FindPathToBinary(const char *name) { - // Nothing here for now. - return 0; -} - bool IsPathSeparator(const char c) { return c == '\\' || c == '/'; } @@ -515,21 +510,32 @@ error_t *error_p) { CHECK(fd != kInvalidFd); - if (fd == kStdoutFd) { - fd = GetStdHandle(STD_OUTPUT_HANDLE); - if (fd == 0) fd = kInvalidFd; - } else if (fd == kStderrFd) { - fd = GetStdHandle(STD_ERROR_HANDLE); - if (fd == 0) fd = kInvalidFd; + // Handle null optional parameters. + error_t dummy_error; + error_p = error_p ? error_p : &dummy_error; + uptr dummy_bytes_written; + bytes_written = bytes_written ? bytes_written : &dummy_bytes_written; + + // Initialize output parameters in case we fail. + *error_p = 0; + *bytes_written = 0; + + // Map the conventional Unix fds 1 and 2 to Windows handles. They might be + // closed, in which case this will fail. + if (fd == kStdoutFd || fd == kStderrFd) { + fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); + if (fd == 0) { + *error_p = ERROR_INVALID_HANDLE; + return false; + } } - DWORD internal_bytes_written; - if (fd == kInvalidFd || - WriteFile(fd, buff, buff_size, &internal_bytes_written, 0)) { - if (error_p) *error_p = GetLastError(); + DWORD bytes_written_32; + if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) { + *error_p = GetLastError(); return false; } else { - if (bytes_written) *bytes_written = internal_bytes_written; + *bytes_written = bytes_written_32; return true; } } Index: test/asan/CMakeLists.txt =================================================================== --- test/asan/CMakeLists.txt +++ test/asan/CMakeLists.txt @@ -13,6 +13,20 @@ endif() endmacro() +set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND ASAN_TEST_DEPS asan) + if(WIN32 AND EXISTS ${CMAKE_SOURCE_DIR}/tools/lld) + list(APPEND CFI_TEST_DEPS + lld + ) + set(ASAN_HAS_LLD True) + else() + set(ASAN_HAS_LLD False) + endif() +endif() +set(ASAN_DYNAMIC_TEST_DEPS ${ASAN_TEST_DEPS}) + foreach(arch ${ASAN_SUPPORTED_ARCH}) if(ANDROID) set(ASAN_TEST_TARGET_ARCH ${arch}-android) @@ -55,12 +69,6 @@ endif() endforeach() -set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) -if(NOT COMPILER_RT_STANDALONE_BUILD) - list(APPEND ASAN_TEST_DEPS asan) -endif() -set(ASAN_DYNAMIC_TEST_DEPS ${ASAN_TEST_DEPS}) - # Add unit tests. if(COMPILER_RT_INCLUDE_TESTS) set(ASAN_TEST_DYNAMIC False) Index: test/asan/TestCases/Windows/bitfield_uaf.cc =================================================================== --- test/asan/TestCases/Windows/bitfield_uaf.cc +++ test/asan/TestCases/Windows/bitfield_uaf.cc @@ -14,7 +14,7 @@ s->bf2 = 2; // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] // CHECK: READ of size {{[124]}} at [[ADDR]] -// CHECK: {{#0 .* make_access .*bitfield_uaf.cc}}:[[@LINE-3]] +// CHECK: {{#0 .* make_access.*bitfield_uaf.cc}}:[[@LINE-3]] // CHECK: {{#1 .* main}} } Index: test/asan/TestCases/Windows/demangled_names.cc =================================================================== --- test/asan/TestCases/Windows/demangled_names.cc +++ test/asan/TestCases/Windows/demangled_names.cc @@ -43,8 +43,8 @@ free(buffer); A a(buffer); // CHECK: AddressSanitizer: heap-use-after-free on address [[ADDR:0x[0-9a-f]+]] -// CHECK: foo::bar<42> {{.*}}demangled_names.cc -// CHECK: foo::spam {{.*}}demangled_names.cc -// CHECK: baz {{.*}}demangled_names.cc -// CHECK: A::~A {{.*}}demangled_names.cc +// CHECK: foo::bar<42>{{.*}}demangled_names.cc +// CHECK: foo::spam{{.*}}demangled_names.cc +// CHECK: baz{{.*}}demangled_names.cc +// CHECK: A::~A{{.*}}demangled_names.cc } Index: test/asan/TestCases/Windows/dll_noreturn.cc =================================================================== --- test/asan/TestCases/Windows/dll_noreturn.cc +++ test/asan/TestCases/Windows/dll_noreturn.cc @@ -11,12 +11,12 @@ _exit(1); // CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T0 -// CHECK-NEXT: noreturn_f {{.*}}dll_noreturn.cc:[[@LINE-4]] -// CHECK-NEXT: test_function {{.*}}dll_noreturn.cc -// CHECK-NEXT: main {{.*}}dll_host.cc +// CHECK-NEXT: noreturn_f{{.*}}dll_noreturn.cc:[[@LINE-4]] +// CHECK-NEXT: test_function{{.*}}dll_noreturn.cc +// CHECK-NEXT: main{{.*}}dll_host.cc // // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame -// CHECK-NEXT: noreturn_f {{.*}}dll_noreturn.cc +// CHECK-NEXT: noreturn_f{{.*}}dll_noreturn.cc // CHECK: 'buffer' <== Memory access at offset [[OFFSET]] underflows this variable // CHECK-LABEL: SUMMARY } Index: test/asan/TestCases/Windows/dll_poison_unpoison.cc =================================================================== --- test/asan/TestCases/Windows/dll_poison_unpoison.cc +++ test/asan/TestCases/Windows/dll_poison_unpoison.cc @@ -24,12 +24,12 @@ should_crash(&buffer[96]); // CHECK: AddressSanitizer: use-after-poison on address [[ADDR:0x[0-9a-f]+]] // CHECK-NEXT: WRITE of size 1 at [[ADDR]] thread T0 -// CHECK-NEXT: should_crash {{.*}}\dll_poison_unpoison.cc -// CHECK-NEXT: test_function {{.*}}\dll_poison_unpoison.cc:[[@LINE-4]] +// CHECK-NEXT: should_crash{{.*}}\dll_poison_unpoison.cc +// CHECK-NEXT: test_function{{.*}}\dll_poison_unpoison.cc:[[@LINE-4]] // CHECK-NEXT: main // // CHECK: [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame -// CHECK-NEXT: test_function {{.*}}\dll_poison_unpoison.cc +// CHECK-NEXT: test_function{{.*}}\dll_poison_unpoison.cc // CHECK: 'buffer' <== Memory access at offset [[OFFSET]] is inside this variable return 0; } Index: test/asan/TestCases/Windows/dll_stack_use_after_return.cc =================================================================== --- test/asan/TestCases/Windows/dll_stack_use_after_return.cc +++ test/asan/TestCases/Windows/dll_stack_use_after_return.cc @@ -17,11 +17,11 @@ *x = 42; // CHECK: AddressSanitizer: stack-use-after-return // CHECK: WRITE of size 1 at [[ADDR:.*]] thread T0 -// CHECK-NEXT: test_function {{.*}}dll_stack_use_after_return.cc:[[@LINE-3]] +// CHECK-NEXT: test_function{{.*}}dll_stack_use_after_return.cc:[[@LINE-3]] // CHECK-NEXT: main // // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame -// CHECK-NEXT: #0 {{.*}} foo {{.*}}dll_stack_use_after_return.cc +// CHECK-NEXT: #0 {{.*}} foo{{.*}}dll_stack_use_after_return.cc // CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] is inside this variable return 0; } Index: test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc =================================================================== --- test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc +++ test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc @@ -11,10 +11,10 @@ stack_buffer[subscript] = 42; // CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T1 -// CHECK-NEXT: thread_proc {{.*}}dll_thread_stack_array_left_oob.cc:[[@LINE-3]] +// CHECK-NEXT: thread_proc{{.*}}dll_thread_stack_array_left_oob.cc:[[@LINE-3]] // // CHECK: Address [[ADDR]] is located in stack of thread T1 at offset [[OFFSET:.*]] in frame -// CHECK-NEXT: thread_proc {{.*}}dll_thread_stack_array_left_oob.cc +// CHECK-NEXT: thread_proc{{.*}}dll_thread_stack_array_left_oob.cc // // CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] underflows this variable @@ -25,8 +25,8 @@ int test_function() { HANDLE thr = CreateThread(NULL, 0, thread_proc, NULL, 0, NULL); // CHECK-LABEL: Thread T1 created by T0 here: -// CHECK: test_function {{.*}}dll_thread_stack_array_left_oob.cc:[[@LINE-2]] -// CHECK-NEXT: main {{.*}}dll_host.cc +// CHECK: test_function{{.*}}dll_thread_stack_array_left_oob.cc:[[@LINE-2]] +// CHECK-NEXT: main{{.*}}dll_host.cc // CHECK-LABEL: SUMMARY if (thr == 0) return 1; Index: test/asan/TestCases/Windows/fuse-lld.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Windows/fuse-lld.cc @@ -0,0 +1,23 @@ +// If we have LLD, see that things more or less work. +// +// REQUIRES: lld +// +// FIXME: Use -fuse-ld=lld after the old COFF linker is removed. +// FIXME: Test will fail until we add flags for requesting dwarf or cv. +// RUNX: %clangxx_asan -O2 %s -o %t.exe -fuse-ld=lld -Wl,-debug +// RUN: %clangxx_asan -c -O2 %s -o %t.o -gdwarf +// RUN: lld-link2 %t.o -out:%t.exe -debug -defaultlib:libcmt %asan_lib %asan_cxx_lib +// RUN: not %run %t.exe 2>&1 | FileCheck %s + +#include + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: heap-use-after-free + // CHECK: free + // CHECK: main{{.*}}fuse-lld.cc:[[@LINE-4]]:3 + // CHECK: malloc + // CHECK: main{{.*}}fuse-lld.cc:[[@LINE-7]]:20 +} Index: test/asan/TestCases/Windows/null_deref.cc =================================================================== --- test/asan/TestCases/Windows/null_deref.cc +++ test/asan/TestCases/Windows/null_deref.cc @@ -10,6 +10,6 @@ } int main() { NullDeref((int*)0); - // CHECK: {{ #1 0x.* in main.*null_deref.cc:}}[[@LINE-1]] + // CHECK: {{ #1 0x.* in main.*null_deref.cc:}}[[@LINE-1]]:3 // CHECK: AddressSanitizer can not provide additional info. } Index: test/asan/TestCases/Windows/operator_delete_wrong_argument.cc =================================================================== --- test/asan/TestCases/Windows/operator_delete_wrong_argument.cc +++ test/asan/TestCases/Windows/operator_delete_wrong_argument.cc @@ -7,6 +7,6 @@ int *x = new int[42]; delete (x + 1); // CHECK: AddressSanitizer: attempting free on address which was not malloc()-ed -// CHECK: {{#0 0x.* operator delete }} +// CHECK: {{#0 0x.* operator delete\(void \*\) }} // CHECK: {{#1 .* main .*operator_delete_wrong_argument.cc}}:[[@LINE-3]] } Index: test/asan/TestCases/Windows/operator_new_left_oob.cc =================================================================== --- test/asan/TestCases/Windows/operator_new_left_oob.cc +++ test/asan/TestCases/Windows/operator_new_left_oob.cc @@ -11,7 +11,7 @@ // CHECK: {{#0 .* main .*operator_new_left_oob.cc}}:[[@LINE-3]] // CHECK: [[ADDR]] is located 1 bytes to the left of 1-byte region // CHECK: allocated by thread T0 here: -// CHECK: {{#0 .* operator new }} +// CHECK: {{#0 .* operator new}} // CHECK: {{#1 .* main .*operator_new_left_oob.cc}}:[[@LINE-8]] delete buffer; } Index: test/asan/TestCases/Windows/operator_new_right_oob.cc =================================================================== --- test/asan/TestCases/Windows/operator_new_right_oob.cc +++ test/asan/TestCases/Windows/operator_new_right_oob.cc @@ -11,7 +11,7 @@ // CHECK: {{#0 .* main .*operator_new_right_oob.cc}}:[[@LINE-3]] // CHECK: [[ADDR]] is located 0 bytes to the right of 1-byte region // CHECK: allocated by thread T0 here: -// CHECK: {{#0 .* operator new }} +// CHECK: {{#0 .* operator new}} // CHECK: {{#1 .* main .*operator_new_right_oob.cc}}:[[@LINE-8]] delete buffer; } Index: test/asan/TestCases/Windows/operator_new_uaf.cc =================================================================== --- test/asan/TestCases/Windows/operator_new_uaf.cc +++ test/asan/TestCases/Windows/operator_new_uaf.cc @@ -12,10 +12,10 @@ // CHECK: {{#0 .* main .*operator_new_uaf.cc}}:[[@LINE-3]] // CHECK: [[ADDR]] is located 0 bytes inside of 1-byte region // CHECK-LABEL: freed by thread T0 here: -// CHECK: {{#0 .* operator delete }} +// CHECK: {{#0 .* operator delete}} // CHECK: {{#1 .* main .*operator_new_uaf.cc}}:[[@LINE-8]] // CHECK-LABEL: previously allocated by thread T0 here: -// CHECK: {{#0 .* operator new }} +// CHECK: {{#0 .* operator new}} // CHECK: {{#1 .* main .*operator_new_uaf.cc}}:[[@LINE-12]] return 0; } Index: test/asan/TestCases/Windows/queue_user_work_item_report.cc =================================================================== --- test/asan/TestCases/Windows/queue_user_work_item_report.cc +++ test/asan/TestCases/Windows/queue_user_work_item_report.cc @@ -11,7 +11,7 @@ stack_buffer[subscript] = 42; // CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T1 -// CHECK: {{#0 .* work_item .*queue_user_work_item_report.cc}}:[[@LINE-3]] +// CHECK: {{#0 .* work_item.*queue_user_work_item_report.cc}}:[[@LINE-3]] // CHECK: Address [[ADDR]] is located in stack of thread T1 at offset {{.*}} in frame // CHECK: work_item SetEvent(done); Index: test/asan/TestCases/Windows/report_after_syminitialize.cc =================================================================== --- test/asan/TestCases/Windows/report_after_syminitialize.cc +++ test/asan/TestCases/Windows/report_after_syminitialize.cc @@ -1,4 +1,5 @@ -// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: env ASAN_OPTIONS=external_symbolizer_path=asdf not %run %t 2>&1 | FileCheck %s #include #include @@ -13,7 +14,8 @@ *(volatile int*)0 = 42; // CHECK: ERROR: AddressSanitizer: access-violation on unknown address + // CHECK-NEXT: {{WARNING: Failed to use and restart external symbolizer}} // CHECK-NEXT: {{WARNING: .*DbgHelp}} - // CHECK: {{#0 0x.* in main.*report_after_syminitialize.cc:}}[[@LINE-3]] + // CHECK: {{#0 0x.* in main.*report_after_syminitialize.cc:}}[[@LINE-4]] // CHECK: AddressSanitizer can not provide additional info. } Index: test/asan/TestCases/Windows/stack_use_after_return.cc =================================================================== --- test/asan/TestCases/Windows/stack_use_after_return.cc +++ test/asan/TestCases/Windows/stack_use_after_return.cc @@ -16,7 +16,7 @@ // CHECK-NEXT: {{#0 0x.* in main .*stack_use_after_return.cc}}:[[@LINE-3]] // // CHECK: is located in stack of thread T0 at offset [[OFFSET:.*]] in frame -// CHECK-NEXT: {{#0 0x.* in foo .*stack_use_after_return.cc}} +// CHECK-NEXT: {{#0 0x.* in foo.*stack_use_after_return.cc}} // // CHECK: 'stack_buffer' <== Memory access at offset [[OFFSET]] is inside this variable } Index: test/asan/TestCases/Windows/symbols_path.cc =================================================================== --- test/asan/TestCases/Windows/symbols_path.cc +++ test/asan/TestCases/Windows/symbols_path.cc @@ -16,7 +16,7 @@ // CHECK-NEXT: {{#0 .* main .*symbols_path.cc}}:[[@LINE-3]] // CHECK: [[ADDR]] is located 1 bytes to the left of 42-byte region // CHECK: allocated by thread T0 here: -// CHECK-NEXT: {{#0 .* malloc }} +// CHECK-NEXT: {{#0 .* malloc}} // CHECK-NEXT: {{#1 .* main .*symbols_path.cc}}:[[@LINE-8]] free(buffer); } Index: test/asan/TestCases/Windows/thread_stack_array_left_oob.cc =================================================================== --- test/asan/TestCases/Windows/thread_stack_array_left_oob.cc +++ test/asan/TestCases/Windows/thread_stack_array_left_oob.cc @@ -9,7 +9,7 @@ stack_buffer[subscript] = 42; // CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T1 -// CHECK: {{#0 .* thread_proc .*thread_stack_array_left_oob.cc}}:[[@LINE-3]] +// CHECK: {{#0 .* thread_proc.*thread_stack_array_left_oob.cc}}:[[@LINE-3]] // CHECK: Address [[ADDR]] is located in stack of thread T1 at offset {{.*}} in frame // CHECK: thread_proc return 0; Index: test/asan/TestCases/Windows/thread_stack_array_right_oob.cc =================================================================== --- test/asan/TestCases/Windows/thread_stack_array_right_oob.cc +++ test/asan/TestCases/Windows/thread_stack_array_right_oob.cc @@ -9,7 +9,7 @@ stack_buffer[subscript] = 42; // CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T1 -// CHECK: {{#0 .* thread_proc .*thread_stack_array_right_oob.cc}}:[[@LINE-3]] +// CHECK: {{#0 .* thread_proc.*thread_stack_array_right_oob.cc}}:[[@LINE-3]] // CHECK: Address [[ADDR]] is located in stack of thread T1 at offset {{.*}} in frame // CHECK: thread_proc return 0; Index: test/asan/lit.cfg =================================================================== --- test/asan/lit.cfg +++ test/asan/lit.cfg @@ -171,6 +171,10 @@ if config.host_os == 'Linux' and config.target_arch == 'x86_64': config.available_features.add('leak-detection') +# Test lld if it is available. +if config.has_lld: + config.available_features.add('lld') + # Set LD_LIBRARY_PATH to pick dynamic runtime up properly. push_dynamic_library_lookup_path(config, config.compiler_rt_libdir) Index: test/asan/lit.site.cfg.in =================================================================== --- test/asan/lit.site.cfg.in +++ test/asan/lit.site.cfg.in @@ -11,6 +11,7 @@ config.android = "@ANDROID@" config.asan_dynamic = @ASAN_TEST_DYNAMIC@ config.target_arch = "@ASAN_TEST_TARGET_ARCH@" +config.has_lld = "@ASAN_HAS_LLD@" # Load common config for all compiler-rt lit tests. lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")