mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-29 16:41:27 +00:00
[sanitizer] Add a 'print_module_map' flag which prints modules with UUIDs on Darwin
This patch add a new sanitizer flag, print_module_map, which enables printing a module map when the process exits, or after each report (for TSan). The output format is very similar to what Crash Reporter produces on Darwin (e.g. the format of module UUIDs). This enables users to use the existing symbol servers to offline symbolicate and aggregate reports. Differential Revision: https://reviews.llvm.org/D27400 llvm-svn: 291277
This commit is contained in:
parent
a7276feed7
commit
b6c6eaf226
@ -179,6 +179,8 @@ class ScopedInErrorReport {
|
||||
if (common_flags()->print_cmdline)
|
||||
PrintCmdline();
|
||||
|
||||
if (common_flags()->print_module_map == 2) PrintModuleMap();
|
||||
|
||||
// Copy the message buffer so that we could start logging without holding a
|
||||
// lock that gets aquired during printing.
|
||||
InternalScopedBuffer<char> buffer_copy(kErrorMessageBufferSize);
|
||||
|
@ -46,6 +46,7 @@ static void AsanDie() {
|
||||
// Don't die twice - run a busy loop.
|
||||
while (1) { }
|
||||
}
|
||||
if (common_flags()->print_module_map >= 1) PrintModuleMap();
|
||||
if (flags()->sleep_before_dying) {
|
||||
Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying);
|
||||
SleepForSeconds(flags()->sleep_before_dying);
|
||||
|
@ -270,6 +270,8 @@ void LoadedModule::set(const char *module_name, uptr base_address,
|
||||
|
||||
void LoadedModule::clear() {
|
||||
InternalFree(full_name_);
|
||||
base_address_ = 0;
|
||||
max_executable_address_ = 0;
|
||||
full_name_ = nullptr;
|
||||
arch_ = kModuleArchUnknown;
|
||||
internal_memset(uuid_, 0, kModuleUUIDSize);
|
||||
@ -285,6 +287,8 @@ void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable) {
|
||||
void *mem = InternalAlloc(sizeof(AddressRange));
|
||||
AddressRange *r = new(mem) AddressRange(beg, end, executable);
|
||||
ranges_.push_back(r);
|
||||
if (executable && end > max_executable_address_)
|
||||
max_executable_address_ = end;
|
||||
}
|
||||
|
||||
bool LoadedModule::containsAddress(uptr address) const {
|
||||
|
@ -283,6 +283,7 @@ void UpdateProcessName();
|
||||
void CacheBinaryName();
|
||||
void DisableCoreDumperIfNecessary();
|
||||
void DumpProcessMap();
|
||||
void PrintModuleMap();
|
||||
bool FileExists(const char *filename);
|
||||
const char *GetEnv(const char *name);
|
||||
bool SetEnv(const char *name, const char *value);
|
||||
@ -665,6 +666,32 @@ enum ModuleArch {
|
||||
kModuleArchARM64
|
||||
};
|
||||
|
||||
// When adding a new architecture, don't forget to also update
|
||||
// script/asan_symbolize.py and sanitizer_symbolizer_libcdep.cc.
|
||||
inline const char *ModuleArchToString(ModuleArch arch) {
|
||||
switch (arch) {
|
||||
case kModuleArchUnknown:
|
||||
return "";
|
||||
case kModuleArchI386:
|
||||
return "i386";
|
||||
case kModuleArchX86_64:
|
||||
return "x86_64";
|
||||
case kModuleArchX86_64H:
|
||||
return "x86_64h";
|
||||
case kModuleArchARMV6:
|
||||
return "armv6";
|
||||
case kModuleArchARMV7:
|
||||
return "armv7";
|
||||
case kModuleArchARMV7S:
|
||||
return "armv7s";
|
||||
case kModuleArchARMV7K:
|
||||
return "armv7k";
|
||||
case kModuleArchARM64:
|
||||
return "arm64";
|
||||
}
|
||||
CHECK(0 && "Invalid module arch");
|
||||
}
|
||||
|
||||
const uptr kModuleUUIDSize = 16;
|
||||
|
||||
// Represents a binary loaded into virtual memory (e.g. this can be an
|
||||
@ -674,6 +701,7 @@ class LoadedModule {
|
||||
LoadedModule()
|
||||
: full_name_(nullptr),
|
||||
base_address_(0),
|
||||
max_executable_address_(0),
|
||||
arch_(kModuleArchUnknown),
|
||||
instrumented_(false) {
|
||||
internal_memset(uuid_, 0, kModuleUUIDSize);
|
||||
@ -688,6 +716,7 @@ class LoadedModule {
|
||||
|
||||
const char *full_name() const { return full_name_; }
|
||||
uptr base_address() const { return base_address_; }
|
||||
uptr max_executable_address() const { return max_executable_address_; }
|
||||
ModuleArch arch() const { return arch_; }
|
||||
const u8 *uuid() const { return uuid_; }
|
||||
bool instrumented() const { return instrumented_; }
|
||||
@ -707,6 +736,7 @@ class LoadedModule {
|
||||
private:
|
||||
char *full_name_; // Owned.
|
||||
uptr base_address_;
|
||||
uptr max_executable_address_;
|
||||
ModuleArch arch_;
|
||||
u8 uuid_[kModuleUUIDSize];
|
||||
bool instrumented_;
|
||||
|
@ -74,6 +74,9 @@ COMMON_FLAG(bool, allocator_may_return_null, false,
|
||||
COMMON_FLAG(bool, print_summary, true,
|
||||
"If false, disable printing error summaries in addition to error "
|
||||
"reports.")
|
||||
COMMON_FLAG(int, print_module_map, 0,
|
||||
"OS X only. 0 = don't print, 1 = print only once before process "
|
||||
"exits, 2 = print after each report.")
|
||||
COMMON_FLAG(bool, check_printf, true, "Check printf arguments.")
|
||||
COMMON_FLAG(bool, handle_segv, true,
|
||||
"If set, registers the tool's custom SIGSEGV/SIGBUS handler.")
|
||||
|
@ -1393,6 +1393,8 @@ void MaybeReexec() {
|
||||
// No need to re-exec on Linux.
|
||||
}
|
||||
|
||||
void PrintModuleMap() { }
|
||||
|
||||
uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding) {
|
||||
UNREACHABLE("FindAvailableMemoryRange is not available");
|
||||
return 0;
|
||||
|
@ -854,6 +854,36 @@ void SignalContext::DumpAllRegisters(void *context) {
|
||||
# undef DUMPREG
|
||||
}
|
||||
|
||||
static inline bool CompareBaseAddress(const LoadedModule &a,
|
||||
const LoadedModule &b) {
|
||||
return a.base_address() < b.base_address();
|
||||
}
|
||||
|
||||
void FormatUUID(char *out, uptr size, const u8 *uuid) {
|
||||
internal_snprintf(out, size,
|
||||
"<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-"
|
||||
"%02X%02X%02X%02X%02X%02X>",
|
||||
uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
|
||||
uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
|
||||
uuid[12], uuid[13], uuid[14], uuid[15]);
|
||||
}
|
||||
|
||||
void PrintModuleMap() {
|
||||
Printf("Process module map:\n");
|
||||
MemoryMappingLayout memory_mapping(false);
|
||||
InternalMmapVector<LoadedModule> modules(/*initial_capacity*/ 128);
|
||||
memory_mapping.DumpListOfModules(&modules);
|
||||
InternalSort(&modules, modules.size(), CompareBaseAddress);
|
||||
for (uptr i = 0; i < modules.size(); ++i) {
|
||||
char uuid_str[128];
|
||||
FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid());
|
||||
Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(),
|
||||
modules[i].max_executable_address(), modules[i].full_name(),
|
||||
ModuleArchToString(modules[i].arch()), uuid_str);
|
||||
}
|
||||
Printf("End of module map.\n");
|
||||
}
|
||||
|
||||
} // namespace __sanitizer
|
||||
|
||||
#endif // SANITIZER_MAC
|
||||
|
@ -43,7 +43,7 @@ static int AppendChar(char **buff, const char *buff_end, char c) {
|
||||
// on the value of |pad_with_zero|.
|
||||
static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
|
||||
u8 base, u8 minimal_num_length, bool pad_with_zero,
|
||||
bool negative) {
|
||||
bool negative, bool uppercase) {
|
||||
uptr const kMaxLen = 30;
|
||||
RAW_CHECK(base == 10 || base == 16);
|
||||
RAW_CHECK(base == 10 || !negative);
|
||||
@ -76,23 +76,25 @@ static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
|
||||
if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-');
|
||||
for (; pos >= 0; pos--) {
|
||||
char digit = static_cast<char>(num_buffer[pos]);
|
||||
result += AppendChar(buff, buff_end, (digit < 10) ? '0' + digit
|
||||
: 'a' + digit - 10);
|
||||
digit = (digit < 10) ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10;
|
||||
result += AppendChar(buff, buff_end, digit);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base,
|
||||
u8 minimal_num_length, bool pad_with_zero) {
|
||||
u8 minimal_num_length, bool pad_with_zero,
|
||||
bool uppercase) {
|
||||
return AppendNumber(buff, buff_end, num, base, minimal_num_length,
|
||||
pad_with_zero, false /* negative */);
|
||||
pad_with_zero, false /* negative */, uppercase);
|
||||
}
|
||||
|
||||
static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
|
||||
u8 minimal_num_length, bool pad_with_zero) {
|
||||
bool negative = (num < 0);
|
||||
return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10,
|
||||
minimal_num_length, pad_with_zero, negative);
|
||||
minimal_num_length, pad_with_zero, negative,
|
||||
false /* uppercase */);
|
||||
}
|
||||
|
||||
static int AppendString(char **buff, const char *buff_end, int precision,
|
||||
@ -112,14 +114,16 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
|
||||
int result = 0;
|
||||
result += AppendString(buff, buff_end, -1, "0x");
|
||||
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
|
||||
SANITIZER_POINTER_FORMAT_LENGTH, true);
|
||||
SANITIZER_POINTER_FORMAT_LENGTH,
|
||||
true /* pad_with_zero */, false /* uppercase */);
|
||||
return result;
|
||||
}
|
||||
|
||||
int VSNPrintf(char *buff, int buff_length,
|
||||
const char *format, va_list args) {
|
||||
static const char *kPrintfFormatsHelp =
|
||||
"Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x}; %p; %(\\.\\*)?s; %c\n";
|
||||
"Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; %(\\.\\*)?s; "
|
||||
"%c\n";
|
||||
RAW_CHECK(format);
|
||||
RAW_CHECK(buff_length > 0);
|
||||
const char *buff_end = &buff[buff_length - 1];
|
||||
@ -164,12 +168,14 @@ int VSNPrintf(char *buff, int buff_length,
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
case 'x': {
|
||||
case 'x':
|
||||
case 'X': {
|
||||
uval = have_ll ? va_arg(args, u64)
|
||||
: have_z ? va_arg(args, uptr)
|
||||
: va_arg(args, unsigned);
|
||||
result += AppendUnsigned(&buff, buff_end, uval,
|
||||
(*cur == 'u') ? 10 : 16, width, pad_with_zero);
|
||||
bool uppercase = (*cur == 'X');
|
||||
result += AppendUnsigned(&buff, buff_end, uval, (*cur == 'u') ? 10 : 16,
|
||||
width, pad_with_zero, uppercase);
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
|
@ -388,6 +388,8 @@ void DumpProcessMap() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void PrintModuleMap() { }
|
||||
|
||||
void DisableCoreDumperIfNecessary() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
@ -358,6 +358,8 @@ void PrintReport(const ReportDesc *rep) {
|
||||
ReportErrorSummary(rep_typ_str, frame->info);
|
||||
}
|
||||
|
||||
if (common_flags()->print_module_map == 2) PrintModuleMap();
|
||||
|
||||
Printf("==================\n");
|
||||
}
|
||||
|
||||
|
@ -404,6 +404,8 @@ void Initialize(ThreadState *thr) {
|
||||
int Finalize(ThreadState *thr) {
|
||||
bool failed = false;
|
||||
|
||||
if (common_flags()->print_module_map == 1) PrintModuleMap();
|
||||
|
||||
if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1)
|
||||
SleepForMillis(flags()->atexit_sleep_ms);
|
||||
|
||||
|
28
compiler-rt/test/asan/TestCases/Darwin/uuid.cc
Normal file
28
compiler-rt/test/asan/TestCases/Darwin/uuid.cc
Normal file
@ -0,0 +1,28 @@
|
||||
// RUN: %clangxx_asan %s -o %t
|
||||
// RUN: %env_asan_opts=print_module_map=1 not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %env_asan_opts=print_module_map=2 not %run %t 2>&1 | FileCheck %s
|
||||
// RUN: %clangxx_asan %s -o %t -fsanitize-recover=address
|
||||
// RUN: %env_asan_opts=print_module_map=2:halt_on_error=0 %run %t 2>&1 | FileCheck %s
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "otool -l %s", argv[0]);
|
||||
system(buf);
|
||||
// CHECK: cmd LC_UUID
|
||||
// CHECK-NEXT: cmdsize 24
|
||||
// CHECK-NEXT: uuid [[UUID:[0-9A-F-]{36}]]
|
||||
|
||||
char *x = (char*)malloc(10 * sizeof(char));
|
||||
free(x);
|
||||
char mybuf[10];
|
||||
memcpy(mybuf, x, 10);
|
||||
// CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
|
||||
// CHECK: Process module map:
|
||||
// CHECK: uuid.cc.tmp {{.*}} <[[UUID]]>
|
||||
|
||||
fprintf(stderr, "Done.\n");
|
||||
}
|
Loading…
Reference in New Issue
Block a user