diff --git a/src/developer/debug/debug_agent/BUILD.gn b/src/developer/debug/debug_agent/BUILD.gn index 33152057bc8693767b8eb24dcdf95e693c9c00cd..eb40be2500f175dad04eb1564ea7667f3266a8ea 100644 --- a/src/developer/debug/debug_agent/BUILD.gn +++ b/src/developer/debug/debug_agent/BUILD.gn @@ -72,6 +72,10 @@ static_library("lib") { assert(false, "Un-supported CPU: ${current_cpu}") } + deps = [ + "//src/lib/elflib", + ] + public_deps = [ "//garnet/lib/process", "//garnet/public/lib/svc/cpp", diff --git a/src/developer/debug/debug_agent/process_info.cc b/src/developer/debug/debug_agent/process_info.cc index 7a5afab5f93cf60b3d06c4234775aa5dbc94e502..e17e91e1abffd924628dc6a754223a49d25d6866 100644 --- a/src/developer/debug/debug_agent/process_info.cc +++ b/src/developer/debug/debug_agent/process_info.cc @@ -4,6 +4,9 @@ #include "src/developer/debug/debug_agent/process_info.h" +// Included early because of conflicts. +#include "src/lib/elflib/elflib.h" + #include <inttypes.h> #include <lib/zx/thread.h> #include <link.h> @@ -16,7 +19,6 @@ #include "src/developer/debug/debug_agent/arch.h" #include "src/developer/debug/debug_agent/object_util.h" -#include "src/developer/debug/shared/elf.h" #include "src/lib/fxl/logging.h" namespace debug_agent { @@ -142,7 +144,22 @@ zx_status_t GetModulesForProcess(const zx::process& process, if (ReadNullTerminatedString(process, str_addr, &module.name) != ZX_OK) return false; - module.build_id = debug_ipc::ExtractBuildID(process, module.base); + auto elf = elflib::ElfLib::Create( + [&process, base = module.base](uint64_t offset, + std::vector<uint8_t>* buf) { + size_t num_read = 0; + + if (process.read_memory(base + offset, buf->data(), buf->size(), + &num_read) != ZX_OK) { + return false; + } + + return num_read == buf->size(); + }); + + if (elf) { + module.build_id = elf->GetGNUBuildID(); + } modules->push_back(std::move(module)); return true; diff --git a/src/developer/debug/shared/BUILD.gn b/src/developer/debug/shared/BUILD.gn index ba071e3aace4fe938746197ad3d96061cf1e2af9..ba071959ae19f5b52e8ba3961cb2789144bef1bf 100644 --- a/src/developer/debug/shared/BUILD.gn +++ b/src/developer/debug/shared/BUILD.gn @@ -12,8 +12,6 @@ static_library("shared") { "buffered_fd.h", "component_utils.cc", "component_utils.h", - "elf.cc", - "elf.h", "message_loop.cc", "message_loop.h", "regex.cc", @@ -32,10 +30,6 @@ static_library("shared") { "//src/lib/fxl", ] - deps = [ - "//src/lib/elflib", - ] - if (current_toolchain == host_toolchain) { # Host toolchain. sources += [ @@ -64,45 +58,17 @@ static_library("shared") { } } -if (current_toolchain == host_toolchain) { - # Copy the test file to the build directory, as elf_unittests will not be run - # near the source code. - copy("copy_test_file") { - # This file is a small valid ELF file for testing the parser with. - # It was generated by compiling the program: - # int main() { return 1; }tests" - # on Linux with: - # gcc -O2 file.c - sources = [ - "testdata/small_test_file.elf", - ] - outputs = [ - "$root_out_dir/test_data/debug_ipc/small_test_file.elf", - ] - - metadata = { - test_runtime_deps = outputs - } - } -} - # Unit tests for this directory. These are intended to be referenced by unit # test targets for the consumers of this library. source_set("tests") { testonly = true sources = [ "component_utils_unittest.cc", - "elf_unittest.cc", "message_loop_unittest.cc", "regex_unittest.cc", "stream_buffer_unittest.cc", ] - if (current_toolchain == host_toolchain) { - data_deps = [ - ":copy_test_file", - ] - } deps = [ ":shared", "//third_party/googletest:gtest", diff --git a/src/developer/debug/shared/elf.cc b/src/developer/debug/shared/elf.cc deleted file mode 100644 index bac35d239a9230ff623c880ed8c87dac23d6978d..0000000000000000000000000000000000000000 --- a/src/developer/debug/shared/elf.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/developer/debug/shared/elf.h" - -#include "src/lib/elflib/elflib.h" - -namespace debug_ipc { - -namespace { - -constexpr size_t kMaxBuildIDSize = 64; - -constexpr uint64_t kNoteGnuBuildId = 3; - -} // namespace - -using elflib::Elf64_Ehdr; -using elflib::Elf64_Phdr; -using elflib::Elf64_Shdr; -using elflib::ElfLib; - -std::string ExtractBuildID( - std::function<bool(uint64_t offset, void* buffer, size_t length)> read_fn) { - // The buffer will hold a hex version of the build ID (2 chars per byte) - // plus the null terminator (1 more). - constexpr size_t buf_size = kMaxBuildIDSize * 2 + 1; - char buf[buf_size]; - - auto elf = - ElfLib::Create([read_fn](uint64_t offset, std::vector<uint8_t>* buf) { - return read_fn(offset, buf->data(), buf->size()); - }); - - if (!elf) { - return std::string(); - } - - auto note = elf->GetNote("GNU", kNoteGnuBuildId); - - if (note && note->size() <= kMaxBuildIDSize) { - size_t i = 0; - for (const auto& c : *note) - snprintf(&buf[i++ * 2], 3, "%02x", c); - return std::string(buf); - } - - return std::string(); -} - -std::string ExtractBuildID(FILE* file) { - return ExtractBuildID([file](uint64_t offset, void* buffer, size_t length) { - if (fseek(file, offset, SEEK_SET) != 0) - return false; - return fread(buffer, 1, length, file) == length; - }); -} - -#if defined(__Fuchsia__) -std::string ExtractBuildID(const zx::process& process, uint64_t base) { - return ExtractBuildID([&process, base](uint64_t offset, void* buffer, - size_t length) { - size_t num_read = 0; - if (process.read_memory(base + offset, buffer, length, &num_read) != ZX_OK) - return false; - return num_read == length; - }); -} -#endif - -} // namespace debug_ipc diff --git a/src/developer/debug/shared/elf.h b/src/developer/debug/shared/elf.h deleted file mode 100644 index 5ede475d2493fdc847e8d2f06ae67c443fc2abc8..0000000000000000000000000000000000000000 --- a/src/developer/debug/shared/elf.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#pragma once - -#include <stdint.h> -#include <stdio.h> - -#include <functional> -#include <string> - -#if defined(__Fuchsia__) -#include <lib/zx/process.h> -#endif - -namespace debug_ipc { - -// Extracts the build ID from some ELF data. Returns the empty string on -// failure. -// -// The parameter is a function that that implements an fread-like interface to -// read from the ELF data. This allows the extractor to handle in-memory and -// on-disk version. It returns true on success, false on failure (failures -// include all partial reads). The offset is relative to the beginning of the -// ELF file. -std::string ExtractBuildID( - std::function<bool(uint64_t offset, void* buffer, size_t length)> read_fn); - -// This variant extracts the build ID from the given file. -std::string ExtractBuildID(FILE* file); - -#if defined(__Fuchsia__) -// This variant extracts the build ID from an ELF file mapped into memory for -// the given process at the given location. -std::string ExtractBuildID(const zx::process& process, uint64_t base); -#endif - -} // namespace debug_ipc diff --git a/src/developer/debug/shared/elf_unittest.cc b/src/developer/debug/shared/elf_unittest.cc deleted file mode 100644 index ddff82de9d4db04e6acee892048909a22a6caeb9..0000000000000000000000000000000000000000 --- a/src/developer/debug/shared/elf_unittest.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2018 The Fuchsia Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "src/developer/debug/shared/elf.h" -#include "gtest/gtest.h" - -#include <unistd.h> - -#if defined(__APPLE__) -#include <mach-o/dyld.h> -#endif - -namespace debug_ipc { - -#if !defined(__Fuchsia__) -// This test requires a test data file which has not been set up for packaging -// on Fuchsia yet. -// TODO(brettw) set this up and enable this test on Fuchsia. - -namespace { - -std::string GetSelfPath() { - std::string result; -#if defined(__APPLE__) - // Executable path can have relative references ("..") depending on how the - // app was launched. - uint32_t length = 0; - _NSGetExecutablePath(nullptr, &length); - result.resize(length); - _NSGetExecutablePath(&result[0], &length); - result.resize(length - 1); // Length included terminator. -#elif defined(__linux__) - // The realpath() call below will resolve the symbolic link. - result.assign("/proc/self/exe"); -#else -#error Write this for your platform. -#endif - - char fullpath[PATH_MAX]; - return std::string(realpath(result.c_str(), fullpath)); -} - -std::string GetSmallTestElfFileName() { - std::string path = GetSelfPath(); - size_t last_slash = path.rfind('/'); - if (last_slash == std::string::npos) { - path = "./"; // Just hope the current directory works. - } else { - path.resize(last_slash + 1); - } - path += "test_data/debug_ipc/small_test_file.elf"; - return path; -} - -} // namespace - -TEST(Elf, ExtractBuildID) { - std::string small_test_file_name = GetSmallTestElfFileName(); - - FILE* small_test_file = fopen(small_test_file_name.c_str(), "rb"); - ASSERT_TRUE(small_test_file) << small_test_file_name.c_str(); - std::string build_id = ExtractBuildID(small_test_file); - fclose(small_test_file); - - // This expected build ID was extracted with: - // eu-readelf -n small_test_file.elf - EXPECT_EQ("763feb38b0e37a89964c330c5cf7f7af2ce79e54", build_id); -} -#endif - -} // namespace debug_ipc diff --git a/src/developer/debug/zxdb/symbols/BUILD.gn b/src/developer/debug/zxdb/symbols/BUILD.gn index 942c3c0104281a20b2902fd22d33eb013bac5222..eb8f1d6af7040a1b20d9e41ad61c6a6f3e7608a1 100644 --- a/src/developer/debug/zxdb/symbols/BUILD.gn +++ b/src/developer/debug/zxdb/symbols/BUILD.gn @@ -172,6 +172,26 @@ copy("copy_test_so") { ] } +if (current_toolchain == host_toolchain) { + copy("copy_test_elf_file") { + # This file is a small valid ELF file for testing the parser with. + # It was generated by compiling the program: + # int main() { return 1; } + # on Linux with: + # gcc -O2 file.c + sources = [ + "test_data/small_test_file.elf", + ] + outputs = [ + "$root_out_dir/test_data/zxdb/small_test_file.elf", + ] + + metadata = { + test_runtime_deps = outputs + } + } +} + source_set("tests") { testonly = true @@ -202,10 +222,10 @@ source_set("tests") { ] deps = [ + ":copy_test_elf_file($host_toolchain)", ":symbols", ":test_support", "//garnet/third_party/llvm:LLVMDebugInfoDWARF", - "//src/developer/debug/shared:copy_test_file($host_toolchain)", "//src/developer/debug/zxdb/common:test_support", "//third_party/googletest:gtest", ] diff --git a/src/developer/debug/zxdb/symbols/build_id_index.cc b/src/developer/debug/zxdb/symbols/build_id_index.cc index 03cec272544ea8667aa597a7fe5199b7c99444bb..740abadf496248ae2261c678fd345cfd5e634c27 100644 --- a/src/developer/debug/zxdb/symbols/build_id_index.cc +++ b/src/developer/debug/zxdb/symbols/build_id_index.cc @@ -6,8 +6,8 @@ #include <algorithm> -#include "src/developer/debug/shared/elf.h" #include "src/developer/debug/zxdb/common/string_util.h" +#include "src/lib/elflib/elflib.h" #include "src/lib/fxl/strings/string_printf.h" #include "src/lib/fxl/strings/string_view.h" #include "src/lib/fxl/strings/trim.h" @@ -242,12 +242,11 @@ void BuildIDIndex::IndexOneSourcePath(const std::string& path) { } bool BuildIDIndex::IndexOneSourceFile(const std::string& file_path) { - FILE* file = fopen(file_path.c_str(), "rb"); - if (!file) + auto elf = elflib::ElfLib::Create(file_path); + if (!elf) return false; - std::string build_id = debug_ipc::ExtractBuildID(file); - fclose(file); + std::string build_id = elf->GetGNUBuildID(); if (!build_id.empty()) { build_id_to_file_[build_id] = file_path; return true; diff --git a/src/developer/debug/zxdb/symbols/build_id_index_unittest.cc b/src/developer/debug/zxdb/symbols/build_id_index_unittest.cc index 67b81797db60e4daff6525bb7cf9ba1f93a993cf..017f5390077c8faa741f6160cd1d779707f6cb0b 100644 --- a/src/developer/debug/zxdb/symbols/build_id_index_unittest.cc +++ b/src/developer/debug/zxdb/symbols/build_id_index_unittest.cc @@ -18,7 +18,7 @@ const char kSmallTestBuildID[] = "763feb38b0e37a89964c330c5cf7f7af2ce79e54"; std::filesystem::path GetTestDataDir() { std::filesystem::path path(GetSelfPath()); path.remove_filename(); - path.append("test_data/debug_ipc/"); + path.append("test_data/zxdb/"); return path; } diff --git a/src/developer/debug/shared/testdata/small_test_file.elf b/src/developer/debug/zxdb/symbols/test_data/small_test_file.elf similarity index 100% rename from src/developer/debug/shared/testdata/small_test_file.elf rename to src/developer/debug/zxdb/symbols/test_data/small_test_file.elf diff --git a/src/lib/elflib/elflib.cc b/src/lib/elflib/elflib.cc index 472927c627e8430e6479de515112663c78f19319..d2512e13fabd1d506eedef213007db344bb773a5 100644 --- a/src/lib/elflib/elflib.cc +++ b/src/lib/elflib/elflib.cc @@ -12,6 +12,9 @@ namespace elflib { namespace { +// NT_GNU_BUILD_ID identifier. +constexpr uint64_t kNoteGnuBuildId = 3; + // Pull a null-terminated string out of an array of bytes at an offset. Returns // empty string if there is no null terminator. std::string GetNullTerminatedStringAt(const uint8_t* data, size_t data_length, @@ -442,6 +445,23 @@ std::optional<std::vector<uint8_t>> ElfLib::GetNote(const std::string& name, return std::nullopt; } +std::string ElfLib::GetGNUBuildID() { + auto note = GetNote("GNU", kNoteGnuBuildId); + if (!note) { + return std::string(); + } + + std::string ret; + + for (const auto& byte : *note) { + char buf[3]; + snprintf(buf, 3, "%02x", byte); + ret += buf; + } + + return ret; +} + ElfLib::MemoryRegion ElfLib::GetSectionData(size_t section) { const Elf64_Shdr* header = GetSectionHeader(section); diff --git a/src/lib/elflib/elflib.h b/src/lib/elflib/elflib.h index 011c6c08eff04c133284270ff8659b7b705f854e..508cda54f1bbd5e73779ec971158163177a72951 100644 --- a/src/lib/elflib/elflib.h +++ b/src/lib/elflib/elflib.h @@ -67,6 +67,10 @@ class ElfLib { std::optional<std::vector<uint8_t>> GetNote(const std::string& name, uint64_t type); + // Get the NT_GNU_BUILD_ID note as a hex string. Return empty string if we + // don't have that note. + std::string GetGNUBuildID(); + // Get a symbol from the symbol table. Return nullptr if there is no such // symbol. Pointer should live as long as the memory accessor. const Elf64_Sym* GetSymbol(const std::string& name); diff --git a/src/lib/elflib/elflib_unittest.cc b/src/lib/elflib/elflib_unittest.cc index a147e527f1523492426a031182c0e85318fef808..ea1783a7c26e4914c5083a7f15420b219e702e46 100644 --- a/src/lib/elflib/elflib_unittest.cc +++ b/src/lib/elflib/elflib_unittest.cc @@ -283,6 +283,9 @@ TEST(ElfLib, GetNote) { for (size_t i = 0; i < 32; i++) { EXPECT_EQ(i % 8, data[i]); } + + EXPECT_EQ("0001020304050607000102030405060700010203040506070001020304050607", + elf->GetGNUBuildID()); } TEST(ElfLib, MissingSections) {