Merge remote-tracking branch 'upstream/master' Change-Id: I2e2358d08907148a5e12f3d4023e9002137148d6
diff --git a/.gitignore b/.gitignore index 2075731..67d9094 100644 --- a/.gitignore +++ b/.gitignore
@@ -4,3 +4,17 @@ /bloaty !tests/testdata/** *.dSYM +.vscode +.clangd +.cache +/build +/CMakeFiles +/Testing +CMakeCache.txt +CTestTestfile.cmake +DartConfiguration.tcl +Makefile +bloaty_package.bloaty +capstone.pc +cmake_install.cmake +compile_commands.json
diff --git a/.gitmodules b/.gitmodules index 712299f..4d4ec30 100644 --- a/.gitmodules +++ b/.gitmodules
@@ -16,6 +16,9 @@ [submodule "third_party/demumble"] path = third_party/demumble url = https://github.com/nico/demumble.git +[submodule "third_party/rustc-demangle"] + path = third_party/rustc-demangle + url = https://github.com/alexcrichton/rustc-demangle.git [submodule "third_party/zlib"] path = third_party/zlib url = https://github.com/madler/zlib
diff --git a/CMakeLists.txt b/CMakeLists.txt index f763653..680b958 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -1,11 +1,12 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.9) cmake_policy(SET CMP0048 NEW) if(POLICY CMP0091) cmake_policy(SET CMP0091 NEW) endif() project (Bloaty VERSION 1.1) -include(CTest) set(CMAKE_CXX_STANDARD 17) +set(CMAKE_GENERATOR "Ninja") +include(CTest) set_property(GLOBAL PROPERTY USE_FOLDERS ON) # Group projects in visual studio # Options we define for users. @@ -15,6 +16,18 @@ option(BLOATY_ENABLE_BUILDID "Enable build id." ON) option(BLOATY_ENABLE_RE2 "Enable the support for regular expression functions." ON) +if(APPLE) +# When building bloaty on macOS infra builders, +# need to add the libc++.a from the CIPD toolchain, otherwise there are linker errors +# e.g. https://ci.chromium.org/p/fuchsia/builders/try/bloaty-x64-mac/b8874842054253378656? +# +# To achieve this, find the libc++.a location from the location of the clang binary. +# CMAKE_CXX_COMPILER +get_filename_component(CLANG_CIPD_TOOLCHAIN_DIR ${CMAKE_CXX_COMPILER} DIRECTORY CACHE) +set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} ${CLANG_CIPD_TOOLCHAIN_DIR}/../lib/libc++.a") +endif(APPLE) + if(UNIX OR MINGW) find_package(PkgConfig) find_package(ZLIB) @@ -22,7 +35,9 @@ pkg_search_module(RE2 re2) endif() pkg_search_module(CAPSTONE capstone) -pkg_search_module(PROTOBUF protobuf) +# Always use bundled protobuf, to accommodate building for other systems +# without protobuf installed. +# pkg_search_module(PROTOBUF protobuf) if(BLOATY_ENABLE_RE2) if(RE2_FOUND) MESSAGE(STATUS "System re2 found, using") @@ -130,6 +145,7 @@ set(CAPSTONE_BUILD_SHARED OFF CACHE BOOL "Build shared library" FORCE) set(CAPSTONE_BUILD_TESTS OFF CACHE BOOL "Build tests" FORCE) + add_subdirectory(third_party/capstone) include_directories(third_party/capstone/include) set_property(TARGET capstone-static PROPERTY FOLDER "third_party") @@ -155,19 +171,29 @@ include_directories(.) include_directories(src) include_directories(third_party/abseil-cpp) +include_directories(third_party/rustc-demangle/crates/capi/include) include_directories("${CMAKE_CURRENT_BINARY_DIR}/src") # Baseline build flags. if(MSVC) set(CMAKE_CXX_FLAGS "/EHsc /wd4018 /D_CRT_SECURE_NO_WARNINGS /DNOMINMAX") else() - set(CMAKE_CXX_FLAGS "-W -Wall -Wno-sign-compare") + set(CMAKE_CXX_FLAGS "-std=c++17 -W -Wall -Wno-sign-compare") set(CMAKE_CXX_FLAGS_DEBUG "-g1") - set(CMAKE_CXX_FLAGS_RELEASE "-O2") + set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g1") set_source_files_properties(third_party/demumble/third_party/libcxxabi/cxa_demangle.cpp PROPERTIES COMPILE_FLAGS -Wno-implicit-fallthrough) endif() +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fexceptions -ffunction-sections -fdata-sections") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -ffunction-sections -fdata-sections") + +if (APPLE) +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip") +else() +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") +endif() + if(APPLE) elseif(UNIX) if(BLOATY_ENABLE_BUILDID) @@ -206,6 +232,13 @@ --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/src -I${CMAKE_CURRENT_SOURCE_DIR}/src ) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/report.pb.cc + DEPENDS protoc ${CMAKE_CURRENT_SOURCE_DIR}/src/report.proto + COMMAND protoc ${CMAKE_CURRENT_SOURCE_DIR}/src/report.proto + --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/src + -I${CMAKE_CURRENT_SOURCE_DIR}/src +) else() add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.cc @@ -213,6 +246,12 @@ --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/src -I${CMAKE_CURRENT_SOURCE_DIR}/src ) +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/src/report.pb.cc + COMMAND protoc ${CMAKE_CURRENT_SOURCE_DIR}/src/report.proto + --cpp_out=${CMAKE_CURRENT_BINARY_DIR}/src + -I${CMAKE_CURRENT_SOURCE_DIR}/src +) endif() file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/bloaty_package.bloaty @@ -223,6 +262,7 @@ src/bloaty.h src/disassemble.cc ${CMAKE_CURRENT_BINARY_DIR}/src/bloaty.pb.cc + ${CMAKE_CURRENT_BINARY_DIR}/src/report.pb.cc src/dwarf/attr.h src/dwarf/attr.cc src/dwarf/dwarf_util.cc @@ -232,6 +272,7 @@ src/dwarf_constants.h src/eh_frame.cc src/elf.cc + src/link_map.cc src/macho.cc src/pe.cc third_party/lief_pe/pe_structures.h @@ -241,6 +282,7 @@ src/util.cc src/util.h src/webassembly.cc + src/write_bloaty_report.cc # Until Abseil has a proper CMake build system third_party/abseil-cpp/absl/base/internal/raw_logging.cc # Grrrr... third_party/abseil-cpp/absl/base/internal/throw_delegate.cc @@ -266,6 +308,53 @@ ) set_property(TARGET libbloaty PROPERTY FOLDER "bloaty") +# Teach CMake how to build rustc-demangle +# RUST_TOOLCHAIN_PREFIX is the directory containing the `cargo` executable. +if(NOT RUST_TOOLCHAIN_PREFIX) + find_program(CARGO_PATH cargo) + if(CARGO_PATH) + message(STATUS "Found cargo: ${CARGO_PATH}") + else() + set(HAVE_GMSH NO) + message(FATAL_ERROR "cargo not found. Please specify RUST_TOOLCHAIN_PREFIX") + endif() +else() + set(CARGO_PATH ${RUST_TOOLCHAIN_PREFIX}/cargo CACHE FILEPATH "Path to the cargo executable") +endif() +file( + GLOB_RECURSE + RUSTC_DEMANGLE_SRC_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/*.rs" + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/*.c" + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/*.toml" + ) +if(NOT APPLE) + # Rust uses libunwind, and embeds the libunwind runtime inside any static + # library produced from the build. + # Bloaty uses C++ exceptions to trigger failure. Use of C++ exceptions also + # requires libunwind, but specifically the version coming with GCC/Clang. + # Their interaction causes strange failures when compiled using GCC 9.3. + # To fix this, take out the unwind-related members from the Rust static lib. + set(RUSTC_DEMANGLE_FIXUP_ARCHIVE ar d target/release/librustc_demangle.a Unwind-EHABI.o Unwind-seh.o Unwind-sjlj.o UnwindLevel1-gcc-ext.o UnwindLevel1.o UnwindRegistersRestore.o UnwindRegistersSave.o libunwind.o) +else() + # On macOS, there doesn't appear to be such problem. + set(RUSTC_DEMANGLE_FIXUP_ARCHIVE "") +endif() +add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/target/release/librustc_demangle.a + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/Cargo.toml + DEPENDS ${RUSTC_DEMANGLE_SRC_FILES} + COMMAND ${CARGO_PATH} build -p rustc-demangle-capi --release + COMMAND ${RUSTC_DEMANGLE_FIXUP_ARCHIVE} +) +add_custom_target( + rustc-demangle-lib + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/target/release/librustc_demangle.a +) +add_dependencies(libbloaty rustc-demangle-lib) + if(UNIX OR MINGW) set(LIBBLOATY_LIBS libbloaty) if(PROTOBUF_FOUND) @@ -290,8 +379,11 @@ else() list(APPEND LIBBLOATY_LIBS zlibstatic) endif() + + set(LIBBLOATY_LIBS ${LIBBLOATY_LIBS} "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/target/release/librustc_demangle.a" dl) + else() - set(LIBBLOATY_LIBS libbloaty libprotoc capstone-static) + set(LIBBLOATY_LIBS libbloaty libprotoc capstone-static "${CMAKE_CURRENT_SOURCE_DIR}/third_party/rustc-demangle/target/release/librustc_demangle.a" dl) if(BLOATY_ENABLE_RE2) list(APPEND LIBBLOATY_LIBS re2) endif() @@ -319,9 +411,29 @@ add_executable(fuzz_target tests/fuzz_target.cc) target_link_libraries(fuzz_target ${LIBBLOATY_LIBS} $ENV{LIB_FUZZING_ENGINE}) else() + include(CheckIPOSupported) + check_ipo_supported(RESULT ipo_supported OUTPUT ipo_error) + add_executable(bloaty src/main.cc) target_link_libraries(bloaty ${LIBBLOATY_LIBS}) + if(ipo_supported) + message(STATUS "IPO / LTO enabled") + set_property(TARGET bloaty PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(STATUS "IPO / LTO not supported: <${ipo_error}>") + endif() + + # All of this is to add -pthread, which is required by re2 (not us). + find_package(Threads REQUIRED) + if(THREADS_HAVE_PTHREAD_ARG) + set_property(TARGET bloaty PROPERTY COMPILE_OPTIONS "-pthread") + set_property(TARGET bloaty PROPERTY INTERFACE_COMPILE_OPTIONS "-pthread") + endif() + if(CMAKE_THREAD_LIBS_INIT) + target_link_libraries(bloaty "${CMAKE_THREAD_LIBS_INIT}") + endif() + set_property(TARGET bloaty PROPERTY FOLDER "bloaty") if(BLOATY_ENABLE_CMAKETARGETS) @@ -368,8 +480,10 @@ set(TEST_TARGETS bloaty_test + bloaty_report_test bloaty_test_pe bloaty_misc_test + link_map_test range_map_test ) @@ -389,9 +503,11 @@ file(GLOB fuzz_corpus tests/testdata/fuzz_corpus/*) + add_test(NAME link_map_test COMMAND link_map_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/link_map) add_test(NAME range_map_test COMMAND range_map_test) add_test(NAME bloaty_test_x86-64 COMMAND bloaty_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/linux-x86_64) add_test(NAME bloaty_test_x86 COMMAND bloaty_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/linux-x86) + add_test(NAME bloaty_report_test_x86-64 COMMAND bloaty_report_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/linux-x86_64) add_test(NAME bloaty_test_pe_x64 COMMAND bloaty_test_pe WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/PE/x64) add_test(NAME bloaty_test_pe_x86 COMMAND bloaty_test_pe WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/PE/x86) add_test(NAME bloaty_misc_test COMMAND bloaty_misc_test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests/testdata/misc)
diff --git a/build_all.sh b/build_all.sh new file mode 100755 index 0000000..7321781 --- /dev/null +++ b/build_all.sh
@@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -xeuf -o pipefail + +pushd "${0%/*}" + +rm -rf build +# Mimic the LUCI environment defined in +# https://fuchsia.googlesource.com/infra/recipes/+/refs/heads/master/recipes/bloaty.py#59 +export CC=`which clang` +export CXX=`which clang++` +# On LUCI, we should set the `RUST_TOOLCHAIN_PREFIX` variable. +# For local builds, cmake will try to detect its location automatically. +cmake . -DCMAKE_GENERATOR=Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=On -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXE_LINKER_FLAGS='-static-libstdc++ -ldl -lpthread' -Bbuild +ninja -C build +ninja -C build test + +popd
diff --git a/src/bloaty.cc b/src/bloaty.cc index ec94647..5ebe13a 100644 --- a/src/bloaty.cc +++ b/src/bloaty.cc
@@ -28,12 +28,14 @@ #include <atomic> #include <cmath> +#include <filesystem> #include <fstream> #include <iostream> #include <limits> #include <map> #include <memory> #include <mutex> +#include <regex> #include <sstream> #include <string> #include <thread> @@ -57,10 +59,11 @@ #include "absl/strings/substitute.h" #include "bloaty.h" #include "bloaty.pb.h" +#include "demangle.h" +#include "rustc_demangle.h" #include "google/protobuf/io/zero_copy_stream_impl.h" #include "google/protobuf/text_format.h" #include "re.h" -#include "util.h" using absl::string_view; @@ -80,6 +83,8 @@ constexpr DataSourceDefinition data_sources[] = { {DataSource::kArchiveMembers, "armembers", "the .o files in a .a file"}, + {DataSource::kAccessPattern, "accesspattern", "which regions in the file" + "is accessed at run-time"}, {DataSource::kCompileUnits, "compileunits", "source file for the .o file (translation unit). requires debug info."}, {DataSource::kInputFiles, "inputfiles", @@ -161,6 +166,23 @@ } } +namespace { + +std::string DemangleRustSymbol(std::string_view mangled) { + constexpr size_t kBufferSize = 8192; + std::unique_ptr<std::array<char, kBufferSize>> buffer = + std::make_unique<std::array<char, kBufferSize>>(); + int result = rustc_demangle(mangled.data(), buffer->data(), kBufferSize); + if (result == 1) { + return std::string(buffer->data()); + } else { + return ""; + } +} + +} // namespace + + extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status); @@ -176,12 +198,48 @@ demangle_from.remove_prefix(1); } + if (absl::StartsWith(demangle_from, "_R")) { + // Demangle Rust symbols + std::string ret = DemangleRustSymbol(demangle_from); + if (!ret.empty()) { + return ret; + } + } + + if (absl::StartsWith(demangle_from, "switch.table._R")) { + // Demangle Rust symbols for switch tables + demangle_from.remove_prefix(13); + std::string ret = DemangleRustSymbol(demangle_from); + if (!ret.empty()) { + return "switch.table." + ret; + } + } + + if (absl::StartsWith(demangle_from, ".Lswitch.table._R")) { + // Demangle Rust symbols for switch tables, with ".L" prefix. + demangle_from.remove_prefix(15); + std::string ret = DemangleRustSymbol(demangle_from); + if (!ret.empty()) { + return "switch.table." + ret; + } + } + if (source == DataSource::kShortSymbols) { - char demangled[1024]; + char demangled[4096]; if (absl::debugging_internal::Demangle(demangle_from.data(), demangled, sizeof(demangled))) { return std::string(demangled); } else { + // TODO(yifeit): Certain symbols have dots (".") in them. Those are not allowed. + // Find and remove the last "." and anything after. + auto pos = demangle_from.find("."); + if (pos != absl::string_view::npos) { + demangle_from.remove_suffix(demangle_from.length() - pos); + std::string shortened(demangle_from); + if (absl::debugging_internal::Demangle(shortened.c_str(), demangled, sizeof(demangled))) { + return std::string(demangled); + } + } return std::string(symbol); } } else if (source == DataSource::kFullSymbols) { @@ -191,6 +249,20 @@ free(demangled); return ret; } else { + // TODO(yifeit): Certain symbols have dots (".") in them. Those are not allowed. + // Find and remove the last "." and anything after. + auto pos = demangle_from.find("."); + if (pos != absl::string_view::npos) { + demangle_from.remove_suffix(demangle_from.length() - pos); + std::string shortened(demangle_from); + char* demangled = + __cxa_demangle(shortened.c_str(), NULL, NULL, NULL); + if (demangled) { + std::string ret(demangled); + free(demangled); + return ret; + } + } return std::string(symbol); } } else { @@ -694,6 +766,9 @@ case bloaty::OutputFormat::kTSV: PrintToCSV(out, /*tabs=*/true, options.showAllSizesCSV); break; + case bloaty::OutputFormat::kProtobuf: + PrintToProtobuf(out); + break; default: BLOATY_UNREACHABLE(); } @@ -881,6 +956,7 @@ } } + // RangeMap //////////////////////////////////////////////////////////////////// constexpr uint64_t RangeSink::kUnknownSize; @@ -1461,6 +1537,8 @@ void AddFilename(const std::string& filename, bool base_file); void AddDebugFilename(const std::string& filename); + void AddLinkMapFilename(const std::string& filename); + void SetColdBytesFilter(const std::string& frequencies); size_t GetSourceCount() const { return sources_.size(); } @@ -1529,6 +1607,8 @@ std::vector<InputFileInfo> base_files_; std::map<std::string, std::string> debug_files_; + // "foo" -> "some/path/foo.map" + std::map<std::string, std::string> link_map_files_; // For allocating memory, like to decompress compressed sections. std::unique_ptr<google::protobuf::Arena> arena_; }; @@ -1540,10 +1620,27 @@ AddBuiltInSources(data_sources, options); } +std::string GetPathStem(const std::string& filename) { + std::regex filename_regex(R"([^\\\/]+(?=\.[\w]+$)|[^\\\/]+$)"); + std::smatch m; + if (!std::regex_search(filename, m, filename_regex)) { + THROWF("Could not extract stem from $0", filename); + } + std::string stem = m.str(0); + return stem; +} + std::unique_ptr<ObjectFile> Bloaty::GetObjectFile( const std::string& filename) const { std::unique_ptr<InputFile> file(file_factory_.OpenFile(filename)); - auto object_file = TryOpenELFFile(file); + + std::string stem = GetPathStem(filename); + std::optional<std::string> link_map_file; + if (link_map_files_.find(stem) != link_map_files_.end()) { + link_map_file = link_map_files_.at(stem); + } + + auto object_file = TryOpenELFFile(file, link_map_file); if (!object_file.get()) { object_file = TryOpenMachOFile(file); @@ -1585,6 +1682,11 @@ debug_files_[build_id] = filename; } +void Bloaty::AddLinkMapFilename(const std::string& filename) { + std::string stem = GetPathStem(filename); + link_map_files_[stem] = filename; +} + void Bloaty::DefineCustomDataSource(const CustomDataSource& source) { if (source.base_data_source() == "symbols") { THROW( @@ -1936,6 +2038,10 @@ -c FILE Load configuration from <file>. -d SOURCE,SOURCE Comma-separated list of sources to scan. --debug-file=FILE Use this file for debug symbols and/or symbol table. + --link-map-file=FILE + Use this file for identifying a link map associated with + a binary. The link map and the binary must share the same + base name (e.g. `foo.map` and `foo`) -C MODE How to demangle symbols. Possible values are: --demangle=MODE --demangle=none no demangling, print raw symbols --demangle=short demangle, but omit arg/return types @@ -2089,6 +2195,8 @@ output_options->output_format = OutputFormat::kCSV; } else if (args.TryParseFlag("--tsv")) { output_options->output_format = OutputFormat::kTSV; + } else if (args.TryParseFlag("--pb")) { + output_options->output_format = OutputFormat::kProtobuf; } else if (args.TryParseFlag("--raw-map")) { options->set_dump_raw_map(true); } else if (args.TryParseOption("-c", &option)) { @@ -2118,6 +2226,12 @@ } } else if (args.TryParseOption("--debug-file", &option)) { options->add_debug_filename(std::string(option)); + } else if (args.TryParseOption("--link-map-file", &option)) { + options->add_link_map_filename(std::string(option)); + } else if (args.TryParseOption("--cold-bytes-filter", &option)) { + options->set_cold_bytes_filter(std::string(option)); + } else if (args.TryParseUint64Option("--access-pattern-frame-size", &uint64_option)) { + options->set_access_pattern_frame_size(uint64_option); } else if (args.TryParseUint64Option("--debug-fileoff", &uint64_option)) { if (options->has_debug_fileoff()) { THROW("currently we only support a single debug fileoff"); @@ -2216,6 +2330,20 @@ } } + if (output_options->output_format == OutputFormat::kProtobuf) { + if ((options->data_source_size() != 2 || + options->data_source()[0] != "compileunits" || + options->data_source()[1] != "symbols") && + (options->data_source_size() != 3 || + options->data_source()[0] != "accesspattern" || + options->data_source()[1] != "compileunits" || + options->data_source()[2] != "symbols")) + { + THROW("Protobuf output only supports '-d compileunits,symbols' " + "or '-d accesspattern,compileunits,symbols' for now"); + } + } + return true; } @@ -2253,6 +2381,10 @@ bloaty.AddDebugFilename(debug_filename); } + for (auto& link_map_filename : options.link_map_filename()) { + bloaty.AddLinkMapFilename(link_map_filename); + } + for (const auto& custom_data_source : options.custom_data_source()) { bloaty.DefineCustomDataSource(custom_data_source); }
diff --git a/src/bloaty.h b/src/bloaty.h index 91a8d10..a41a67c 100644 --- a/src/bloaty.h +++ b/src/bloaty.h
@@ -39,6 +39,7 @@ #include "bloaty.pb.h" #include "range_map.h" #include "re.h" +#include "util.h" namespace bloaty { @@ -51,6 +52,7 @@ enum class DataSource { kArchiveMembers, + kAccessPattern, kCompileUnits, kInlines, kInputFiles, @@ -264,6 +266,9 @@ : file_data_(std::move(file_data)), debug_file_(this) {} virtual ~ObjectFile() {} + // Searches for a binary string representing the identity of this object file. + // It is typically a hash of the unstripped object during the build. + // If not found, returns an empty string. virtual std::string GetBuildId() const = 0; // Process this file, pushing data to |sinks| as appropriate for each data @@ -275,6 +280,10 @@ DataSource symbol_source, DisassemblyInfo* info) const = 0; + virtual std::optional<std::unordered_map<std::string, std::string>> TakeSymbolToCrateMap() { + return std::nullopt; + } + const InputFile& file_data() const { return *file_data_; } // Sets the debug file for |this|. |file| must outlive this instance. @@ -290,7 +299,7 @@ const ObjectFile* debug_file_; }; -std::unique_ptr<ObjectFile> TryOpenELFFile(std::unique_ptr<InputFile>& file); +std::unique_ptr<ObjectFile> TryOpenELFFile(std::unique_ptr<InputFile>& file, std::optional<std::string> link_map_file); std::unique_ptr<ObjectFile> TryOpenMachOFile(std::unique_ptr<InputFile>& file); std::unique_ptr<ObjectFile> TryOpenWebAssemblyFile(std::unique_ptr<InputFile>& file); std::unique_ptr<ObjectFile> TryOpenPEFile(std::unique_ptr<InputFile>& file); @@ -312,6 +321,22 @@ // controls what demangling mode we are using. std::string ItaniumDemangle(absl::string_view symbol, DataSource source); +// Encode |symbol| and |crate| together into a single string. +std::string EncodeSymbolWithCrateId(absl::string_view symbol, absl::string_view crate); + +struct DecodeCrateIdResult { + std::string symbol; + std::string crate; +}; + +inline bool operator==(const DecodeCrateIdResult& lhs, const DecodeCrateIdResult& rhs) { + return lhs.symbol == rhs.symbol && lhs.crate == rhs.crate; +} + +// Parse out |symbol| and |crate| from a string encoded with |EncodeSymbolWithCrateId|. +// If the string was not encoded, |crate| will be the empty string. +DecodeCrateIdResult DecodeSymbolWithCrateId(absl::string_view symbol); + // DualMap ///////////////////////////////////////////////////////////////////// @@ -375,6 +400,7 @@ kPrettyPrint, kCSV, kTSV, + kProtobuf, }; enum class ShowDomain { @@ -401,7 +427,9 @@ } const std::vector<std::string>& source_names() const { return source_names_; } + void Print(const OutputOptions& options, std::ostream* out); + void SetDisassembly(absl::string_view disassembly) { disassembly_ = std::string(disassembly); } @@ -425,6 +453,7 @@ static bool IsSame(const std::string& a, const std::string& b); void PrettyPrint(const OutputOptions& options, std::ostream* out) const; void PrintToCSV(std::ostream* out, bool tabs, bool csvDiff) const; + void PrintToProtobuf(std::ostream* out) const; void PrettyPrintRow(const RollupRow& row, size_t indent, const OutputOptions& options, std::ostream* out) const; void PrettyPrintTree(const RollupRow& row, size_t indent, @@ -437,6 +466,9 @@ std::ostream* out, bool tabs, bool csvDiff) const; }; +// Shim for `std::filesystem::path(filename).stem()`. +std::string GetPathStem(const std::string& filename); + bool ParseOptions(bool skip_unknown, int* argc, char** argv[], Options* options, OutputOptions* output_options, std::string* error); bool BloatyMain(const Options& options, const InputFileFactory& file_factory,
diff --git a/src/bloaty.proto b/src/bloaty.proto index 1679328..f8bc306 100644 --- a/src/bloaty.proto +++ b/src/bloaty.proto
@@ -29,6 +29,27 @@ // debug_filename will *not* have their file size counted. repeated string debug_filename = 10; + // Link map files to assist symbol and compile unit parsing. + // We will match these to files with the same base name / stem. + // E.g. "foo.map" will be used to analyze the executable "foo". + repeated string link_map_filename = 30; + + // If set, reads how frequently a range of bytes is accessed in CSV format. + // The filter is in a sparse pattern format: assuming the frame size (see + // access_pattern_frame_size) is 8 KiB, given a 24 KiB file and a `0:1,2:3` + // pattern, it means that the first 8 KiB frame is accessed once, and the + // third 8 KiB frame is accessed 3 times. The second 8 KiB frame is left + // out, meaning it was never accessed. The frame indices in the pattern is not + // guaranteed to be sorted. This option works together with the + // `accesspattern` data source. When the `accesspattern` data source is the + // first data source, the protobuf output format will only output items whose + // region overlaps with a never-accessed frame. + optional string cold_bytes_filter = 31; + + // If set, customizes how many bytes is in a frame as used by the pattern + // in cold_bytes_filter. + optional uint64 access_pattern_frame_size = 32 [default = 8192]; + // The data sources to scan in each file. At least one data source must be // specified. If more than one source is specified, the output is // hierarchical.
diff --git a/src/demangle.cc b/src/demangle.cc new file mode 100644 index 0000000..284782e --- /dev/null +++ b/src/demangle.cc
@@ -0,0 +1,1901 @@ +// Copyright 2018 The Abseil Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// For reference check out: +// https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling +// +// Note that we only have partial C++11 support yet. + +#include "demangle.h" + +#include <cstdint> +#include <cstdio> +#include <limits> + +namespace absl { +namespace debugging_internal { + +typedef struct { + const char *abbrev; + const char *real_name; + // Number of arguments in <expression> context, or 0 if disallowed. + int arity; +} AbbrevPair; + +// List of operators from Itanium C++ ABI. +static const AbbrevPair kOperatorList[] = { + // New has special syntax (not currently supported). + {"nw", "new", 0}, + {"na", "new[]", 0}, + + // Works except that the 'gs' prefix is not supported. + {"dl", "delete", 1}, + {"da", "delete[]", 1}, + + {"ps", "+", 1}, // "positive" + {"ng", "-", 1}, // "negative" + {"ad", "&", 1}, // "address-of" + {"de", "*", 1}, // "dereference" + {"co", "~", 1}, + + {"pl", "+", 2}, + {"mi", "-", 2}, + {"ml", "*", 2}, + {"dv", "/", 2}, + {"rm", "%", 2}, + {"an", "&", 2}, + {"or", "|", 2}, + {"eo", "^", 2}, + {"aS", "=", 2}, + {"pL", "+=", 2}, + {"mI", "-=", 2}, + {"mL", "*=", 2}, + {"dV", "/=", 2}, + {"rM", "%=", 2}, + {"aN", "&=", 2}, + {"oR", "|=", 2}, + {"eO", "^=", 2}, + {"ls", "<<", 2}, + {"rs", ">>", 2}, + {"lS", "<<=", 2}, + {"rS", ">>=", 2}, + {"eq", "==", 2}, + {"ne", "!=", 2}, + {"lt", "<", 2}, + {"gt", ">", 2}, + {"le", "<=", 2}, + {"ge", ">=", 2}, + {"nt", "!", 1}, + {"aa", "&&", 2}, + {"oo", "||", 2}, + {"pp", "++", 1}, + {"mm", "--", 1}, + {"cm", ",", 2}, + {"pm", "->*", 2}, + {"pt", "->", 0}, // Special syntax + {"cl", "()", 0}, // Special syntax + {"ix", "[]", 2}, + {"qu", "?", 3}, + {"st", "sizeof", 0}, // Special syntax + {"sz", "sizeof", 1}, // Not a real operator name, but used in expressions. + {nullptr, nullptr, 0}, +}; + +// List of builtin types from Itanium C++ ABI. +// +// Invariant: only one- or two-character type abbreviations here. +static const AbbrevPair kBuiltinTypeList[] = { + {"v", "void", 0}, + {"w", "wchar_t", 0}, + {"b", "bool", 0}, + {"c", "char", 0}, + {"a", "signed char", 0}, + {"h", "unsigned char", 0}, + {"s", "short", 0}, + {"t", "unsigned short", 0}, + {"i", "int", 0}, + {"j", "unsigned int", 0}, + {"l", "long", 0}, + {"m", "unsigned long", 0}, + {"x", "long long", 0}, + {"y", "unsigned long long", 0}, + {"n", "__int128", 0}, + {"o", "unsigned __int128", 0}, + {"f", "float", 0}, + {"d", "double", 0}, + {"e", "long double", 0}, + {"g", "__float128", 0}, + {"z", "ellipsis", 0}, + + {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) + {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) + {"Dc", "decltype(auto)", 0}, + {"Da", "auto", 0}, + {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) + {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) + {"Di", "char32_t", 0}, + {"Ds", "char16_t", 0}, + {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) + {nullptr, nullptr, 0}, +}; + +// List of substitutions Itanium C++ ABI. +static const AbbrevPair kSubstitutionList[] = { + {"St", "", 0}, + {"Sa", "allocator", 0}, + {"Sb", "basic_string", 0}, + // std::basic_string<char, std::char_traits<char>,std::allocator<char> > + {"Ss", "string", 0}, + // std::basic_istream<char, std::char_traits<char> > + {"Si", "istream", 0}, + // std::basic_ostream<char, std::char_traits<char> > + {"So", "ostream", 0}, + // std::basic_iostream<char, std::char_traits<char> > + {"Sd", "iostream", 0}, + {nullptr, nullptr, 0}, +}; + +// State needed for demangling. This struct is copied in almost every stack +// frame, so every byte counts. +typedef struct { + int mangled_idx; // Cursor of mangled name. + int out_cur_idx; // Cursor of output string. + int prev_name_idx; // For constructors/destructors. + signed int prev_name_length : 16; // For constructors/destructors. + signed int nest_level : 15; // For nested names. + unsigned int append : 1; // Append flag. + // Note: for some reason MSVC can't pack "bool append : 1" into the same int + // with the above two fields, so we use an int instead. Amusingly it can pack + // "signed bool" as expected, but relying on that to continue to be a legal + // type seems ill-advised (as it's illegal in at least clang). +} ParseState; + +static_assert(sizeof(ParseState) == 4 * sizeof(int), + "unexpected size of ParseState"); + +// One-off state for demangling that's not subject to backtracking -- either +// constant data, data that's intentionally immune to backtracking (steps), or +// data that would never be changed by backtracking anyway (recursion_depth). +// +// Only one copy of this exists for each call to Demangle, so the size of this +// struct is nearly inconsequential. +typedef struct { + const char *mangled_begin; // Beginning of input string. + char *out; // Beginning of output string. + int out_end_idx; // One past last allowed output character. + int recursion_depth; // For stack exhaustion prevention. + int steps; // Cap how much work we'll do, regardless of depth. + ParseState parse_state; // Backtrackable state copied for most frames. +} State; + +namespace { +// Prevent deep recursion / stack exhaustion. +// Also prevent unbounded handling of complex inputs. +// absl:google3-begin(Buganizer links) +// See also http://b/34269257 and http://b/37793125 +// absl:google3-end +class ComplexityGuard { + public: + explicit ComplexityGuard(State *state) : state_(state) { + ++state->recursion_depth; + ++state->steps; + } + ~ComplexityGuard() { --state_->recursion_depth; } + + // 256 levels of recursion seems like a reasonable upper limit on depth. + // 128 is not enough to demagle synthetic tests from demangle_unittest.txt: + // "_ZaaZZZZ..." and "_ZaaZcvZcvZ..." + static constexpr int kRecursionDepthLimit = 256; + + // We're trying to pick a charitable upper-limit on how many parse steps are + // necessary to handle something that a human could actually make use of. + // This is mostly in place as a bound on how much work we'll do if we are + // asked to demangle an mangled name from an untrusted source, so it should be + // much larger than the largest expected symbol, but much smaller than the + // amount of work we can do in, e.g., a second. + // + // Some real-world symbols from an arbitrary binary started failing between + // 2^12 and 2^13, so we multiply the latter by an extra factor of 16 to set + // the limit. + // + // Spending one second on 2^17 parse steps would require each step to take + // 7.6us, or ~30000 clock cycles, so it's safe to say this can be done in + // under a second. + static constexpr int kParseStepsLimit = 1 << 17; + + bool IsTooComplex() const { + return state_->recursion_depth > kRecursionDepthLimit || + state_->steps > kParseStepsLimit; + } + + private: + State *state_; +}; +} // namespace + +// We don't use strlen() in libc since it's not guaranteed to be async +// signal safe. +static size_t StrLen(const char *str) { + size_t len = 0; + while (*str != '\0') { + ++str; + ++len; + } + return len; +} + +// Returns true if "str" has at least "n" characters remaining. +static bool AtLeastNumCharsRemaining(const char *str, int n) { + for (int i = 0; i < n; ++i) { + if (str[i] == '\0') { + return false; + } + } + return true; +} + +// Returns true if "str" has "prefix" as a prefix. +static bool StrPrefix(const char *str, const char *prefix) { + size_t i = 0; + while (str[i] != '\0' && prefix[i] != '\0' && str[i] == prefix[i]) { + ++i; + } + return prefix[i] == '\0'; // Consumed everything in "prefix". +} + +static void InitState(State *state, const char *mangled, char *out, + int out_size) { + state->mangled_begin = mangled; + state->out = out; + state->out_end_idx = out_size; + state->recursion_depth = 0; + state->steps = 0; + + state->parse_state.mangled_idx = 0; + state->parse_state.out_cur_idx = 0; + state->parse_state.prev_name_idx = 0; + state->parse_state.prev_name_length = -1; + state->parse_state.nest_level = -1; + state->parse_state.append = true; +} + +static inline const char *RemainingInput(State *state) { + return &state->mangled_begin[state->parse_state.mangled_idx]; +} + +// Returns true and advances "mangled_idx" if we find "one_char_token" +// at "mangled_idx" position. It is assumed that "one_char_token" does +// not contain '\0'. +static bool ParseOneCharToken(State *state, const char one_char_token) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == one_char_token) { + ++state->parse_state.mangled_idx; + return true; + } + return false; +} + +// Returns true and advances "mangled_cur" if we find "two_char_token" +// at "mangled_cur" position. It is assumed that "two_char_token" does +// not contain '\0'. +static bool ParseTwoCharToken(State *state, const char *two_char_token) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == two_char_token[0] && + RemainingInput(state)[1] == two_char_token[1]) { + state->parse_state.mangled_idx += 2; + return true; + } + return false; +} + +// Returns true and advances "mangled_cur" if we find any character in +// "char_class" at "mangled_cur" position. +static bool ParseCharClass(State *state, const char *char_class) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (RemainingInput(state)[0] == '\0') { + return false; + } + const char *p = char_class; + for (; *p != '\0'; ++p) { + if (RemainingInput(state)[0] == *p) { + ++state->parse_state.mangled_idx; + return true; + } + } + return false; +} + +static bool ParseDigit(State *state, int *digit) { + char c = RemainingInput(state)[0]; + if (ParseCharClass(state, "0123456789")) { + if (digit != nullptr) { + *digit = c - '0'; + } + return true; + } + return false; +} + +// This function is used for handling an optional non-terminal. +static bool Optional(bool /*status*/) { return true; } + +// This function is used for handling <non-terminal>+ syntax. +typedef bool (*ParseFunc)(State *); +static bool OneOrMore(ParseFunc parse_func, State *state) { + if (parse_func(state)) { + while (parse_func(state)) { + } + return true; + } + return false; +} + +// This function is used for handling <non-terminal>* syntax. The function +// always returns true and must be followed by a termination token or a +// terminating sequence not handled by parse_func (e.g. +// ParseOneCharToken(state, 'E')). +static bool ZeroOrMore(ParseFunc parse_func, State *state) { + while (parse_func(state)) { + } + return true; +} + +// Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is +// set to out_end_idx+1. The output string is ensured to +// always terminate with '\0' as long as there is no overflow. +static void Append(State *state, const char *const str, const int length) { + for (int i = 0; i < length; ++i) { + if (state->parse_state.out_cur_idx + 1 < + state->out_end_idx) { // +1 for '\0' + state->out[state->parse_state.out_cur_idx++] = str[i]; + } else { + // signal overflow + state->parse_state.out_cur_idx = state->out_end_idx + 1; + break; + } + } + if (state->parse_state.out_cur_idx < state->out_end_idx) { + state->out[state->parse_state.out_cur_idx] = + '\0'; // Terminate it with '\0' + } +} + +// We don't use equivalents in libc to avoid locale issues. +static bool IsLower(char c) { return c >= 'a' && c <= 'z'; } + +static bool IsAlpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static bool IsDigit(char c) { return c >= '0' && c <= '9'; } + +// Returns true if "str" is a function clone suffix. These suffixes are used +// by GCC 4.5.x and later versions (and our locally-modified version of GCC +// 4.4.x) to indicate functions which have been cloned during optimization. +// We treat any sequence (.<alpha>+.<digit>+)+ as a function clone suffix. +static bool IsFunctionCloneSuffix(const char *str) { + size_t i = 0; + while (str[i] != '\0') { + // Consume a single .<alpha>+.<digit>+ sequence. + if (str[i] != '.' || !IsAlpha(str[i + 1])) { + return false; + } + i += 2; + while (IsAlpha(str[i])) { + ++i; + } + if (str[i] != '.' || !IsDigit(str[i + 1])) { + return false; + } + i += 2; + while (IsDigit(str[i])) { + ++i; + } + } + return true; // Consumed everything in "str". +} + +static bool EndsWith(State *state, const char chr) { + return state->parse_state.out_cur_idx > 0 && + chr == state->out[state->parse_state.out_cur_idx - 1]; +} + +// Append "str" with some tweaks, iff "append" state is true. +static void MaybeAppendWithLength(State *state, const char *const str, + const int length) { + if (state->parse_state.append && length > 0) { + // Append a space if the output buffer ends with '<' and "str" + // starts with '<' to avoid <<<. + if (str[0] == '<' && EndsWith(state, '<')) { + Append(state, " ", 1); + } + // Remember the last identifier name for ctors/dtors. + if (IsAlpha(str[0]) || str[0] == '_') { + state->parse_state.prev_name_idx = state->parse_state.out_cur_idx; + state->parse_state.prev_name_length = length; + } + Append(state, str, length); + } +} + +// Appends a positive decimal number to the output if appending is enabled. +static bool MaybeAppendDecimal(State *state, unsigned int val) { + // Max {32-64}-bit unsigned int is 20 digits. + constexpr size_t kMaxLength = 20; + char buf[kMaxLength]; + + // We can't use itoa or sprintf as neither is specified to be + // async-signal-safe. + if (state->parse_state.append) { + // We can't have a one-before-the-beginning pointer, so instead start with + // one-past-the-end and manipulate one character before the pointer. + char *p = &buf[kMaxLength]; + do { // val=0 is the only input that should write a leading zero digit. + *--p = (val % 10) + '0'; + val /= 10; + } while (p > buf && val != 0); + + // 'p' landed on the last character we set. How convenient. + Append(state, p, kMaxLength - (p - buf)); + } + + return true; +} + +// A convenient wrapper around MaybeAppendWithLength(). +// Returns true so that it can be placed in "if" conditions. +static bool MaybeAppend(State *state, const char *const str) { + if (state->parse_state.append) { + int length = StrLen(str); + MaybeAppendWithLength(state, str, length); + } + return true; +} + +// This function is used for handling nested names. +static bool EnterNestedName(State *state) { + state->parse_state.nest_level = 0; + return true; +} + +// This function is used for handling nested names. +static bool LeaveNestedName(State *state, int16_t prev_value) { + state->parse_state.nest_level = prev_value; + return true; +} + +// Disable the append mode not to print function parameters, etc. +static bool DisableAppend(State *state) { + state->parse_state.append = false; + return true; +} + +// Restore the append mode to the previous state. +static bool RestoreAppend(State *state, bool prev_value) { + state->parse_state.append = prev_value; + return true; +} + +// Increase the nest level for nested names. +static void MaybeIncreaseNestLevel(State *state) { + if (state->parse_state.nest_level > -1) { + ++state->parse_state.nest_level; + } +} + +// Appends :: for nested names if necessary. +static void MaybeAppendSeparator(State *state) { + if (state->parse_state.nest_level >= 1) { + MaybeAppend(state, "::"); + } +} + +// Cancel the last separator if necessary. +static void MaybeCancelLastSeparator(State *state) { + if (state->parse_state.nest_level >= 1 && state->parse_state.append && + state->parse_state.out_cur_idx >= 2) { + state->parse_state.out_cur_idx -= 2; + state->out[state->parse_state.out_cur_idx] = '\0'; + } +} + +// Returns true if the identifier of the given length pointed to by +// "mangled_cur" is anonymous namespace. +static bool IdentifierIsAnonymousNamespace(State *state, int length) { + // Returns true if "anon_prefix" is a proper prefix of "mangled_cur". + static const char anon_prefix[] = "_GLOBAL__N_"; + return (length > static_cast<int>(sizeof(anon_prefix) - 1) && + StrPrefix(RemainingInput(state), anon_prefix)); +} + +// Forward declarations of our parsing functions. +static bool ParseMangledName(State *state); +static bool ParseEncoding(State *state); +static bool ParseName(State *state); +static bool ParseUnscopedName(State *state); +static bool ParseNestedName(State *state); +static bool ParsePrefix(State *state); +static bool ParseUnqualifiedName(State *state); +static bool ParseSourceName(State *state); +static bool ParseLocalSourceName(State *state); +static bool ParseUnnamedTypeName(State *state); +static bool ParseNumber(State *state, int *number_out); +static bool ParseFloatNumber(State *state); +static bool ParseSeqId(State *state); +static bool ParseIdentifier(State *state, int length); +static bool ParseOperatorName(State *state, int *arity); +static bool ParseSpecialName(State *state); +static bool ParseCallOffset(State *state); +static bool ParseNVOffset(State *state); +static bool ParseVOffset(State *state); +static bool ParseCtorDtorName(State *state); +static bool ParseDecltype(State *state); +static bool ParseType(State *state); +static bool ParseCVQualifiers(State *state); +static bool ParseBuiltinType(State *state); +static bool ParseFunctionType(State *state); +static bool ParseBareFunctionType(State *state); +static bool ParseClassEnumType(State *state); +static bool ParseArrayType(State *state); +static bool ParsePointerToMemberType(State *state); +static bool ParseTemplateParam(State *state); +static bool ParseTemplateTemplateParam(State *state); +static bool ParseTemplateArgs(State *state); +static bool ParseTemplateArg(State *state); +static bool ParseBaseUnresolvedName(State *state); +static bool ParseUnresolvedName(State *state); +static bool ParseExpression(State *state); +static bool ParseExprPrimary(State *state); +static bool ParseExprCastValue(State *state); +static bool ParseLocalName(State *state); +static bool ParseLocalNameSuffix(State *state); +static bool ParseDiscriminator(State *state); +static bool ParseSubstitution(State *state, bool accept_std); + +// Implementation note: the following code is a straightforward +// translation of the Itanium C++ ABI defined in BNF with a couple of +// exceptions. +// +// - Support GNU extensions not defined in the Itanium C++ ABI +// - <prefix> and <template-prefix> are combined to avoid infinite loop +// - Reorder patterns to shorten the code +// - Reorder patterns to give greedier functions precedence +// We'll mark "Less greedy than" for these cases in the code +// +// Each parsing function changes the parse state and returns true on +// success, or returns false and doesn't change the parse state (note: +// the parse-steps counter increases regardless of success or failure). +// To ensure that the parse state isn't changed in the latter case, we +// save the original state before we call multiple parsing functions +// consecutively with &&, and restore it if unsuccessful. See +// ParseEncoding() as an example of this convention. We follow the +// convention throughout the code. +// +// Originally we tried to do demangling without following the full ABI +// syntax but it turned out we needed to follow the full syntax to +// parse complicated cases like nested template arguments. Note that +// implementing a full-fledged demangler isn't trivial (libiberty's +// cp-demangle.c has +4300 lines). +// +// Note that (foo) in <(foo) ...> is a modifier to be ignored. +// +// Reference: +// - Itanium C++ ABI +// <https://mentorembedded.github.io/cxx-abi/abi.html#mangling> + +// <mangled-name> ::= _Z <encoding> +static bool ParseMangledName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); +} + +// <encoding> ::= <(function) name> <bare-function-type> +// ::= <(data) name> +// ::= <special-name> +static bool ParseEncoding(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + // Implementing the first two productions together as <name> + // [<bare-function-type>] avoids exponential blowup of backtracking. + // + // Since Optional(...) can't fail, there's no need to copy the state for + // backtracking. + if (ParseName(state) && Optional(ParseBareFunctionType(state))) { + return true; + } + + if (ParseSpecialName(state)) { + return true; + } + return false; +} + +// <name> ::= <nested-name> +// ::= <unscoped-template-name> <template-args> +// ::= <unscoped-name> +// ::= <local-name> +static bool ParseName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseNestedName(state) || ParseLocalName(state)) { + return true; + } + + // We reorganize the productions to avoid re-parsing unscoped names. + // - Inline <unscoped-template-name> productions: + // <name> ::= <substitution> <template-args> + // ::= <unscoped-name> <template-args> + // ::= <unscoped-name> + // - Merge the two productions that start with unscoped-name: + // <name> ::= <unscoped-name> [<template-args>] + + ParseState copy = state->parse_state; + // "std<...>" isn't a valid name. + if (ParseSubstitution(state, /*accept_std=*/false) && + ParseTemplateArgs(state)) { + return true; + } + state->parse_state = copy; + + // Note there's no need to restore state after this since only the first + // subparser can fail. + return ParseUnscopedName(state) && Optional(ParseTemplateArgs(state)); +} + +// <unscoped-name> ::= <unqualified-name> +// ::= St <unqualified-name> +static bool ParseUnscopedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseUnqualifiedName(state)) { + return true; + } + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") && + ParseUnqualifiedName(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <ref-qualifer> ::= R // lvalue method reference qualifier +// ::= O // rvalue method reference qualifier +static inline bool ParseRefQualifier(State *state) { + return ParseCharClass(state, "OR"); +} + +// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> +// <unqualified-name> E +// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> +// <template-args> E +static bool ParseNestedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'N') && EnterNestedName(state) && + Optional(ParseCVQualifiers(state)) && + Optional(ParseRefQualifier(state)) && ParsePrefix(state) && + LeaveNestedName(state, copy.nest_level) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return false; +} + +// This part is tricky. If we literally translate them to code, we'll +// end up infinite loop. Hence we merge them to avoid the case. +// +// <prefix> ::= <prefix> <unqualified-name> +// ::= <template-prefix> <template-args> +// ::= <template-param> +// ::= <substitution> +// ::= # empty +// <template-prefix> ::= <prefix> <(template) unqualified-name> +// ::= <template-param> +// ::= <substitution> +static bool ParsePrefix(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + bool has_something = false; + while (true) { + MaybeAppendSeparator(state); + if (ParseTemplateParam(state) || + ParseSubstitution(state, /*accept_std=*/true) || + ParseUnscopedName(state) || + // absl:google3-begin(Internal link) + // Lambda initializer scope: + // http://google3/third_party/binutils/binutils/libiberty/cp-demangle.c?l=1566&rcl=181793049 + // absl:google3-end + (ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) { + has_something = true; + MaybeIncreaseNestLevel(state); + continue; + } + MaybeCancelLastSeparator(state); + if (has_something && ParseTemplateArgs(state)) { + return ParsePrefix(state); + } else { + break; + } + } + return true; +} + +// <unqualified-name> ::= <operator-name> +// ::= <ctor-dtor-name> +// ::= <source-name> +// ::= <local-source-name> // GCC extension; see below. +// ::= <unnamed-type-name> +static bool ParseUnqualifiedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || + ParseSourceName(state) || ParseLocalSourceName(state) || + ParseUnnamedTypeName(state)); +} + +// <source-name> ::= <positive length number> <identifier> +static bool ParseSourceName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + int length = -1; + if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <local-source-name> ::= L <source-name> [<discriminator>] +// +// References: +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +// https://gcc.gnu.org/viewcvs?view=rev&revision=124467 +static bool ParseLocalSourceName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && + Optional(ParseDiscriminator(state))) { + return true; + } + state->parse_state = copy; + return false; +} + +// <unnamed-type-name> ::= Ut [<(nonnegative) number>] _ +// ::= <closure-type-name> +// <closure-type-name> ::= Ul <lambda-sig> E [<(nonnegative) number>] _ +// <lambda-sig> ::= <(parameter) type>+ +static bool ParseUnnamedTypeName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + // Type's 1-based index n is encoded as { "", n == 1; itoa(n-2), otherwise }. + // Optionally parse the encoded value into 'which' and add 2 to get the index. + int which = -1; + + // Unnamed type local to function or class. + if (ParseTwoCharToken(state, "Ut") && Optional(ParseNumber(state, &which)) && + which <= std::numeric_limits<int>::max() - 2 && // Don't overflow. + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "{unnamed type#"); + MaybeAppendDecimal(state, 2 + which); + MaybeAppend(state, "}"); + return true; + } + state->parse_state = copy; + + // Closure type. + which = -1; + if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) && + OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) && + ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) && + which <= std::numeric_limits<int>::max() - 2 && // Don't overflow. + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "{lambda()#"); + MaybeAppendDecimal(state, 2 + which); + MaybeAppend(state, "}"); + return true; + } + state->parse_state = copy; + + return false; +} + +// <number> ::= [n] <non-negative decimal integer> +// If "number_out" is non-null, then *number_out is set to the value of the +// parsed number on success. +static bool ParseNumber(State *state, int *number_out) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + bool negative = false; + if (ParseOneCharToken(state, 'n')) { + negative = true; + } + const char *p = RemainingInput(state); + uint64_t number = 0; + for (; *p != '\0'; ++p) { + if (IsDigit(*p)) { + number = number * 10 + (*p - '0'); + } else { + break; + } + } + // Apply the sign with uint64 arithmetic so overflows aren't UB. Gives + // "incorrect" results for out-of-range inputs, but negative values only + // appear for literals, which aren't printed. + if (negative) { + number = ~number + 1; + } + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); + if (number_out != nullptr) { + // Note: possibly truncate "number". + *number_out = number; + } + return true; + } + return false; +} + +// Floating-point literals are encoded using a fixed-length lowercase +// hexadecimal string. +static bool ParseFloatNumber(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + const char *p = RemainingInput(state); + for (; *p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { + break; + } + } + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); + return true; + } + return false; +} + +// The <seq-id> is a sequence number in base 36, +// using digits and upper case letters +static bool ParseSeqId(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + const char *p = RemainingInput(state); + for (; *p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { + break; + } + } + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); + return true; + } + return false; +} + +// <identifier> ::= <unqualified source code identifier> (of given length) +static bool ParseIdentifier(State *state, int length) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (length < 0 || !AtLeastNumCharsRemaining(RemainingInput(state), length)) { + return false; + } + if (IdentifierIsAnonymousNamespace(state, length)) { + MaybeAppend(state, "(anonymous namespace)"); + } else { + MaybeAppendWithLength(state, RemainingInput(state), length); + } + state->parse_state.mangled_idx += length; + return true; +} + +// <operator-name> ::= nw, and other two letters cases +// ::= cv <type> # (cast) +// ::= v <digit> <source-name> # vendor extended operator +static bool ParseOperatorName(State *state, int *arity) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (!AtLeastNumCharsRemaining(RemainingInput(state), 2)) { + return false; + } + // First check with "cv" (cast) case. + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") && + EnterNestedName(state) && ParseType(state) && + LeaveNestedName(state, copy.nest_level)) { + if (arity != nullptr) { + *arity = 1; + } + return true; + } + state->parse_state = copy; + + // Then vendor extended operators. + if (ParseOneCharToken(state, 'v') && ParseDigit(state, arity) && + ParseSourceName(state)) { + return true; + } + state->parse_state = copy; + + // Other operator names should start with a lower alphabet followed + // by a lower/upper alphabet. + if (!(IsLower(RemainingInput(state)[0]) && + IsAlpha(RemainingInput(state)[1]))) { + return false; + } + // We may want to perform a binary search if we really need speed. + const AbbrevPair *p; + for (p = kOperatorList; p->abbrev != nullptr; ++p) { + if (RemainingInput(state)[0] == p->abbrev[0] && + RemainingInput(state)[1] == p->abbrev[1]) { + if (arity != nullptr) { + *arity = p->arity; + } + MaybeAppend(state, "operator"); + if (IsLower(*p->real_name)) { // new, delete, etc. + MaybeAppend(state, " "); + } + MaybeAppend(state, p->real_name); + state->parse_state.mangled_idx += 2; + return true; + } + } + return false; +} + +// <special-name> ::= TV <type> +// ::= TT <type> +// ::= TI <type> +// ::= TS <type> +// ::= Tc <call-offset> <call-offset> <(base) encoding> +// ::= GV <(object) name> +// ::= T <call-offset> <(base) encoding> +// G++ extensions: +// ::= TC <type> <(offset) number> _ <(base) type> +// ::= TF <type> +// ::= TJ <type> +// ::= GR <name> +// ::= GA <encoding> +// ::= Th <call-offset> <(base) encoding> +// ::= Tv <call-offset> <(base) encoding> +// +// Note: we don't care much about them since they don't appear in +// stack traces. The are special data. +static bool ParseSpecialName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTIS") && + ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "GV") && ParseName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && + ParseEncoding(state)) { + return true; + } + state->parse_state = copy; + + // G++ extensions + if (ParseTwoCharToken(state, "TC") && ParseType(state) && + ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + DisableAppend(state) && ParseType(state)) { + RestoreAppend(state, copy.append); + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && + ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "GR") && ParseName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <call-offset> ::= h <nv-offset> _ +// ::= v <v-offset> _ +static bool ParseCallOffset(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'h') && ParseNVOffset(state) && + ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'v') && ParseVOffset(state) && + ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <nv-offset> ::= <(offset) number> +static bool ParseNVOffset(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return ParseNumber(state, nullptr); +} + +// <v-offset> ::= <(offset) number> _ <(virtual offset) number> +static bool ParseVOffset(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + ParseNumber(state, nullptr)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <ctor-dtor-name> ::= C1 | C2 | C3 +// ::= D0 | D1 | D2 +// # GCC extensions: "unified" constructor/destructor. See +// # https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847 +// ::= C4 | D4 +static bool ParseCtorDtorName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'C') && ParseCharClass(state, "1234")) { + const char *const prev_name = state->out + state->parse_state.prev_name_idx; + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "0124")) { + const char *const prev_name = state->out + state->parse_state.prev_name_idx; + MaybeAppend(state, "~"); + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); + return true; + } + state->parse_state = copy; + return false; +} + +// <decltype> ::= Dt <expression> E # decltype of an id-expression or class +// # member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) +static bool ParseDecltype(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && + ParseExpression(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <type> ::= <CV-qualifiers> <type> +// ::= P <type> # pointer-to +// ::= R <type> # reference-to +// ::= O <type> # rvalue reference-to (C++0x) +// ::= C <type> # complex pair (C 2000) +// ::= G <type> # imaginary (C 2000) +// ::= U <source-name> <type> # vendor extended type qualifier +// ::= <builtin-type> +// ::= <function-type> +// ::= <class-enum-type> # note: just an alias for <name> +// ::= <array-type> +// ::= <pointer-to-member-type> +// ::= <template-template-param> <template-args> +// ::= <template-param> +// ::= <decltype> +// ::= <substitution> +// ::= Dp <type> # pack expansion of (C++0x) +// +static bool ParseType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + + // We should check CV-qualifers, and PRGC things first. + // + // CV-qualifiers overlap with some operator names, but an operator name is not + // valid as a type. To avoid an ambiguity that can lead to exponential time + // complexity, refuse to backtrack the CV-qualifiers. + // + // _Z4aoeuIrMvvE + // => _Z 4aoeuI rM v v E + // aoeu<operator%=, void, void> + // => _Z 4aoeuI r Mv v E + // aoeu<void void::* restrict> + // + // By consuming the CV-qualifiers first, the former parse is disabled. + if (ParseCVQualifiers(state)) { + const bool result = ParseType(state); + if (!result) state->parse_state = copy; + return result; + } + state->parse_state = copy; + + // Similarly, these tag characters can overlap with other <name>s resulting in + // two different parse prefixes that land on <template-args> in the same + // place, such as "C3r1xI...". So, disable the "ctor-name = C3" parse by + // refusing to backtrack the tag characters. + if (ParseCharClass(state, "OPRCG")) { + const bool result = ParseType(state); + if (!result) state->parse_state = copy; + return result; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && + ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseBuiltinType(state) || ParseFunctionType(state) || + ParseClassEnumType(state) || ParseArrayType(state) || + ParsePointerToMemberType(state) || ParseDecltype(state) || + // "std" on its own isn't a type. + ParseSubstitution(state, /*accept_std=*/false)) { + return true; + } + + if (ParseTemplateTemplateParam(state) && ParseTemplateArgs(state)) { + return true; + } + state->parse_state = copy; + + // Less greedy than <template-template-param> <template-args>. + if (ParseTemplateParam(state)) { + return true; + } + + return false; +} + +// <CV-qualifiers> ::= [r] [V] [K] +// We don't allow empty <CV-qualifiers> to avoid infinite loop in +// ParseType(). +static bool ParseCVQualifiers(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + int num_cv_qualifiers = 0; + num_cv_qualifiers += ParseOneCharToken(state, 'r'); + num_cv_qualifiers += ParseOneCharToken(state, 'V'); + num_cv_qualifiers += ParseOneCharToken(state, 'K'); + return num_cv_qualifiers > 0; +} + +// <builtin-type> ::= v, etc. # single-character builtin types +// ::= u <source-name> +// ::= Dd, etc. # two-character builtin types +// +// Not supported: +// ::= DF <number> _ # _FloatN (N bits) +// +static bool ParseBuiltinType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + const AbbrevPair *p; + for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { + // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. + if (p->abbrev[1] == '\0') { + if (ParseOneCharToken(state, p->abbrev[0])) { + MaybeAppend(state, p->real_name); + return true; + } + } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) { + MaybeAppend(state, p->real_name); + return true; + } + } + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <function-type> ::= F [Y] <bare-function-type> E +static bool ParseFunctionType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'F') && + Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return false; +} + +// <bare-function-type> ::= <(signature) type>+ +static bool ParseBareFunctionType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + DisableAppend(state); + if (OneOrMore(ParseType, state)) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "()"); + return true; + } + state->parse_state = copy; + return false; +} + +// <class-enum-type> ::= <name> +static bool ParseClassEnumType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return ParseName(state); +} + +// <array-type> ::= A <(positive dimension) number> _ <(element) type> +// ::= A [<(dimension) expression>] _ <(element) type> +static bool ParseArrayType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'A') && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_') && ParseType(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && + ParseOneCharToken(state, '_') && ParseType(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <pointer-to-member-type> ::= M <(class) type> <(member) type> +static bool ParsePointerToMemberType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <template-param> ::= T_ +// ::= T <parameter-2 non-negative number> _ +static bool ParseTemplateParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseTwoCharToken(state, "T_")) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + state->parse_state = copy; + return false; +} + +// <template-template-param> ::= <template-param> +// ::= <substitution> +static bool ParseTemplateTemplateParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + return (ParseTemplateParam(state) || + // "std" on its own isn't a template. + ParseSubstitution(state, /*accept_std=*/false)); +} + +// <template-args> ::= I <template-arg>+ E +static bool ParseTemplateArgs(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + DisableAppend(state); + if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "<>"); + return true; + } + state->parse_state = copy; + return false; +} + +// <template-arg> ::= <type> +// ::= <expr-primary> +// ::= J <template-arg>* E # argument pack +// ::= X <expression> E +static bool ParseTemplateArg(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'J') && ZeroOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + // There can be significant overlap between the following leading to + // exponential backtracking: + // + // <expr-primary> ::= L <type> <expr-cast-value> E + // e.g. L 2xxIvE 1 E + // <type> ==> <local-source-name> <template-args> + // e.g. L 2xx IvE + // + // This means parsing an entire <type> twice, and <type> can contain + // <template-arg>, so this can generate exponential backtracking. There is + // only overlap when the remaining input starts with "L <source-name>", so + // parse all cases that can start this way jointly to share the common prefix. + // + // We have: + // + // <template-arg> ::= <type> + // ::= <expr-primary> + // + // First, drop all the productions of <type> that must start with something + // other than 'L'. All that's left is <class-enum-type>; inline it. + // + // <type> ::= <nested-name> # starts with 'N' + // ::= <unscoped-name> + // ::= <unscoped-template-name> <template-args> + // ::= <local-name> # starts with 'Z' + // + // Drop and inline again: + // + // <type> ::= <unscoped-name> + // ::= <unscoped-name> <template-args> + // ::= <substitution> <template-args> # starts with 'S' + // + // Merge the first two, inline <unscoped-name>, drop last: + // + // <type> ::= <unqualified-name> [<template-args>] + // ::= St <unqualified-name> [<template-args>] # starts with 'S' + // + // Drop and inline: + // + // <type> ::= <operator-name> [<template-args>] # starts with lowercase + // ::= <ctor-dtor-name> [<template-args>] # starts with 'C' or 'D' + // ::= <source-name> [<template-args>] # starts with digit + // ::= <local-source-name> [<template-args>] + // ::= <unnamed-type-name> [<template-args>] # starts with 'U' + // + // One more time: + // + // <type> ::= L <source-name> [<template-args>] + // + // Likewise with <expr-primary>: + // + // <expr-primary> ::= L <type> <expr-cast-value> E + // ::= LZ <encoding> E # cannot overlap; drop + // ::= L <mangled_name> E # cannot overlap; drop + // + // By similar reasoning as shown above, the only <type>s starting with + // <source-name> are "<source-name> [<template-args>]". Inline this. + // + // <expr-primary> ::= L <source-name> [<template-args>] <expr-cast-value> E + // + // Now inline both of these into <template-arg>: + // + // <template-arg> ::= L <source-name> [<template-args>] + // ::= L <source-name> [<template-args>] <expr-cast-value> E + // + // Merge them and we're done: + // <template-arg> + // ::= L <source-name> [<template-args>] [<expr-cast-value> E] + if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) { + copy = state->parse_state; + if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return true; + } + + // Now that the overlapping cases can't reach this code, we can safely call + // both of these. + if (ParseType(state) || ParseExprPrimary(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'X') && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return false; +} + +// <unresolved-type> ::= <template-param> [<template-args>] +// ::= <decltype> +// ::= <substitution> +static inline bool ParseUnresolvedType(State *state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + return (ParseTemplateParam(state) && Optional(ParseTemplateArgs(state))) || + ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/false); +} + +// <simple-id> ::= <source-name> [<template-args>] +static inline bool ParseSimpleId(State *state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + + // Note: <simple-id> cannot be followed by a parameter pack; see comment in + // ParseUnresolvedType. + return ParseSourceName(state) && Optional(ParseTemplateArgs(state)); +} + +// <base-unresolved-name> ::= <source-name> [<template-args>] +// ::= on <operator-name> [<template-args>] +// ::= dn <destructor-name> +static bool ParseBaseUnresolvedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (ParseSimpleId(state)) { + return true; + } + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "on") && ParseOperatorName(state, nullptr) && + Optional(ParseTemplateArgs(state))) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "dn") && + (ParseUnresolvedType(state) || ParseSimpleId(state))) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <unresolved-name> ::= [gs] <base-unresolved-name> +// ::= sr <unresolved-type> <base-unresolved-name> +// ::= srN <unresolved-type> <unresolved-qualifier-level>+ E +// <base-unresolved-name> +// ::= [gs] sr <unresolved-qualifier-level>+ E +// <base-unresolved-name> +static bool ParseUnresolvedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + ParseState copy = state->parse_state; + if (Optional(ParseTwoCharToken(state, "gs")) && + ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseUnresolvedType(state) && + ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') && + ParseUnresolvedType(state) && + OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) && + ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (Optional(ParseTwoCharToken(state, "gs")) && + ParseTwoCharToken(state, "sr") && + OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) && + ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <expression> ::= <1-ary operator-name> <expression> +// ::= <2-ary operator-name> <expression> <expression> +// ::= <3-ary operator-name> <expression> <expression> <expression> +// ::= cl <expression>+ E +// ::= cv <type> <expression> # type (expression) +// ::= cv <type> _ <expression>* E # type (expr-list) +// ::= st <type> +// ::= <template-param> +// ::= <function-param> +// ::= <expr-primary> +// ::= dt <expression> <unresolved-name> # expr.name +// ::= pt <expression> <unresolved-name> # expr->name +// ::= sp <expression> # argument pack expansion +// ::= sr <type> <unqualified-name> <template-args> +// ::= sr <type> <unqualified-name> +// <function-param> ::= fp <(top-level) CV-qualifiers> _ +// ::= fp <(top-level) CV-qualifiers> <number> _ +// ::= fL <number> p <(top-level) CV-qualifiers> _ +// ::= fL <number> p <(top-level) CV-qualifiers> <number> _ +static bool ParseExpression(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseTemplateParam(state) || ParseExprPrimary(state)) { + return true; + } + + // Object/function call expression. + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + // Function-param expression (level 0). + if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + // Function-param expression (level 1+). + if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) && + ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + // Parse the conversion expressions jointly to avoid re-parsing the <type> in + // their common prefix. Parsed as: + // <expression> ::= cv <type> <conversion-args> + // <conversion-args> ::= _ <expression>* E + // ::= <expression> + // + // Also don't try ParseOperatorName after seeing "cv", since ParseOperatorName + // also needs to accept "cv <type>" in other contexts. + if (ParseTwoCharToken(state, "cv")) { + if (ParseType(state)) { + ParseState copy2 = state->parse_state; + if (ParseOneCharToken(state, '_') && ZeroOrMore(ParseExpression, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy2; + if (ParseExpression(state)) { + return true; + } + } + } else { + // Parse unary, binary, and ternary operator expressions jointly, taking + // care not to re-parse subexpressions repeatedly. Parse like: + // <expression> ::= <operator-name> <expression> + // [<one-to-two-expressions>] + // <one-to-two-expressions> ::= <expression> [<expression>] + int arity = -1; + if (ParseOperatorName(state, &arity) && + arity > 0 && // 0 arity => disabled. + (arity < 3 || ParseExpression(state)) && + (arity < 2 || ParseExpression(state)) && + (arity < 1 || ParseExpression(state))) { + return true; + } + } + state->parse_state = copy; + + // sizeof type + if (ParseTwoCharToken(state, "st") && ParseType(state)) { + return true; + } + state->parse_state = copy; + + // Object and pointer member access expressions. + if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) && + ParseExpression(state) && ParseType(state)) { + return true; + } + state->parse_state = copy; + + // Pointer-to-member access expressions. This parses the same as a binary + // operator, but it's implemented separately because "ds" shouldn't be + // accepted in other contexts that parse an operator name. + if (ParseTwoCharToken(state, "ds") && ParseExpression(state) && + ParseExpression(state)) { + return true; + } + state->parse_state = copy; + + // Parameter pack expansion + if (ParseTwoCharToken(state, "sp") && ParseExpression(state)) { + return true; + } + state->parse_state = copy; + + return ParseUnresolvedName(state); +} + +// <expr-primary> ::= L <type> <(value) number> E +// ::= L <type> <(value) float> E +// ::= L <mangled-name> E +// // A bug in g++'s C++ ABI version 2 (-fabi-version=2). +// ::= LZ <encoding> E +// +// Warning, subtle: the "bug" LZ production above is ambiguous with the first +// production where <type> starts with <local-name>, which can lead to +// exponential backtracking in two scenarios: +// +// - When whatever follows the E in the <local-name> in the first production is +// not a name, we backtrack the whole <encoding> and re-parse the whole thing. +// +// - When whatever follows the <local-name> in the first production is not a +// number and this <expr-primary> may be followed by a name, we backtrack the +// <name> and re-parse it. +// +// Moreover this ambiguity isn't always resolved -- for example, the following +// has two different parses: +// +// _ZaaILZ4aoeuE1x1EvE +// => operator&&<aoeu, x, E, void> +// => operator&&<(aoeu::x)(1), void> +// +// To resolve this, we just do what GCC's demangler does, and refuse to parse +// casts to <local-name> types. +static bool ParseExprPrimary(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + + // The "LZ" special case: if we see LZ, we commit to accept "LZ <encoding> E" + // or fail, no backtracking. + if (ParseTwoCharToken(state, "LZ")) { + if (ParseEncoding(state) && ParseOneCharToken(state, 'E')) { + return true; + } + + state->parse_state = copy; + return false; + } + + // The merged cast production. + if (ParseOneCharToken(state, 'L') && ParseType(state) && + ParseExprCastValue(state)) { + return true; + } + state->parse_state = copy; + + if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <number> or <float>, followed by 'E', as described above ParseExprPrimary. +static bool ParseExprCastValue(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + // We have to be able to backtrack after accepting a number because we could + // have e.g. "7fffE", which will accept "7" as a number but then fail to find + // the 'E'. + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + if (ParseFloatNumber(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>] +// ::= Z <(function) encoding> E s [<discriminator>] +// +// Parsing a common prefix of these two productions together avoids an +// exponential blowup of backtracking. Parse like: +// <local-name> := Z <encoding> E <local-name-suffix> +// <local-name-suffix> ::= s [<discriminator>] +// ::= <name> [<discriminator>] + +static bool ParseLocalNameSuffix(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + + if (MaybeAppend(state, "::") && ParseName(state) && + Optional(ParseDiscriminator(state))) { + return true; + } + + // Since we're not going to overwrite the above "::" by re-parsing the + // <encoding> (whose trailing '\0' byte was in the byte now holding the + // first ':'), we have to rollback the "::" if the <name> parse failed. + if (state->parse_state.append) { + state->out[state->parse_state.out_cur_idx - 2] = '\0'; + } + + return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state)); +} + +static bool ParseLocalName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseOneCharToken(state, 'E') && ParseLocalNameSuffix(state)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <discriminator> := _ <(non-negative) number> +static bool ParseDiscriminator(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) { + return true; + } + state->parse_state = copy; + return false; +} + +// <substitution> ::= S_ +// ::= S <seq-id> _ +// ::= St, etc. +// +// "St" is special in that it's not valid as a standalone name, and it *is* +// allowed to precede a name without being wrapped in "N...E". This means that +// if we accept it on its own, we can accept "St1a" and try to parse +// template-args, then fail and backtrack, accept "St" on its own, then "1a" as +// an unqualified name and re-parse the same template-args. To block this +// exponential backtracking, we disable it with 'accept_std=false' in +// problematic contexts. +static bool ParseSubstitution(State *state, bool accept_std) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseTwoCharToken(state, "S_")) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + state->parse_state = copy; + + // Expand abbreviations like "St" => "std". + if (ParseOneCharToken(state, 'S')) { + const AbbrevPair *p; + for (p = kSubstitutionList; p->abbrev != nullptr; ++p) { + if (RemainingInput(state)[0] == p->abbrev[1] && + (accept_std || p->abbrev[1] != 't')) { + MaybeAppend(state, "std"); + if (p->real_name[0] != '\0') { + MaybeAppend(state, "::"); + MaybeAppend(state, p->real_name); + } + ++state->parse_state.mangled_idx; + return true; + } + } + } + state->parse_state = copy; + return false; +} + +// Parse <mangled-name>, optionally followed by either a function-clone suffix +// or version suffix. Returns true only if all of "mangled_cur" was consumed. +static bool ParseTopLevelMangledName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) return false; + if (ParseMangledName(state)) { + if (RemainingInput(state)[0] != '\0') { + // Drop trailing function clone suffix, if any. + if (IsFunctionCloneSuffix(RemainingInput(state))) { + return true; + } + // Append trailing version suffix if any. + // ex. _Z3foo@@GLIBCXX_3.4 + if (RemainingInput(state)[0] == '@') { + MaybeAppend(state, RemainingInput(state)); + return true; + } + return false; // Unconsumed suffix. + } + return true; + } + return false; +} + +static bool Overflowed(const State *state) { + return state->parse_state.out_cur_idx >= state->out_end_idx; +} + +} // namespace debugging_internal +} // namespace absl + +// The demangler entry point. +bool Demangle(const char *mangled, char *out, int out_size) { + using namespace absl::debugging_internal; + State state; + InitState(&state, mangled, out, out_size); + return ParseTopLevelMangledName(&state) && !Overflowed(&state); +}
diff --git a/src/demangle.h b/src/demangle.h new file mode 100644 index 0000000..823b0da --- /dev/null +++ b/src/demangle.h
@@ -0,0 +1,58 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// Author: satorux@google.com (Satoru Takabayashi) +// +// An async-signal-safe and thread-safe demangler for Itanium C++ ABI +// (aka G++ V3 ABI). +// The demangler is implemented to be used in async signal handlers to +// symbolize stack traces. We cannot use libstdc++'s +// abi::__cxa_demangle() in such signal handlers since it's not async +// signal safe (it uses malloc() internally). +// +// Note that this demangler doesn't support full demangling. More +// specifically, it doesn't print types of function parameters and +// types of template arguments. It just skips them. However, it's +// still very useful to extract basic information such as class, +// function, constructor, destructor, and operator names. +// +// See the implementation note in demangle.cc if you are interested. +// +// Example: +// +// | Mangled Name | The Demangler | abi::__cxa_demangle() +// |---------------|---------------|----------------------- +// | _Z1fv | f() | f() +// | _Z1fi | f() | f(int) +// | _Z3foo3bar | foo() | foo(bar) +// | _Z1fIiEvi | f<>() | void f<int>(int) +// | _ZN1N1fE | N::f | N::f +// | _ZN3Foo3BarEv | Foo::Bar() | Foo::Bar() +// | _Zrm1XS_" | operator%() | operator%(X, X) +// | _ZN3FooC1Ev | Foo::Foo() | Foo::Foo() +// | _Z1fSs | f() | f(std::basic_string<char, +// | | | std::char_traits<char>, +// | | | std::allocator<char> >) +// +// See the unit test for more examples. +// +// Note: we might want to write demanglers for ABIs other than Itanium +// C++ ABI in the future. +// +#ifndef BASE_DEMANGLE_H_ +#define BASE_DEMANGLE_H_ +// Demangle "mangled". On success, return true and write the +// demangled symbol name to "out". Otherwise, return false. +// "out" is modified even if demangling is unsuccessful. +bool Demangle(const char *mangled, char *out, int out_size); +#endif // BASE_DEMANGLE_H_
diff --git a/src/dwarf/attr.cc b/src/dwarf/attr.cc index 8ee3114..a540341 100644 --- a/src/dwarf/attr.cc +++ b/src/dwarf/attr.cc
@@ -135,6 +135,8 @@ return AttrValue::UnresolvedString(form, ReadFixed<uint8_t>(data)); case DW_FORM_strx2: return AttrValue::UnresolvedString(form, ReadFixed<uint16_t>(data)); + case DW_FORM_strx3: + return AttrValue::UnresolvedString(form, ReadFixed<uint32_t, 3>(data)); case DW_FORM_strx4: return AttrValue::UnresolvedString(form, ReadFixed<uint32_t>(data)); case DW_FORM_strx:
diff --git a/src/elf.cc b/src/elf.cc index 7d1a364..9832566 100644 --- a/src/elf.cc +++ b/src/elf.cc
@@ -15,13 +15,15 @@ #include <algorithm> #include <string> #include <iostream> +#include <fstream> +#include <sstream> #include "absl/numeric/int128.h" #include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "third_party/freebsd_elf/elf.h" #include "bloaty.h" -#include "util.h" +#include "link_map.h" #include <assert.h> #include <limits.h> @@ -140,6 +142,10 @@ : elf_(§ion.elf()), remaining_(section.contents()) { Next(); } + NoteIter(const Segment& segment, const ElfFile* elf) + : elf_(elf), remaining_(segment.contents()) { + Next(); + } bool IsDone() const { return done_; } uint32_t type() const { return type_; } @@ -893,9 +899,17 @@ uint64_t full_addr = ToVMAddr(sym.st_value, index_base + sym.st_shndx, is_object); if (sink && !(capstone_available && disassemble)) { - sink->AddVMRangeAllowAlias( - "elf_symbols", full_addr, sym.st_size, - ItaniumDemangle(name, sink->data_source())); + // Checks for a negative number in two's complement + if (sym.st_size > 0x7fffffffffffffffULL) { + fprintf(stderr, "Invalid symbol size at 0x%" PRIx64 \ + ", size: 0x%" PRIx64 ", shndx: %d, name: %.*s\n", + full_addr, sym.st_size, sym.st_shndx, + static_cast<int>(name.size()), name.data()); + } else { + sink->AddVMRangeAllowAlias( + "elf_symbols", full_addr, sym.st_size, + ItaniumDemangle(name, sink->data_source())); + } } if (table) { table->insert( @@ -1265,8 +1279,27 @@ class ElfObjectFile : public ObjectFile { public: - ElfObjectFile(std::unique_ptr<InputFile> file) - : ObjectFile(std::move(file)) {} + ElfObjectFile(std::unique_ptr<InputFile> file, std::optional<std::string> link_map_file) + : ObjectFile(std::move(file)) { + if (link_map_file.has_value()) { + std::ifstream infile(*link_map_file); + std::string link_map; + + // Strip comments and empty lines. + for (std::string line; getline(infile, line);) { + if (line.empty()) continue; + if (line[0] == '#') continue; + link_map += line; + link_map += '\n'; + } + + absl::StripLeadingAsciiWhitespace(&link_map); + absl::StripTrailingAsciiWhitespace(&link_map); + + link_map_symbols_ = bloaty_link_map::ParseLldLinkMap(link_map); + link_map_sections_ = bloaty_link_map::ParseLldLinkMapSections(link_map); + } + } std::string GetBuildId() const override { if (IsObjectFile(file_data().data())) { @@ -1276,6 +1309,7 @@ ElfFile elf(file_data().data()); assert(elf.IsOpen()); + // Search for a build-id section. for (Elf64_Xword i = 1; i < elf.section_count(); i++) { ElfFile::Section section; elf.ReadSection(i, §ion); @@ -1289,11 +1323,123 @@ } } } + // Search for a build-id segment. + for (Elf64_Xword i = 0; i < elf.header().e_phnum; i++) { + ElfFile::Segment segment; + elf.ReadSegment(i, &segment); + const auto &header = segment.header(); + if (header.p_type != PT_NOTE) { + continue; + } + for (ElfFile::NoteIter notes(segment, &elf); !notes.IsDone(); notes.Next()) { + if (notes.name() == "GNU" && notes.type() == NT_GNU_BUILD_ID) { + return std::string(notes.descriptor()); + } + } + } // No build id section found. return std::string(); } + void ReadAccessPattern(RangeSink* sink) const { + if (!sink->options().has_cold_bytes_filter()) { + THROW("need to specify cold bytes filter"); + } + // Each element corresponds to |kAccessPatternFrameSize| bytes. + std::vector<bool> access_pattern; + uint64_t kAccessPatternFrameSize = sink->options().access_pattern_frame_size(); + auto frequencies = sink->options().cold_bytes_filter(); + size_t file_size = sink->input_file().data().size(); + size_t num_frames = (file_size + kAccessPatternFrameSize - 1) / kAccessPatternFrameSize; + access_pattern.reserve(num_frames); + for (size_t i = 0; i < num_frames; i ++) { + access_pattern.push_back(false); + } + std::vector<std::string> frequencies_vec = absl::StrSplit(frequencies, ','); + for (const auto& part : frequencies_vec) { + std::vector<std::string> frame_and_count = absl::StrSplit(part, ':'); + if (frame_and_count.size() != 2) { + THROWF("Invalid format in cold bytes filter: $0", part); + } + size_t frame = std::stoi(frame_and_count[0]); + size_t count = std::stoi(frame_and_count[1]); + if (frame >= access_pattern.size()) { + THROW("access pattern exceeded end of file"); + } + if (count > 0) { + access_pattern[frame] = true; + } + } + for (size_t i = 0; i < access_pattern.size(); i++) { + std::string label = access_pattern[i] ? "Hot" : "Cold"; + size_t length; + size_t file_size = sink->input_file().data().size(); + if (i * kAccessPatternFrameSize > file_size) { + THROW("access pattern exceeded end of file"); + } + if (i * kAccessPatternFrameSize + kAccessPatternFrameSize > file_size) { + // We're at the last frame in the ELF, and it is not fully 32 KiB. + length = file_size % kAccessPatternFrameSize; + } else { + length = kAccessPatternFrameSize; + } + sink->AddFileRange("access_pattern", label, + i * kAccessPatternFrameSize, length); + } + } + + void ReadLinkMapSymbols(RangeSink* sink) const { + if (!link_map_symbols_.has_value()) return; + const auto& symbols = *link_map_symbols_; + for (const auto& symbol : symbols) { + auto maybe_transformed_compile_unit = + bloaty_link_map::TransformCompileUnitForFuchsia(symbol.compile_unit); + auto demangled = ItaniumDemangle(symbol.name, sink->data_source()); + if (maybe_transformed_compile_unit.has_value()) { + auto [transformed_compile_unit, maybe_rust_crate] = + *maybe_transformed_compile_unit; + if (maybe_rust_crate.has_value()) { + auto symbol_with_crate_id = + EncodeSymbolWithCrateId(demangled, *maybe_rust_crate); + sink->AddVMRange("link_map", symbol.addr, symbol.size, + symbol_with_crate_id); + continue; + } + } + sink->AddVMRange("link_map", symbol.addr, symbol.size, demangled); + } + + if (!link_map_sections_.has_value()) return; + const auto& sections = *link_map_sections_; + for (const auto& section : sections) { + sink->AddVMRange("link_map", section.addr, section.size, + "[section " + section.name + "]"); + } + } + + void ReadLinkMapCompileUnits(RangeSink* sink) const { + if (!link_map_symbols_.has_value()) return; + const auto& symbols = *link_map_symbols_; + for (const auto& symbol : symbols) { + auto maybe_transformed_compile_unit = + bloaty_link_map::TransformCompileUnitForFuchsia(symbol.compile_unit); + if (maybe_transformed_compile_unit.has_value()) { + auto [transformed_compile_unit, maybe_rust_crate] = + *maybe_transformed_compile_unit; + sink->AddVMRange("link_map", symbol.addr, symbol.size, + transformed_compile_unit); + } + } + + if (!link_map_sections_.has_value()) return; + const auto& sections = *link_map_sections_; + for (const auto& section : sections) { + sink->AddVMRange("link_map", section.addr, section.size, + "[section " + section.name + "]"); + } + } + void ProcessFile(const std::vector<RangeSink*>& sinks) const override { for (auto sink : sinks) { if (verbose_level > 1) { @@ -1309,11 +1455,16 @@ case DataSource::kRawSymbols: case DataSource::kShortSymbols: case DataSource::kFullSymbols: + ReadLinkMapSymbols(sink); ReadELFSymbols(debug_file().file_data(), sink, nullptr, false); break; case DataSource::kArchiveMembers: DoReadELFSections(sink, kReportByArchiveMember); break; + case DataSource::kAccessPattern: { + ReadAccessPattern(sink); + break; + } case DataSource::kCompileUnits: { CheckNotObject("compileunits", sink); SymbolTable symtab; @@ -1329,6 +1480,7 @@ dwarf::File dwarf; ReadDWARFSections(debug_file().file_data(), &dwarf, sink); ReadDWARFCompileUnits(dwarf, symbol_map, sink); + ReadLinkMapCompileUnits(sink); break; } case DataSource::kInlines: { @@ -1347,6 +1499,7 @@ case DataSource::kSegments: case DataSource::kSections: case DataSource::kArchiveMembers: + case DataSource::kAccessPattern: break; default: // Add these *after* processing all other data sources. @@ -1410,15 +1563,23 @@ return ReadElfArchMode(file_data(), &info->arch, &info->mode); } + + private: + std::optional<std::vector<bloaty_link_map::Symbol>> link_map_symbols_ = std::nullopt; + std::optional<std::vector<bloaty_link_map::Section>> link_map_sections_ = std::nullopt; }; } // namespace -std::unique_ptr<ObjectFile> TryOpenELFFile(std::unique_ptr<InputFile>& file) { +std::unique_ptr<ObjectFile> TryOpenELFFile(std::unique_ptr<InputFile>& file, + std::optional<std::string> link_map_file) { ElfFile elf(file->data()); ArFile ar(file->data()); if (elf.IsOpen() || ar.IsOpen()) { - return std::unique_ptr<ObjectFile>(new ElfObjectFile(std::move(file))); + if (link_map_file.has_value()) { + std::cerr << "Using link map: " << *link_map_file << std::endl; + } + return std::unique_ptr<ObjectFile>(new ElfObjectFile(std::move(file), link_map_file)); } else { return nullptr; }
diff --git a/src/link_map.cc b/src/link_map.cc new file mode 100644 index 0000000..9eca7f8 --- /dev/null +++ b/src/link_map.cc
@@ -0,0 +1,626 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "link_map.h" + +#include <algorithm> +#include <functional> +#include <iostream> +#include <optional> +#include <sstream> +#include <string> +#include <tuple> +#include <unordered_set> + +#include "bloaty.h" + +ABSL_ATTRIBUTE_NORETURN +static void Throw(const char* str, int line) { + std::cerr << __FILE__ << ":" << line << ", " << str << std::endl; + throw bloaty::Error(str, __FILE__, line); +} +#define THROW(msg) Throw(msg, __LINE__) + +#define likely(x) __builtin_expect((x), 1) +#define unlikely(x) __builtin_expect((x), 0) + +#define GUARD(x) if (unlikely(!(x))) + +namespace bloaty_link_map { + +namespace { + +// VMA LMA Size Align Out In Symbol +re2::RE2 header_regex(R"(^\s*VMA\s*LMA\s*Size\s*Align\s*Out\s*In\s*Symbol)"); + +// 194 194 13 1 .interp +re2::RE2 line_regex(R"(\s*[0-9a-f]+\s+([0-9a-f]+)\s+([0-9a-f]+)\s+(\d+) ( *)(.*))"); + +// obj/zircon/system/uapp/blobfs/blobfs.main.cc.o:(.rodata.main) +re2::RE2 level2_regex(R"(^(.*):\((.*)\))"); + +enum class Level { k1 = 1, k2 = 2, k3 = 3 }; + +struct Token { + std::string_view line; + uint64_t address; + uint64_t size; + Level level; + std::optional<uint64_t> span; + std::string_view tok; +}; + +std::string_view into_string_view(const re2::StringPiece& s) { + return std::string_view(s.data(), s.size()); +} + +re2::RE2 PROMOTED_GLOBAL_NAME_DEMANGLED_PATTERN(R"( \((\.\d+)?\.llvm\.\d+\)$)"); +re2::RE2 PROMOTED_GLOBAL_NAME_RAW_PATTERN(R"((\.\d+)?\.llvm\.\d+$)"); + +std::string StripLlvmPromotedGlobalNames(std::string_view name) { + auto llvm_pos = name.find(".llvm."); + if (llvm_pos == std::string_view::npos) { + return std::string(name); + } + if (absl::EndsWith(name, ")")) { + std::string name_rep(name); + RE2::Replace(&name_rep, PROMOTED_GLOBAL_NAME_DEMANGLED_PATTERN, ""); + return name_rep; + } + std::string name_rep(name); + RE2::Replace(&name_rep, PROMOTED_GLOBAL_NAME_RAW_PATTERN, ""); + return name_rep; +} + +const std::string STRING_LITERAL_NAME = "string literal"; + +std::string_view NormalizeName(std::string_view name) { + if (absl::StartsWith(name, ".L.str")) { + return STRING_LITERAL_NAME; + } + if (absl::EndsWith(name, " (.cfi)")) { + return name.substr(0, name.size() - 7); + } + return name; +} + +// Decides whether a Level 3 token is an annotation. +// +// Returns: +// A 2-tuple (is_annotation, next_thumb2_mode): +// is_annotation: Whether |tok| is an annotation. +// next_thumb2_mode: New |thumb2_mode| value, or None if keep old value. +std::tuple<bool, std::optional<bool>> ParseArmAnnotations(std::string_view tok) { + // Annotations for ARM match '$t', '$d.1', but not '$_21::invoke'. + if (absl::StartsWith(tok, "$") && (tok.size() == 2 || (tok.size() >= 3 && tok[2] == '.'))) { + if (absl::StartsWith(tok, "$t")) { + // Is annotation, enter Thumb2 mode. + return std::tuple<bool, std::optional<bool>>{true, std::optional<bool>{true}}; + } + if (absl::StartsWith(tok, "$a")) { + // Is annotation, enter ARM32 mode. + return std::tuple<bool, std::optional<bool>>{true, std::optional<bool>{false}}; + } + // Is annotation, keep old |thumb2_mode| value. + return std::tuple<bool, std::optional<bool>>{true, std::nullopt}; + } + // Not annotation, keep old |thumb2_mode| value. + return std::tuple<bool, std::optional<bool>>{false, std::nullopt}; +} + +void Tokenize(std::istringstream& lines, std::function<void(const Token&)> cb) { + std::string one_line; + // A Level 3 symbol can have |size == 0| in some situations (e.g., assembly + // code symbols). To provided better size estimates in this case, the "span" + // of a Level 3 symbol is computed as: + // (A) The |address| difference compared to the next Level 3 symbol. + // (B) If the Level 3 symbol is the last among Level 3 lines nested under a + // Level 2 line: The difference between the Level 3 symbol's |address| + // and the containing Level 2 line's end address. + // To handle (A), |lines| is visited using a one-step lookahead, using + // |sentinel| to handle the last line. To handle (B), |level2_end_address| is + // computed for each Level 2 line. + const std::string sentinel = "0 0 0 0 THE_END"; + GUARD(RE2::FullMatch(sentinel, line_regex)) { THROW("sentinel must match regex"); } + uint64_t level2_end_address = 0; + bool thumb2_mode = false; + + std::string line; + std::optional<uint64_t> address = std::nullopt; + uint64_t size = 0; + Level level = Level::k1; + std::string tok; + + auto process_line = [&](const std::string& next_line) { + re2::StringPiece str_next_address; + re2::StringPiece str_next_size; + re2::StringPiece str_next_level; + re2::StringPiece next_tok; + GUARD(RE2::FullMatch(next_line, line_regex, &str_next_address, &str_next_size, (void*)NULL, + &str_next_level, &next_tok)) { + return; + } + uint64_t next_address = std::stoul(str_next_address.as_string(), nullptr, 16); + uint64_t next_size = std::stoul(str_next_size.as_string(), nullptr, 16); + int int_level = (str_next_level.size() / 8) + 1; + GUARD(int_level >= 1 && int_level <= 3) { THROW("invalid level"); } + Level next_level = static_cast<Level>(int_level); + + if (next_level == Level::k3) { + GUARD(level == Level::k2 || level == Level::k3) { + THROW("Cannot jump from Level 1 to Level l3"); + } + // Detect annotations. If found, maybe update |thumb2_mode|, then skip. + auto [is_annotation, next_thumb2_mode] = ParseArmAnnotations(into_string_view(next_tok)); + if (is_annotation) { + if (next_thumb2_mode.has_value()) { + thumb2_mode = next_thumb2_mode.value(); + } + // Skip annotations. + return; + } + if (thumb2_mode) { + // Adjust odd address to even. Alignment is not guanteed for all + // symbols (e.g., data, or x86), so this is judiciously applied. + next_address &= ~(1ULL); + } + } else { + // Resets on leaving Level 3. + thumb2_mode = false; + } + + if (address.has_value()) { + std::optional<uint64_t> span = std::nullopt; + if (level == Level::k3) { + span = next_level == Level::k3 ? next_address : level2_end_address; + *span -= *address; + } else if (level == Level::k2) { + level2_end_address = *address + size; + } + cb(Token{ + .line = line, + .address = *address, + .size = size, + .level = level, + .span = span, + .tok = tok, + }); + } + + line = next_line; + address = next_address; + size = next_size; + level = next_level; + tok = next_tok.as_string(); + }; + while (getline(lines, one_line)) { + process_line(one_line); + } + process_line(sentinel); +} + +const std::string SECTION_BSS = ".bss"; +const std::string SECTION_BSS_REL_RO = ".bss.rel.ro"; +const std::string SECTION_DATA = ".data"; +const std::string SECTION_DATA_REL_RO = ".data.rel.ro"; +const std::string SECTION_DATA_REL_RO_LOCAL = ".data.rel.ro.local"; +const std::string SECTION_DEX = ".dex"; +const std::string SECTION_DEX_METHOD = ".dex.method"; +const std::string SECTION_OTHER = ".other"; +const std::string SECTION_PAK_NONTRANSLATED = ".pak.nontranslated"; +const std::string SECTION_PAK_TRANSLATIONS = ".pak.translations"; +const std::string SECTION_PART_END = ".part.end"; +const std::string SECTION_RODATA = ".rodata"; +const std::string SECTION_TEXT = ".text"; +// Used by SymbolGroup when they contain a mix of sections. +const std::string SECTION_MULTIPLE = ".*"; + +const std::unordered_set<std::string> BSS_SECTIONS = { + SECTION_BSS, + SECTION_BSS_REL_RO, + SECTION_PART_END, +}; + +} // namespace + +std::vector<Symbol> ParseLldLinkMap(const std::string& content) { + std::vector<Symbol> syms; + std::istringstream lines(content); + std::string one_line; + + GUARD(getline(lines, one_line)) { THROW("must have at least the header line"); } + GUARD(header_regex.ok()) { THROW("can't compile header regex"); } + GUARD(RE2::FullMatch(one_line, header_regex)) { THROW("link map is not in lld v1 format"); } + + // Example Format: + // VMA LMA Size Align Out In Symbol + // 194 194 13 1 .interp + // 194 194 13 1 <internal>:(.interp) + // 1a8 1a8 22d8 4 .ARM.exidx + // 1b0 1b0 8 4 obj/sandbox/syscall.o:(.ARM.exidx) + // 400 400 123400 64 .text + // 600 600 14 4 ...:(.text.OUTLINED_FUNCTION_0) + // 600 600 0 1 $x.3 + // 600 600 14 1 OUTLINED_FUNCTION_0 + // 123800 123800 20000 256 .rodata + // 123800 123800 4 4 ...:o:(.rodata._ZN3fooE.llvm.1234) + // 123800 123800 4 1 foo (.llvm.1234) + // 123804 123804 4 4 ...:o:(.rodata.bar.llvm.1234) + // 123804 123804 4 1 bar.llvm.1234 + // ^ ^ ^ + // Level 1 ^ ^ + // Level 2 ^ + // Level 3 + // + // Level 1 (Out) specify sections or PROVIDE_HIDDEN lines. + // Level 2 (In) specify object paths and section names within objects, or '<internal>:...'. + // Level 3 (Symbol) specify symbol names or special names such as + // '.L_MergeGlobals'. Annotations such as '$d', $t.42' also appear at Level 3, + // but they are consumed by |tokenizer|, so don't appear hear. + GUARD(line_regex.ok()) { THROW("can't compile line regex"); } + GUARD(level2_regex.ok()) { THROW("can't compile level2 regex"); } + + std::optional<std::string> cur_section; + + bool cur_section_is_useful = false; + + uint64_t promoted_name_count = 0; + + // |is_partial| indicates that an eligible Level 3 line should be used to + // update |syms[-1].full_name| instead of creating a new symbol. + bool is_partial = false; + + // Assembly code can create consecutive Level 3 lines with |size == 0|. These + // lines can represent + // (1) assembly functions (should form symbol), or + // (2) assembly labels (should NOT form symbol). + // It seems (2) correlates with the presence of a leading Level 3 line with + // |size > 0|. This gives rise to the following strategy: Each symbol S from + // a Level 3 line suppresses Level 3 lines with |address| less than + // |next_usable_address := S.address + S.size|. + uint64_t next_usable_address = 0; + + bool in_partitions = false; + bool in_jump_table = false; + uint64_t jump_tables_count = 0; + uint64_t jump_entries_count = 0; + uint64_t mangled_start_idx = 0; + + std::string cur_obj; + // Assembly code can create consecutive Level 3 lines with |size == 0|. These + // lines can represent + // (1) assembly functions (should form symbol), or + // (2) assembly labels (should NOT form symbol). + // It seems (2) correlates with the presence of a leading Level 3 line with + // |size > 0|. This gives rise to the following strategy: Each symbol S from + // a Level 3 line suppresses Level 3 lines with |address| less than + // |next_usable_address := S.address + S.size|. + Tokenize(lines, [&](const Token& token) { + // Level 1 data match the "Out" column. They specify sections or + // PROVIDE_HIDDEN lines. + if (token.level == Level::k1) { + // Ignore sections that belong to feature library partitions. Seeing a + // partition name is an indicator that we've entered a list of feature + // partitions. After these, a single .part.end section will follow to + // reserve memory at runtime. Seeing the .part.end section also marks the + // end of partition sections in the map file. + if (absl::EndsWith(token.tok, "_partition")) { + in_partitions = true; + } else if (token.tok == ".part.end") { + // Note that we want to retain .part.end section, so it's fine to + // restart processing on this section, rather than the next one. + in_partitions = false; + } + + if (in_partitions) { + // For now, completely ignore feature partitions. + cur_section = std::nullopt; + cur_section_is_useful = false; + } else { + cur_section = token.tok; + // E.g., Want to convert "(.text._name)" -> "_name" later. + mangled_start_idx = cur_section->size() + 1; + cur_section_is_useful = ((BSS_SECTIONS.find(*cur_section) != BSS_SECTIONS.end()) || + SECTION_RODATA == *cur_section || SECTION_TEXT == *cur_section || + absl::StartsWith(*cur_section, SECTION_DATA)); + } + } else if (cur_section_is_useful) { + // Level 2 data match the "In" column. They specify object paths and + // section names within objects, or '<internal>:...'. + if (token.level == Level::k2) { + std::string mangled_name; + re2::StringPiece paren_value; + GUARD(RE2::FullMatch(token.tok, level2_regex, &cur_obj, &paren_value)) { + THROW("Level 2 regex did not match"); + } + bool in_jump_table = absl::StrContains(into_string_view(paren_value), ".L.cfi.jumptable"); + if (in_jump_table) { + // Store each CFI jump table as a Level 2 symbol, whose Level 3 + // details are discarded. + jump_tables_count++; + // Replaces 'lto.tmp' to prevent problem later. + cur_obj = ""; + mangled_name = "** CFI jump table"; + } else { + // E.g., '(.text.unlikely._name)' -> '_name'. + mangled_name = into_string_view(paren_value.substr(mangled_start_idx)); + is_partial = true; + // As of 2017/11 LLD does not distinguish merged strings from other + // merged data. Feature request is filed under: + // https://bugs.llvm.org/show_bug.cgi?id=35248 + if (cur_obj == "<internal>") { + if (cur_section.has_value() && *cur_section == ".rodata" && mangled_name == "") { + // Treat all <internal> sections within .rodata as as string + // literals. Some may hold numeric constants or other data, but + // there is currently no way to distinguish them. + mangled_name = "** lld merge strings"; + } else { + // e.g. <internal>:(.text.thunk) + mangled_name = "** " + mangled_name; + } + + is_partial = false; + cur_obj = ""; + } else if (cur_obj == "lto.tmp" || + absl::StrContains(into_string_view(cur_obj), "thinlto-cache")) { + cur_obj = ""; + } + } + + // Create a symbol here since there may be no ensuing Level 3 lines. + // But if there are, then the symbol can be modified later as sym[-1]. + syms.push_back(Symbol{ + .name = std::move(mangled_name), + .compile_unit = cur_obj, + .section = *cur_section, + .addr = token.address, + .size = token.size, + }); + // Level 3 |address| is nested under Level 2, don't add |size|. + next_usable_address = token.address; + } else if (token.level == Level::k3) { + // Level 3 data match the "Symbol" column. They specify symbol names or + // special names such as '.L_MergeGlobals'. Annotations such as '$d', + // '$t.42' also appear at Level 3, but they are consumed by |tokenizer|, + // so don't appear hear. + + // Handle .L.cfi.jumptable. + if (in_jump_table) { + // Level 3 entries in CFI jump tables are thunks with mangled names. + // Extracting them as symbols is not worthwhile; we only store the + // Level 2 symbol, and print the count for verbose output. For + // counting, '__typeid_' entries are excluded since they're likely + // just annotations. + if (!absl::StartsWith(token.tok, "__typeid_")) { + jump_entries_count++; + } + return; + } + + // Ignore anything with '.L_MergedGlobals' prefix. This seems to only + // happen for ARM (32-bit) builds. + if (absl::StartsWith(token.tok, ".L_MergedGlobals")) { + return; + } + + // Use |span| to decide whether to use a Level 3 line for Symbols. This + // is useful for two purposes: + // * This is a better indicator than |size|, which can be 0 for + // assembly functions. + // * If multiple Level 3 lines have the same starting address, this + // cause all but the last line to have |span > 0|. This dedups lines + // with identical symbol names (why do they exist?). Note that this + // also skips legitimate aliases, but that's desired because nm.py + // (downstream) assumes no aliases already exist. + if (*token.span > 0) { + std::string stripped_tok = StripLlvmPromotedGlobalNames(token.tok); + std::string tok; + if (token.tok.size() != stripped_tok.size()) { + promoted_name_count++; + tok = stripped_tok; + } else { + tok = token.tok; + } + tok = NormalizeName(tok); + + // Handle special case where a partial symbol consumes bytes before + // the first Level 3 symbol. + if (is_partial && syms.back().addr < token.address) { + // Truncate the partial symbol and leave it without |full_name|. + // The data from the current line will form a new symbol. + syms.back().size = token.address - syms.back().addr; + next_usable_address = token.address; + is_partial = false; + } + + if (is_partial) { + syms.back().name = tok; + syms.back().size = + token.size > 0 ? token.size : std::min(syms.back().size, *token.span); + next_usable_address = token.address + syms.back().size; + is_partial = false; + } else if (token.address >= next_usable_address) { + uint64_t size_to_use; + if (absl::StartsWith(tok, "__typeid_")) { + GUARD(token.size == 1) { THROW("Token size"); } + if (absl::EndsWith(tok, "_byte_array")) { + // CFI byte array table: |size| is inaccurate, so use |span|. + size_to_use = *token.span; + } else { + // Likely '_global_addr' or '_unique_member'. These should be: + // * Skipped since they're in CFI tables. + // * Suppressed (via |next_usable_address|) by another Level 3 + // symbol. + // Anything that makes it here would be an anomaly worthy of + // investigation, so print warnings. + std::cerr << "Unrecognized __typeid_ symbol at " << token.address << std::endl; + return; + } + } else { + size_to_use = token.size > 0 ? token.size : *token.span; + } + syms.push_back(Symbol{ + .name = std::move(tok), + .compile_unit = cur_obj, + .section = *cur_section, + .addr = token.address, + .size = size_to_use, + }); + + // Suppress symbols with overlapping |address|. This eliminates + // labels from assembly sources. + next_usable_address = token.address + size_to_use; + if (!cur_obj.empty()) { + syms.back().compile_unit = cur_obj; + } + } + } + } + } + }); + + if (promoted_name_count > 0) { + std::cerr << "Found " << promoted_name_count << " promoted global names" << std::endl; + } + if (jump_tables_count > 0) { + std::cerr << "Found " << jump_tables_count << " CFI jump tables with " << jump_entries_count + << "total entries" << std::endl; + } + + return syms; +} + +std::vector<Section> ParseLldLinkMapSections(const std::string& content) { + std::vector<Section> sections; + + std::istringstream lines(content); + std::string one_line; + + Tokenize(lines, [&](const Token& token) { + // Level 1 data match the "Out" column. They specify sections or + // PROVIDE_HIDDEN lines. + if (token.level == Level::k1) { + sections.push_back(Section{ + .name = std::string(token.tok), + .addr = token.address, + .size = token.size, + }); + } + }); + + return sections; +} + +namespace { + +// ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o +re2::RE2 library_crate_regex( + R"(\/[a-zA-Z0-9_]+\.[a-zA-Z0-9_-]+\.([a-zA-Z0-9_]+)\.[a-zA-Z0-9-]+.*\.rcgu\.o$)"); + +// ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o +re2::RE2 bin_crate_regex(R"(\/[a-zA-Z0-9_-]+\.([a-zA-Z0-9_]+)\.[a-zA-Z0-9-]+.*\.rcgu\.o$)"); + +// foobar.rlib(libregex_syntax-579ced0738b0164d-579ced0738b0164d.regex_syntax.c02sfxfu-cgu.13.rcgu.o) +re2::RE2 rlib_crate_regex(R"(rlib\([a-zA-Z_\-0-9]+\.([a-zA-Z0-9_]+)\.[a-zA-Z0-9-]+.*\.rcgu\.o\)$)"); + +// /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o +re2::RE2 zircon_lib_regex(R"(\/out\/[a-zA-Z0-9_-]+\.zircon\/.*\/obj\/system\/ulib\/(.*)\.o$)"); + +// obj/out/default/fidling/gen/sdk/fidl/fuchsia.hardware.block/fuchsia.hardware.block_tables.fuchsia.hardware.block.fidl.tables.c.o +re2::RE2 fidling_regex(R"(^obj\/out\/.*\/fidling\/gen\/(.*)\.o$)"); + +// obj/zircon/public/lib/fidl_base/libfidl_base.a(libfidl_base.decoding.cc.o) +re2::RE2 zircon_fidl_lib_regex( + R"(^obj\/zircon\/public\/lib\/fidl_base\/libfidl_base\.a\(libfidl_base\.(.*)\.cc\.o\)$)"); + +// obj/zircon/system/uapp/blobfs/blobfs.main.cc.o +re2::RE2 zircon_lib_regex2(R"(^obj\/zircon\/system\/(.*)\.o$)"); + +} // namespace + +std::optional<std::tuple<std::string, std::optional<std::string>>> TransformCompileUnitForFuchsia( + const std::string& compile_unit) { + GUARD(library_crate_regex.ok()) { THROW("can't compile library crate regex"); } + { + std::string crate_name; + if (RE2::PartialMatch(compile_unit, library_crate_regex, &crate_name)) { + return std::tuple{"[crate: " + crate_name + "]", crate_name}; + } + } + + GUARD(bin_crate_regex.ok()) { THROW("can't compile bin crate regex"); } + { + std::string crate_name; + if (RE2::PartialMatch(compile_unit, bin_crate_regex, &crate_name)) { + return std::tuple{"[crate: " + crate_name + "]", crate_name}; + } + } + + GUARD(rlib_crate_regex.ok()) { THROW("can't compile rlib crate regex"); } + { + std::string crate_name; + if (RE2::PartialMatch(compile_unit, rlib_crate_regex, &crate_name)) { + return std::tuple{"[crate: " + crate_name + "]", crate_name}; + } + } + + GUARD(zircon_lib_regex.ok()) { THROW("can't compile zircon lib regex"); } + { + std::string cc_path; + if (RE2::PartialMatch(compile_unit, zircon_lib_regex, &cc_path)) { + // Remove the prefix in last path component + // c/crt1.Scrt1.cc -> c/Scrt1.cc + static re2::RE2 prefix_regex(R"(\/[a-zA-Z0-9\-_]+\.([a-zA-Z0-9\-_]+\.(cc|c))$)"); + if (RE2::Replace(&cc_path, prefix_regex, R"(/\1)")) { + return std::tuple{"../../zircon/system/ulib/" + cc_path, std::nullopt}; + } + } + } + + GUARD(fidling_regex.ok()) { THROW("can't compile fidling regex"); } + { + std::string cc_path; + if (RE2::PartialMatch(compile_unit, fidling_regex, &cc_path)) { + // A: sdk/fidl/fuchsia.hardware.block/fuchsia.hardware.block_tables.fuchsia.hardware.block.fidl.tables.c + // B: sdk/fidl/fuchsia.hardware.block/fuchsia.hardware.block.fidl.tables.c + // A: sdk/fidl/fuchsia.hardware.block/fuchsia/hardware/block/c/fuchsia.hardware.block_c_client.fidl.client.c + // B: sdk/fidl/fuchsia.hardware.block/fuchsia/hardware/block/c/fidl.client.c + // A: sdk/fidl/fuchsia.io/fuchsia/io/llcpp/fuchsia.io_llcpp.fidl.cc + // B: sdk/fidl/fuchsia.io/fuchsia/io/llcpp/fidl.cc + static re2::RE2 prefix_regex( + R"([a-zA-Z0-9\-\.]+_[a-zA-Z0-9\-_]+\.([a-zA-Z0-9\-\.]+\.(c|cc))$)"); + if (RE2::Replace(&cc_path, prefix_regex, R"(\1)")) { + return std::tuple{"fidling/gen/" + cc_path, std::nullopt}; + } + } + } + + GUARD(zircon_fidl_lib_regex.ok()) { THROW("can't compile zircon fidl lib regex"); } + { + std::string cc_name; + if (RE2::PartialMatch(compile_unit, zircon_fidl_lib_regex, &cc_name)) { + return std::tuple{"../../zircon/system/ulib/fidl/" + cc_name + ".cc", std::nullopt}; + } + } + + // Rust-specific special casing... + if (absl::StartsWith(compile_unit, "obj/third_party/rust_crates/compat/ring/libring-core.a")) { + return std::tuple<std::string, std::optional<std::string>>{ + "[crate: ring]", std::optional<std::string>{"ring"}}; + } + + return std::nullopt; +} + +} // namespace bloaty_link_map
diff --git a/src/link_map.h b/src/link_map.h new file mode 100644 index 0000000..38d9b8f --- /dev/null +++ b/src/link_map.h
@@ -0,0 +1,128 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef BLOATY_LINK_MAP_H_ +#define BLOATY_LINK_MAP_H_ + +#include <cstddef> +#include <cstdint> +#include <optional> +#include <string> +#include <tuple> +#include <vector> + +namespace bloaty_link_map { + +struct Symbol { + std::string name; + std::string compile_unit; + std::string section; + uint64_t addr; + uint64_t size; +}; + +// Parses a linker map file in lld v1 format. +// +// This function is adapted from +// https://chromium.googlesource.com/chromium/src.git/+/master/tools/binary_size/libsupersize/linker_map_parser.py +// +// Args: +// content: Contents of the link map, the first line of which is the header. +// +// Returns: +// A vector of symbols. +// +// Example format: +// VMA LMA Size Align Out In Symbol +// 194 194 13 1 .interp +// 194 194 13 1 <internal>:(.interp) +// 1a8 1a8 22d8 4 .ARM.exidx +// 1b0 1b0 8 4 obj/sandbox/syscall.o:(.ARM.exidx) +// 400 400 123400 64 .text +// 600 600 14 4 ...:(.text.OUTLINED_FUNCTION_0) +// 600 600 0 1 $x.3 +// 600 600 14 1 OUTLINED_FUNCTION_0 +// 123800 123800 20000 256 .rodata +// 123800 123800 4 4 ...:o:(.rodata._ZN3fooE.llvm.1234) +// 123800 123800 4 1 foo (.llvm.1234) +// 123804 123804 4 4 ...:o:(.rodata.bar.llvm.1234) +// 123804 123804 4 1 bar.llvm.1234 +// +// Notes: +// - `VMA` and `LMA` are mostly identical. +// - In a position-independent executable, the base address is zero. +// Hence the minimum value of the addresses is quite small. +// - Stripping of the binary will not relocate symbols, hence the addresses +// will be the same between a stripped and unstripped binary. +std::vector<Symbol> ParseLldLinkMap(const std::string& content); + +struct Section { + std::string name; + uint64_t addr; + uint64_t size; +}; + +std::vector<Section> ParseLldLinkMapSections(const std::string& content); + +// Transform the compile unit path parsed from link maps to a format +// easier to organize, specialized to a Fuchsia build. +// If the compile unit could not be understood by the transformer, it will +// return `std::nullopt`. Since bloaty can still recover some compile unit +// information from DWARF, we do not have to cover 100% here. For Rust however, +// this allows setting a reasonable fallback compile unit at the crate level. +// Therefore, the output of this transform should be inserted after the +// usual DWARF-based compile unit data source. +// +// Examples: +// +// `./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o` +// becomes `[crate: alloc]`. +// +// `./exe.unstripped/component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o.rcgu.o` +// becomes `[crate: component_manager_lib]`. +// +// `./exe.unstripped/component_manager.libcm_fidl_translator.cm_fidl_translator.3a1fbbbh-cgu.0.rcgu.o.rcgu.o` +// becomes `[crate: cm_fidl_translator]`. +// +// `./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o` +// becomes `[crate: component_manager]`. +// +// `./exe.unstripped/component_manager.libfidl_fuchsia_io.fidl_fuchsia_io.3a1fbbbh-cgu.0.rcgu.o.rcgu.o` +// becomes `[crate: fidl_fuchsia_io]`. +// +// `/usr/local/google/home/yifeit/vg/out/default/obj/third_party/rust_crates/libregex_syntax-579ced0738b0164d.rlib(libregex_syntax-579ced0738b0164d-579ced0738b0164d.regex_syntax.c02sfxfu-cgu.13.rcgu.o)` +// becomes `[crate: regex_syntax]`. +// +// `/usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o` +// becomes `../../zircon/system/ulib/c/Scrt1.cc`. +// +// `obj/out/default/fidling/gen/sdk/fidl/fuchsia.hardware.block/fuchsia.hardware.block_tables.fuchsia.hardware.block.fidl.tables.c.o` +// becomes `fidling/gen/sdk/fidl/fuchsia.hardware.block/fuchsia.hardware.block.fidl.tables.c`. +// +// `obj/zircon/public/lib/fidl_base/libfidl_base.a(libfidl_base.decoding.cc.o)` +// becomes `../../zircon/system/ulib/fidl/decoding.cc` +// +// `obj/zircon/system/uapp/blobfs/blobfs.main.cc.o` +// becomes `../../zircon/system/uapp/blobfs/main.cc` +// +// `obj/zircon/system/ulib/trace-provider/libtrace-provider-with-fdio.a(libtrace-provider-with-fdio.fdio_connect.cc.o)` +// is not suppported. +// +// `obj/zircon/public/lib/lz4/liblz4.a(liblz4.lz4hc.c.o)` is not supported. +std::optional<std::tuple<std::string, std::optional<std::string>>> TransformCompileUnitForFuchsia( + const std::string& compile_unit); + +} // namespace bloaty_link_map + +#endif // BLOATY_LINK_MAP_H_
diff --git a/src/report.proto b/src/report.proto new file mode 100644 index 0000000..e4cab01 --- /dev/null +++ b/src/report.proto
@@ -0,0 +1,41 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; +option cc_enable_arenas = true; + +package bloaty_report; + +message SizeInfo { + uint32 file_actual = 1; + uint32 vm_actual = 2; +} + +message Symbol { + SizeInfo sizes = 1; + string name = 2; + string maybe_rust_crate = 3; +} + +message CompileUnit { + SizeInfo sizes = 1; + repeated Symbol symbols = 2; + string name = 3; +} + +message Report { + repeated CompileUnit compile_units = 1; + uint32 file_total = 2; + uint32 vm_total = 3; +}
diff --git a/src/write_bloaty_report.cc b/src/write_bloaty_report.cc new file mode 100644 index 0000000..593eff6 --- /dev/null +++ b/src/write_bloaty_report.cc
@@ -0,0 +1,132 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <stdexcept> + +#include "absl/strings/match.h" +#include "absl/strings/str_split.h" +#include "absl/strings/substitute.h" + +#include "google/protobuf/arena.h" + +#include "bloaty.h" +#include "report.pb.h" + +namespace bloaty { + +void RollupOutput::PrintToProtobuf(std::ostream* out) const { + using namespace ::bloaty_report; + + google::protobuf::Arena arena; + Report* report = google::protobuf::Arena::CreateMessage<Report>(&arena); + report->set_file_total(toplevel_row_.size.file); + report->set_vm_total(toplevel_row_.size.vm); + + ([&] { + // If 2 levels, assume compileunits,symbols. + // If 3 levels, assume accesspattern,compileunits,symbols; and pick the cold accesses. + auto* compile_unit_row = &toplevel_row_; + if (toplevel_row_.sorted_children.front().sorted_children.size() > 0 && + toplevel_row_.sorted_children.front().sorted_children.front().sorted_children.size() > 0) { + bool found_hot = false; + for (const auto& access : toplevel_row_.sorted_children) { + if (access.name == "Hot") { + found_hot = true; + } + if (access.name == "Cold") { + compile_unit_row = &access; + break; + } + } + if (compile_unit_row == &toplevel_row_) { + if (!found_hot) { + THROWF("Could not find hot/cold regions in the binary, top-level row has $0 children " + "([$1, ...]), " + "next level row has $2 children ([$3, ...])", + toplevel_row_.sorted_children.size(), + toplevel_row_.sorted_children.front().name, + toplevel_row_.sorted_children.front().sorted_children.size(), + toplevel_row_.sorted_children.front().sorted_children.front().name); + } else { + // No cold regions, hence nothing to output. + return; + } + } + } + + for (const auto& child_row : compile_unit_row->sorted_children) { + CompileUnit* compile_unit = report->add_compile_units(); + for (const auto& symbol_row : child_row.sorted_children) { + SizeInfo* info = google::protobuf::Arena::CreateMessage<SizeInfo>(&arena); + info->set_file_actual(symbol_row.size.file); + info->set_vm_actual(symbol_row.size.vm); + Symbol* symbol = compile_unit->add_symbols(); + symbol->set_allocated_sizes(info); + auto decoded_symbol = DecodeSymbolWithCrateId(symbol_row.name); + symbol->set_name(decoded_symbol.symbol); + symbol->set_maybe_rust_crate(decoded_symbol.crate); + } + if (child_row.sorted_children.empty()) { + // Add a "fake symbol" that is the same as the compile unit, + // to make sizes add up. + SizeInfo* info = google::protobuf::Arena::CreateMessage<SizeInfo>(&arena); + info->set_file_actual(child_row.size.file); + info->set_vm_actual(child_row.size.vm); + Symbol* symbol = compile_unit->add_symbols(); + symbol->set_allocated_sizes(info); + symbol->set_name(child_row.name); + } + + SizeInfo* info = google::protobuf::Arena::CreateMessage<SizeInfo>(&arena); + info->set_file_actual(child_row.size.file); + info->set_vm_actual(child_row.size.vm); + compile_unit->set_allocated_sizes(info); + compile_unit->set_name(child_row.name); + } + })(); + + bool good = report->SerializeToOstream(out); + if (!good) { + throw std::runtime_error("Failed to serialize report"); + } + out->flush(); +} + +namespace { + +const std::string kSeparator = ", in crate "; + +} // namespace + +std::string EncodeSymbolWithCrateId(absl::string_view symbol, absl::string_view crate) { + // The encoding format is "$symbol, in crate $crate". Since regular C++ and + // Rust code is extremely unlikely to produce the word "in crate", This format + // is unlikely to cause ambiguities, while staying readable if bloaty is + // invoked from the command line. + return std::string(symbol) + kSeparator + std::string(crate); +} + +DecodeCrateIdResult DecodeSymbolWithCrateId(absl::string_view symbol) { + if (absl::StrContains(symbol, kSeparator)) { + std::vector<std::string> parts = absl::StrSplit(symbol, kSeparator); + if (parts.size() != 2) { + throw std::runtime_error("Unexpected symbol when decoding: " + std::string(symbol)); + } + return DecodeCrateIdResult{.symbol = parts[0], .crate = parts[1]}; + } else { + return DecodeCrateIdResult{.symbol = std::string(symbol)}; + } +} + +} // namespace bloaty
diff --git a/tests/bloaty_misc_test.cc b/tests/bloaty_misc_test.cc index a1d5084..da794d8 100644 --- a/tests/bloaty_misc_test.cc +++ b/tests/bloaty_misc_test.cc
@@ -58,3 +58,24 @@ RunBloaty(args); // Heavily multithreaded test. EXPECT_EQ(top_row_->size.file, file_size * 100); } + +TEST(GetPathStem, Normal) { + EXPECT_EQ(bloaty::GetPathStem("foo"), "foo"); + EXPECT_EQ(bloaty::GetPathStem("foo/bar/baz"), "baz"); + EXPECT_EQ(bloaty::GetPathStem("/foo/bar/baz"), "baz"); + EXPECT_EQ(bloaty::GetPathStem("/foo/../bar/baz"), "baz"); +} + +TEST(GetPathStem, DotSuffix) { + EXPECT_EQ(bloaty::GetPathStem("foo.b"), "foo"); + EXPECT_EQ(bloaty::GetPathStem("foo/bar/baz.b"), "baz"); + EXPECT_EQ(bloaty::GetPathStem("/foo/bar/baz.b"), "baz"); + EXPECT_EQ(bloaty::GetPathStem("/foo/../bar/baz.b"), "baz"); +} + +TEST(GetPathStem, DotPrefix) { + EXPECT_EQ(bloaty::GetPathStem(".foo"), ".foo"); + EXPECT_EQ(bloaty::GetPathStem("foo/bar/.baz"), ".baz"); + EXPECT_EQ(bloaty::GetPathStem("/foo/bar/.baz"), ".baz"); + EXPECT_EQ(bloaty::GetPathStem("/foo/../bar/.baz"), ".baz"); +}
diff --git a/tests/bloaty_report_test.cc b/tests/bloaty_report_test.cc new file mode 100644 index 0000000..aad4e47 --- /dev/null +++ b/tests/bloaty_report_test.cc
@@ -0,0 +1,208 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "bloaty.h" +#include "test.h" +#include "report.pb.h" + +TEST_F(BloatyTest, ProtobufOutput) { + std::string file = "05-binary.bin"; + uint64_t size; + ASSERT_TRUE(GetFileSize(file, &size)); + + RunBloaty({"bloaty", "-d", "compileunits,symbols", file}); + + // Select protobuf output. + std::ostringstream stream; + bloaty::OutputOptions options; + options.output_format = bloaty::OutputFormat::kProtobuf; + output_->Print(options, &stream); + + // Check Protobuf output. + bloaty_report::Report report; + ASSERT_TRUE(report.ParseFromString(stream.str())); + + // These are the internal structure of `05-binary.bin`. + // They shouldn't change if the test data don't change. + // See below for a CSV version of the same output; here we ensure that + // the Protobuf output matches the CSV version. + // See also `BloatyTest.SimpleBinary` in `bloaty_test.cc`. + ASSERT_EQ(21, report.compile_units().size()); + auto& bar_o = report.compile_units()[0]; + ASSERT_EQ("bar.o.c", bar_o.name()); + EXPECT_NEAR(4.50, bar_o.sizes().file_actual() / 1024.0, .01); + EXPECT_NEAR(3.99, bar_o.sizes().vm_actual() / 1024.0, .01); + + ASSERT_GT(bar_o.symbols().size(), 0); + auto& bar_x = bar_o.symbols()[0]; + ASSERT_EQ("bar_x", bar_x.name()); + EXPECT_NEAR(3.94, bar_x.sizes().file_actual() / 1024.0, .01); + EXPECT_NEAR(3.91, bar_x.sizes().vm_actual() / 1024.0, .01); + + ASSERT_GT(bar_o.symbols().size(), 1); + auto& debug_info = bar_o.symbols()[1]; + ASSERT_EQ("[section .debug_info]", debug_info.name()); + EXPECT_EQ(169, debug_info.sizes().file_actual()); + EXPECT_EQ(0, debug_info.sizes().vm_actual()); + + EXPECT_NEAR(14.1, report.file_total() / 1024.0, .1); + EXPECT_NEAR(10.3, report.vm_total() / 1024.0, .1); +} + +TEST_F(BloatyTest, ProtobufOutputFilterHotSymbolsAllHot) { + std::string file = "05-binary.bin"; + uint64_t size; + ASSERT_TRUE(GetFileSize(file, &size)); + + RunBloaty({"bloaty", "-d", "accesspattern,compileunits,symbols", file, + // The first 32 KiB frame is accessed 100 times. + "--cold-bytes-filter", "0:100", + "--access-pattern-frame-size", "32768"}); + + // Select protobuf output. + std::ostringstream stream; + bloaty::OutputOptions options; + options.output_format = bloaty::OutputFormat::kProtobuf; + output_->Print(options, &stream); + + // Check Protobuf output. + bloaty_report::Report report; + ASSERT_TRUE(report.ParseFromString(stream.str())); + + // Since the test binary `05-binary.bin` is smaller than 32 KiB, + // every symbol (except zero-filled segments synthesized at run-time) + // inside the file will be hot, as designated by our command line + // arguments. So the list of compile units should be empty. + ASSERT_EQ(0, report.compile_units().size()); + + EXPECT_NEAR(14.1, report.file_total() / 1024.0, .1); + EXPECT_NEAR(10.3, report.vm_total() / 1024.0, .1); +} + +TEST_F(BloatyTest, ProtobufOutputFilterHotSymbols) { + std::string file = "05-binary.bin"; + uint64_t size; + ASSERT_TRUE(GetFileSize(file, &size)); + + RunBloaty({"bloaty", "-d", "accesspattern,compileunits,symbols", file, + // Default frame size is 8 KiB. + // The first 8 KiB frame is accessed 100 times. + "--cold-bytes-filter", "0:100"}); + + // Select protobuf output. + std::ostringstream stream; + bloaty::OutputOptions options; + options.output_format = bloaty::OutputFormat::kProtobuf; + output_->Print(options, &stream); + + // Check Protobuf output. + bloaty_report::Report report; + ASSERT_TRUE(report.ParseFromString(stream.str())); + + // This test is only run on x86_64. + // The test binary `05-binary.bin` is around 14 KiB. + // We would expect to see some compile units. + EXPECT_NEAR(12, report.compile_units().size(), 1); + + EXPECT_NEAR(14.1, static_cast<double>(size) / 1024.0, .1); + EXPECT_NEAR(14.1, report.file_total() / 1024.0, .1); + EXPECT_NEAR(10.3, report.vm_total() / 1024.0, .1); +} + +TEST_F(BloatyTest, ProtobufOutputFilterHotSymbolsPatternTooLarge) { + std::string file = "05-binary.bin"; + std::string errmsg = "access pattern exceeded end of file"; + AssertBloatyFails({"bloaty", "-d", "accesspattern,compileunits,symbols", file, + // Default frame size is 8 KiB. + // The file is 14 KiB, so specifying the non-existent + // third frame should fail. + "--cold-bytes-filter", "0:100,1:100,2:100"}, errmsg); +} + +// Here is a regular bloaty print-out on the same file for reference: +// +// FILE SIZE VM SIZE +// -------------- -------------- +// 30.9% 4.36Ki 38.8% 3.99Ki bar.o.c +// 90.3% 3.94Ki 97.9% 3.91Ki bar_x +// 3.8% 169 0.0% 0 [section .debug_info] +// 2.4% 109 1.9% 76 bar_func +// 1.3% 58 0.0% 0 [section .debug_line] +// 0.8% 34 0.1% 4 bar_y +// 0.7% 31 0.0% 0 [section .debug_str] +// 0.7% 30 0.1% 4 bar_z +// 3.9% 564 38.8% 3.98Ki foo.o.c +// 5.3% 30 98.0% 3.91Ki foo_x +// 27.7% 156 0.0% 0 [section .debug_abbrev] +// 26.2% 148 0.0% 0 [section .debug_info] +// 19.3% 109 1.9% 76 foo_func +// 10.3% 58 0.0% 0 [section .debug_line] +// 5.9% 33 0.0% 0 [section .debug_str] +// 5.3% 30 0.1% 4 foo_y +// 15.5% 2.19Ki 0.0% 0 [ELF Headers] +// 91.4% 2.00Ki NAN% 0 [ELF Headers] +// 5.7% 128 NAN% 0 __libc_csu_init +// 2.9% 64 NAN% 0 _IO_stdin_used +// 12.1% 1.71Ki 0.0% 0 [Unmapped] +// 11.6% 1.64Ki 0.0% 0 [section .symtab] +// 78.6% 1.29Ki NAN% 0 [section .symtab] +// 17.1% 288 NAN% 0 __libc_csu_init +// 1.4% 24 NAN% 0 _IO_stdin_used +// 1.4% 24 NAN% 0 __libc_csu_fini +// 1.4% 24 NAN% 0 completed.6973 +// 4.1% 593 5.6% 593 [LOAD #2 [RX]] +// 3.9% 561 0.0% 0 [section .strtab] +// 53.5% 300 NAN% 0 [section .strtab] +// 38.3% 215 NAN% 0 __libc_csu_init +// 2.9% 16 NAN% 0 __libc_csu_fini +// 2.7% 15 NAN% 0 _IO_stdin_used +// 2.7% 15 NAN% 0 completed.6973 +// 3.2% 464 4.4% 464 [section .dynamic] +// 3.0% 430 3.9% 405 [20 Others] +// 2.5% 356 3.4% 356 [section .text] +// 71.1% 253 71.1% 253 [section .text] +// 28.4% 101 28.4% 101 __libc_csu_init +// 0.6% 2 0.6% 2 __libc_csu_fini +// 2.3% 328 0.0% 0 [section .shstrtab] +// 1.5% 220 0.4% 46 main.o.c +// 37.3% 82 0.0% 0 [section .debug_info] +// 34.1% 75 100.0% 46 main +// 26.8% 59 0.0% 0 [section .debug_line] +// 1.8% 4 0.0% 0 [section .debug_str] +// 1.5% 212 2.0% 212 [section .eh_frame] +// 54.7% 116 54.7% 116 [section .eh_frame] +// 34.0% 72 34.0% 72 __libc_csu_init +// 11.3% 24 11.3% 24 __libc_csu_fini +// 1.0% 144 0.0% 0 [section .debug_aranges] +// 0.7% 101 0.0% 0 [section .debug_abbrev] +// 0.6% 93 0.0% 0 +// 0.5% 72 0.7% 72 [section .dynsym] +// 0.4% 56 0.5% 56 [section .dynstr] +// 0.2% 24 0.5% 52 [LOAD #3 [RW]] +// 0.3% 48 0.5% 48 [section .plt] +// 0.3% 48 0.5% 48 [section .rela.plt] +// 50.0% 24 50.0% 24 [section .rela.plt] +// 50.0% 24 50.0% 24 __libc_csu_init +// 100.0% 14.1Ki 100.0% 10.3Ki TOTAL + +TEST_F(BloatyTest, EncodeSymbolWithCrate) { + EXPECT_EQ(bloaty::EncodeSymbolWithCrateId("foo", "bar"), "foo, in crate bar"); +} + +TEST_F(BloatyTest, DecodeSymbolWithCrate) { + auto expected1 = bloaty::DecodeCrateIdResult{.symbol = "foo", .crate = ""}; + EXPECT_EQ(bloaty::DecodeSymbolWithCrateId("foo"), expected1); + auto expected2 = bloaty::DecodeCrateIdResult{.symbol = "foo", .crate = "bar"}; + EXPECT_EQ(bloaty::DecodeSymbolWithCrateId("foo, in crate bar"), expected2); +}
diff --git a/tests/link_map_test.cc b/tests/link_map_test.cc new file mode 100644 index 0000000..e729fcf --- /dev/null +++ b/tests/link_map_test.cc
@@ -0,0 +1,971 @@ +// Copyright 2020 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "link_map.h" + +#include <fstream> +#include <tuple> + +#include "bloaty.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +TEST(TransformCompileUnitForFuchsiaTest, RustLibraryCrate1) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), "[crate: alloc]"); + ASSERT_EQ(*std::get<1>(*result), "alloc"); +} + +TEST(TransformCompileUnitForFuchsiaTest, RustLibraryCrate2) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "./exe.unstripped/" + "component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o.rcgu." + "o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), "[crate: component_manager_lib]"); + ASSERT_EQ(*std::get<1>(*result), "component_manager_lib"); +} + +TEST(TransformCompileUnitForFuchsiaTest, RustLibraryCrate3) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "./exe.unstripped/" + "component_manager.libcm_fidl_translator.cm_fidl_translator.3a1fbbbh-cgu.0.rcgu.o.rcgu.o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), "[crate: cm_fidl_translator]"); + ASSERT_EQ(*std::get<1>(*result), "cm_fidl_translator"); +} + +TEST(TransformCompileUnitForFuchsiaTest, RustLibraryCrate4) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "./exe.unstripped/" + "component_manager.libfidl_fuchsia_io.fidl_fuchsia_io.3a1fbbbh-cgu.0.rcgu.o.rcgu.o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), "[crate: fidl_fuchsia_io]"); + ASSERT_EQ(*std::get<1>(*result), "fidl_fuchsia_io"); +} + +TEST(TransformCompileUnitForFuchsiaTest, RustBinaryCrate) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), "[crate: component_manager]"); + ASSERT_EQ(*std::get<1>(*result), "component_manager"); +} + +TEST(TransformCompileUnitForFuchsiaTest, RustRLibCrate) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "/usr/local/google/home/yifeit/vg/out/default/obj/third_party/rust_crates/" + "libregex_syntax-579ced0738b0164d.rlib(libregex_syntax-579ced0738b0164d-579ced0738b0164d." + "regex_syntax.c02sfxfu-cgu.13.rcgu.o)"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), "[crate: regex_syntax]"); + ASSERT_EQ(*std::get<1>(*result), "regex_syntax"); +} + +TEST(TransformCompileUnitForFuchsiaTest, ZirconLib) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "/usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/" + "c/crt1.Scrt1.cc.o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), "../../zircon/system/ulib/c/Scrt1.cc"); + ASSERT_EQ(std::get<1>(*result), std::nullopt); +} + +TEST(TransformCompileUnitForFuchsiaTest, Fidling1) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.hardware.block/" + "fuchsia.hardware.block_tables.fuchsia.hardware.block.fidl.tables.c.o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), + "fidling/gen/sdk/fidl/fuchsia.hardware.block/fuchsia.hardware.block.fidl.tables.c"); + ASSERT_EQ(std::get<1>(*result), std::nullopt); +} + +TEST(TransformCompileUnitForFuchsiaTest, Fidling2) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.hardware.block/fuchsia/hardware/block/c/" + "fuchsia.hardware.block_c_client.fidl.client.c.o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), + "fidling/gen/sdk/fidl/fuchsia.hardware.block/fuchsia/hardware/block/c/fidl.client.c"); + ASSERT_EQ(std::get<1>(*result), std::nullopt); +} + +TEST(TransformCompileUnitForFuchsiaTest, Fidling3) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.security.resource/fuchsia/security/resource/" + "llcpp/fuchsia.security.resource_llcpp.fidl.cc.o"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ( + std::get<0>(*result), + "fidling/gen/sdk/fidl/fuchsia.security.resource/fuchsia/security/resource/llcpp/fidl.cc"); + ASSERT_EQ(std::get<1>(*result), std::nullopt); +} + +TEST(TransformCompileUnitForFuchsiaTest, ZirconFidlLib) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia( + "obj/zircon/public/lib/fidl_base/libfidl_base.a(libfidl_base.decoding.cc.o)"); + ASSERT_TRUE(result.has_value()); + ASSERT_EQ(std::get<0>(*result), "../../zircon/system/ulib/fidl/decoding.cc"); + ASSERT_EQ(std::get<1>(*result), std::nullopt); +} + +TEST(TransformCompileUnitForFuchsiaTest, PassthroughUnknown) { + auto result = bloaty_link_map::TransformCompileUnitForFuchsia("foobar"); + ASSERT_FALSE(result.has_value()); +} + +std::string LoadLinkMapFile(const std::string& name) { + std::ifstream infile(name); + std::string link_map; + + // Strip comments and empty lines. + for (std::string line; getline(infile, line);) { + if (line.empty()) continue; + if (line[0] == '#') continue; + link_map += line; + link_map += '\n'; + } + + absl::StripLeadingAsciiWhitespace(&link_map); + absl::StripTrailingAsciiWhitespace(&link_map); + + return link_map; +} + +TEST(LinkMapTest, Empty) { + const std::string link_map = " VMA LMA Size Align Out In Symbol\n"; + auto symbols = bloaty_link_map::ParseLldLinkMap(link_map); + ASSERT_EQ(symbols.size(), 0); +} + +TEST(LinkMapTest, ExampleCpp) { + std::string link_map = LoadLinkMapFile("example_cpp.map"); + absl::StripLeadingAsciiWhitespace(&link_map); + absl::StripTrailingAsciiWhitespace(&link_map); + + auto symbols = bloaty_link_map::ParseLldLinkMap(link_map); + + struct Golden { + uint64_t addr; + uint64_t size; + std::string name; + std::string compile_unit; + std::string section; + }; + std::vector<Golden> goldens = { + {0x4380, 0x14, "main", "obj/zircon/system/uapp/blobfs/blobfs.main.cc.o", ".rodata"}, + {0x4394, 0x8a18, "** lld merge strings", "", ".rodata"}, + {0xcdb0, 0xa8, "** lld merge strings", "", ".rodata"}, + {0xce58, 0xc, "HandlefifonullableTable", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.hardware.block/" + "fuchsia.hardware.block_tables.fuchsia.hardware.block.fidl.tables.c.o", + ".rodata"}, + {0xce64, 0xc, "HandlevmononnullableTable", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.hardware.block/" + "fuchsia.hardware.block_tables.fuchsia.hardware.block.fidl.tables.c.o", + ".rodata"}, + {0xce70, 0xc, "HandleresourcenonnullableTable", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.security.resource/" + "fuchsia.security.resource_tables.fuchsia.security.resource.fidl.tables.c.o", + ".rodata"}, + {0xce7c, 0xa, "_ZN5llcpp7fuchsia2io8NodeInfo9reset_ptrEON4fidl12tracking_ptrIvEE", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.io/fuchsia/io/llcpp/" + "fuchsia.io_llcpp.fidl.cc.o", + ".rodata"}, + {0xce86, 0x2b, "fidl::kErrorWriteFailed", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.io/fuchsia/io/llcpp/" + "fuchsia.io_llcpp.fidl.cc.o", + ".rodata"}, + {0xceb4, 0xc, "Request15fuchsia_io_NodenonnullableTable", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.io/" + "fuchsia.io_tables.fuchsia.io.fidl.tables.c.o", + ".rodata"}, + {0xcec0, 0x8, "String4096nonnullableTable", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.io/" + "fuchsia.io_tables.fuchsia.io.fidl.tables.c.o", + ".rodata"}, + {0xd030, 0x100, "** lld merge strings", "", ".rodata"}, + {0xd130, 0xc, "HandleeventnonnullableTable", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.fs/" + "fuchsia.fs_tables.fuchsia.fs.fidl.tables.c.o", + ".rodata"}, + {0xd13c, 0x8, "String32nonnullableTable", + "obj/out/default/fidling/gen/sdk/fidl/fuchsia.fs/" + "fuchsia.fs_tables.fuchsia.fs.fidl.tables.c.o", + ".rodata"}, + {0xfb38, 0x9c, "LZ4HC_compress_generic_internal.clTable", + "obj/zircon/public/lib/lz4/liblz4.a(liblz4.lz4hc.c.o)", ".rodata"}, + {0xfbd4, 0x10, "XXH32_finalize", "obj/zircon/public/lib/lz4/liblz4.a(liblz4.xxhash.c.o)", + ".rodata"}, + {0xfbe4, 0x27, "kServicePath", + "obj/zircon/system/ulib/trace-provider/" + "libtrace-provider-with-fdio.a(libtrace-provider-with-fdio.fdio_connect.cc.o)", + ".rodata"}, + {0x21000, 0x14, "_start", + "/usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/" + "c/crt1.Scrt1.cc.o", + ".text"}, + {0x21014, 0x494, "main", "obj/zircon/system/uapp/blobfs/blobfs.main.cc.o", ".text"}, + {0x3a04c, 0x8, "non-virtual thunk to blobfs::TransactionManager::~TransactionManager()", + "obj/zircon/system/ulib/blobfs/libblobfs.a(libblobfs.blobfs.cc.o)", ".text"}, + {0x108120, 0xdc, "trace::internal::Session::~Session()", + "obj/zircon/system/ulib/trace-provider/" + "libtrace-provider-with-fdio.a(libtrace-provider-with-fdio.session.cc.o)", + ".text"}, + + }; + + // for (const auto& sym : symbols) { + // std::cout << std::hex << "{0x" << sym.addr << ", 0x" << sym.size << ", \"" << sym.name + // << "\", \"" << sym.compile_unit << "\", \"" << sym.section << "\"}," << std::endl; + // } + + ASSERT_EQ(symbols.size(), goldens.size()); + for (size_t i = 0; i < symbols.size(); i++) { + ASSERT_EQ(symbols[i].addr, goldens[i].addr); + ASSERT_EQ(symbols[i].size, goldens[i].size); + ASSERT_EQ(symbols[i].name, goldens[i].name); + ASSERT_EQ(symbols[i].compile_unit, goldens[i].compile_unit); + ASSERT_EQ(symbols[i].section, goldens[i].section); + } +} + +TEST(LinkMapTest, ExampleRust) { + std::string link_map = LoadLinkMapFile("example_rust.map"); + absl::StripLeadingAsciiWhitespace(&link_map); + absl::StripTrailingAsciiWhitespace(&link_map); + + auto symbols = bloaty_link_map::ParseLldLinkMap(link_map); + + struct Golden { + uint64_t addr; + uint64_t size; + std::string name; + std::string compile_unit; + std::string section; + }; + std::vector<Golden> goldens = { + {0x2800, 0x27, ".Lanon.745d157954bc19b57d1f47812cead630.3", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x2828, 0x0, ".Lanon.745d157954bc19b57d1f47812cead630.5", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x2828, 0x33, ".Lanon.745d157954bc19b57d1f47812cead630.13", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x2885, 0x11, "anon.745d157954bc19b57d1f47812cead630", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x2896, 0x3, ".Lanon.745d157954bc19b57d1f47812cead630.23", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x2899, 0x16, ".Lanon.745d157954bc19b57d1f47812cead630.26", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x292a, 0xd, "anon.745d157954bc19b57d1f47812cead630", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x2937, 0x5, "anon.745d157954bc19b57d1f47812cead630", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x293c, 0x5, "anon.745d157954bc19b57d1f47812cead630", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x2950, 0xa90, "** lld merge strings", "", ".rodata"}, + {0x33e0, 0x29, ".Lanon.256afffd819e3254289014381fd650e5.2", + "./exe.unstripped/" + "component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x346d, 0x0, ".Lanon.256afffd819e3254289014381fd650e5.14", + "./exe.unstripped/" + "component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x3470, 0x38, ".Lanon.256afffd819e3254289014381fd650e5.16", + "./exe.unstripped/" + "component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x34a8, 0x1b08, "** lld merge strings", "", ".rodata"}, + {0x4fb0, 0x3, ".Lanon.256afffd819e3254289014381fd650e5.18", + "./exe.unstripped/" + "component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x5070, 0x2780, "** lld merge strings", "", ".rodata"}, + {0x77f0, 0x6, ".Lanon.256afffd819e3254289014381fd650e5.26", + "./exe.unstripped/" + "component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x77f8, 0x118, ".Lanon.256afffd819e3254289014381fd650e5.28", + "./exe.unstripped/" + "component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x7910, 0xc, "anon.256afffd819e3254289014381fd650e5", + "./exe.unstripped/" + "component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x791c, 0x6, "anon.256afffd819e3254289014381fd650e5", + "./exe.unstripped/" + "component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x7922, 0x10, + "_RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeNtCsbDqzXfLQacH_15fidl_fuchsia_" + "io11FileRequestECs4fqI2P2rA04_17component_manager", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o", ".rodata"}, + {0x7950, 0xb1, + "_RNvXsc_NtCsbDqzXfLQacH_17fuchsia_component6serverINtB5_9ServiceFsINtNtB5_" + "7service10ServiceObjuEENtNtCseXumDWplyBO_12futures_core6stream6Stream9poll_" + "nextCs4fqI2P2rA04_17component_manager", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o", ".rodata"}, + {0x7a08, 0x380, "** lld merge strings", "", ".rodata"}, + {0x7d88, 0x2b, ".Lanon.dda71605b8fec89c031783be146b7025.1", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o", ".rodata"}, + {0x8cf1, 0x4e, ".Lanon.8db4583f006f833985ea18d2ab89bab7.1", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.7.rcgu.o", ".rodata"}, + {0x8d3f, 0x1f, "anon.8db4583f006f833985ea18d2ab89bab7", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.7.rcgu.o", ".rodata"}, + {0x8d5e, 0x25, ".Lanon.a1c52c190a58d1713a23e7a7bdb0f9c0.2", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.8.rcgu.o", ".rodata"}, + {0x8d83, 0x7, + "_RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeINtNvNtB4_6future14from_" + "generator9GenFutureNCNvMNtNtNtCsbDqzXfLQacH_21component_manager_" + "lib5model6events6sourceNtB1s_11EventSource3new0EECs4fqI2P2rA04_17component_manager", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o", ".rodata"}, + {0x8d8a, 0x8, + "_RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeINtNvNtB4_6future14from_" + "generator9GenFutureNCNvMs2_NtNtCsbDqzXfLQacH_21component_manager_lib5model5realmNtB1v_" + "5Realm19lock_resolved_state0EECs4fqI2P2rA04_17component_manager", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o", ".rodata"}, + {0x8d92, 0x16, + "_RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeINtNvNtB4_6future14from_" + "generator9GenFutureNCNvMs_NtCsbDqzXfLQacH_21component_manager_lib19builtin_" + "environmentNtB1u_18BuiltinEnvironment3new0EECs4fqI2P2rA04_17component_manager", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o", ".rodata"}, + {0x8da8, 0x4, + "_RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeNtCsbDqzXfLQacH_7cm_" + "rust10ExposeDeclECs4fqI2P2rA04_17component_manager", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o", ".rodata"}, + {0x8dac, 0x6, + "_RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeNtCsbDqzXfLQacH_7cm_" + "rust7UseDeclECs4fqI2P2rA04_17component_manager", + "./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o", ".rodata"}, + {0x91e3, 0x4, "_ZN4core3fmt9Formatter3pad17h9c055cdb18c92cc1E", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x91e7, 0x4, "_ZN4core3fmt9Formatter19pad_formatted_parts17heb4f002989281314E", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x9214, 0x29, "_ZN41_$LT$char$u20$as$u20$core..fmt..Debug$GT$3fmt17hb14158a7386f6b1aE", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x925c, 0x6f, + "_ZN61_$LT$core..str..EscapeDebug$u20$as$u20$core..fmt..Display$GT$3fmt17hf1238c3db5de3de5E", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x92d0, 0x0, "anon.6741f024a682c8cb8e59ecab4fc7e9ed.12.llvm.9325873315546439775", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x92d0, 0x1, ".Lanon.6741f024a682c8cb8e59ecab4fc7e9ed.14", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x92d1, 0x1d, ".Lanon.6741f024a682c8cb8e59ecab4fc7e9ed.27", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x9344, 0x28, "core::num::flt2dec::strategy::dragon::POW10::he881f1848ad745b6", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x936c, 0x28, "core::num::flt2dec::strategy::dragon::TWOPOW10::h8ee033ba72605fde", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x9394, 0x8, "core::num::flt2dec::strategy::dragon::POW10TO16::hd008c9b35d67c443", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x939c, 0x10, "core::num::flt2dec::strategy::dragon::POW10TO32::h94bb3ddee75ca692", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0x93ac, 0x1c, "core::num::flt2dec::strategy::dragon::POW10TO64::h2607b29da45ca92b", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".rodata"}, + {0xebf8, 0x28, ".Lanon.31de557278eced41e2a6dce0e33ed7ec.9", + "./exe.unstripped/" + "component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o." + "rcgu.o", + ".rodata"}, + {0xec20, 0x72, ".Lanon.31de557278eced41e2a6dce0e33ed7ec.15", + "./exe.unstripped/" + "component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o." + "rcgu.o", + ".rodata"}, + {0xec92, 0x2e, "anon.31de557278eced41e2a6dce0e33ed7ec", + "./exe.unstripped/" + "component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o." + "rcgu.o", + ".rodata"}, + {0xecc0, 0x13, "anon.31de557278eced41e2a6dce0e33ed7ec", + "./exe.unstripped/" + "component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o." + "rcgu.o", + ".rodata"}, + {0xecd3, 0x4a, ".Lanon.31de557278eced41e2a6dce0e33ed7ec.34", + "./exe.unstripped/" + "component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o." + "rcgu.o", + ".rodata"}, + {0x2e20c, 0xc, ".Lanon.93519e6636017643a07f78e678f4f11e.3501", + "./exe.unstripped/" + "component_manager.libidna-5c3c4f537fb8313f-5c3c4f537fb8313f.idna.axcknxfw-cgu.15.rcgu.o." + "rcgu.o", + ".rodata"}, + {0x2e218, 0xc, ".Lanon.93519e6636017643a07f78e678f4f11e.3502", + "./exe.unstripped/" + "component_manager.libidna-5c3c4f537fb8313f-5c3c4f537fb8313f.idna.axcknxfw-cgu.15.rcgu.o." + "rcgu.o", + ".rodata"}, + {0x2e224, 0xc, ".Lanon.93519e6636017643a07f78e678f4f11e.3503", + "./exe.unstripped/" + "component_manager.libidna-5c3c4f537fb8313f-5c3c4f537fb8313f.idna.axcknxfw-cgu.15.rcgu.o." + "rcgu.o", + ".rodata"}, + {0x2f4ca, 0x1b, "anon.7cbed09a5c2e2961b1298b053de6d71a", + "./exe.unstripped/component_manager.libio_util.io_util.3a1fbbbh-cgu.3.rcgu.o.rcgu.o", + ".rodata"}, + {0x2f4e5, 0x28, ".Lanon.2dc76abe0f971e71acd588990ffc8513.0", + "./exe.unstripped/component_manager.libio_util.io_util.3a1fbbbh-cgu.8.rcgu.o.rcgu.o", + ".rodata"}, + {0x2f50d, 0x2b, ".Lanon.6f9bafb4adb671d7ac9f8408e86ff31a.0", + "./exe.unstripped/" + "component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.1.rcgu.o.rcgu.o", + ".rodata"}, + {0x3aacc, 0x134, + "_ZN9libunwind17DwarfInstructionsINS_17LocalAddressSpaceENS_15Registers_" + "arm64EE18evaluateExpressionEmRS1_RKS2_m", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".rodata"}, + {0x3ac00, 0x39, + "_ZN9libunwind10CFI_ParserINS_17LocalAddressSpaceEE8parseCIEERS1_mPNS2_8CIE_InfoE", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".rodata"}, + {0x3ac39, 0xc, "_ZN9libunwind14EHHeaderParserINS_17LocalAddressSpaceEE17getTableEntrySizeEh", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".rodata"}, + {0xb2640, 0x14, "_start", + "/usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/" + "c/crt1.Scrt1.cc.o", + ".text"}, + {0xb2654, 0x1c, "_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$::fmt::h12b31caec1d188c4", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0xb2670, 0xb4, "_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$::fmt::h268a568299531efb", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0xcd52c, 0x98, + "core::num::_$LT$impl$u20$core..str..FromStr$u20$for$u20$usize$GT$::from_str::" + "h4398f35a1c0b4be7", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0xcd5c4, 0xe4, + "_$LT$core..num..TryFromIntError$u20$as$u20$core..fmt..Debug$GT$::fmt::h10047a113409f418", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0xcd6a8, 0x34, + "core::fmt::float::_$LT$impl$u20$core..fmt..Display$u20$for$u20$f64$GT$::fmt::" + "hac033855e28d5eb4", + "./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0xe2554, 0x10, "OUTLINED_FUNCTION_359", + "./exe.unstripped/" + "component_manager.libcm_fidl_translator.cm_fidl_translator.3a1fbbbh-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0xe2564, 0x10, "OUTLINED_FUNCTION_360", + "./exe.unstripped/" + "component_manager.libcm_fidl_translator.cm_fidl_translator.3a1fbbbh-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0xeb888, 0x40, + "_RINvNtNtCs6QHBvYLDz7W_4core4iter8adapters15process_resultsINtB2_3MapINtNtCsiEMmsMe2HZG_" + "5alloc3vec8IntoIterNtNtCsbDqzXfLQacH_7cm_json2cm8ResolverENCNvXs_CsbDqzXfLQacH_18cm_fidl_" + "translatorINtB17_3VecB1G_EINtB2q_6CmIntoIB2Z_NtCsbDqzXfLQacH_17fidl_fuchsia_" + "sys212ResolverDeclEE7cm_into0EB3x_NtB1K_5ErrorNCINvXsx_NtB6_6resultINtB4V_6ResultB3s_B4A_" + "EINtNtNtB4_6traits7collect12FromIteratorIB58_B3x_B4A_EE9from_iterBU_E0B3s_EB2q_", + "./exe.unstripped/" + "component_manager.libcm_fidl_translator.cm_fidl_translator.3a1fbbbh-cgu.4.rcgu.o.rcgu.o", + ".text"}, + {0x197340, 0x70, + "_RINvNtCsevZhND7KDbE_9hashbrown3raw16calculate_layoutTjINtNtCsiEMmsMe2HZG_" + "5alloc4sync3ArcDNtNtCsbDqzXfLQacH_13fuchsia_async8executor14PacketReceiverEL_EEEB1t_", + "./exe.unstripped/" + "component_manager.libfuchsia_async.fuchsia_async.3a1fbbbh-cgu.1.rcgu.o.rcgu.o", + ".text"}, + {0x200800, 0x38, + "std::sys_common::thread_info::THREAD_INFO::__getit::__KEY::h836843d10e59cada", + "./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0x200840, 0x18, + "std::panicking::update_panic_count::PANIC_COUNT::__getit::__KEY::h69785e8fa31a63e6", + "./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o", + ".text"}, + {0x1ff610, 0x30, ".Lanon.745d157954bc19b57d1f47812cead630.1", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x1ff640, 0x20, ".Lanon.745d157954bc19b57d1f47812cead630.2", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x1ff660, 0x18, ".Lanon.745d157954bc19b57d1f47812cead630.4", + "./exe.unstripped/" + "component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x202a88, 0x20, "anon.e57b3f2ae8b5861992cfe341e01c75d6", + "./exe.unstripped/" + "component_manager.libcm_fidl_validator.cm_fidl_validator.3a1fbbbh-cgu.0.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x2110a8, 0x18, ".Lanon.6f9bafb4adb671d7ac9f8408e86ff31a.3", + "./exe.unstripped/" + "component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.1.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x211108, 0x10, "anon.6f9bafb4adb671d7ac9f8408e86ff31a", + "./exe.unstripped/" + "component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.1.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x211118, 0x38, "anon.6f9bafb4adb671d7ac9f8408e86ff31a", + "./exe.unstripped/" + "component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.1.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x211150, 0x20, "anon.54c490f3ebdb496a1657b64fb6de7217", + "./exe.unstripped/" + "component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.10.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x211170, 0x18, ".Lanon.39cf32991562f2df00749835baa6fdc6.1", + "./exe.unstripped/" + "component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.11.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x215018, 0x18, "anon.d8e73c4ab2af7c41d0ca8802142f134e", + "./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o", + ".data.rel.ro"}, + {0x215030, 0x88, + "vtable for libunwind::UnwindCursor<libunwind::LocalAddressSpace, " + "libunwind::Registers_arm64>", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".data.rel.ro"}, + {0x2150b8, 0x310, ".Lswitch.table._ZN9libunwind15Registers_arm6415getRegisterNameEi", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".data.rel.ro"}, + {0x225848, 0x10, "_RNvCsi1CgODtnl2B_3log6LOGGER", + "./exe.unstripped/" + "component_manager.liblog-51a8548556518bd3-51a8548556518bd3.log.7ncv9mgr-cgu.0.rcgu.o.rcgu." + "o", + ".data"}, + {0x225858, 0x8, "std::thread::ThreadId::new::COUNTER::hb3a413a29c0f81f6", + "./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o", + ".data"}, + {0x225860, 0x38, "std::io::stdio::stderr::INSTANCE::hb24941292040ef06", + "./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o", + ".data"}, + {0x2258c8, 0x8, "libunwind::DwarfFDECache<libunwind::LocalAddressSpace>::_bufferEnd", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".data"}, + {0x225c68, 0x20, + "_RNvNvNvXs15_CsbDqzXfLQacH_7cm_rustNtBa_13DATA_TYPENAMENtNtNtCs6QHBvYLDz7W_" + "4core3ops5deref5Deref5deref11___stability4LAZY", + "./exe.unstripped/component_manager.libcm_rust.cm_rust.3a1fbbbh-cgu.0.rcgu.o.rcgu.o", + ".bss"}, + {0x225c88, 0x20, + "_RNvNvNvXs17_CsbDqzXfLQacH_7cm_rustNtBa_14CACHE_TYPENAMENtNtNtCs6QHBvYLDz7W_" + "4core3ops5deref5Deref5deref11___stability4LAZY", + "./exe.unstripped/component_manager.libcm_rust.cm_rust.3a1fbbbh-cgu.0.rcgu.o.rcgu.o", + ".bss"}, + {0x225ca8, 0x20, + "_RNvNvNvXs19_CsbDqzXfLQacH_7cm_rustNtBa_13META_TYPENAMENtNtNtCs6QHBvYLDz7W_" + "4core3ops5deref5Deref5deref11___stability4LAZY", + "./exe.unstripped/component_manager.libcm_rust.cm_rust.3a1fbbbh-cgu.0.rcgu.o.rcgu.o", + ".bss"}, + {0x225cc8, 0x38, + "_RNvNvNvXs0_NtNtNtCsbDqzXfLQacH_21component_manager_lib5model6events14source_factoryNtB9_" + "25EVENT_SOURCE_SERVICE_PATHNtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___" + "stability4LAZY", + "./exe.unstripped/" + "component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.12.rcgu.o." + "rcgu.o", + ".bss"}, + {0x225d00, 0x38, + "_RNvNvNvXs2_NtNtNtCsbDqzXfLQacH_21component_manager_lib5model6events14source_factoryNtB9_" + "30EVENT_SOURCE_SYNC_SERVICE_PATHNtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___" + "stability4LAZY", + "./exe.unstripped/" + "component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.12.rcgu.o." + "rcgu.o", + ".bss"}, + {0x2262ac, 0x8, "libunwind::DwarfFDECache<libunwind::LocalAddressSpace>::_lock", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".bss"}, + {0x2262b8, 0x800, "libunwind::DwarfFDECache<libunwind::LocalAddressSpace>::_initialBuffer", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".bss"}, + {0x226ab8, 0x1, "logAPIs::checked", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".bss"}, + {0x226abc, 0x1, "logAPIs::log", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".bss"}, + {0x226abd, 0x1, "logUnwinding::checked", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".bss"}, + {0x226ac0, 0x1, "logUnwinding::log", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".bss"}, + {0x226ac1, 0x1, "logDWARF::checked", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".bss"}, + {0x226ac4, 0x1, "logDWARF::log", + "/tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o)", ".bss"}, + }; + + // for (const auto& sym : symbols) { + // std::cout << std::hex << "{0x" << sym.addr << ", 0x" << sym.size << ", \"" << sym.name + // << "\", \"" << sym.compile_unit << "\", \"" << sym.section << "\"}," << std::endl; + // } + + ASSERT_EQ(symbols.size(), goldens.size()); + for (size_t i = 0; i < symbols.size(); i++) { + ASSERT_EQ(symbols[i].addr, goldens[i].addr); + ASSERT_EQ(symbols[i].size, goldens[i].size); + ASSERT_EQ(symbols[i].name, goldens[i].name); + ASSERT_EQ(symbols[i].compile_unit, goldens[i].compile_unit); + ASSERT_EQ(symbols[i].section, goldens[i].section); + } +} + +TEST(LinkMapTest, SuperSizeGoldens) { + std::ifstream infile("test_lld-lto_v1.map"); + std::string link_map; + for (std::string line; getline(infile, line);) { + if (line.empty()) continue; + if (line[0] == '#') continue; + link_map += line; + link_map += '\n'; + } + + absl::StripLeadingAsciiWhitespace(&link_map); + absl::StripTrailingAsciiWhitespace(&link_map); + + struct Golden { + uint64_t addr; + uint64_t size; + std::string name; + std::string compile_unit; + std::string section; + }; + std::vector<Golden> goldens = { + {0x213200, 0x4, "v8_Default_embedded_blob_size_", "obj/v8/v8_external_snapshot/embedded.o", + ".rodata"}, + {0x213210, 0x10, "pmmp", + "obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o)", ".rodata"}, + {0x213220, 0x10, "mppm", + "obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o)", ".rodata"}, + {0x213230, 0xc0, "std::__ndk1::(anonymous namespace)::small_primes", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(hash.o)", + ".rodata"}, + {0x2132f0, 0xc0, "std::__ndk1::(anonymous namespace)::indices", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(hash.o)", + ".rodata"}, + {0x2133b0, 0x4, "std::__ndk1::ios_base::boolalpha", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133b4, 0x4, "std::__ndk1::ios_base::dec", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133b8, 0x4, "std::__ndk1::ios_base::fixed", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133bc, 0x4, "std::__ndk1::ios_base::hex", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133c0, 0x4, "std::__ndk1::ios_base::internal", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133c4, 0x4, "std::__ndk1::ios_base::left", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133c8, 0x4, "std::__ndk1::ios_base::oct", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133cc, 0x4, "std::__ndk1::ios_base::right", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133d0, 0x4, "std::__ndk1::ios_base::scientific", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133d4, 0x4, "std::__ndk1::ios_base::showbase", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133d8, 0x4, "std::__ndk1::ios_base::showpoint", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133dc, 0x4, "std::__ndk1::ios_base::showpos", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133e0, 0x4, "std::__ndk1::ios_base::skipws", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133e4, 0x4, "std::__ndk1::ios_base::unitbuf", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133e8, 0x4, "std::__ndk1::ios_base::uppercase", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133ec, 0x4, "std::__ndk1::ios_base::adjustfield", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133f0, 0x4, "std::__ndk1::ios_base::basefield", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133f4, 0x4, "std::__ndk1::ios_base::floatfield", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133f8, 0x4, "std::__ndk1::ios_base::badbit", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x2133fc, 0x4, "std::__ndk1::ios_base::eofbit", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213400, 0x4, "std::__ndk1::ios_base::failbit", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213404, 0x4, "std::__ndk1::ios_base::goodbit", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213408, 0x4, "std::__ndk1::ios_base::app", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x21340c, 0x4, "std::__ndk1::ios_base::ate", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213410, 0x4, "std::__ndk1::ios_base::binary", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213414, 0x4, "std::__ndk1::ios_base::in", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213418, 0x4, "std::__ndk1::ios_base::out", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x21341c, 0x4, "std::__ndk1::ios_base::trunc", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213420, 0x1d, "typeinfo name for std::__ndk1::ios_base::failure", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213440, 0x15, "typeinfo name for std::__ndk1::ios_base", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213460, 0x21, "typeinfo name for std::__ndk1::__iostream_category", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x213490, 0x2d, "_ZTSNSt6__ndk19basic_iosIcNS_11char_traitsIcEEEE", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".rodata"}, + {0x21368b, 0x2c378e, "** lld merge strings", "", ".rodata"}, + {0x4d6e20, 0x1b, "typeinfo name for std::__ndk1::__stdinbuf<wchar_t>", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(iostream.o)", + ".rodata"}, + {0x4d7920, 0x266c, "** lld merge strings", "", ".rodata"}, + {0x4d9f90, 0x18, "typeinfo name for std::__ndk1::ctype_base", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(locale.o)", + ".rodata"}, + {0x4d9fb0, 0x1a, "typeinfo name for std::__ndk1::codecvt_base", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(locale.o)", + ".rodata"}, + {0x503eb3, 0x1, "network::mojom::CookieManagerProxy_SetCanonicalCookie_Message::kMessageTag", + "", ".rodata"}, + {0x503eb4, 0x1, + "network::mojom::CookieManagerProxy_DeleteCanonicalCookie_Message::kMessageTag", "", + ".rodata"}, + {0x82f000, 0x40, "", "obj/third_party/boringssl/boringssl_asm/chacha-armv4.o", ".text"}, + {0x82f040, 0x3f0, "ChaCha20_ctr32", "obj/third_party/boringssl/boringssl_asm/chacha-armv4.o", + ".text"}, + {0x82f440, 0x89c, "ChaCha20_neon", "obj/third_party/boringssl/boringssl_asm/chacha-armv4.o", + ".text"}, + {0x82fce0, 0x540, "AES_Te", "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", ".text"}, + {0x830220, 0x60, "aes_nohw_encrypt", "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", + ".text"}, + {0x830280, 0x1d8, "_armv4_AES_encrypt", "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", + ".text"}, + {0x830460, 0x2a0, "aes_nohw_set_encrypt_key", + "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", ".text"}, + {0x830700, 0x20, "aes_nohw_set_decrypt_key", + "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", ".text"}, + {0x830720, 0x124, "AES_set_enc2dec_key", + "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", ".text"}, + {0x830860, 0x500, "AES_Td", "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", ".text"}, + {0x830d60, 0x60, "aes_nohw_decrypt", "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", + ".text"}, + {0x830dc0, 0x1f8, "_armv4_AES_decrypt", "obj/third_party/boringssl/boringssl_asm/aes-armv4.o", + ".text"}, + {0x831000, 0x40, "", "obj/third_party/boringssl/boringssl_asm/aesv8-armx32.o", ".text"}, + {0x831040, 0x218, "aes_hw_set_encrypt_key", + "obj/third_party/boringssl/boringssl_asm/aesv8-armx32.o", ".text"}, + {0x831260, 0x60, "aes_hw_set_decrypt_key", + "obj/third_party/boringssl/boringssl_asm/aesv8-armx32.o", ".text"}, + {0x8312c0, 0x50, "aes_hw_encrypt", "obj/third_party/boringssl/boringssl_asm/aesv8-armx32.o", + ".text"}, + {0x831320, 0x50, "aes_hw_decrypt", "obj/third_party/boringssl/boringssl_asm/aesv8-armx32.o", + ".text"}, + {0x83c640, 0x2e60, "v8_Default_embedded_blob_data_", "obj/v8/v8_external_snapshot/embedded.o", + ".text"}, + {0x83f4a0, 0x320, "Builtins_RecordWrite", "obj/v8/v8_external_snapshot/embedded.o", ".text"}, + {0x83f7c0, 0x40, "Builtins_AdaptorWithExitFrame", "obj/v8/v8_external_snapshot/embedded.o", + ".text"}, + {0x83f800, 0x40, "Builtins_AdaptorWithBuiltinExitFrame", + "obj/v8/v8_external_snapshot/embedded.o", ".text"}, + {0x83f840, 0x100, "Builtins_ArgumentsAdaptorTrampoline", + "obj/v8/v8_external_snapshot/embedded.o", ".text"}, + {0x83f940, 0x100, "Builtins_CallFunction_ReceiverIsNullOrUndefined", + "obj/v8/v8_external_snapshot/embedded.o", ".text"}, + {0x83fa40, 0x140, "Builtins_CallFunction_ReceiverIsNotNullOrUndefined", + "obj/v8/v8_external_snapshot/embedded.o", ".text"}, + {0x83fb80, 0x180, "Builtins_CallFunction_ReceiverIsAny", + "obj/v8/v8_external_snapshot/embedded.o", ".text"}, + {0x83fd00, 0x137820, "Builtins_CallBoundFunction", "obj/v8/v8_external_snapshot/embedded.o", + ".text"}, + {0x977520, 0x18, "PushAllRegisters", + "obj/third_party/blink/renderer/platform/heap/asm/asm/SaveRegisters_arm.o", ".text"}, + {0x97c834, 0x4dc, "vpx_convolve8_avg_horiz_filter_type1_neon", + "obj/third_party/libvpx/libvpx_assembly_arm.a(libvpx_assembly_arm/" + "vpx_convolve8_avg_horiz_filter_type1_neon.asm.o)", + ".text"}, + {0x99cad8, 0xfc, "sinh", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libandroid_support.a(e_sinh.o)", + ".text"}, + {0x1401788, 0xa4, "$_21::operator()(FamilyData*, char const*, char const**) const", "", + ".text"}, + {0x1401820, 0x10, "UnlikelyFunc", "", ".text"}, + {0x1401850, 0x10, "StartUpFunc", "", ".text"}, + {0x2d4c000, 0x4, "v8_Default_embedded_blob_", "obj/v8/v8_external_snapshot/embedded.o", + ".data"}, + {0x2d4c004, 0x4, "__dso_handle", + "../../third_party/android_ndk/platforms/android-16/arch-arm/usr/lib/crtbegin_so.o", + ".data"}, + {0x2d69000, 0x3c, "fft_tab_vfp", + "obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_vfp.o)", ".data.rel.ro"}, + {0x2d6903c, 0x3c, "fft_tab_neon", + "obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o)", ".data.rel.ro"}, + {0x2d69078, 0x40, + "vtable for std::__ndk1::basic_streambuf<wchar_t, std::__ndk1::char_traits<wchar_t> >", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d690b8, 0x28, + "vtable for std::__ndk1::basic_istream<char, std::__ndk1::char_traits<char> >", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d690e0, 0x28, + "vtable for std::__ndk1::basic_istream<wchar_t, std::__ndk1::char_traits<wchar_t> >", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d69108, 0x28, + "vtable for std::__ndk1::basic_ostream<char, std::__ndk1::char_traits<char> >", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d69130, 0x28, + "vtable for std::__ndk1::basic_ostream<wchar_t, std::__ndk1::char_traits<wchar_t> >", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d69158, 0x24, "vtable for std::__ndk1::__iostream_category", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d6917c, 0x14, "vtable for std::__ndk1::ios_base::failure", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d69190, 0x10, "vtable for std::__ndk1::ios_base", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d691a0, 0xc, "typeinfo for std::__ndk1::ios_base::failure", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d691ac, 0x8, "typeinfo for std::__ndk1::ios_base", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2d691b4, 0xc, "typeinfo for std::__ndk1::__iostream_category", + "../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/" + "libc++_static.a(ios.o)", + ".data.rel.ro"}, + {0x2f56000, 0x4, "WebRtcSpl_CrossCorrelation", "", ".bss"}, + {0x2f56004, 0x4, "WebRtcSpl_DownsampleFast", "", ".bss"}, + {0x2f56008, 0x4, "WebRtcSpl_MaxAbsValueW16", "", ".bss"}, + {0x0, 0x1000, "** ", "", ".part.end"}, + }; + + auto symbols = bloaty_link_map::ParseLldLinkMap(link_map); + // for (const auto& sym : symbols) { + // std::cout << std::hex << "{0x" << sym.addr << ", 0x" << sym.size << ", \"" << sym.name + // << "\", \"" << sym.compile_unit << "\", \"" << sym.section << "\"}," << std::endl; + // } + + ASSERT_EQ(symbols.size(), goldens.size()); + for (size_t i = 0; i < symbols.size(); i++) { + ASSERT_EQ(symbols[i].addr, goldens[i].addr); + ASSERT_EQ(symbols[i].size, goldens[i].size); + ASSERT_EQ(symbols[i].name, goldens[i].name); + ASSERT_EQ(symbols[i].compile_unit, goldens[i].compile_unit); + ASSERT_EQ(symbols[i].section, goldens[i].section); + } +}
diff --git a/tests/testdata/link_map/example_cpp.map b/tests/testdata/link_map/example_cpp.map new file mode 100644 index 0000000..ff80ed1 --- /dev/null +++ b/tests/testdata/link_map/example_cpp.map
@@ -0,0 +1,103 @@ + VMA LMA Size Align Out In Symbol + 2a8 2a8 8 1 .interp + 2a8 2a8 8 1 <internal>:(.interp) + 2b0 2b0 18 4 .note.gnu.build-id + 2b0 2b0 18 4 <internal>:(.note.gnu.build-id) + 2c8 2c8 12f0 8 .dynsym + 2c8 2c8 12f0 8 <internal>:(.dynsym) + 15b8 15b8 1c 8 .gnu.hash + 15b8 15b8 1c 8 <internal>:(.gnu.hash) + 15d8 15d8 1e0 8 .dynamic + 15d8 15d8 1e0 8 <internal>:(.dynamic) + 17b8 17b8 143c 1 .dynstr + 17b8 17b8 143c 1 <internal>:(.dynstr) + 2bf8 2bf8 540 8 .rela.dyn + 2bf8 2bf8 540 8 <internal>:(.rela.dyn) + 3138 3138 150 8 .relr.dyn + 3138 3138 150 8 <internal>:(.relr.dyn) + 3288 3288 10f8 8 .rela.plt + 3288 3288 10f8 8 <internal>:(.rela.plt) + 4380 4380 b88b 16 .rodata + 4380 4380 14 1 obj/zircon/system/uapp/blobfs/blobfs.main.cc.o:(.rodata.main) + 4380 4380 0 1 $d.1 + 4394 4394 8a18 1 <internal>:(.rodata) + cdb0 cdb0 a8 8 <internal>:(.rodata) + ce58 ce58 c 4 obj/out/default/fidling/gen/sdk/fidl/fuchsia.hardware.block/fuchsia.hardware.block_tables.fuchsia.hardware.block.fidl.tables.c.o:(.rodata.HandlefifonullableTable) + ce58 ce58 0 1 $d.45 + ce58 ce58 c 1 HandlefifonullableTable + ce64 ce64 c 4 obj/out/default/fidling/gen/sdk/fidl/fuchsia.hardware.block/fuchsia.hardware.block_tables.fuchsia.hardware.block.fidl.tables.c.o:(.rodata.HandlevmononnullableTable) + ce64 ce64 0 1 $d.46 + ce64 ce64 c 1 HandlevmononnullableTable + ce70 ce70 c 4 obj/out/default/fidling/gen/sdk/fidl/fuchsia.security.resource/fuchsia.security.resource_tables.fuchsia.security.resource.fidl.tables.c.o:(.rodata.HandleresourcenonnullableTable) + ce70 ce70 0 1 $d.5 + ce70 ce70 c 1 HandleresourcenonnullableTable + ce7c ce7c a 1 obj/out/default/fidling/gen/sdk/fidl/fuchsia.io/fuchsia/io/llcpp/fuchsia.io_llcpp.fidl.cc.o:(.rodata._ZN5llcpp7fuchsia2io8NodeInfo9reset_ptrEON4fidl12tracking_ptrIvEE) + ce7c ce7c 0 1 $d.1175 + ce86 ce86 2b 1 obj/out/default/fidling/gen/sdk/fidl/fuchsia.io/fuchsia/io/llcpp/fuchsia.io_llcpp.fidl.cc.o:(.rodata._ZN4fidlL17kErrorWriteFailedE) + ce86 ce86 0 1 $d.1404 + ce86 ce86 2b 1 fidl::kErrorWriteFailed + ceb4 ceb4 c 4 obj/out/default/fidling/gen/sdk/fidl/fuchsia.io/fuchsia.io_tables.fuchsia.io.fidl.tables.c.o:(.rodata.Request15fuchsia_io_NodenonnullableTable) + ceb4 ceb4 0 1 $d.251 + ceb4 ceb4 c 1 Request15fuchsia_io_NodenonnullableTable + cec0 cec0 8 4 obj/out/default/fidling/gen/sdk/fidl/fuchsia.io/fuchsia.io_tables.fuchsia.io.fidl.tables.c.o:(.rodata.String4096nonnullableTable) + cec0 cec0 0 1 $d.253 + cec0 cec0 8 1 String4096nonnullableTable + d030 d030 100 16 <internal>:(.rodata) + d130 d130 c 4 obj/out/default/fidling/gen/sdk/fidl/fuchsia.fs/fuchsia.fs_tables.fuchsia.fs.fidl.tables.c.o:(.rodata.HandleeventnonnullableTable) + d130 d130 0 1 $d.22 + d130 d130 c 1 HandleeventnonnullableTable + d13c d13c 8 4 obj/out/default/fidling/gen/sdk/fidl/fuchsia.fs/fuchsia.fs_tables.fuchsia.fs.fidl.tables.c.o:(.rodata.String32nonnullableTable) + d13c d13c 0 1 $d.23 + d13c d13c 8 1 String32nonnullableTable + fb38 fb38 9c 4 obj/zircon/public/lib/lz4/liblz4.a(liblz4.lz4hc.c.o):(.rodata.LZ4HC_compress_generic_internal.clTable) + fb38 fb38 0 1 $d.39 + fb38 fb38 9c 1 LZ4HC_compress_generic_internal.clTable + fbd4 fbd4 10 1 obj/zircon/public/lib/lz4/liblz4.a(liblz4.xxhash.c.o):(.rodata.XXH32_finalize) + fbd4 fbd4 0 1 $d.22 + fbe4 fbe4 27 1 obj/zircon/system/ulib/trace-provider/libtrace-provider-with-fdio.a(libtrace-provider-with-fdio.fdio_connect.cc.o):(.rodata._ZL12kServicePath) + fbe4 fbe4 0 1 $d.1 + fbe4 fbe4 27 1 kServicePath + fc0c fc0c 361c 4 .eh_frame_hdr + fc0c fc0c 361c 4 <internal>:(.eh_frame_hdr) + 13228 13228 d63c 8 .eh_frame + 13228 13228 14 1 /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o:(.eh_frame+0x0) + 13260 13260 54 1 obj/zircon/system/uapp/blobfs/blobfs.main.cc.o:(.eh_frame+0x14) + 13320 13320 2c 1 obj/out/default/fidling/gen/sdk/fidl/fuchsia.security.resource/fuchsia/security/resource/llcpp/fuchsia.security.resource_llcpp.fidl.cc.o:(.eh_frame+0x34) + 207d8 207d8 1c 1 obj/zircon/system/ulib/trace-provider/libtrace-provider-with-fdio.a(libtrace-provider-with-fdio.utils.cc.o):(.eh_frame+0x14) + 20818 20818 14 1 ../../out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/sync/libsync.a(sync._sources.completion.c.o):(.eh_frame+0x54) + 20848 20848 14 1 ../../prebuilt/third_party/clang/linux-x64/lib/clang/11.0.0/lib/aarch64-unknown-fuchsia/libclang_rt.builtins.a(udivmodti4.c.obj):(.eh_frame+0x14) + 21000 21000 e84f0 8 .text + 21000 21000 14 8 /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o:(.text._start) + 21000 21000 0 1 $x.0 + 21000 21000 14 1 _start + 21014 21014 494 4 obj/zircon/system/uapp/blobfs/blobfs.main.cc.o:(.text.main) + 21014 21014 0 1 $x.0 + 21014 21014 494 1 main + 3a04c 3a04c 8 4 obj/zircon/system/ulib/blobfs/libblobfs.a(libblobfs.blobfs.cc.o):(.text._ZThn8_N6blobfs18TransactionManagerD1Ev) + 3a04c 3a04c 0 1 $x.83 + 3a04c 3a04c 8 1 non-virtual thunk to blobfs::TransactionManager::~TransactionManager() + 108120 108120 dc 4 obj/zircon/system/ulib/trace-provider/libtrace-provider-with-fdio.a(libtrace-provider-with-fdio.session.cc.o):(.text._ZN5trace8internal7SessionD2Ev) + 108120 108120 0 1 $x.1 + 108120 108120 dc 1 trace::internal::Session::~Session() + 0 0 d41f13 1 .debug_loc + 0 0 39 1 /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o:(.debug_loc) + 0 0 0 1 $d.1 108120 108120 dc 1 trace::internal::Session::~Session() + d411ac d411ac 458 1 ../../out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/sync/libsync.a(sync._sources.completion.c.o):(.debug_loc) + d411ac d411ac 0 1 $d.6 + d41604 d41604 50 1 ../../prebuilt/third_party/clang/linux-x64/lib/clang/11.0.0/lib/aarch64-unknown-fuchsia/libclang_rt.builtins.a(udivti3.c.obj):(.debug_loc) + d41604 d41604 0 1 $d.1 + d41654 d41654 8bf 1 ../../prebuilt/third_party/clang/linux-x64/lib/clang/11.0.0/lib/aarch64-unknown-fuchsia/libclang_rt.builtins.a(udivmodti4.c.obj):(.debug_loc) + d41654 d41654 0 1 $d.1 + 0 0 52a34 1 .debug_abbrev + 0 0 6d 1 /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o:(.debug_abbrev) + 0 0 0 1 $d.2 + 7fa 7fa 93f 1 obj/out/default/fidling/gen/sdk/fidl/fuchsia.security.resource/fuchsia/security/resource/llcpp/fuchsia.security.resource_llcpp.fidl.cc.o:(.debug_abbrev) + 7fa 7fa 0 1 $d.40 + 3323a0 3323a0 30 1 ../../prebuilt/third_party/clang/linux-x64/lib/clang/11.0.0/lib/aarch64-unknown-fuchsia/libclang_rt.builtins.a(udivmodti4.c.obj):(.debug_ranges) + 3323a0 3323a0 0 1 $d.4 + 0 0 2ed88 8 .symtab + 0 0 2ed88 8 <internal>:(.symtab) + 0 0 127 1 .shstrtab + 0 0 127 1 <internal>:(.shstrtab) + 0 0 3b51c 1 .strtab + 0 0 3b51c 1 <internal>:(.strtab)
diff --git a/tests/testdata/link_map/example_rust.map b/tests/testdata/link_map/example_rust.map new file mode 100644 index 0000000..fda29bc --- /dev/null +++ b/tests/testdata/link_map/example_rust.map
@@ -0,0 +1,272 @@ + VMA LMA Size Align Out In Symbol + 2e0 2e0 8 1 .interp + 2e0 2e0 8 1 <internal>:(.interp) + 2e8 2e8 18 4 .note.gnu.build-id + 2e8 2e8 18 4 <internal>:(.note.gnu.build-id) + 300 300 ac8 8 .dynsym + 300 300 ac8 8 <internal>:(.dynsym) + dc8 dc8 1c 8 .gnu.hash + dc8 dc8 1c 8 <internal>:(.gnu.hash) + de8 de8 170 8 .dynamic + de8 de8 170 8 <internal>:(.dynamic) + f58 f58 821 1 .dynstr + f58 f58 821 1 <internal>:(.dynstr) + 1780 1780 48 8 .rela.dyn + 1780 1780 48 8 <internal>:(.rela.dyn) + 17c8 17c8 5b8 8 .relr.dyn + 17c8 17c8 5b8 8 <internal>:(.relr.dyn) + 1d80 1d80 a80 8 .rela.plt + 1d80 1d80 a80 8 <internal>:(.rela.plt) + 2800 2800 38445 16 .rodata + 2800 2800 27 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.745d157954bc19b57d1f47812cead630.3) + 2800 2800 0 1 $d.27 + 2828 2828 0 8 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.745d157954bc19b57d1f47812cead630.5) + 2828 2828 33 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.745d157954bc19b57d1f47812cead630.13) + 2828 2828 0 1 $d.30 + 2885 2885 11 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata.anon.745d157954bc19b57d1f47812cead630.19.llvm.1460520995096272372) + 2885 2885 0 1 $d.34 + 2885 2885 11 1 anon.745d157954bc19b57d1f47812cead630.19.llvm.1460520995096272372 + 2896 2896 3 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.745d157954bc19b57d1f47812cead630.23) + 2896 2896 0 1 $d.36 + 2899 2899 16 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.745d157954bc19b57d1f47812cead630.26) + 2899 2899 0 1 $d.37 + 292a 292a d 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata.anon.745d157954bc19b57d1f47812cead630.56.llvm.1460520995096272372) + 292a 292a 0 1 $d.57 + 292a 292a d 1 anon.745d157954bc19b57d1f47812cead630.56.llvm.1460520995096272372 + 2937 2937 5 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata.anon.745d157954bc19b57d1f47812cead630.57.llvm.1460520995096272372) + 2937 2937 0 1 $d.58 + 2937 2937 5 1 anon.745d157954bc19b57d1f47812cead630.57.llvm.1460520995096272372 + 293c 293c 5 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.rodata.anon.745d157954bc19b57d1f47812cead630.59.llvm.1460520995096272372) + 293c 293c 0 1 $d.60 + 293c 293c 5 1 anon.745d157954bc19b57d1f47812cead630.59.llvm.1460520995096272372 + 2950 2950 a90 16 <internal>:(.rodata) + 33e0 33e0 29 1 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.256afffd819e3254289014381fd650e5.2) + 33e0 33e0 0 1 $d.20 + 346d 346d 0 1 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.256afffd819e3254289014381fd650e5.14) + 3470 3470 38 8 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.256afffd819e3254289014381fd650e5.16) + 3470 3470 0 1 $d.25 + 34a8 34a8 1b08 4 <internal>:(.rodata) + 4fb0 4fb0 3 1 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.256afffd819e3254289014381fd650e5.18) + 4fb0 4fb0 0 1 $d.27 + 5070 5070 2780 8 <internal>:(.rodata) + 77f0 77f0 6 1 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.256afffd819e3254289014381fd650e5.26) + 77f0 77f0 0 1 $d.34 + 77f8 77f8 118 8 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.256afffd819e3254289014381fd650e5.28) + 77f8 77f8 0 1 $d.36 + 7910 7910 c 1 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.rodata.anon.256afffd819e3254289014381fd650e5.29.llvm.13894642946385378479) + 7910 7910 0 1 $d.37 + 7910 7910 c 1 anon.256afffd819e3254289014381fd650e5.29.llvm.13894642946385378479 + 791c 791c 6 1 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.rodata.anon.256afffd819e3254289014381fd650e5.30.llvm.13894642946385378479) + 791c 791c 0 1 $d.38 + 791c 791c 6 1 anon.256afffd819e3254289014381fd650e5.30.llvm.13894642946385378479 + 7922 7922 10 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o:(.rodata._RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeNtCsbDqzXfLQacH_15fidl_fuchsia_io11FileRequestECs4fqI2P2rA04_17component_manager) + 7922 7922 0 1 $d.13 + 7950 7950 b1 2 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o:(.rodata._RNvXsc_NtCsbDqzXfLQacH_17fuchsia_component6serverINtB5_9ServiceFsINtNtB5_7service10ServiceObjuEENtNtCseXumDWplyBO_12futures_core6stream6Stream9poll_nextCs4fqI2P2rA04_17component_manager) + 7950 7950 0 1 $d.44 + 7a08 7a08 380 8 <internal>:(.rodata) + 7d88 7d88 2b 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.0.rcgu.o:(.rodata..Lanon.dda71605b8fec89c031783be146b7025.1) + 7d88 7d88 0 1 $d.101 + 8cf1 8cf1 4e 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.7.rcgu.o:(.rodata..Lanon.8db4583f006f833985ea18d2ab89bab7.1) + 8cf1 8cf1 0 1 $d.47 + 8d3f 8d3f 1f 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.7.rcgu.o:(.rodata.anon.8db4583f006f833985ea18d2ab89bab7.4.llvm.7664684806704912226) + 8d3f 8d3f 0 1 $d.50 + 8d3f 8d3f 1f 1 anon.8db4583f006f833985ea18d2ab89bab7.4.llvm.7664684806704912226 + 8d5e 8d5e 25 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.8.rcgu.o:(.rodata..Lanon.a1c52c190a58d1713a23e7a7bdb0f9c0.2) + 8d5e 8d5e 0 1 $d.17 + 8d83 8d83 7 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o:(.rodata._RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeINtNvNtB4_6future14from_generator9GenFutureNCNvMNtNtNtCsbDqzXfLQacH_21component_manager_lib5model6events6sourceNtB1s_11EventSource3new0EECs4fqI2P2rA04_17component_manager) + 8d83 8d83 0 1 $d.56 + 8d8a 8d8a 8 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o:(.rodata._RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeINtNvNtB4_6future14from_generator9GenFutureNCNvMs2_NtNtCsbDqzXfLQacH_21component_manager_lib5model5realmNtB1v_5Realm19lock_resolved_state0EECs4fqI2P2rA04_17component_manager) + 8d8a 8d8a 0 1 $d.59 + 8d92 8d92 16 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o:(.rodata._RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeINtNvNtB4_6future14from_generator9GenFutureNCNvMs_NtCsbDqzXfLQacH_21component_manager_lib19builtin_environmentNtB1u_18BuiltinEnvironment3new0EECs4fqI2P2rA04_17component_manager) + 8d92 8d92 0 1 $d.64 + 8da8 8da8 4 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o:(.rodata._RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeNtCsbDqzXfLQacH_7cm_rust10ExposeDeclECs4fqI2P2rA04_17component_manager) + 8da8 8da8 0 1 $d.74 + 8dac 8dac 6 1 ./exe.unstripped/component_manager.component_manager.7rcbfp3g-cgu.9.rcgu.o:(.rodata._RINvNtCs6QHBvYLDz7W_4core3ptr13drop_in_placeNtCsbDqzXfLQacH_7cm_rust7UseDeclECs4fqI2P2rA04_17component_manager) + 8dac 8dac 0 1 $d.81 + 91e3 91e3 4 1 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN4core3fmt9Formatter3pad17h9c055cdb18c92cc1E) + 91e3 91e3 0 1 $d.54 + 91e7 91e7 4 1 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN4core3fmt9Formatter19pad_formatted_parts17heb4f002989281314E) + 91e7 91e7 0 1 $d.56 + 9214 9214 29 1 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN41_$LT$char$u20$as$u20$core..fmt..Debug$GT$3fmt17hb14158a7386f6b1aE) + 9214 9214 0 1 $d.73 + 925c 925c 6f 1 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN61_$LT$core..str..EscapeDebug$u20$as$u20$core..fmt..Display$GT$3fmt17hf1238c3db5de3de5E) + 925c 925c 0 1 $d.130 + 92d0 92d0 0 8 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata.anon.6741f024a682c8cb8e59ecab4fc7e9ed.12.llvm.9325873315546439775) + 92d0 92d0 0 1 anon.6741f024a682c8cb8e59ecab4fc7e9ed.12.llvm.9325873315546439775 + 92d0 92d0 1 1 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.6741f024a682c8cb8e59ecab4fc7e9ed.14) + 92d0 92d0 0 1 $d.133 + 92d1 92d1 1d 1 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata..Lanon.6741f024a682c8cb8e59ecab4fc7e9ed.27) + 92d1 92d1 0 1 $d.134 + 9344 9344 28 4 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN4core3num7flt2dec8strategy6dragon5POW1017he881f1848ad745b6E) + 9344 9344 0 1 $d.142 + 9344 9344 28 1 core::num::flt2dec::strategy::dragon::POW10::he881f1848ad745b6 + 936c 936c 28 4 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN4core3num7flt2dec8strategy6dragon8TWOPOW1017h8ee033ba72605fdeE) + 936c 936c 0 1 $d.143 + 936c 936c 28 1 core::num::flt2dec::strategy::dragon::TWOPOW10::h8ee033ba72605fde + 9394 9394 8 4 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN4core3num7flt2dec8strategy6dragon9POW10TO1617hd008c9b35d67c443E) + 9394 9394 0 1 $d.144 + 9394 9394 8 1 core::num::flt2dec::strategy::dragon::POW10TO16::hd008c9b35d67c443 + 939c 939c 10 4 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN4core3num7flt2dec8strategy6dragon9POW10TO3217h94bb3ddee75ca692E) + 939c 939c 0 1 $d.145 + 939c 939c 10 1 core::num::flt2dec::strategy::dragon::POW10TO32::h94bb3ddee75ca692 + 93ac 93ac 1c 4 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.rodata._ZN4core3num7flt2dec8strategy6dragon9POW10TO6417h2607b29da45ca92bE) + 93ac 93ac 0 1 $d.146 + 93ac 93ac 1c 1 core::num::flt2dec::strategy::dragon::POW10TO64::h2607b29da45ca92b + ebf8 ebf8 28 1 ./exe.unstripped/component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o.rcgu.o:(.rodata..Lanon.31de557278eced41e2a6dce0e33ed7ec.9) + ebf8 ebf8 0 1 $d.392 + ec20 ec20 1c 1 ./exe.unstripped/component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o.rcgu.o:(.rodata..Lanon.31de557278eced41e2a6dce0e33ed7ec.15) + ec20 ec20 0 1 $d.393 + ec92 ec92 2e 1 anon.31de557278eced41e2a6dce0e33ed7ec.20.llvm.11955293494338827311 + ecc0 ecc0 13 1 ./exe.unstripped/component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o.rcgu.o:(.rodata.anon.31de557278eced41e2a6dce0e33ed7ec.33.llvm.13709288095524858756) + ecc0 ecc0 0 1 $d.400 + ecc0 ecc0 13 1 anon.31de557278eced41e2a6dce0e33ed7ec.33.llvm.13709288095524858756 + ecd3 ecd3 4a 1 ./exe.unstripped/component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.2.rcgu.o.rcgu.o:(.rodata..Lanon.31de557278eced41e2a6dce0e33ed7ec.34) + ecd3 ecd3 0 1 $d.401 + 2e20c 2e20c c 4 ./exe.unstripped/component_manager.libidna-5c3c4f537fb8313f-5c3c4f537fb8313f.idna.axcknxfw-cgu.15.rcgu.o.rcgu.o:(.rodata..Lanon.93519e6636017643a07f78e678f4f11e.3501) + 2e20c 2e20c 0 1 $d.656 + 2e218 2e218 c 4 ./exe.unstripped/component_manager.libidna-5c3c4f537fb8313f-5c3c4f537fb8313f.idna.axcknxfw-cgu.15.rcgu.o.rcgu.o:(.rodata..Lanon.93519e6636017643a07f78e678f4f11e.3502) + 2e218 2e218 0 1 $d.657 + 2e224 2e224 c 4 ./exe.unstripped/component_manager.libidna-5c3c4f537fb8313f-5c3c4f537fb8313f.idna.axcknxfw-cgu.15.rcgu.o.rcgu.o:(.rodata..Lanon.93519e6636017643a07f78e678f4f11e.3503) + 2e224 2e224 0 1 $d.658 + 2f4ca 2f4ca 1b 1 ./exe.unstripped/component_manager.libio_util.io_util.3a1fbbbh-cgu.3.rcgu.o.rcgu.o:(.rodata.anon.7cbed09a5c2e2961b1298b053de6d71a.14.llvm.4649089991547526960) + 2f4ca 2f4ca 0 1 $d.33 + 2f4ca 2f4ca 1b 1 anon.7cbed09a5c2e2961b1298b053de6d71a.14.llvm.4649089991547526960 + 2f4e5 2f4e5 28 1 ./exe.unstripped/component_manager.libio_util.io_util.3a1fbbbh-cgu.8.rcgu.o.rcgu.o:(.rodata..Lanon.2dc76abe0f971e71acd588990ffc8513.0) + 2f4e5 2f4e5 0 1 $d.16 + 2f50d 2f50d 2b 1 ./exe.unstripped/component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.1.rcgu.o.rcgu.o:(.rodata..Lanon.6f9bafb4adb671d7ac9f8408e86ff31a.0) + 2f50d 2f50d 0 1 $d.8 + 3aacc 3aacc 134 2 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.rodata._ZN9libunwind17DwarfInstructionsINS_17LocalAddressSpaceENS_15Registers_arm64EE18evaluateExpressionEmRS1_RKS2_m) + 3aacc 3aacc 0 1 $d.44 + 3ac00 3ac00 39 1 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.rodata._ZN9libunwind10CFI_ParserINS_17LocalAddressSpaceEE8parseCIEERS1_mPNS2_8CIE_InfoE) + 3ac00 3ac00 0 1 $d.51 + 3ac39 3ac39 c 1 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.rodata._ZN9libunwind14EHHeaderParserINS_17LocalAddressSpaceEE17getTableEntrySizeEh) + 3ac39 3ac39 0 1 $d.53 + 3ac45 3ac45 22 1 .debug_gdb_scripts + 3ac45 3ac45 22 1 <internal>:(.debug_gdb_scripts) + 3ac68 3ac68 17624 4 .eh_frame_hdr + 3ac68 3ac68 17624 4 <internal>:(.eh_frame_hdr) + 52290 52290 503ac 8 .eh_frame + 52290 52290 14 1 /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o:(.eh_frame+0x0) + 522a8 522a8 1c 1 /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o:(.eh_frame+0x14) + 522c8 522c8 14 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.eh_frame+0x14) + 522e0 522e0 34 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.eh_frame+0x28) + a2620 a2620 14 1 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.eh_frame+0x5ec) + b2640 b2640 13c78c 8 .text + b2640 b2640 14 8 /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o:(.text._start) + b2640 b2640 0 1 $x.0 + b2640 b2640 14 1 _start + b2654 b2654 1c 4 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.text._ZN42_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$3fmt17h12b31caec1d188c4E) + b2654 b2654 0 1 $x.0 + b2654 b2654 1c 1 _$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$::fmt::h12b31caec1d188c4 + b2670 b2670 b4 4 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.text._ZN42_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$3fmt17h268a568299531efbE) + b2670 b2670 0 1 $x.1 + b2670 b2670 b4 1 _$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$::fmt::h268a568299531efb + cd52c cd52c 98 4 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.text._ZN4core3num52_$LT$impl$u20$core..str..FromStr$u20$for$u20$u64$GT$8from_str17he51bc12ed2f79990E) + cd52c cd52c 0 1 $x.102 + cd52c cd52c 98 1 core::num::_$LT$impl$u20$core..str..FromStr$u20$for$u20$u64$GT$::from_str::he51bc12ed2f79990 + cd52c cd52c 98 1 core::num::_$LT$impl$u20$core..str..FromStr$u20$for$u20$usize$GT$::from_str::h4398f35a1c0b4be7 + cd5c4 cd5c4 e4 4 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.text._ZN63_$LT$core..num..TryFromIntError$u20$as$u20$core..fmt..Debug$GT$3fmt17h10047a113409f418E) + cd5c4 cd5c4 0 1 $x.103 + cd5c4 cd5c4 e4 1 _$LT$core..num..TryFromIntError$u20$as$u20$core..fmt..Debug$GT$::fmt::h10047a113409f418 + cd6a8 cd6a8 34 4 ./exe.unstripped/component_manager.core-6a4b1e7af979d229.core.3ocx6h1n-cgu.0.rcgu.o.rcgu.o:(.text._ZN4core3fmt5float52_$LT$impl$u20$core..fmt..Display$u20$for$u20$f64$GT$3fmt17hac033855e28d5eb4E) + cd6a8 cd6a8 0 1 $x.104 + cd6a8 cd6a8 34 1 core::fmt::float::_$LT$impl$u20$core..fmt..Display$u20$for$u20$f64$GT$::fmt::hac033855e28d5eb4 + e2554 e2554 10 4 ./exe.unstripped/component_manager.libcm_fidl_translator.cm_fidl_translator.3a1fbbbh-cgu.0.rcgu.o.rcgu.o:(.text.OUTLINED_FUNCTION_359) + e2554 e2554 0 1 $x.752 + e2554 e2554 10 1 OUTLINED_FUNCTION_359 + e2564 e2564 10 4 ./exe.unstripped/component_manager.libcm_fidl_translator.cm_fidl_translator.3a1fbbbh-cgu.0.rcgu.o.rcgu.o:(.text.OUTLINED_FUNCTION_360) + e2564 e2564 0 1 $x.753 + e2564 e2564 10 1 OUTLINED_FUNCTION_360 + eb888 eb888 40 4 ./exe.unstripped/component_manager.libcm_fidl_translator.cm_fidl_translator.3a1fbbbh-cgu.4.rcgu.o.rcgu.o:(.text._RINvNtNtCs6QHBvYLDz7W_4core4iter8adapters15process_resultsINtB2_3MapINtNtCsiEMmsMe2HZG_5alloc3vec8IntoIterNtNtCsbDqzXfLQacH_7cm_json2cm8ResolverENCNvXs_CsbDqzXfLQacH_18cm_fidl_translatorINtB17_3VecB1G_EINtB2q_6CmIntoIB2Z_NtCsbDqzXfLQacH_17fidl_fuchsia_sys212ResolverDeclEE7cm_into0EB3x_NtB1K_5ErrorNCINvXsx_NtB6_6resultINtB4V_6ResultB3s_B4A_EINtNtNtB4_6traits7collect12FromIteratorIB58_B3x_B4A_EE9from_iterBU_E0B3s_EB2q_) + eb888 eb888 0 1 $x.15 + eb888 eb888 40 1 _RINvNtNtCs6QHBvYLDz7W_4core4iter8adapters15process_resultsINtB2_3MapINtNtCsiEMmsMe2HZG_5alloc3vec8IntoIterNtNtCsbDqzXfLQacH_7cm_json2cm8ResolverENCNvXs_CsbDqzXfLQacH_18cm_fidl_translatorINtB17_3VecB1G_EINtB2q_6CmIntoIB2Z_NtCsbDqzXfLQacH_17fidl_fuchsia_sys212ResolverDeclEE7cm_into0EB3x_NtB1K_5ErrorNCINvXsx_NtB6_6resultINtB4V_6ResultB3s_B4A_EINtNtNtB4_6traits7collect12FromIteratorIB58_B3x_B4A_EE9from_iterBU_E0B3s_EB2q_ + 197340 197340 70 4 ./exe.unstripped/component_manager.libfuchsia_async.fuchsia_async.3a1fbbbh-cgu.1.rcgu.o.rcgu.o:(.text._RINvNtCsevZhND7KDbE_9hashbrown3raw16calculate_layoutTjINtNtCsiEMmsMe2HZG_5alloc4sync3ArcDNtNtCsbDqzXfLQacH_13fuchsia_async8executor14PacketReceiverEL_EEEB1t_) + 197340 197340 0 1 $x.6 + 197340 197340 70 1 _RINvNtCsevZhND7KDbE_9hashbrown3raw16calculate_layoutTjINtNtCsiEMmsMe2HZG_5alloc4sync3ArcDNtNtCsbDqzXfLQacH_13fuchsia_async8executor14PacketReceiverEL_EEEB1t_ + 200800 200800 38 32 ./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o:(.tbss._ZN3std10sys_common11thread_info11THREAD_INFO7__getit5__KEY17h836843d10e59cadaE.llvm.8019338592007173243) + 1300 1300 38 1 std::sys_common::thread_info::THREAD_INFO::__getit::__KEY::h836843d10e59cada (.llvm.8019338592007173243) + 200800 200800 0 1 $d.517 + 200840 200840 18 16 ./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o:(.tbss._ZN3std9panicking18update_panic_count11PANIC_COUNT7__getit5__KEY17h69785e8fa31a63e6E.llvm.8019338592007173243) + 1340 1340 18 1 std::panicking::update_panic_count::PANIC_COUNT::__getit::__KEY::h69785e8fa31a63e6 (.llvm.8019338592007173243) + 200840 200840 0 1 $d.557 + 1ff610 1ff610 15db8 8 .data.rel.ro + 1ff610 1ff610 30 8 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.data.rel.ro..Lanon.745d157954bc19b57d1f47812cead630.1) + 1ff610 1ff610 0 1 $d.25 + 1ff640 1ff640 20 8 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.data.rel.ro..Lanon.745d157954bc19b57d1f47812cead630.2) + 1ff640 1ff640 0 1 $d.26 + 1ff660 1ff660 18 8 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.data.rel.ro..Lanon.745d157954bc19b57d1f47812cead630.4) + 1ff660 1ff660 0 1 $d.28 + 202a88 202a88 20 8 ./exe.unstripped/component_manager.libcm_fidl_validator.cm_fidl_validator.3a1fbbbh-cgu.0.rcgu.o.rcgu.o:(.data.rel.ro.anon.e57b3f2ae8b5861992cfe341e01c75d6.1.llvm.9882057474999025597) + 202a88 202a88 0 1 $d.287 + 202a88 202a88 20 1 anon.e57b3f2ae8b5861992cfe341e01c75d6.1.llvm.9882057474999025597 + 2110a8 2110a8 18 8 ./exe.unstripped/component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.1.rcgu.o.rcgu.o:(.data.rel.ro..Lanon.6f9bafb4adb671d7ac9f8408e86ff31a.3) + 2110a8 2110a8 0 1 $d.10 + 211108 211108 10 8 ./exe.unstripped/component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.1.rcgu.o.rcgu.o:(.data.rel.ro.anon.6f9bafb4adb671d7ac9f8408e86ff31a.9.llvm.14732610969729869220) + 211108 211108 0 1 $d.16 + 211108 211108 10 1 anon.6f9bafb4adb671d7ac9f8408e86ff31a.9.llvm.14732610969729869220 + 211118 211118 38 8 ./exe.unstripped/component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.1.rcgu.o.rcgu.o:(.data.rel.ro.anon.6f9bafb4adb671d7ac9f8408e86ff31a.12.llvm.14732610969729869220) + 211118 211118 0 1 $d.19 + 211118 211118 38 1 anon.6f9bafb4adb671d7ac9f8408e86ff31a.12.llvm.14732610969729869220 + 211150 211150 20 8 ./exe.unstripped/component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.10.rcgu.o.rcgu.o:(.data.rel.ro.anon.54c490f3ebdb496a1657b64fb6de7217.0.llvm.16392173952262690328) + 211150 211150 0 1 $d.7 + 211150 211150 20 1 anon.54c490f3ebdb496a1657b64fb6de7217.0.llvm.16392173952262690328 + 211170 211170 18 8 ./exe.unstripped/component_manager.liblibrary_loader.library_loader.3a1fbbbh-cgu.11.rcgu.o.rcgu.o:(.data.rel.ro..Lanon.39cf32991562f2df00749835baa6fdc6.1) + 211170 211170 0 1 $d.19 + 215018 215018 18 8 ./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o:(.data.rel.ro.anon.d8e73c4ab2af7c41d0ca8802142f134e.759.llvm.8019338592007173243) + 215018 215018 0 1 $d.556 + 215018 215018 18 1 anon.d8e73c4ab2af7c41d0ca8802142f134e.759.llvm.8019338592007173243 + 215030 215030 88 8 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.data.rel.ro._ZTVN9libunwind12UnwindCursorINS_17LocalAddressSpaceENS_15Registers_arm64EEE) + 215030 215030 0 1 $d.58 + 215030 215030 88 1 vtable for libunwind::UnwindCursor<libunwind::LocalAddressSpace, libunwind::Registers_arm64> + 2150b8 2150b8 310 8 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.data.rel.ro..Lswitch.table._ZN9libunwind15Registers_arm6415getRegisterNameEi) + 2150b8 2150b8 0 1 $d.64 + 2153c8 2153c8 480 8 .got + 2153c8 2153c8 480 8 <internal>:(.got) + 225848 225848 88 8 .data + 225848 225848 10 8 ./exe.unstripped/component_manager.liblog-51a8548556518bd3-51a8548556518bd3.log.7ncv9mgr-cgu.0.rcgu.o.rcgu.o:(.data._RNvCsi1CgODtnl2B_3log6LOGGER.llvm.13428694738827280366) + 225848 225848 0 1 $d.13 + 225848 225848 10 1 _RNvCsi1CgODtnl2B_3log6LOGGER.llvm.13428694738827280366 + 225858 225858 8 8 ./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o:(.data._ZN3std6thread8ThreadId3new7COUNTER17hb3a413a29c0f81f6E) + 225858 225858 0 1 $d.306 + 225858 225858 8 1 std::thread::ThreadId::new::COUNTER::hb3a413a29c0f81f6 + 225860 225860 38 8 ./exe.unstripped/component_manager.std-7a72ffd2f3a7a812.std.a0uwjzoe-cgu.0.rcgu.o.rcgu.o:(.data._ZN3std2io5stdio6stderr8INSTANCE17hb24941292040ef06E) + 225860 225860 0 1 $d.363 + 225860 225860 38 1 std::io::stdio::stderr::INSTANCE::hb24941292040ef06 + 2258c8 2258c8 8 8 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.data._ZN9libunwind13DwarfFDECacheINS_17LocalAddressSpaceEE10_bufferEndE) + 2258c8 2258c8 0 1 $d.63 + 2258c8 2258c8 8 1 libunwind::DwarfFDECache<libunwind::LocalAddressSpace>::_bufferEnd + 2258d0 2258d0 398 8 .got.plt + 2258d0 2258d0 398 8 <internal>:(.got.plt) + 225c68 225c68 e5d 8 .bss + 225c68 225c68 60 8 ./exe.unstripped/component_manager.libcm_rust.cm_rust.3a1fbbbh-cgu.0.rcgu.o.rcgu.o:(.bss..L_MergedGlobals) + 225c68 225c68 0 1 $d.378 + 225c68 225c68 20 1 _RNvNvNvXs15_CsbDqzXfLQacH_7cm_rustNtBa_13DATA_TYPENAMENtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___stability4LAZY.llvm.2893117974861896834 + 225c88 225c88 20 1 _RNvNvNvXs17_CsbDqzXfLQacH_7cm_rustNtBa_14CACHE_TYPENAMENtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___stability4LAZY.llvm.2893117974861896834 + 225ca8 225ca8 20 1 _RNvNvNvXs19_CsbDqzXfLQacH_7cm_rustNtBa_13META_TYPENAMENtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___stability4LAZY.llvm.2893117974861896834 + 225cc8 225cc8 38 8 ./exe.unstripped/component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.12.rcgu.o.rcgu.o:(.bss._RNvNvNvXs0_NtNtNtCsbDqzXfLQacH_21component_manager_lib5model6events14source_factoryNtB9_25EVENT_SOURCE_SERVICE_PATHNtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___stability4LAZY.llvm.16599889146946869799) + 225cc8 225cc8 0 1 $d.347 + 225cc8 225cc8 38 1 _RNvNvNvXs0_NtNtNtCsbDqzXfLQacH_21component_manager_lib5model6events14source_factoryNtB9_25EVENT_SOURCE_SERVICE_PATHNtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___stability4LAZY.llvm.16599889146946869799 + 225d00 225d00 38 8 ./exe.unstripped/component_manager.libcomponent_manager_lib.component_manager_lib.3a1fbbbh-cgu.12.rcgu.o.rcgu.o:(.bss._RNvNvNvXs2_NtNtNtCsbDqzXfLQacH_21component_manager_lib5model6events14source_factoryNtB9_30EVENT_SOURCE_SYNC_SERVICE_PATHNtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___stability4LAZY.llvm.16599889146946869799) + 225d00 225d00 0 1 $d.348 + 225d00 225d00 38 1 _RNvNvNvXs2_NtNtNtCsbDqzXfLQacH_21component_manager_lib5model6events14source_factoryNtB9_30EVENT_SOURCE_SYNC_SERVICE_PATHNtNtNtCs6QHBvYLDz7W_4core3ops5deref5Deref5deref11___stability4LAZY.llvm.16599889146946869799 + 2262ac 2262ac 8 4 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.bss._ZN9libunwind13DwarfFDECacheINS_17LocalAddressSpaceEE5_lockE) + 2262ac 2262ac 0 1 $d.59 + 2262ac 2262ac 8 1 libunwind::DwarfFDECache<libunwind::LocalAddressSpace>::_lock + 2262b8 2262b8 800 8 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.bss._ZN9libunwind13DwarfFDECacheINS_17LocalAddressSpaceEE14_initialBufferE) + 2262b8 2262b8 0 1 $d.62 + 2262b8 2262b8 800 1 libunwind::DwarfFDECache<libunwind::LocalAddressSpace>::_initialBuffer + 226ab8 226ab8 d 4 /tmp/rustcL27BaP/libunwind-e14dabfa3d8ce62b.rlib(libunwind.o):(.bss..L_MergedGlobals) + 226ab8 226ab8 0 1 $d.65 + 226ab8 226ab8 1 1 logAPIs::checked + 226abc 226abc 1 1 logAPIs::log + 226abd 226abd 1 1 logUnwinding::checked + 226ac0 226ac0 1 1 logUnwinding::log + 226ac1 226ac1 1 1 logDWARF::checked + 226ac4 226ac4 1 1 logDWARF::log + 0 0 48c441 1 .debug_loc + 0 0 39 1 /usr/local/google/home/yifeit/vg/out/default.zircon/user-arm64-clang.shlib/obj/system/ulib/c/crt1.Scrt1.cc.o:(.debug_loc) + 0 0 0 1 $d.1 + 39 39 6d97 1 ./exe.unstripped/component_manager.alloc-54127f36ba192482.alloc.4k1iwrm2-cgu.0.rcgu.o.rcgu.o:(.debug_loc) + 39 39 0 1 $d.63 + 6dd0 6dd0 280c 1 ./exe.unstripped/component_manager.backtrace-5619ad41e613872b.backtrace.e1f4vorm-cgu.0.rcgu.o.rcgu.o:(.debug_loc) + 6dd0 6dd0 0 1 $d.41
diff --git a/tests/testdata/link_map/test_lld-lto_v1.map b/tests/testdata/link_map/test_lld-lto_v1.map new file mode 100644 index 0000000..9658718 --- /dev/null +++ b/tests/testdata/link_map/test_lld-lto_v1.map
@@ -0,0 +1,410 @@ +# Test Linker map for LLD with ThinLTO, "v1" format. +# .map files actually don't have comments and blank lines! These are added to +# improve documentation, and are stripped by tests. + +# First line is needed to identify .map file type. + VMA LMA Size Align Out In Symbol +# Extract sizes for every section, and symbols for selected sections only. +# Size-only sections. + 174 174 13 1 .interp + 174 174 13 1 <internal>:(.interp) + 188 188 1e2678 4 .ARM.exidx + 188 188 8 4 obj/third_party/breakpad/libclient.a(client/breakpad_getcontext.o):(.ARM.exidx) + 190 190 8 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(chrono.o):(.ARM.exidx.text.__clang_call_terminate) + 190 190 0 1 $d + 198 198 8 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(chrono.o):(.ARM.exidx.text._ZNSt6__ndk16chrono12steady_clock3nowEv) + 198 198 0 1 $d + 1a0 1a0 8 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(exception.o):(.ARM.exidx.text._ZSt18uncaught_exceptionv) + 1a0 1a0 0 1 $d + 2340 2340 8 4 lto.tmp:(.ARM.exidx.text._ZN5media18CdmPromiseTemplateIJEE26RejectPromiseOnDestructionEv) + 2348 2348 8 4 lto.tmp:(.ARM.exidx.text._ZN5media18CdmPromiseTemplateIJEED2Ev) + 1e2800 1e2800 1870 4 .dynsym + 1e2800 1e2800 1870 4 <internal>:(.dynsym) + 1e4070 1e4070 30e 2 .gnu.version + 1e4070 1e4070 30e 2 <internal>:(.gnu.version) + 1e4380 1e4380 60 4 .gnu.version_r + 1e4380 1e4380 60 4 <internal>:(.gnu.version_r) + 1e43e0 1e43e0 1c 4 .gnu.hash + 1e43e0 1e43e0 1c 4 <internal>:(.gnu.hash) + 1e43fc 1e43fc f6b 1 .dynstr + 1e43fc 1e43fc f6b 1 <internal>:(.dynstr) + 1e5368 1e5368 2d1d5 4 .rel.dyn + 1e5368 1e5368 2d1d5 4 <internal>:(.rel.dyn) + 212540 212540 bd8 4 .rel.plt + 212540 212540 bd8 4 <internal>:(.rel.plt) + 213118 213118 1c 4 .note.crashpad.info + 213118 213118 1c 4 obj/third_party/crashpad/crashpad/client/libclient.a(client/crashpad_info_note.o):(.note.crashpad.info) + 213118 213118 1c 1 CRASHPAD_NOTE + 213124 213124 0 1 name + 21312d 21312d 0 1 name_end + 213130 213130 0 1 desc + 213134 213134 0 1 desc_end + 213134 213134 98 4 .note.android.ident + 213134 213134 98 4 ../../third_party/android_ndk/platforms/android-16/arch-arm/usr/lib/crtbegin_so.o:(.note.android.ident) + 213134 213134 0 1 $d + 213134 213134 98 1 note_android_ident + 213140 213140 0 1 note_name + 213148 213148 0 1 note_data + 21314c 21314c 0 1 ndk_version + 21318c 21318c 0 1 ndk_build_number + 2131cc 2131cc 0 1 note_end + 2131cc 2131cc 24 4 .note.gnu.build-id + 2131cc 2131cc 24 4 <internal>:(.note.gnu.build-id) + +# .rodata: Extract symbols and size (read-only data). + 213200 213200 611e4b 256 .rodata + 213200 213200 4 1 obj/v8/v8_external_snapshot/embedded.o:(.rodata) + 213200 213200 0 1 v8_Default_embedded_blob_size_ + 213210 213210 20 16 obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o):(.rodata) + 213210 213210 10 1 pmmp + 213220 213220 10 1 mppm + 213230 213230 180 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(hash.o):(.rodata) + 213230 213230 c0 1 std::__ndk1::(anonymous namespace)::small_primes + 213230 213230 0 1 $d + 2132f0 2132f0 c0 1 std::__ndk1::(anonymous namespace)::indices + 2133b0 2133b0 d1 16 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.rodata) + 2133b0 2133b0 0 1 $d + 2133b0 2133b0 4 1 std::__ndk1::ios_base::boolalpha + 2133b4 2133b4 4 1 std::__ndk1::ios_base::dec + 2133b8 2133b8 4 1 std::__ndk1::ios_base::fixed + 2133bc 2133bc 4 1 std::__ndk1::ios_base::hex + 2133c0 2133c0 4 1 std::__ndk1::ios_base::internal + 2133c4 2133c4 4 1 std::__ndk1::ios_base::left + 2133c8 2133c8 4 1 std::__ndk1::ios_base::oct + 2133cc 2133cc 4 1 std::__ndk1::ios_base::right + 2133d0 2133d0 4 1 std::__ndk1::ios_base::scientific + 2133d4 2133d4 4 1 std::__ndk1::ios_base::showbase + 2133d8 2133d8 4 1 std::__ndk1::ios_base::showpoint + 2133dc 2133dc 4 1 std::__ndk1::ios_base::showpos + 2133e0 2133e0 4 1 std::__ndk1::ios_base::skipws + 2133e4 2133e4 4 1 std::__ndk1::ios_base::unitbuf + 2133e8 2133e8 4 1 std::__ndk1::ios_base::uppercase + 2133ec 2133ec 4 1 std::__ndk1::ios_base::adjustfield + 2133f0 2133f0 4 1 std::__ndk1::ios_base::basefield + 2133f4 2133f4 4 1 std::__ndk1::ios_base::floatfield + 2133f8 2133f8 4 1 std::__ndk1::ios_base::badbit + 2133fc 2133fc 4 1 std::__ndk1::ios_base::eofbit + 213400 213400 4 1 std::__ndk1::ios_base::failbit + 213404 213404 4 1 std::__ndk1::ios_base::goodbit + 213408 213408 4 1 std::__ndk1::ios_base::app + 21340c 21340c 4 1 std::__ndk1::ios_base::ate + 213410 213410 4 1 std::__ndk1::ios_base::binary + 213414 213414 4 1 std::__ndk1::ios_base::in + 213418 213418 4 1 std::__ndk1::ios_base::out + 21341c 21341c 4 1 std::__ndk1::ios_base::trunc + 213420 213420 1d 1 typeinfo name for std::__ndk1::ios_base::failure + 213440 213440 15 1 typeinfo name for std::__ndk1::ios_base + 213460 213460 21 1 typeinfo name for std::__ndk1::__iostream_category + 213490 213490 2d 16 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.rodata._ZTSNSt6__ndk19basic_iosIcNS_11char_traitsIcEEEE) + 213490 213490 0 1 $d + +# <internal>, which is Level 2 only. + 21368b 21368b 2c378e 1 <internal>:(.rodata) +# Single symbol mixed with $d. + 4d6e20 4d6e20 1b 16 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(iostream.o):(.rodata._ZTSNSt6__ndk110__stdinbufIwEE) + 4d6e20 4d6e20 0 1 $d + 4d6e20 4d6e20 1b 1 typeinfo name for std::__ndk1::__stdinbuf<wchar_t> + +# More <internal>. + 4d7920 4d7920 266c 4 <internal>:(.rodata) + 4d9f90 4d9f90 18 16 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(locale.o):(.rodata._ZTSNSt6__ndk110ctype_baseE) + 4d9f90 4d9f90 0 1 $d + 4d9f90 4d9f90 18 1 typeinfo name for std::__ndk1::ctype_base + 4d9fb0 4d9fb0 1a 16 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(locale.o):(.rodata._ZTSNSt6__ndk112codecvt_baseE) + 4d9fb0 4d9fb0 0 1 $d + 4d9fb0 4d9fb0 1a 1 typeinfo name for std::__ndk1::codecvt_base +# thinlto-cache at Level 2. + 503eb3 503eb3 1 1 thinlto-cache/Thin-84596a.tmp.o:(.rodata._ZN7network5mojom45CookieManagerProxy_SetCanonicalCookie_Message11kMessageTagE) + 503eb3 503eb3 1 1 network::mojom::CookieManagerProxy_SetCanonicalCookie_Message::kMessageTag + 503eb4 503eb4 1 1 thinlto-cache/Thin-84596a.tmp.o:(.rodata._ZN7network5mojom48CookieManagerProxy_DeleteCanonicalCookie_Message11kMessageTagE) + 503eb4 503eb4 1 1 network::mojom::CookieManagerProxy_DeleteCanonicalCookie_Message::kMessageTag + +# Size-only sections. + 82504c 82504c 8ffc 4 .ARM.extab + 82504c 82504c 40 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(chrono.o):(.ARM.extab.text._ZNSt6__ndk16chrono12steady_clock3nowEv) + 82504c 82504c 0 1 $d + 825058 825058 0 1 GCC_except_table4 + 82508c 82508c 38 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(hash.o):(.ARM.extab.text._ZNSt6__ndk112__next_primeEj) + 82508c 82508c 0 1 $d + 825098 825098 0 1 GCC_except_table0 + 8250c4 8250c4 48 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.ARM.extab.text._ZNSt6__ndk18ios_base5clearEj) + 8250c4 8250c4 0 1 $d + 8250d0 8250d0 0 1 GCC_except_table5 + +# .text: Extract symbols and size (executable code). + 82f000 82f000 251b7e8 64 .text +# Multiple Level 3 symbols under Level 2. $a annotates ARM symbols (not Thumb2). +# Also, data precede first Level 3 line. + 82f000 82f000 cdc 32 obj/third_party/boringssl/boringssl_asm/chacha-armv4.o:(.text) + 82f004 82f004 0 1 $d.0 + 82f040 82f040 0 1 $a.1 + 82f040 82f040 3f0 1 ChaCha20_ctr32 + 82f440 82f440 89c 1 ChaCha20_neon + 82fce0 82fce0 130c 32 obj/third_party/boringssl/boringssl_asm/aes-armv4.o:(.text) + 82fce0 82fce0 0 1 $d.0 + 82fce0 82fce0 540 1 AES_Te + 830220 830220 0 1 $a.1 + 830220 830220 60 1 aes_nohw_encrypt + 830280 830280 1d8 1 _armv4_AES_encrypt + 830460 830460 0 1 _armv4_AES_set_encrypt_key + 830460 830460 2a0 1 aes_nohw_set_encrypt_key + 830700 830700 20 1 aes_nohw_set_decrypt_key + 830720 830720 0 1 _armv4_AES_set_enc2dec_key + 830720 830720 124 1 AES_set_enc2dec_key + 830860 830860 0 1 $d.2 + 830860 830860 500 1 AES_Td + 830d60 830d60 0 1 $a.3 + 830d60 830d60 60 1 aes_nohw_decrypt + 830dc0 830dc0 1f8 1 _armv4_AES_decrypt + 830fb8 830fb8 0 1 $d.4 + +# Many Level 3 annotations. Also, data precede first Level 3 line. + 831000 831000 a28 32 obj/third_party/boringssl/boringssl_asm/aesv8-armx32.o:(.text) + 831004 831004 0 1 $d.0 + 831040 831040 0 1 $a.1 + 831040 831040 0 1 .Lenc_key + 831040 831040 218 1 aes_hw_set_encrypt_key + 8310b0 8310b0 0 1 $d.2 + 8310b4 8310b4 0 1 $a.3 + 8310f0 8310f0 0 1 $d.4 + 8310f4 8310f4 0 1 $a.5 + 831124 831124 0 1 $d.6 + 831128 831128 0 1 $a.7 + 831180 831180 0 1 $d.8 + 831184 831184 0 1 $a.9 + 8311f0 8311f0 0 1 $d.10 + 8311f4 8311f4 0 1 $a.11 + 831228 831228 0 1 $d.12 + 83122c 83122c 0 1 $a.13 + 831260 831260 60 1 aes_hw_set_decrypt_key + 831294 831294 0 1 $d.14 + 83129c 83129c 0 1 $a.15 + 8312b0 8312b0 0 1 $d.16 + 8312b4 8312b4 0 1 $a.17 + 8312c0 8312c0 50 1 aes_hw_encrypt + 8312d4 8312d4 0 1 $d.18 + 8312dc 8312dc 0 1 $a.19 + 8312e4 8312e4 0 1 $d.20 + 8312ec 8312ec 0 1 $a.21 + 8312f4 8312f4 0 1 $d.22 + 8312fc 8312fc 0 1 $a.23 + 831300 831300 0 1 $d.24 + 831304 831304 0 1 $a.25 + 831320 831320 50 1 aes_hw_decrypt + 831334 831334 0 1 $d.26 + 83133c 83133c 0 1 $a.27 + 831344 831344 0 1 $d.28 + 83134c 83134c 0 1 $a.29 + 831354 831354 0 1 $d.30 + 83135c 83135c 0 1 $a.31 + 831360 831360 0 1 $d.32 + 831364 831364 0 1 $a.33 +# ... (truncated). + +# Symbols with Size = 0 from assembly. Level 3 items are functions. + 83c640 83c640 13aee0 32 obj/v8/v8_external_snapshot/embedded.o:(.text) + 83c640 83c640 0 1 v8_Default_embedded_blob_data_ + 83f4a0 83f4a0 0 1 Builtins_RecordWrite + 83f7c0 83f7c0 0 1 Builtins_AdaptorWithExitFrame + 83f800 83f800 0 1 Builtins_AdaptorWithBuiltinExitFrame + 83f840 83f840 0 1 Builtins_ArgumentsAdaptorTrampoline + 83f940 83f940 0 1 Builtins_CallFunction_ReceiverIsNullOrUndefined + 83fa40 83fa40 0 1 Builtins_CallFunction_ReceiverIsNotNullOrUndefined + 83fb80 83fb80 0 1 Builtins_CallFunction_ReceiverIsAny + 83fd00 83fd00 0 1 Builtins_CallBoundFunction +# ... (truncated). + + 977520 977520 18 16 obj/third_party/blink/renderer/platform/heap/asm/asm/SaveRegisters_arm.o:(.text) + 977520 977520 0 1 $a.0 + 977520 977520 0 1 PushAllRegisters + +# Symbols with Size = 0 from assembly. Many Level 3 items are labels. + 97c834 97c834 4dc 4 obj/third_party/libvpx/libvpx_assembly_arm.a(libvpx_assembly_arm/vpx_convolve8_avg_horiz_filter_type1_neon.asm.o):(.text) + 97c834 97c834 0 1 $a.0 + 97c834 97c834 0 1 _vpx_convolve8_avg_horiz_filter_type1_neon +# This is a function. + 97c834 97c834 4dc 1 vpx_convolve8_avg_horiz_filter_type1_neon +# These are labels. + 97c848 97c848 0 1 start_loop_count + 97c8d0 97c8d0 0 1 outer_loop8_residual + 97c8f0 97c8f0 0 1 outer_loop_8 + 97c900 97c900 0 1 inner_loop_8 + 97c9c8 97c9c8 0 1 end_inner_loop_8 + 97c9e4 97c9e4 0 1 end_loops + 97c9e8 97c9e8 0 1 outer_loop_16 + 97ca54 97ca54 0 1 inner_loop_16 + 97cbcc 97cbcc 0 1 epilog_16 + 97cc08 97cc08 0 1 end_loops1 + 97cc0c 97cc0c 0 1 outer_loop4_residual + 97cc2c 97cc2c 0 1 outer_loop_4 + 97cc3c 97cc3c 0 1 inner_loop_4 + 97ccf8 97ccf8 0 1 end_inner_loop_4 + 97cd08 97cd08 0 1 end_func + +# Thumb2 symbols (indicated by odd Level 3 addresses) with aliases. + 99cad8 99cad8 fc 8 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libandroid_support.a(e_sinh.o):(.text.sinh) + 99cad8 99cad8 0 1 $t + 99cad9 99cad9 fc 1 sinhl + 99cad9 99cad9 fc 1 sinh + 99cbb8 99cbb8 0 1 $d + +# On rare occasions, some symbol name start with '$'. + 1401788 1401788 a4 4 thinlto-cache/Thin-826b9f.tmp.o:(.text._ZNK4$_21clEP10FamilyDataPKcPS3_) + 1401788 1401788 0 1 $t.78 + 1401789 1401789 a4 1 $_21::operator()(FamilyData*, char const*, char const**) const + 1401818 1401818 0 1 $d.79 + +# Sections prefixed with ".unlikely" or ".hot" + 1401820 1401820 10 4 thinlto-cache/Thin-826b9f.tmp.o:(.text.unlikely.UnlikelyFunc) + 1401820 1401820 0 1 $t.78 + 1401821 1401821 10 1 UnlikelyFunc + 1401831 1401831 10 1 UnlikelyFunc2 + 1401841 1401841 0 1 $d.79 + +# Sections prefixed with ".startup" + 1401850 1401850 10 4 thinlto-cache/Thin-826b9f.tmp.o:(.text.startup) + 1401850 1401850 0 1 $t.78 + 1401851 1401851 10 1 StartUpFunc + 1401861 1401861 0 1 $d.79 + +# Size-only sections. + 2d4a7f0 2d4a7f0 17d0 16 .plt + 2d4a7f0 2d4a7f0 17d0 16 <internal>:(.plt) + +# .data: Extract symbols and size. + 2d4c000 2d4c000 1cfc8 8 .data + 2d4c000 2d4c000 4 1 obj/v8/v8_external_snapshot/embedded.o:(.data) + 2d4c000 2d4c000 0 1 v8_Default_embedded_blob_ + 2d4c004 2d4c004 4 4 ../../third_party/android_ndk/platforms/android-16/arch-arm/usr/lib/crtbegin_so.o:(.data) + 2d4c004 2d4c004 0 1 $d + 2d4c004 2d4c004 4 1 __dso_handle + +# .data.rel.ro: Extract symbols and size. + 2d69000 2d69000 1eb760 16 .data.rel.ro + 2d69000 2d69000 3c 4 obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_vfp.o):(.data.rel.ro) + 2d69000 2d69000 3c 1 fft_tab_vfp + 2d6903c 2d6903c 3c 4 obj/third_party/ffmpeg/libffmpeg_internal.a(ffmpeg_internal/fft_neon.o):(.data.rel.ro) + 2d6903c 2d6903c 3c 1 fft_tab_neon + 2d69078 2d69078 40 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.data.rel.ro._ZTVNSt6__ndk115basic_streambufIwNS_11char_traitsIwEEEE) + 2d69078 2d69078 0 1 $d + 2d69078 2d69078 40 1 vtable for std::__ndk1::basic_streambuf<wchar_t, std::__ndk1::char_traits<wchar_t> > + 2d690b8 2d690b8 28 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.data.rel.ro._ZTVNSt6__ndk113basic_istreamIcNS_11char_traitsIcEEEE) + 2d690b8 2d690b8 0 1 $d + 2d690b8 2d690b8 28 1 vtable for std::__ndk1::basic_istream<char, std::__ndk1::char_traits<char> > + 2d690e0 2d690e0 28 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.data.rel.ro._ZTVNSt6__ndk113basic_istreamIwNS_11char_traitsIwEEEE) + 2d690e0 2d690e0 0 1 $d + 2d690e0 2d690e0 28 1 vtable for std::__ndk1::basic_istream<wchar_t, std::__ndk1::char_traits<wchar_t> > + 2d69108 2d69108 28 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.data.rel.ro._ZTVNSt6__ndk113basic_ostreamIcNS_11char_traitsIcEEEE) + 2d69108 2d69108 0 1 $d + 2d69108 2d69108 28 1 vtable for std::__ndk1::basic_ostream<char, std::__ndk1::char_traits<char> > + 2d69130 2d69130 28 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.data.rel.ro._ZTVNSt6__ndk113basic_ostreamIwNS_11char_traitsIwEEEE) + 2d69130 2d69130 0 1 $d + 2d69130 2d69130 28 1 vtable for std::__ndk1::basic_ostream<wchar_t, std::__ndk1::char_traits<wchar_t> > + 2d69158 2d69158 68 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(ios.o):(.data.rel.ro) + 2d69158 2d69158 0 1 $d + 2d69158 2d69158 24 1 vtable for std::__ndk1::__iostream_category + 2d6917c 2d6917c 14 1 vtable for std::__ndk1::ios_base::failure + 2d69190 2d69190 10 1 vtable for std::__ndk1::ios_base + 2d691a0 2d691a0 c 1 typeinfo for std::__ndk1::ios_base::failure + 2d691ac 2d691ac 8 1 typeinfo for std::__ndk1::ios_base + 2d691b4 2d691b4 c 1 typeinfo for std::__ndk1::__iostream_category + +# Size-only sections. + 2f54760 2f54760 8 4 .fini_array + 2f54760 2f54760 4 4 ../../third_party/android_ndk/platforms/android-16/arch-arm/usr/lib/crtbegin_so.o:(.fini_array) + 2f54760 2f54760 0 1 $d + 2f54764 2f54764 4 1 ../../third_party/android_ndk/platforms/android-16/arch-arm/usr/lib/crtend_so.o:(.fini_array) + 2f54768 2f54768 10 4 .init_array + 2f54768 2f54768 4 4 ../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libc++_static.a(iostream.o):(.init_array) + 2f54768 2f54768 0 1 $d + 2f5476c 2f5476c 4 4 ../../third_party/gvr-android-sdk/libgvr_shim_static_arm.a(base_logging.o):(.init_array) + 2f54770 2f54770 4 1 ../../third_party/android_ndk/platforms/android-16/arch-arm/usr/lib/crtend_so.o:(.init_array) + 2f54774 2f54774 4 4 thinlto-cache/Thin-b0997c.tmp.o:(.init_array) + 2f54778 2f54778 f0 4 .dynamic + 2f54778 2f54778 f0 4 <internal>:(.dynamic) + 2f54868 2f54868 5ac 4 .got + 2f54868 2f54868 5ac 4 <internal>:(.got) + 2f54e14 2f54e14 5f8 4 .got.plt + 2f54e14 2f54e14 5f8 4 <internal>:(.got.plt) + +# .bss: Extract symbols and size (does not consume size on disk). + 2f56000 2f56000 109f7c 32 .bss + 2f56000 2f56000 4 4 thinlto-cache/Thin-cfd493.tmp.o:(COMMON) + 2f56000 2f56000 4 1 WebRtcSpl_CrossCorrelation + 2f56000 2f56000 4 1 WebRtcSpl_CrossCorrelation + 2f56000 2f56000 4 1 WebRtcSpl_CrossCorrelation + 2f56004 2f56004 4 4 thinlto-cache/Thin-cfd493.tmp.o:(COMMON) + 2f56004 2f56004 4 1 WebRtcSpl_DownsampleFast + 2f56004 2f56004 4 1 WebRtcSpl_DownsampleFast + 2f56004 2f56004 4 1 WebRtcSpl_DownsampleFast + 2f56004 2f56004 4 1 WebRtcSpl_DownsampleFast + 2f56008 2f56008 4 4 thinlto-cache/Thin-cfd493.tmp.o:(COMMON) + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + 2f56008 2f56008 4 1 WebRtcSpl_MaxAbsValueW16 + +# Partitions should be ignored at this point. Otherwise, their .text, .rodata, +# etc. sections will overwrite those of the main partition. + 34be000 34be000 34 1 vr_partition + 34be000 34be000 34 1 <internal>:(vr_partition) + 34be034 34be034 140 1 .phdrs + 34be034 34be034 140 1 <internal>:(.phdrs) + 34be174 34be174 13 1 .interp + 34be174 34be174 13 1 <internal>:(.interp) + 34be188 34be188 1c 4 .note.crashpad.info + 34be188 34be188 1c 4 obj/third_party/crashpad/crashpad/client/libclient.a(client/crashpad_info_note.o):(.note.crashpad.info) + 34be1a4 34be1a4 98 4 .note.android.ident + 34be1a4 34be1a4 98 4 ../../third_party/android_ndk/platforms/android-16/arch-arm/usr/lib/crtbegin_so.o:(.note.android.ident) + 34be23c 34be23c 24 4 .note.gnu.build-id + 34be23c 34be23c 24 4 <internal>:(.note.gnu.build-id) + 34be260 34be260 20 4 .dynsym + 34be260 34be260 20 4 <internal>:(.dynsym) + 34be280 34be280 4 2 .gnu.version + 34be280 34be280 4 2 <internal>:(.gnu.version) + 34be284 34be284 60 4 .gnu.version_r + 34be284 34be284 60 4 <internal>:(.gnu.version_r) + 34be2e4 34be2e4 1c 4 .gnu.hash + 34be2e4 34be2e4 1c 4 <internal>:(.gnu.hash) + 34be300 34be300 6c 1 .dynstr + 34be300 34be300 6c 1 <internal>:(.dynstr) + 34be36c 34be36c 257 4 .rel.dyn + 34be36c 34be36c 257 4 <internal>:(.rel.dyn) + 34be5c4 34be5c4 10 4 .ARM.exidx + 34be5c4 34be5c4 10 4 <internal>:(.ARM.exidx) + 34be5d8 34be5d8 2428 8 .rodata + 34be5d8 34be5d8 10 1 thinlto-cache/Thin-5e976b.tmp.o:(.rodata._ZN2vrL17kRepositionIconIdE) + 34be5d8 34be5d8 10 1 vr::kRepositionIconId + 34c0a00 34c0a00 2c9ec 16 .text + 34c0a00 34c0a00 18 4 thinlto-cache/Thin-ec84a4.tmp.o:(.text._ZN6SkFont7setSizeEf) + 34c0a00 34c0a00 0 1 $t.11 + 34c0a01 34c0a01 18 1 SkFont::setSize(float) + 34c0a14 34c0a14 0 1 $d.12 +# .part.end is unique, even if multiple lib*.so, .phdrs, etc. exist. + 0 0 1000 1 .part.end + 0 0 1000 1 <internal>:(.part.end) + +# Various .debug sections can exist, but they're omitted for simplicity. + +# Size-only sections. + 0 0 23 1 .ARM.attributes + 0 0 23 1 obj/third_party/boringssl/boringssl_asm/chacha-armv4.o:(.ARM.attributes) + 0 0 a8 1 .comment + 0 0 a8 1 <internal>:(.comment) + 0 0 11c3610 4 .symtab + 0 0 11c3610 4 <internal>:(.symtab) + 0 0 1c4 1 .shstrtab + 0 0 1c4 1 <internal>:(.shstrtab) + 0 0 2f73332 1 .strtab + 0 0 2f73332 1 <internal>:(.strtab) + +# PROVIDE_HIDDEN are not Level 1 symbols. + 2f73332 0 0 1 PROVIDE_HIDDEN ( linker_script_start_of_text = ADDR ( .text ) ) + 2f73332 0 0 1 PROVIDE_HIDDEN ( linker_script_end_of_text = ADDR ( .text ) + SIZEOF ( .text ) )
diff --git a/third_party/demumble b/third_party/demumble index 01098ea..0cc7805 160000 --- a/third_party/demumble +++ b/third_party/demumble
@@ -1 +1 @@ -Subproject commit 01098eab821b33bd31b9778aea38565cd796aa85 +Subproject commit 0cc780526909546f1e41bef501e248e1cdbf2a9b
diff --git a/third_party/googletest b/third_party/googletest index 565f1b8..dcc92d0 160000 --- a/third_party/googletest +++ b/third_party/googletest
@@ -1 +1 @@ -Subproject commit 565f1b848215b77c3732bca345fe76a0431d8b34 +Subproject commit dcc92d0ab6c4ce022162a23566d44f673251eee4
diff --git a/third_party/protobuf b/third_party/protobuf index bc1773c..31ebe2a 160000 --- a/third_party/protobuf +++ b/third_party/protobuf
@@ -1 +1 @@ -Subproject commit bc1773c42c9c3c522145a3119e989e0dff2a8d54 +Subproject commit 31ebe2ac71400344a5db91ffc13c4ddfb7589f92
diff --git a/third_party/rustc-demangle b/third_party/rustc-demangle new file mode 160000 index 0000000..c4e3ab0 --- /dev/null +++ b/third_party/rustc-demangle
@@ -0,0 +1 @@ +Subproject commit c4e3ab004edda9fbe5e2d8149d2b62c10668d10b