| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2013 Google, Inc. |
| // |
| // 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 <debug.h> |
| #include <lib/console.h> |
| #include <lib/version.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <ktl/byte.h> |
| #include <ktl/span.h> |
| #include <ktl/unique_ptr.h> |
| #include <lk/init.h> |
| #include <phys/handoff.h> |
| #include <vm/vm.h> |
| |
| #include <ktl/enforce.h> |
| |
| namespace { |
| |
| ktl::unique_ptr<char[]> gVersionStringBuffer; |
| size_t gVersionStringSize; |
| |
| // If the build ID were SHA256, it would be 32 bytes. |
| // (The algorithms used for build IDs today actually produce fewer than that.) |
| // This string needs 2 bytes to print each byte in hex, plus a NUL terminator. |
| char gElfBuildIdString[65]; |
| |
| // Standard ELF note layout (Elf{32,64}_Nhdr in <elf.h>). |
| // The name and type fields' values are what GNU and GNU-compatible |
| // tools (i.e. everything in the Unix-like world in recent years) |
| // specify for build ID notes. |
| struct build_id_note { |
| uint32_t namesz; |
| uint32_t descsz; |
| uint32_t type; |
| #define NT_GNU_BUILD_ID 3 |
| #define NOTE_NAME "GNU" |
| char name[(sizeof(NOTE_NAME) + 3) & -4]; |
| ktl::byte id[]; |
| }; |
| |
| extern "C" const struct build_id_note __build_id_note_start; |
| extern "C" const ktl::byte __build_id_note_end[]; |
| |
| void init_build_id(uint level) { |
| const build_id_note* const note = &__build_id_note_start; |
| if (note->type != NT_GNU_BUILD_ID || note->namesz != sizeof(NOTE_NAME) || |
| memcmp(note->name, NOTE_NAME, sizeof(NOTE_NAME)) != 0 || |
| ¬e->id[note->descsz] != __build_id_note_end) { |
| panic("ELF build ID note has bad format!\n"); |
| } |
| ktl::span<const ktl::byte> id = ElfBuildId(); |
| if (id.size() * 2 >= sizeof(gElfBuildIdString)) { |
| panic("ELF build ID is %zu bytes, expected %zu or fewer\n", id.size(), |
| sizeof(gElfBuildIdString) / 2); |
| } |
| for (size_t i = 0; i < id.size(); ++i) { |
| snprintf(&gElfBuildIdString[i * 2], 3, "%02x", static_cast<unsigned int>(id[i])); |
| } |
| } |
| |
| // This must happen before print_version below and should happen as early as possible to ensure we |
| // get useful backtraces when the kernel panics. |
| LK_INIT_HOOK(elf_build_id, &init_build_id, LK_INIT_LEVEL_EARLIEST) |
| |
| void print_module(FILE* f, const char* build_id) { |
| fprintf(f, "{{{module:0:kernel:elf:%s}}}\n", build_id); |
| } |
| |
| // TODO(eieio): Consider whether it makes sense to locate the logic for printing |
| // mappings somewhere else (perhaps in vm/vm.cpp?). |
| void print_mmap(FILE* f, uintptr_t bias, const void* begin, const void* end, const char* perm) { |
| const uintptr_t start = reinterpret_cast<uintptr_t>(begin); |
| const size_t size = reinterpret_cast<uintptr_t>(end) - start; |
| if (size != 0) { |
| fprintf(f, "{{{mmap:%#lx:%#lx:load:0:%s:%#lx}}}\n", start, size, perm, start + bias); |
| } |
| } |
| |
| void init_version(uint level) { |
| ktl::string_view version = gPhysHandoff->version_string.get(); |
| ASSERT(!version.empty()); |
| fbl::AllocChecker ac; |
| gVersionStringBuffer.reset(new (ac) char[version.size()]); |
| ASSERT_MSG(ac.check(), "cannot allocate %zu bytes for version string", version.size()); |
| gVersionStringSize = version.copy(gVersionStringBuffer.get(), version.size()); |
| print_version(); |
| } |
| |
| LK_INIT_HOOK(version, init_version, LK_INIT_LEVEL_HEAP) |
| |
| } // namespace |
| |
| ktl::string_view VersionString() { |
| // If init_version has run, return the heap copy. |
| ktl::string_view version{ |
| gVersionStringBuffer.get(), |
| gVersionStringSize, |
| }; |
| if (version.empty() && gPhysHandoff) { |
| // Before that, try to use the string from handoff via the physmap. |
| version = gPhysHandoff->version_string.get(); |
| } |
| if (version.empty()) { |
| // If all else fails, use a fixed string. |
| version = "early-panic-try-kernel.phys.verbose"; |
| } |
| return version; |
| } |
| |
| const char* elf_build_id_string() { return gElfBuildIdString; } |
| |
| ktl::span<const ktl::byte> ElfBuildId() { |
| const build_id_note* const note = &__build_id_note_start; |
| return {note->id, note->descsz}; |
| } |
| |
| void print_version() { |
| dprintf(ALWAYS, "version:\n"); |
| dprintf(ALWAYS, "\tarch: %s\n", ARCH); |
| dprintf(ALWAYS, "\tzx_system_get_version_string: %.*s\n", |
| static_cast<int>(VersionString().size()), VersionString().data()); |
| dprintf(ALWAYS, "\tELF build ID: %s\n", gElfBuildIdString); |
| dprintf(ALWAYS, "\tLK_DEBUGLEVEL: %d\n", LK_DEBUGLEVEL); |
| } |
| |
| void PrintSymbolizerContext(FILE* f) { |
| const uintptr_t bias = KERNEL_BASE - reinterpret_cast<uintptr_t>(__executable_start); |
| fprintf(f, "{{{reset}}}\n"); |
| print_module(f, gElfBuildIdString); |
| // These four mappings match the mappings printed by vm_init(). |
| print_mmap(f, bias, __code_start, __code_end, "rx"); |
| print_mmap(f, bias, __rodata_start, __rodata_end, "r"); |
| print_mmap(f, bias, __relro_start, __relro_end, "r"); |
| print_mmap(f, bias, __data_start, __data_end, "rw"); |
| print_mmap(f, bias, __bss_start, _end, "rw"); |
| } |
| |
| void print_backtrace_version_info(FILE* f) { |
| fprintf(f, "zx_system_get_version_string %.*s\n\n", static_cast<int>(VersionString().size()), |
| VersionString().data()); |
| |
| // Log the ELF build ID in the format the symbolizer scripts understand. |
| if (gElfBuildIdString[0] != '\0') { |
| PrintSymbolizerContext(f); |
| fprintf(f, "dso: id=%s base=%#lx name=zircon.elf\n", gElfBuildIdString, |
| reinterpret_cast<uintptr_t>(__executable_start)); |
| } |
| } |
| |
| static int cmd_version(int argc, const cmd_args* argv, uint32_t flags) { |
| print_version(); |
| return 0; |
| } |
| |
| STATIC_COMMAND_START |
| STATIC_COMMAND("version", "print version", &cmd_version) |
| STATIC_COMMAND_END(version) |