| // Copyright 2020 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include "symbolize.h" |
| |
| #include <stdarg.h> |
| #include <stdint.h> |
| #include <zircon/assert.h> |
| |
| #include <ktl/string_view.h> |
| |
| #include "stack.h" |
| |
| namespace { |
| |
| // On x86-32, there is a fixed link-time address. |
| #ifdef __i386__ |
| static constexpr auto kLinkTimeAddress = PHYS_LOAD_ADDRESS; |
| #else |
| static constexpr uintptr_t kLinkTimeAddress = 0; |
| #endif |
| |
| // Arbitrary, but larger than any known format in use. |
| static constexpr size_t kMaxBuildIdSize = 32; |
| |
| struct BuildIdNote final { |
| static constexpr char kName_[] = "GNU"; |
| static constexpr uint32_t kType_ = 3; // NT_GNU_BUILD_ID |
| |
| uint32_t n_namesz, n_descsz, n_type; |
| alignas(4) char name[sizeof(kName_)]; |
| alignas(4) uint8_t build_id[/* n_descsz */]; |
| |
| bool matches() const { |
| return (n_type == kType_ && |
| // n_namesz includes the NUL terminator. |
| ktl::string_view{name, n_namesz} == ktl::string_view{kName_, sizeof(kName_)}); |
| } |
| |
| const BuildIdNote* next() const { |
| return reinterpret_cast<const BuildIdNote*>(&name[(n_namesz + 3) & -4] + ((n_descsz + 3) & -4)); |
| } |
| }; |
| |
| // These are defined by the linker script. |
| extern "C" void __code_start(); |
| extern "C" const BuildIdNote __start_note_gnu_build_id[]; |
| extern "C" const BuildIdNote __stop_note_gnu_build_id[]; |
| |
| class BuildId { |
| public: |
| BuildId(const BuildIdNote* start = __start_note_gnu_build_id, |
| const BuildIdNote* stop = __stop_note_gnu_build_id) |
| : note_(start) { |
| ZX_ASSERT(note_->matches()); |
| ZX_ASSERT(note_->next() <= stop); |
| } |
| |
| auto begin() const { return note_->build_id; } |
| auto end() const { return begin() + size(); } |
| size_t size() const { return note_->n_descsz; } |
| |
| ktl::string_view Print() { |
| ZX_ASSERT(size() <= kMaxBuildIdSize); |
| char* p = hex_; |
| for (uint8_t byte : *this) { |
| *p++ = "0123456789abcdef"[byte >> 4]; |
| *p++ = "0123456789abcdef"[byte & 0xf]; |
| } |
| return {hex_, size() * 2}; |
| } |
| |
| private: |
| const BuildIdNote* note_ = nullptr; |
| char hex_[kMaxBuildIdSize * 2]; |
| }; |
| |
| } // namespace |
| |
| Symbolize Symbolize::instance_; |
| |
| void Symbolize::Printf(const char* fmt, ...) { |
| va_list args; |
| va_start(args, fmt); |
| vfprintf(output_, fmt, args); |
| va_end(args); |
| } |
| |
| void Symbolize::PrintModule() { |
| Printf("{{{module:0:%s:elf:%V}}}\n", kProgramName_, BuildId().Print()); |
| } |
| |
| void Symbolize::PrintMmap() { |
| auto start = reinterpret_cast<uintptr_t>(__code_start); |
| auto end = reinterpret_cast<uintptr_t>(_end); |
| Printf("{{{mmap:%p:%#zx:load:0:rwx:%p}}}\n", __code_start, end - start, kLinkTimeAddress); |
| } |
| |
| void Symbolize::ContextAlways() { |
| Printf("{{{reset}}}\n"); |
| PrintModule(); |
| PrintMmap(); |
| } |
| |
| void Symbolize::Context() { |
| if (!context_done_) { |
| context_done_ = true; |
| ContextAlways(); |
| } |
| } |
| |
| void Symbolize::BackTraceFrame(unsigned int n, uintptr_t pc) { |
| // Just print the line in markup format. Context() was called earlier. |
| Printf("{{{bt:%u:%#zx}}}\n", n, pc); |
| } |
| |
| void Symbolize::DumpFile(ktl::string_view type, ktl::string_view name) { |
| Context(); |
| Printf("{{{dumpfile:%V:%V}}}\n", type, name); |
| } |