Merge branch 'master' into better-tests
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..c7873ee
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,64 @@
+name: build
+
+on:
+ pull_request:
+ branches:
+ - master
+ workflow_dispatch:
+
+jobs:
+ windows:
+ runs-on: windows-latest
+
+ strategy:
+ fail-fast: false
+ matrix:
+ arch: ["Win32", "x64"] # "ARM64"
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: configure
+ run: cmake -B build -D CMAKE_BUILD_TYPE=Debug -G "Visual Studio 16 2019" -A ${{ matrix.arch }} -S .
+ - name: build
+ run: cmake --build build --config Debug
+ - name: test
+ run: ctest build
+
+ macOS:
+ runs-on: macos-latest
+
+ strategy:
+ matrix:
+ include:
+ - { cflags: "", cxxflags: "" }
+ - { cflags: "-fmodules", cxxflags: "-fmodules" }
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: configure
+ run: cmake -B build -D CMAKE_BUILD_TYPE=Debug -D CMAKE_C_FLAGS=${{ matrix.cflags }} -D CMAKE_CXX_FLAGS=${{ matrix.cxxflags }} -S .
+ - name: build
+ run: cmake --build build --config Debug
+ - name: test
+ run: ctest build
+
+ ubuntu:
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ include:
+ - { CC: gcc, CXX: g++ }
+ - { CC: clang, CXX: clang++ }
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: configure
+ run: cmake -B build -D CMAKE_BUILD_TYPE=Debug -S .
+ env:
+ CC: ${{ matrix.CC }}
+ CXX: ${{ matrix.CXX }}
+ - name: build
+ run: cmake --build build --config Debug
+ - name: test
+ run: ctest build
diff --git a/.gitmodules b/.gitmodules
index 11b244e..712299f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -9,7 +9,7 @@
url = https://github.com/abseil/abseil-cpp.git
[submodule "third_party/protobuf"]
path = third_party/protobuf
- url = https://github.com/google/protobuf.git
+ url = https://github.com/protocolbuffers/protobuf.git
[submodule "third_party/capstone"]
path = third_party/capstone
url = https://github.com/aquynh/capstone.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 868d557..58f5b79 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,8 @@
cmake_minimum_required(VERSION 3.5)
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)
@@ -67,17 +70,21 @@
# Set MSVC runtime before including thirdparty libraries
if(MSVC)
- # Link also the runtime library statically so that MSVCR*.DLL is not required at runtime.
- # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
- # This is achieved by replacing msvc option /MD with /MT and /MDd with /MTd
- # http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F
- foreach(flag_var
- CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
- CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
- if (flag_var MATCHES "/MD")
- string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
- endif()
- endforeach()
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.15)
+ set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>)
+ else()
+ # Link also the runtime library statically so that MSVCR*.DLL is not required at runtime.
+ # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx
+ # This is achieved by replacing msvc option /MD with /MT and /MDd with /MTd
+ # http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F
+ foreach(flag_var
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ if (flag_var MATCHES "/MD")
+ string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
+ endif()
+ endforeach()
+ endif()
endif()
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
diff --git a/src/dwarf.cc b/src/dwarf.cc
index dad4317..b1d0bec 100644
--- a/src/dwarf.cc
+++ b/src/dwarf.cc
@@ -596,6 +596,8 @@
uint64_t debug_abbrev_offset_;
std::string unit_name_;
string_view unit_range_;
+ UnitType unit_type_;
+ uint64_t dwo_id_;
CompilationUnitSizes unit_sizes_;
AbbrevTable* unit_abbrev_;
@@ -605,7 +607,7 @@
// both the current abbrev. table and the sizes.
uint32_t abbrev_version_;
- std::map<std::pair<AbbrevTable*, CompilationUnitSizes>, uint32_t>
+ std::map<std::pair<AbbrevTable*, CompilationUnitSizes>, size_t>
abbrev_versions_;
// Only for .debug_types
@@ -885,10 +887,9 @@
return AttrValue(ReadBytes(4, data));
case DW_FORM_data8:
return AttrValue(ReadBytes(8, data));
- case DW_FORM_rnglistx: {
- auto val = AttrValue(ReadLEB128<uint64_t>(data));
- return val;
- }
+ case DW_FORM_loclistx:
+ case DW_FORM_rnglistx:
+ return AttrValue(ReadLEB128<uint64_t>(data));
// Bloaty doesn't currently care about any bool or signed data.
// So we fudge it a bit and just stuff these in a uint64.
@@ -977,10 +978,34 @@
}
if (unit_sizes_.dwarf_version() == 5) {
- uint8_t unit_type = ReadFixed<uint8_t>(&remaining_);
- (void)unit_type; // We don't use this currently.
+ unit_type_ = static_cast<UnitType>(ReadFixed<uint8_t>(&remaining_));
unit_sizes_.SetAddressSize(ReadFixed<uint8_t>(&remaining_));
debug_abbrev_offset_ = unit_sizes_.ReadDWARFOffset(&remaining_);
+ switch (unit_type_) {
+ case DW_UT_skeleton:
+ case DW_UT_split_compile:
+ case DW_UT_split_type:
+ dwo_id_ = ReadFixed<uint64_t>(&remaining_);
+ break;
+ case DW_UT_type:
+ unit_type_signature_ = ReadFixed<uint64_t>(&remaining_);
+ unit_type_offset_ = unit_sizes_.ReadDWARFOffset(&remaining_);
+ break;
+ case DW_UT_compile:
+ case DW_UT_partial:
+ break;
+#if defined(_GNUC)
+ case DW_UT_lo_user ... DW_UT_hi_user:
+#else
+ case DW_UT_lo_user:
+ case DW_UT_hi_user:
+#endif
+ // User defined unit types which we do not really know about ...
+ if (verbose_level > 0) {
+ fprintf(stderr, "Unknown DWARF Unit Type in user defined range\n");
+ }
+ break;
+ }
} else {
debug_abbrev_offset_ = unit_sizes_.ReadDWARFOffset(&remaining_);
unit_sizes_.SetAddressSize(ReadFixed<uint8_t>(&remaining_));
@@ -1636,15 +1661,18 @@
// Sometimes a location is given as an offset into debug_loc.
if (die.location && die.location->IsUint()) {
uint64_t location = die.location->GetUint(die_reader);
- if (location < file.debug_loc.size()) {
- absl::string_view loc_range = file.debug_loc.substr(location);
- loc_range = GetLocationListRange(die_reader.unit_sizes(), loc_range);
- sink->AddFileRange("dwarf_locrange", name, loc_range);
- } else if (verbose_level > 0) {
- fprintf(stderr,
- "bloaty: warning: DWARF location out of range, location=%" PRIx64
- "\n",
- location);
+ if (die.location->form() == DW_FORM_sec_offset) {
+ if (location < file.debug_loc.size()) {
+ absl::string_view loc_range = file.debug_loc.substr(location);
+ loc_range = GetLocationListRange(die_reader.unit_sizes(), loc_range);
+ sink->AddFileRange("dwarf_locrange", name, loc_range);
+ } else if (verbose_level > 0) {
+ fprintf(
+ stderr,
+ "bloaty: warning: DWARF location out of range, location=%" PRIx64
+ "\n",
+ location);
+ }
}
}
@@ -1653,8 +1681,10 @@
if (die.ranges && die.ranges->form() == DW_FORM_rnglistx && die.ranges->IsUint()) {
uint64_t range_list = die.ranges->GetUint(die_reader);
const dwarf::CompilationUnitSizes& sizes = die_reader.unit_sizes();
+ size_t offset_size = die_reader.unit_sizes().dwarf64() ? 8 : 4;
string_view offset_data = StrictSubstr(
- file.debug_rnglists, die_reader.unit_sizes().range_lists_base() + range_list);
+ file.debug_rnglists, die_reader.unit_sizes().range_lists_base() +
+ (range_list * offset_size));
uint64_t offset = die_reader.unit_sizes().ReadDWARFOffset(&offset_data);
string_view data = StrictSubstr(
file.debug_rnglists, die_reader.unit_sizes().range_lists_base() + offset);
diff --git a/src/dwarf_constants.h b/src/dwarf_constants.h
index 78891fb..7e6fc04 100644
--- a/src/dwarf_constants.h
+++ b/src/dwarf_constants.h
@@ -704,5 +704,16 @@
DW_RLE_start_length = 0x07,
};
+enum UnitType {
+ DW_UT_compile = 0x01,
+ DW_UT_type = 0x02,
+ DW_UT_partial = 0x03,
+ DW_UT_skeleton = 0x04,
+ DW_UT_split_compile = 0x05,
+ DW_UT_split_type = 0x06,
+ DW_UT_lo_user = 0x80,
+ DW_UT_hi_user = 0xff,
+};
+
} // namespace dwarf2reader
#endif // UTIL_DEBUGINFO_DWARF2ENUMS_H__
diff --git a/src/elf.cc b/src/elf.cc
index 499317d..355210b 100644
--- a/src/elf.cc
+++ b/src/elf.cc
@@ -777,60 +777,70 @@
}
}
-static void ElfMachineToCapstone(Elf64_Half e_machine, cs_arch* arch,
+static bool ElfMachineToCapstone(Elf64_Half e_machine, cs_arch* arch,
cs_mode* mode) {
switch (e_machine) {
case EM_386:
*arch = CS_ARCH_X86;
*mode = CS_MODE_32;
- break;
+ return true;
case EM_X86_64:
*arch = CS_ARCH_X86;
*mode = CS_MODE_64;
- break;
+ return true;
// These aren't tested, but we include them on the off-chance
// that it will work.
case EM_ARM:
*arch = CS_ARCH_ARM;
*mode = CS_MODE_LITTLE_ENDIAN;
- break;
+ return true;
case EM_AARCH64:
*arch = CS_ARCH_ARM64;
*mode = CS_MODE_ARM;
- break;
+ return true;
case EM_MIPS:
*arch = CS_ARCH_MIPS;
- break;
+ return true;
case EM_PPC:
*arch = CS_ARCH_PPC;
*mode = CS_MODE_32;
- break;
+ return true;
case EM_PPC64:
*arch = CS_ARCH_PPC;
*mode = CS_MODE_64;
- break;
+ return true;
case EM_SPARC:
*arch = CS_ARCH_SPARC;
*mode = CS_MODE_BIG_ENDIAN;
- break;
+ return true;
case EM_SPARCV9:
*arch = CS_ARCH_SPARC;
*mode = CS_MODE_V9;
- break;
+ return true;
+
default:
- THROWF("Unknown ELF machine value: $0'", e_machine);
+ if (verbose_level > 1) {
+ printf(
+ "Unable to map to capstone target, disassembly will be "
+ "unavailable");
+ }
+ return false;
}
}
-static void ReadElfArchMode(const InputFile& file, cs_arch* arch, cs_mode* mode) {
+static bool ReadElfArchMode(const InputFile& file, cs_arch* arch, cs_mode* mode) {
+ bool capstone_available = true;
ForEachElf(file, nullptr,
- [=](const ElfFile& elf, string_view /*filename*/,
- uint32_t /*index_base*/) {
+ [&capstone_available, arch, mode](const ElfFile& elf,
+ string_view /*filename*/,
+ uint32_t /*index_base*/) {
// Last .o file wins? (For .a files)? It's kind of arbitrary,
// but a single .a file shouldn't have multiple archs in it.
- ElfMachineToCapstone(elf.header().e_machine, arch, mode);
+ capstone_available &=
+ ElfMachineToCapstone(elf.header().e_machine, arch, mode);
});
+ return capstone_available;
}
static void ReadELFSymbols(const InputFile& file, RangeSink* sink,
@@ -838,7 +848,7 @@
bool is_object = IsObjectFile(file.data());
DisassemblyInfo info;
DisassemblyInfo* infop = &info;
- ReadElfArchMode(file, &info.arch, &info.mode);
+ bool capstone_available = ReadElfArchMode(file, &info.arch, &info.mode);
ForEachElf(
file, sink,
@@ -882,7 +892,7 @@
string_view name = strtab_section.ReadString(sym.st_name);
uint64_t full_addr =
ToVMAddr(sym.st_value, index_base + sym.st_shndx, is_object);
- if (sink && !disassemble) {
+ if (sink && !(capstone_available && disassemble)) {
sink->AddVMRangeAllowAlias(
"elf_symbols", full_addr, sym.st_size,
ItaniumDemangle(name, sink->data_source()));
@@ -891,7 +901,8 @@
table->insert(
std::make_pair(name, std::make_pair(full_addr, sym.st_size)));
}
- if (disassemble && ELF64_ST_TYPE(sym.st_info) == STT_FUNC) {
+ if (capstone_available && disassemble &&
+ ELF64_ST_TYPE(sym.st_info) == STT_FUNC) {
if (verbose_level > 1) {
printf("Disassembling function: %s\n", name.data());
}
@@ -1401,8 +1412,7 @@
info->start_address = vmaddr;
}
- ReadElfArchMode(file_data(), &info->arch, &info->mode);
- return true;
+ return ReadElfArchMode(file_data(), &info->arch, &info->mode);
}
};
diff --git a/src/pe.cc b/src/pe.cc
index d5e01d3..4a0364b 100644
--- a/src/pe.cc
+++ b/src/pe.cc
@@ -23,39 +23,43 @@
const uint16_t dos_magic = 0x5A4D; // MZ
//! Sizes in bytes of various things in the COFF format.
-namespace STRUCT_SIZES {
-enum {
- Header16Size = 20,
- Header32Size = 56,
- NameSize = 8,
- Symbol16Size = 18,
- Symbol32Size = 20,
- SectionSize = 40,
- RelocationSize = 10,
- BaseRelocationBlockSize = 8,
- ImportDirectoryTableEntrySize = 20,
- ResourceDirectoryTableSize = 16,
- ResourceDirectoryEntriesSize = 8,
- ResourceDataEntrySize = 16
+enum class StructSizes {
+ kHeader16Size = 20,
+ kHeader32Size = 56,
+ kNameSize = 8,
+ kSymbol16Size = 18,
+ kSymbol32Size = 20,
+ kSectionSize = 40,
+ kRelocationSize = 10,
+ kBaseRelocationBlockSize = 8,
+ kImportDirectoryTableEntrySize = 20,
+ kResourceDirectoryTableSize = 16,
+ kResourceDirectoryEntriesSize = 8,
+ kResourceDataEntrySize = 16,
};
-}
#include "third_party/lief_pe/pe_structures.h"
-static_assert(STRUCT_SIZES::SectionSize == sizeof(pe_section), "Compiler options broke LIEF struct layout");
-static_assert(STRUCT_SIZES::RelocationSize == sizeof(pe_relocation), "Compiler options broke LIEF struct layout");
-static_assert(STRUCT_SIZES::BaseRelocationBlockSize ==
+static_assert(static_cast<size_t>(StructSizes::kSectionSize) ==
+ sizeof(pe_section),
+ "Compiler options broke LIEF struct layout");
+static_assert(static_cast<size_t>(StructSizes::kRelocationSize) ==
+ sizeof(pe_relocation),
+ "Compiler options broke LIEF struct layout");
+static_assert(static_cast<size_t>(StructSizes::kBaseRelocationBlockSize) ==
sizeof(pe_base_relocation_block),
"Compiler options broke LIEF struct layout");
-static_assert(STRUCT_SIZES::ImportDirectoryTableEntrySize == sizeof(pe_import),
- "Compiler options broke LIEF struct layout");
-static_assert(STRUCT_SIZES::ResourceDirectoryTableSize ==
+static_assert(
+ static_cast<size_t>(StructSizes::kImportDirectoryTableEntrySize) ==
+ sizeof(pe_import),
+ "Compiler options broke LIEF struct layout");
+static_assert(static_cast<size_t>(StructSizes::kResourceDirectoryTableSize) ==
sizeof(pe_resource_directory_table),
"Compiler options broke LIEF struct layout");
-static_assert(STRUCT_SIZES::ResourceDirectoryEntriesSize ==
+static_assert(static_cast<size_t>(StructSizes::kResourceDirectoryEntriesSize) ==
sizeof(pe_resource_directory_entries),
"Compiler options broke LIEF struct layout");
-static_assert(STRUCT_SIZES::ResourceDataEntrySize ==
+static_assert(static_cast<size_t>(StructSizes::kResourceDataEntrySize) ==
sizeof(pe_resource_data_entry),
"Compiler options broke LIEF struct layout");
@@ -159,8 +163,9 @@
// For longer names, this member contains a forward slash (/) followed by an
// ASCII representation of a decimal number that is an offset into the
// string table.
- name = std::string(header_.Name,
- strnlen(header_.Name, STRUCT_SIZES::NameSize));
+ name = std::string(
+ header_.Name,
+ strnlen(header_.Name, static_cast<size_t>(StructSizes::kNameSize)));
}
private:
diff --git a/src/range_map.h b/src/range_map.h
index 9c13bbc..aab0771 100644
--- a/src/range_map.h
+++ b/src/range_map.h
@@ -268,6 +268,7 @@
return;
}
+ iters.reserve(range_maps.size());
for (auto range_map : range_maps) {
iters.push_back(range_map->mappings_.begin());
}
diff --git a/src/re.h b/src/re.h
index d990767..e415c42 100644
--- a/src/re.h
+++ b/src/re.h
@@ -22,7 +22,6 @@
#endif
#include "absl/base/attributes.h"
-#include "bloaty.h"
namespace bloaty {
diff --git a/src/util.h b/src/util.h
index 1aad13f..b934027 100644
--- a/src/util.h
+++ b/src/util.h
@@ -134,7 +134,7 @@
}
template <class T, size_t N = sizeof(T)> T ReadFixed(absl::string_view *data) {
- static_assert(N <= sizeof(N), "N too big for this data type");
+ static_assert(N <= sizeof(T), "N too big for this data type");
T val = 0;
if (data->size() < N) {
THROW("premature EOF reading fixed-length data");
diff --git a/tests/bloaty_test.cc b/tests/bloaty_test.cc
index 2783ac8..8187aca 100644
--- a/tests/bloaty_test.cc
+++ b/tests/bloaty_test.cc
@@ -253,8 +253,9 @@
ASSERT_TRUE(GetFileSize(file1, &size1));
ASSERT_TRUE(GetFileSize(file2, &size2));
RunBloaty({"bloaty", file1, file2, "-d", "inputfiles"});
- AssertChildren(*top_row_, {std::make_tuple(file1, kUnknown, size1),
- std::make_tuple(file2, kUnknown, size2)});
+ AssertChildren(*top_row_,
+ {std::make_tuple(file1, kUnknown, static_cast<int>(size1)),
+ std::make_tuple(file2, kUnknown, static_cast<int>(size2))});
// Should work with custom data sources.
bloaty::Options options;
@@ -273,8 +274,8 @@
)", &options);
RunBloatyWithOptions(options, bloaty::OutputOptions());
- AssertChildren(*top_row_,
- {std::make_tuple("binary", kUnknown, size1 + size2)});
+ AssertChildren(*top_row_, {std::make_tuple("binary", kUnknown,
+ static_cast<int>(size1 + size2))});
}
TEST_F(BloatyTest, DiffMode) {
diff --git a/tests/fuzz_driver.cc b/tests/fuzz_driver.cc
index 2e286d4..4980317 100644
--- a/tests/fuzz_driver.cc
+++ b/tests/fuzz_driver.cc
@@ -21,7 +21,7 @@
int main(int argc, char **argv) {
for (int i = 1; i < argc; i++) {
- std::ifstream in(argv[i]);
+ std::ifstream in(argv[i], std::ios_base::in | std::ios_base::binary);
in.seekg(0, in.end);
size_t length = in.tellg();
in.seekg (0, in.beg);