diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp --- a/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp +++ b/compiler-rt/lib/sanitizer_common/sanitizer_stacktrace_printer.cpp @@ -14,6 +14,12 @@ #include "sanitizer_file.h" #include "sanitizer_fuchsia.h" +#if SANITIZER_LINUX +# include +# include +# include +#endif + namespace __sanitizer { // sanitizer_symbolizer_markup.cpp implements these differently. @@ -104,6 +110,73 @@ return function; } +# if SANITIZER_LINUX +template +static uptr alignUp(uintptr_t Ptr) { + static_assert((Alignment & (Alignment - 1)) == 0, + "Alignment is power of two"); + if ((Ptr & (Alignment - 1)) == 0) + return Ptr; + + Ptr += Alignment - (Ptr & (Alignment - 1)); + return Ptr; +} + +struct BuildId { + const char *buf; + uptr size; +}; + +static BuildId GetBuildIdForAddr(uptr module_base, uptr pc) { + auto *ehdr = reinterpret_cast(module_base); + if (ehdr->e_ident[EI_MAG0] != ELFMAG0 || ehdr->e_ident[EI_MAG1] != ELFMAG1 || + ehdr->e_ident[EI_MAG2] != ELFMAG2 || ehdr->e_ident[EI_MAG3] != ELFMAG3) { + return {nullptr, 0u}; + } + auto *phdr_begin = reinterpret_cast( + reinterpret_cast(ehdr) + ehdr->e_phoff); + + for (const auto &phdr : + ArrayRef(phdr_begin, phdr_begin + ehdr->e_phnum)) { + if (phdr.p_type != PT_NOTE) + continue; + uptr off = 0; + while (off < phdr.p_memsz) { + auto *nhdr = reinterpret_cast( + reinterpret_cast(ehdr) + phdr.p_offset + off); + constexpr auto kGnuNamesz = 4; // "GNU" with NUL-byte. + if (nhdr->n_type == NT_GNU_BUILD_ID && nhdr->n_namesz == kGnuNamesz) { + const char *name = reinterpret_cast(nhdr) + sizeof(*nhdr); + if (internal_memcmp(name, "GNU", 3) == 0) { + const char *value = + reinterpret_cast(nhdr) + sizeof(*nhdr) + kGnuNamesz; + return {value, nhdr->n_descsz}; + } + } + off += sizeof(*nhdr) + alignUp<4>(nhdr->n_namesz) + + alignUp<4>(nhdr->n_descsz); + } + } + return {nullptr, 0u}; +} + +# endif + +static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace, + InternalScopedString *buffer) { +# if SANITIZER_LINUX + BuildId buildid = GetBuildIdForAddr(info.module_base(), info.address); + if (buildid.buf) { + if (PrefixSpace) + buffer->append(" "); + buffer->append("(BuildId: "); + for (uptr i = 0; i < buildid.size; ++i) { + buffer->append("%x", buildid.buf[i]); + } + buffer->append(")"); + } +# endif +} static const char kDefaultFormat[] = " #%n %p %F %L"; void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, @@ -140,6 +213,9 @@ case 'o': buffer->append("0x%zx", info->module_offset); break; + case 'b': + MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer); + break; case 'f': buffer->append("%s", DemangleFunctionName(StripFunctionName( info->function, strip_func_prefix))); @@ -181,6 +257,8 @@ } else if (info->module) { RenderModuleLocation(buffer, info->module, info->module_offset, info->module_arch, strip_path_prefix); + + MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer); } else { buffer->append("()"); } @@ -193,6 +271,7 @@ // Always strip the module name for %M. RenderModuleLocation(buffer, StripModuleName(info->module), info->module_offset, info->module_arch, ""); + MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer); } else { buffer->append("(%p)", (void *)address); } diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_symbolizer.h @@ -45,6 +45,7 @@ // Deletes all strings and resets all fields. void Clear(); void FillModuleInfo(const char *mod_name, uptr mod_offset, ModuleArch arch); + uptr module_base() const { return address - module_offset; } }; // Linked list of symbolized frames (each frame is described by AddressInfo). diff --git a/compiler-rt/test/hwasan/TestCases/build-ids.c b/compiler-rt/test/hwasan/TestCases/build-ids.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/hwasan/TestCases/build-ids.c @@ -0,0 +1,17 @@ +// RUN: %clang_hwasan -Wl,--build-id=0xaba493998257fbdd %s -o %t +// RUN: HWASAN_SYMBOLIZER_PATH= not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,NOSYM +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,SYM + +#include + +#include + +int main(int argc, char **argv) { + __hwasan_enable_allocator_tagging(); + char *buf = (char *)malloc(1); + buf[32] = 'x'; + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch + // NOSYM: #0 0x{{.*}} {{.*}}build-ids.c{{.*}} (BuildId: aba493998257fbdd) + // SYM: #0 0x{{.*}} in main {{.*}}build-ids.c:[[@LINE-3]]:{{[0-9]+}} + return 0; +} diff --git a/compiler-rt/test/sanitizer_common/android_commands/android_run.py b/compiler-rt/test/sanitizer_common/android_commands/android_run.py --- a/compiler-rt/test/sanitizer_common/android_commands/android_run.py +++ b/compiler-rt/test/sanitizer_common/android_commands/android_run.py @@ -12,7 +12,8 @@ # Android linker ignores RPATH. Set LD_LIBRARY_PATH to Output dir. args.append('LD_LIBRARY_PATH=%s' % (ANDROID_TMPDIR,)) for (key, value) in list(os.environ.items()): - if key in ['ASAN_ACTIVATION_OPTIONS', 'SCUDO_OPTIONS'] or key.endswith('SAN_OPTIONS'): + if key in ['ASAN_ACTIVATION_OPTIONS', 'SCUDO_OPTIONS'] or key.endswith('SAN_OPTIONS') \ + or key.startswith('HWASAN'): args.append('%s="%s"' % (key, value.replace('"', '\\"'))) return ' '.join(args)