1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519 |
- #include "binary_annotator.h"
- #include <algorithm>
- #include <cstdint>
- #include <iostream>
- #include <limits>
- #include <string>
- #include <vector>
- #include "flatbuffers/base.h"
- #include "flatbuffers/reflection.h"
- #include "flatbuffers/util.h"
- #include "flatbuffers/verifier.h"
- namespace flatbuffers {
- namespace {
- static bool BinaryRegionSort(const BinaryRegion &a, const BinaryRegion &b) {
- return a.offset < b.offset;
- }
- static void SetError(BinaryRegionComment &comment, BinaryRegionStatus status,
- std::string message = "") {
- comment.status = status;
- comment.status_message = message;
- }
- static BinaryRegion MakeBinaryRegion(
- const uint64_t offset = 0, const uint64_t length = 0,
- const BinaryRegionType type = BinaryRegionType::Unknown,
- const uint64_t array_length = 0, const uint64_t points_to_offset = 0,
- BinaryRegionComment comment = {}) {
- BinaryRegion region;
- region.offset = offset;
- region.length = length;
- region.type = type;
- region.array_length = array_length;
- region.points_to_offset = points_to_offset;
- region.comment = std::move(comment);
- return region;
- }
- static BinarySection MakeBinarySection(const std::string &name,
- const BinarySectionType type,
- std::vector<BinaryRegion> regions) {
- BinarySection section;
- section.name = name;
- section.type = type;
- section.regions = std::move(regions);
- return section;
- }
- static BinarySection MakeSingleRegionBinarySection(const std::string &name,
- const BinarySectionType type,
- const BinaryRegion ®ion) {
- std::vector<BinaryRegion> regions;
- regions.push_back(region);
- return MakeBinarySection(name, type, std::move(regions));
- }
- static bool IsNonZeroRegion(const uint64_t offset, const uint64_t length,
- const uint8_t *const binary) {
- for (uint64_t i = offset; i < offset + length; ++i) {
- if (binary[i] != 0) { return true; }
- }
- return false;
- }
- static bool IsPrintableRegion(const uint64_t offset, const uint64_t length,
- const uint8_t *const binary) {
- for (uint64_t i = offset; i < offset + length; ++i) {
- if (!isprint(binary[i])) { return false; }
- }
- return true;
- }
- static BinarySection GenerateMissingSection(const uint64_t offset,
- const uint64_t length,
- const uint8_t *const binary) {
- std::vector<BinaryRegion> regions;
- // Check if the region is all zeros or not, as that can tell us if it is
- // padding or not.
- if (IsNonZeroRegion(offset, length, binary)) {
- // Some of the padding bytes are non-zero, so this might be an unknown
- // section of the binary.
- // TODO(dbaileychess): We could be a bit smarter with different sized
- // alignments. For now, the 8 byte check encompasses all the smaller
- // alignments.
- BinaryRegionComment comment;
- comment.type = BinaryRegionCommentType::Unknown;
- if (length >= 8) {
- SetError(comment, BinaryRegionStatus::WARN_NO_REFERENCES);
- } else {
- SetError(comment, BinaryRegionStatus::WARN_CORRUPTED_PADDING);
- }
- regions.push_back(MakeBinaryRegion(offset, length * sizeof(uint8_t),
- BinaryRegionType::Unknown, length, 0,
- comment));
- return MakeBinarySection("no known references", BinarySectionType::Unknown,
- std::move(regions));
- }
- BinaryRegionComment comment;
- comment.type = BinaryRegionCommentType::Padding;
- if (length >= 8) {
- SetError(comment, BinaryRegionStatus::WARN_PADDING_LENGTH);
- }
- // This region is most likely padding.
- regions.push_back(MakeBinaryRegion(offset, length * sizeof(uint8_t),
- BinaryRegionType::Uint8, length, 0,
- comment));
- return MakeBinarySection("", BinarySectionType::Padding, std::move(regions));
- }
- } // namespace
- std::map<uint64_t, BinarySection> BinaryAnnotator::Annotate() {
- flatbuffers::Verifier verifier(bfbs_, static_cast<size_t>(bfbs_length_));
- if ((is_size_prefixed_ &&
- !reflection::VerifySizePrefixedSchemaBuffer(verifier)) ||
- !reflection::VerifySchemaBuffer(verifier)) {
- return {};
- }
- // The binary is too short to read as a flatbuffers.
- if (binary_length_ < FLATBUFFERS_MIN_BUFFER_SIZE) { return {}; }
- // Make sure we start with a clean slate.
- vtables_.clear();
- sections_.clear();
- // First parse the header region which always start at offset 0.
- // The returned offset will point to the root_table location.
- const uint64_t root_table_offset = BuildHeader(0);
- if (IsValidOffset(root_table_offset)) {
- // Build the root table, and all else will be referenced from it.
- BuildTable(root_table_offset, BinarySectionType::RootTable,
- schema_->root_table());
- }
- // Now that all the sections are built, make sure the binary sections are
- // contiguous.
- FixMissingRegions();
- // Then scan the area between BinarySections insert padding sections that are
- // implied.
- FixMissingSections();
- return sections_;
- }
- uint64_t BinaryAnnotator::BuildHeader(const uint64_t header_offset) {
- uint64_t offset = header_offset;
- std::vector<BinaryRegion> regions;
- // If this binary is a size prefixed one, attempt to parse the size.
- if (is_size_prefixed_) {
- BinaryRegionComment prefix_length_comment;
- prefix_length_comment.type = BinaryRegionCommentType::SizePrefix;
- bool has_prefix_value = false;
- const auto prefix_length = ReadScalar<uoffset64_t>(offset);
- if (*prefix_length <= binary_length_) {
- regions.push_back(MakeBinaryRegion(offset, sizeof(uoffset64_t),
- BinaryRegionType::Uint64, 0, 0,
- prefix_length_comment));
- offset += sizeof(uoffset64_t);
- has_prefix_value = true;
- }
- if (!has_prefix_value) {
- const auto prefix_length = ReadScalar<uoffset_t>(offset);
- if (*prefix_length <= binary_length_) {
- regions.push_back(MakeBinaryRegion(offset, sizeof(uoffset_t),
- BinaryRegionType::Uint32, 0, 0,
- prefix_length_comment));
- offset += sizeof(uoffset_t);
- has_prefix_value = true;
- }
- }
- if (!has_prefix_value) {
- SetError(prefix_length_comment, BinaryRegionStatus::ERROR);
- }
- }
- const auto root_table_offset = ReadScalar<uint32_t>(offset);
- if (!root_table_offset.has_value()) {
- // This shouldn't occur, since we validate the min size of the buffer
- // before. But for completion sake, we shouldn't read passed the binary end.
- return std::numeric_limits<uint64_t>::max();
- }
- const auto root_table_loc = offset + *root_table_offset;
- BinaryRegionComment root_offset_comment;
- root_offset_comment.type = BinaryRegionCommentType::RootTableOffset;
- root_offset_comment.name = schema_->root_table()->name()->str();
- if (!IsValidOffset(root_table_loc)) {
- SetError(root_offset_comment,
- BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
- }
- regions.push_back(MakeBinaryRegion(offset, sizeof(uint32_t),
- BinaryRegionType::UOffset, 0,
- root_table_loc, root_offset_comment));
- offset += sizeof(uint32_t);
- if (IsValidRead(offset, flatbuffers::kFileIdentifierLength) &&
- IsPrintableRegion(offset, flatbuffers::kFileIdentifierLength, binary_)) {
- BinaryRegionComment comment;
- comment.type = BinaryRegionCommentType::FileIdentifier;
- // Check if the file identifier region has non-zero data, and assume its
- // the file identifier. Otherwise, it will get filled in with padding
- // later.
- regions.push_back(MakeBinaryRegion(
- offset, flatbuffers::kFileIdentifierLength * sizeof(uint8_t),
- BinaryRegionType::Char, flatbuffers::kFileIdentifierLength, 0,
- comment));
- }
- AddSection(header_offset, MakeBinarySection("", BinarySectionType::Header,
- std::move(regions)));
- return root_table_loc;
- }
- BinaryAnnotator::VTable *BinaryAnnotator::GetOrBuildVTable(
- const uint64_t vtable_offset, const reflection::Object *const table,
- const uint64_t offset_of_referring_table) {
- // Get a list of vtables (if any) already defined at this offset.
- std::list<VTable> &vtables = vtables_[vtable_offset];
- // See if this vtable for the table type has been generated before.
- for (VTable &vtable : vtables) {
- if (vtable.referring_table == table) { return &vtable; }
- }
- // If we are trying to make a new vtable and it is already encompassed by
- // another binary section, something is corrupted.
- if (vtables.empty() && ContainsSection(vtable_offset)) { return nullptr; }
- const std::string referring_table_name = table->name()->str();
- BinaryRegionComment vtable_size_comment;
- vtable_size_comment.type = BinaryRegionCommentType::VTableSize;
- const auto vtable_length = ReadScalar<uint16_t>(vtable_offset);
- if (!vtable_length.has_value()) {
- const uint64_t remaining = RemainingBytes(vtable_offset);
- SetError(vtable_size_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- "2");
- AddSection(vtable_offset,
- MakeSingleRegionBinarySection(
- referring_table_name, BinarySectionType::VTable,
- MakeBinaryRegion(vtable_offset, remaining,
- BinaryRegionType::Unknown, remaining, 0,
- vtable_size_comment)));
- return nullptr;
- }
- // Vtables start with the size of the vtable
- const uint16_t vtable_size = vtable_length.value();
- if (!IsValidOffset(vtable_offset + vtable_size - 1)) {
- SetError(vtable_size_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
- // The vtable_size points to off the end of the binary.
- AddSection(vtable_offset,
- MakeSingleRegionBinarySection(
- referring_table_name, BinarySectionType::VTable,
- MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
- BinaryRegionType::Uint16, 0, 0,
- vtable_size_comment)));
- return nullptr;
- } else if (vtable_size < 2 * sizeof(uint16_t)) {
- SetError(vtable_size_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_SHORT,
- "4");
- // The size includes itself and the table size which are both uint16_t.
- AddSection(vtable_offset,
- MakeSingleRegionBinarySection(
- referring_table_name, BinarySectionType::VTable,
- MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
- BinaryRegionType::Uint16, 0, 0,
- vtable_size_comment)));
- return nullptr;
- }
- std::vector<BinaryRegion> regions;
- regions.push_back(MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
- BinaryRegionType::Uint16, 0, 0,
- vtable_size_comment));
- uint64_t offset = vtable_offset + sizeof(uint16_t);
- BinaryRegionComment ref_table_len_comment;
- ref_table_len_comment.type =
- BinaryRegionCommentType::VTableRefferingTableLength;
- // Ensure we can read the next uint16_t field, which is the size of the
- // referring table.
- const auto table_length = ReadScalar<uint16_t>(offset);
- if (!table_length.has_value()) {
- const uint64_t remaining = RemainingBytes(offset);
- SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- "2");
- AddSection(offset, MakeSingleRegionBinarySection(
- referring_table_name, BinarySectionType::VTable,
- MakeBinaryRegion(
- offset, remaining, BinaryRegionType::Unknown,
- remaining, 0, ref_table_len_comment)));
- return nullptr;
- }
- // Then they have the size of the table they reference.
- const uint16_t table_size = table_length.value();
- if (!IsValidOffset(offset_of_referring_table + table_size - 1)) {
- SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
- } else if (table_size < 4) {
- SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_SHORT,
- "4");
- }
- regions.push_back(MakeBinaryRegion(offset, sizeof(uint16_t),
- BinaryRegionType::Uint16, 0, 0,
- ref_table_len_comment));
- offset += sizeof(uint16_t);
- const uint64_t offset_start = offset;
- // A mapping between field (and its id) to the relative offset (uin16_t) from
- // the start of the table.
- std::map<uint16_t, VTable::Entry> fields;
- // Counter for determining if the binary has more vtable entries than the
- // schema provided. This can occur if the binary was created at a newer schema
- // version and is being processed with an older one.
- uint16_t fields_processed = 0;
- // Loop over all the fields.
- ForAllFields(table, /*reverse=*/false, [&](const reflection::Field *field) {
- const uint64_t field_offset = offset_start + field->id() * sizeof(uint16_t);
- if (field_offset >= vtable_offset + vtable_size) {
- // This field_offset is too large for this vtable, so it must come from a
- // newer schema than the binary was create with or the binary writer did
- // not write it. For either case, it is safe to ignore.
- // TODO(dbaileychess): We could show which fields are not set an their
- // default values if we want. We just need a way to make it obvious that
- // it isn't part of the buffer.
- return;
- }
- BinaryRegionComment field_comment;
- field_comment.type = BinaryRegionCommentType::VTableFieldOffset;
- field_comment.name = std::string(field->name()->c_str()) +
- "` (id: " + std::to_string(field->id()) + ")";
- const auto offset_from_table = ReadScalar<uint16_t>(field_offset);
- if (!offset_from_table.has_value()) {
- const uint64_t remaining = RemainingBytes(field_offset);
- SetError(field_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
- regions.push_back(MakeBinaryRegion(field_offset, remaining,
- BinaryRegionType::Unknown, remaining,
- 0, field_comment));
- return;
- }
- if (!IsValidOffset(offset_of_referring_table + offset_from_table.value() -
- 1)) {
- SetError(field_comment, BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
- regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
- BinaryRegionType::VOffset, 0, 0,
- field_comment));
- return;
- }
- VTable::Entry entry;
- entry.field = field;
- entry.offset_from_table = offset_from_table.value();
- fields.insert(std::make_pair(field->id(), entry));
- std::string default_label;
- if (offset_from_table.value() == 0) {
- // Not present, so could be default or be optional.
- if (field->required()) {
- SetError(field_comment,
- BinaryRegionStatus::ERROR_REQUIRED_FIELD_NOT_PRESENT);
- // If this is a required field, make it known this is an error.
- regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
- BinaryRegionType::VOffset, 0, 0,
- field_comment));
- return;
- } else {
- // Its an optional field, so get the default value and interpret and
- // provided an annotation for it.
- if (IsScalar(field->type()->base_type())) {
- default_label += "<defaults to ";
- default_label += IsFloat(field->type()->base_type())
- ? std::to_string(field->default_real())
- : std::to_string(field->default_integer());
- default_label += "> (";
- } else {
- default_label += "<null> (";
- }
- default_label +=
- reflection::EnumNameBaseType(field->type()->base_type());
- default_label += ")";
- }
- }
- field_comment.default_value = default_label;
- regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
- BinaryRegionType::VOffset, 0, 0,
- field_comment));
- fields_processed++;
- });
- // Check if we covered all the expectant fields. If not, we need to add them
- // as unknown fields.
- uint16_t expectant_vtable_fields =
- (vtable_size - sizeof(uint16_t) - sizeof(uint16_t)) / sizeof(uint16_t);
- // Prevent a bad binary from declaring a really large vtable_size, that we can
- // not independently verify.
- expectant_vtable_fields = std::min(
- static_cast<uint16_t>(fields_processed * 3), expectant_vtable_fields);
- for (uint16_t id = fields_processed; id < expectant_vtable_fields; ++id) {
- const uint64_t field_offset = offset_start + id * sizeof(uint16_t);
- const auto offset_from_table = ReadScalar<uint16_t>(field_offset);
- BinaryRegionComment field_comment;
- field_comment.type = BinaryRegionCommentType::VTableUnknownFieldOffset;
- field_comment.index = id;
- if (!offset_from_table.has_value()) {
- const uint64_t remaining = RemainingBytes(field_offset);
- SetError(field_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
- regions.push_back(MakeBinaryRegion(field_offset, remaining,
- BinaryRegionType::Unknown, remaining,
- 0, field_comment));
- continue;
- }
- VTable::Entry entry;
- entry.field = nullptr; // No field to reference.
- entry.offset_from_table = offset_from_table.value();
- fields.insert(std::make_pair(id, entry));
- regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
- BinaryRegionType::VOffset, 0, 0,
- field_comment));
- }
- // If we have never added this vtable before record the Binary section.
- if (vtables.empty()) {
- sections_[vtable_offset] = MakeBinarySection(
- referring_table_name, BinarySectionType::VTable, std::move(regions));
- } else {
- // Add the current table name to the name of the section.
- sections_[vtable_offset].name += ", " + referring_table_name;
- }
- VTable vtable;
- vtable.referring_table = table;
- vtable.fields = std::move(fields);
- vtable.table_size = table_size;
- vtable.vtable_size = vtable_size;
- // Add this vtable to the collection of vtables at this offset.
- vtables.push_back(std::move(vtable));
- // Return the vtable we just added.
- return &vtables.back();
- }
- void BinaryAnnotator::BuildTable(const uint64_t table_offset,
- const BinarySectionType type,
- const reflection::Object *const table) {
- if (ContainsSection(table_offset)) { return; }
- BinaryRegionComment vtable_offset_comment;
- vtable_offset_comment.type = BinaryRegionCommentType::TableVTableOffset;
- const auto vtable_soffset = ReadScalar<int32_t>(table_offset);
- if (!vtable_soffset.has_value()) {
- const uint64_t remaining = RemainingBytes(table_offset);
- SetError(vtable_offset_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- "4");
- AddSection(
- table_offset,
- MakeSingleRegionBinarySection(
- table->name()->str(), type,
- MakeBinaryRegion(table_offset, remaining, BinaryRegionType::Unknown,
- remaining, 0, vtable_offset_comment)));
- // If there aren't enough bytes left to read the vtable offset, there is
- // nothing we can do.
- return;
- }
- // Tables start with the vtable
- const uint64_t vtable_offset = table_offset - vtable_soffset.value();
- if (!IsValidOffset(vtable_offset)) {
- SetError(vtable_offset_comment,
- BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
- AddSection(table_offset,
- MakeSingleRegionBinarySection(
- table->name()->str(), type,
- MakeBinaryRegion(table_offset, sizeof(int32_t),
- BinaryRegionType::SOffset, 0, vtable_offset,
- vtable_offset_comment)));
- // There isn't much to do with an invalid vtable offset, as we won't be able
- // to intepret the rest of the table fields.
- return;
- }
- std::vector<BinaryRegion> regions;
- regions.push_back(MakeBinaryRegion(table_offset, sizeof(int32_t),
- BinaryRegionType::SOffset, 0,
- vtable_offset, vtable_offset_comment));
- // Parse the vtable first so we know what the rest of the fields in the table
- // are.
- const VTable *const vtable =
- GetOrBuildVTable(vtable_offset, table, table_offset);
- if (vtable == nullptr) {
- // There is no valid vtable for this table, so we cannot process the rest of
- // the table entries.
- return;
- }
- // This is the size and length of this table.
- const uint16_t table_size = vtable->table_size;
- uint64_t table_end_offset = table_offset + table_size;
- if (!IsValidOffset(table_end_offset - 1)) {
- // We already validated the table size in BuildVTable, but we have to make
- // sure we don't use a bad value here.
- table_end_offset = binary_length_;
- }
- // We need to iterate over the vtable fields by their offset in the binary,
- // not by their IDs. So copy them over to another vector that we can sort on
- // the offset_from_table property.
- std::vector<VTable::Entry> fields;
- for (const auto &vtable_field : vtable->fields) {
- fields.push_back(vtable_field.second);
- }
- std::stable_sort(fields.begin(), fields.end(),
- [](const VTable::Entry &a, const VTable::Entry &b) {
- return a.offset_from_table < b.offset_from_table;
- });
- // Iterate over all the fields by order of their offset.
- for (size_t i = 0; i < fields.size(); ++i) {
- const reflection::Field *field = fields[i].field;
- const uint16_t offset_from_table = fields[i].offset_from_table;
- if (offset_from_table == 0) {
- // Skip non-present fields.
- continue;
- }
- // The field offsets are relative to the start of the table.
- const uint64_t field_offset = table_offset + offset_from_table;
- if (!IsValidOffset(field_offset)) {
- // The field offset is larger than the binary, nothing we can do.
- continue;
- }
- // We have a vtable entry for a non-existant field, that means its a binary
- // generated by a newer schema than we are currently processing.
- if (field == nullptr) {
- // Calculate the length of this unknown field.
- const uint64_t unknown_field_length =
- // Check if there is another unknown field after this one.
- ((i + 1 < fields.size())
- ? table_offset + fields[i + 1].offset_from_table
- // Otherwise use the known end of the table.
- : table_end_offset) -
- field_offset;
- if (unknown_field_length == 0) { continue; }
- std::string hint;
- if (unknown_field_length == 4) {
- const auto relative_offset = ReadScalar<uint32_t>(field_offset);
- if (relative_offset.has_value()) {
- // The field is 4 in length, so it could be an offset? Provide a hint.
- hint += "<possibly an offset? Check Loc: +0x";
- hint += ToHex(field_offset + relative_offset.value());
- hint += ">";
- }
- }
- BinaryRegionComment unknown_field_comment;
- unknown_field_comment.type = BinaryRegionCommentType::TableUnknownField;
- if (!IsValidRead(field_offset, unknown_field_length)) {
- const uint64_t remaining = RemainingBytes(field_offset);
- SetError(unknown_field_comment,
- BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- std::to_string(unknown_field_length));
- regions.push_back(MakeBinaryRegion(field_offset, remaining,
- BinaryRegionType::Unknown, remaining,
- 0, unknown_field_comment));
- continue;
- }
- unknown_field_comment.default_value = hint;
- regions.push_back(MakeBinaryRegion(
- field_offset, unknown_field_length, BinaryRegionType::Unknown,
- unknown_field_length, 0, unknown_field_comment));
- continue;
- }
- if (IsScalar(field->type()->base_type())) {
- // These are the raw values store in the table.
- const uint64_t type_size = GetTypeSize(field->type()->base_type());
- const BinaryRegionType region_type =
- GetRegionType(field->type()->base_type());
- BinaryRegionComment scalar_field_comment;
- scalar_field_comment.type = BinaryRegionCommentType::TableField;
- scalar_field_comment.name =
- std::string(field->name()->c_str()) + "` (" +
- reflection::EnumNameBaseType(field->type()->base_type()) + ")";
- if (!IsValidRead(field_offset, type_size)) {
- const uint64_t remaining = RemainingBytes(field_offset);
- SetError(scalar_field_comment,
- BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- std::to_string(type_size));
- regions.push_back(MakeBinaryRegion(field_offset, remaining,
- BinaryRegionType::Unknown, remaining,
- 0, scalar_field_comment));
- continue;
- }
- if (IsUnionType(field)) {
- // This is a type for a union. Validate the value
- const auto enum_value = ReadScalar<uint8_t>(field_offset);
- // This should always have a value, due to the IsValidRead check above.
- if (!IsValidUnionValue(field, enum_value.value())) {
- SetError(scalar_field_comment,
- BinaryRegionStatus::ERROR_INVALID_UNION_TYPE);
- regions.push_back(MakeBinaryRegion(field_offset, type_size,
- region_type, 0, 0,
- scalar_field_comment));
- continue;
- }
- }
- regions.push_back(MakeBinaryRegion(field_offset, type_size, region_type,
- 0, 0, scalar_field_comment));
- continue;
- }
- // Read the offset
- uint64_t offset = 0;
- uint64_t length = sizeof(uint32_t);
- BinaryRegionType region_type = BinaryRegionType::UOffset;
- if (field->offset64()) {
- length = sizeof(uint64_t);
- region_type = BinaryRegionType::UOffset64;
- offset = ReadScalar<uint64_t>(field_offset).value_or(0);
- } else {
- offset = ReadScalar<uint32_t>(field_offset).value_or(0);
- }
- // const auto offset_from_field = ReadScalar<uint32_t>(field_offset);
- uint64_t offset_of_next_item = 0;
- BinaryRegionComment offset_field_comment;
- offset_field_comment.type = BinaryRegionCommentType::TableOffsetField;
- offset_field_comment.name = field->name()->c_str();
- const std::string offset_prefix =
- "offset to field `" + std::string(field->name()->c_str()) + "`";
- // Validate any field that isn't inline (i.e., non-structs).
- if (!IsInlineField(field)) {
- if (offset == 0) {
- const uint64_t remaining = RemainingBytes(field_offset);
- SetError(offset_field_comment,
- BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
- regions.push_back(MakeBinaryRegion(field_offset, remaining,
- BinaryRegionType::Unknown, remaining,
- 0, offset_field_comment));
- continue;
- }
- offset_of_next_item = field_offset + offset;
- if (!IsValidOffset(offset_of_next_item)) {
- SetError(offset_field_comment,
- BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
- regions.push_back(MakeBinaryRegion(field_offset, length, region_type, 0,
- offset_of_next_item,
- offset_field_comment));
- continue;
- }
- }
- switch (field->type()->base_type()) {
- case reflection::BaseType::Obj: {
- const reflection::Object *next_object =
- schema_->objects()->Get(field->type()->index());
- if (next_object->is_struct()) {
- // Structs are stored inline.
- BuildStruct(field_offset, regions, field->name()->c_str(),
- next_object);
- } else {
- offset_field_comment.default_value = "(table)";
- regions.push_back(MakeBinaryRegion(field_offset, length, region_type,
- 0, offset_of_next_item,
- offset_field_comment));
- BuildTable(offset_of_next_item, BinarySectionType::Table,
- next_object);
- }
- } break;
- case reflection::BaseType::String: {
- offset_field_comment.default_value = "(string)";
- regions.push_back(MakeBinaryRegion(field_offset, length, region_type, 0,
- offset_of_next_item,
- offset_field_comment));
- BuildString(offset_of_next_item, table, field);
- } break;
- case reflection::BaseType::Vector: {
- offset_field_comment.default_value = "(vector)";
- regions.push_back(MakeBinaryRegion(field_offset, length, region_type, 0,
- offset_of_next_item,
- offset_field_comment));
- BuildVector(offset_of_next_item, table, field, table_offset,
- vtable->fields);
- } break;
- case reflection::BaseType::Vector64: {
- offset_field_comment.default_value = "(vector64)";
- regions.push_back(MakeBinaryRegion(field_offset, length, region_type, 0,
- offset_of_next_item,
- offset_field_comment));
- BuildVector(offset_of_next_item, table, field, table_offset,
- vtable->fields);
- } break;
- case reflection::BaseType::Union: {
- const uint64_t union_offset = offset_of_next_item;
- // The union type field is always one less than the union itself.
- const uint16_t union_type_id = field->id() - 1;
- auto vtable_field = vtable->fields.find(union_type_id);
- if (vtable_field == vtable->fields.end()) {
- // TODO(dbaileychess): need to capture this error condition.
- break;
- }
- offset_field_comment.default_value = "(union)";
- const uint64_t type_offset =
- table_offset + vtable_field->second.offset_from_table;
- const auto realized_type = ReadScalar<uint8_t>(type_offset);
- if (!realized_type.has_value()) {
- const uint64_t remaining = RemainingBytes(type_offset);
- SetError(offset_field_comment,
- BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "1");
- regions.push_back(MakeBinaryRegion(
- type_offset, remaining, BinaryRegionType::Unknown, remaining, 0,
- offset_field_comment));
- continue;
- }
- if (!IsValidUnionValue(field, realized_type.value())) {
- // We already export an error in the union type field, so just skip
- // building the union itself and it will default to an unreference
- // Binary section.
- continue;
- }
- const std::string enum_type =
- BuildUnion(union_offset, realized_type.value(), field);
- offset_field_comment.default_value =
- "(union of type `" + enum_type + "`)";
- regions.push_back(MakeBinaryRegion(field_offset, length, region_type, 0,
- union_offset, offset_field_comment));
- } break;
- default: break;
- }
- }
- // Handle the case where there is padding after the last known binary
- // region. Calculate where we left off towards the expected end of the
- // table.
- const uint64_t i = regions.back().offset + regions.back().length + 1;
- if (i < table_end_offset) {
- const uint64_t pad_bytes = table_end_offset - i + 1;
- BinaryRegionComment padding_comment;
- padding_comment.type = BinaryRegionCommentType::Padding;
- regions.push_back(MakeBinaryRegion(i - 1, pad_bytes * sizeof(uint8_t),
- BinaryRegionType::Uint8, pad_bytes, 0,
- padding_comment));
- }
- AddSection(table_offset,
- MakeBinarySection(table->name()->str(), type, std::move(regions)));
- }
- uint64_t BinaryAnnotator::BuildStruct(const uint64_t struct_offset,
- std::vector<BinaryRegion> ®ions,
- const std::string referring_field_name,
- const reflection::Object *const object) {
- if (!object->is_struct()) { return struct_offset; }
- uint64_t offset = struct_offset;
- // Loop over all the fields in increasing order
- ForAllFields(object, /*reverse=*/false, [&](const reflection::Field *field) {
- if (IsScalar(field->type()->base_type())) {
- // Structure Field value
- const uint64_t type_size = GetTypeSize(field->type()->base_type());
- const BinaryRegionType region_type =
- GetRegionType(field->type()->base_type());
- BinaryRegionComment comment;
- comment.type = BinaryRegionCommentType::StructField;
- comment.name = referring_field_name + "." + field->name()->str();
- comment.default_value = "of '" + object->name()->str() + "' (" +
- std::string(reflection::EnumNameBaseType(
- field->type()->base_type())) +
- ")";
- if (!IsValidRead(offset, type_size)) {
- const uint64_t remaining = RemainingBytes(offset);
- SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- std::to_string(type_size));
- regions.push_back(MakeBinaryRegion(offset, remaining,
- BinaryRegionType::Unknown, remaining,
- 0, comment));
- // TODO(dbaileychess): Should I bail out here? This sets offset to the
- // end of the binary. So all other reads in the loop should fail.
- offset += remaining;
- return;
- }
- regions.push_back(
- MakeBinaryRegion(offset, type_size, region_type, 0, 0, comment));
- offset += type_size;
- } else if (field->type()->base_type() == reflection::BaseType::Obj) {
- // Structs are stored inline, even when nested.
- offset = BuildStruct(offset, regions,
- referring_field_name + "." + field->name()->str(),
- schema_->objects()->Get(field->type()->index()));
- } else if (field->type()->base_type() == reflection::BaseType::Array) {
- const bool is_scalar = IsScalar(field->type()->element());
- const uint64_t type_size = GetTypeSize(field->type()->element());
- const BinaryRegionType region_type =
- GetRegionType(field->type()->element());
- // Arrays are just repeated structures.
- for (uint16_t i = 0; i < field->type()->fixed_length(); ++i) {
- if (is_scalar) {
- BinaryRegionComment array_comment;
- array_comment.type = BinaryRegionCommentType::ArrayField;
- array_comment.name =
- referring_field_name + "." + field->name()->str();
- array_comment.index = i;
- array_comment.default_value =
- "of '" + object->name()->str() + "' (" +
- std::string(
- reflection::EnumNameBaseType(field->type()->element())) +
- ")";
- if (!IsValidRead(offset, type_size)) {
- const uint64_t remaining = RemainingBytes(offset);
- SetError(array_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- std::to_string(type_size));
- regions.push_back(MakeBinaryRegion(offset, remaining,
- BinaryRegionType::Unknown,
- remaining, 0, array_comment));
- // TODO(dbaileychess): Should I bail out here? This sets offset to
- // the end of the binary. So all other reads in the loop should
- // fail.
- offset += remaining;
- break;
- }
- regions.push_back(MakeBinaryRegion(offset, type_size, region_type, 0,
- 0, array_comment));
- offset += type_size;
- } else {
- // Array of Structs.
- //
- // TODO(dbaileychess): This works, but the comments on the fields lose
- // some context. Need to figure a way how to plumb the nested arrays
- // comments together that isn't too confusing.
- offset =
- BuildStruct(offset, regions,
- referring_field_name + "." + field->name()->str(),
- schema_->objects()->Get(field->type()->index()));
- }
- }
- }
- // Insert any padding after this field.
- const uint16_t padding = field->padding();
- if (padding > 0 && IsValidOffset(offset + padding)) {
- BinaryRegionComment padding_comment;
- padding_comment.type = BinaryRegionCommentType::Padding;
- regions.push_back(MakeBinaryRegion(offset, padding,
- BinaryRegionType::Uint8, padding, 0,
- padding_comment));
- offset += padding;
- }
- });
- return offset;
- }
- void BinaryAnnotator::BuildString(const uint64_t string_offset,
- const reflection::Object *const table,
- const reflection::Field *const field) {
- // Check if we have already generated this string section, and this is a
- // shared string instance.
- if (ContainsSection(string_offset)) { return; }
- std::vector<BinaryRegion> regions;
- const auto string_length = ReadScalar<uint32_t>(string_offset);
- BinaryRegionComment string_length_comment;
- string_length_comment.type = BinaryRegionCommentType::StringLength;
- if (!string_length.has_value()) {
- const uint64_t remaining = RemainingBytes(string_offset);
- SetError(string_length_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- "4");
- regions.push_back(MakeBinaryRegion(string_offset, remaining,
- BinaryRegionType::Unknown, remaining, 0,
- string_length_comment));
- } else {
- const uint32_t string_size = string_length.value();
- const uint64_t string_end =
- string_offset + sizeof(uint32_t) + string_size + sizeof(char);
- if (!IsValidOffset(string_end - 1)) {
- SetError(string_length_comment,
- BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
- regions.push_back(MakeBinaryRegion(string_offset, sizeof(uint32_t),
- BinaryRegionType::Uint32, 0, 0,
- string_length_comment));
- } else {
- regions.push_back(MakeBinaryRegion(string_offset, sizeof(uint32_t),
- BinaryRegionType::Uint32, 0, 0,
- string_length_comment));
- BinaryRegionComment string_comment;
- string_comment.type = BinaryRegionCommentType::StringValue;
- regions.push_back(MakeBinaryRegion(string_offset + sizeof(uint32_t),
- string_size, BinaryRegionType::Char,
- string_size, 0, string_comment));
- BinaryRegionComment string_terminator_comment;
- string_terminator_comment.type =
- BinaryRegionCommentType::StringTerminator;
- regions.push_back(MakeBinaryRegion(
- string_offset + sizeof(uint32_t) + string_size, sizeof(char),
- BinaryRegionType::Char, 0, 0, string_terminator_comment));
- }
- }
- AddSection(string_offset,
- MakeBinarySection(std::string(table->name()->c_str()) + "." +
- field->name()->c_str(),
- BinarySectionType::String, std::move(regions)));
- }
- void BinaryAnnotator::BuildVector(
- const uint64_t vector_offset, const reflection::Object *const table,
- const reflection::Field *const field, const uint64_t parent_table_offset,
- const std::map<uint16_t, VTable::Entry> vtable_fields) {
- if (ContainsSection(vector_offset)) { return; }
- BinaryRegionComment vector_length_comment;
- vector_length_comment.type = BinaryRegionCommentType::VectorLength;
- const bool is_64_bit_vector =
- field->type()->base_type() == reflection::BaseType::Vector64;
- flatbuffers::Optional<uint64_t> vector_length;
- uint32_t vector_length_size_type = 0;
- BinaryRegionType region_type = BinaryRegionType::Uint32;
- BinarySectionType section_type = BinarySectionType::Vector;
- if (is_64_bit_vector) {
- auto v = ReadScalar<uint64_t>(vector_offset);
- if (v.has_value()) { vector_length = v.value(); }
- vector_length_size_type = sizeof(uint64_t);
- region_type = BinaryRegionType::Uint64;
- section_type = BinarySectionType::Vector64;
- } else {
- auto v = ReadScalar<uint32_t>(vector_offset);
- if (v.has_value()) { vector_length = v.value(); }
- vector_length_size_type = sizeof(uint32_t);
- region_type = BinaryRegionType::Uint32;
- section_type = BinarySectionType::Vector;
- }
- if (!vector_length.has_value()) {
- const uint64_t remaining = RemainingBytes(vector_offset);
- SetError(vector_length_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- "4");
- AddSection(
- vector_offset,
- MakeSingleRegionBinarySection(
- std::string(table->name()->c_str()) + "." + field->name()->c_str(),
- BinarySectionType::Vector,
- MakeBinaryRegion(vector_offset, remaining,
- BinaryRegionType::Unknown, remaining, 0,
- vector_length_comment)));
- return;
- }
- // Validate there are enough bytes left in the binary to process all the
- // items.
- const uint64_t last_item_offset =
- vector_offset + vector_length_size_type +
- vector_length.value() * GetElementSize(field);
- if (!IsValidOffset(last_item_offset - 1)) {
- SetError(vector_length_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
- AddSection(
- vector_offset,
- MakeSingleRegionBinarySection(
- std::string(table->name()->c_str()) + "." + field->name()->c_str(),
- BinarySectionType::Vector,
- MakeBinaryRegion(vector_offset, vector_length_size_type,
- region_type, 0, 0, vector_length_comment)));
- return;
- }
- std::vector<BinaryRegion> regions;
- regions.push_back(MakeBinaryRegion(vector_offset, vector_length_size_type,
- region_type, 0, 0, vector_length_comment));
- // Consume the vector length offset.
- uint64_t offset = vector_offset + vector_length_size_type;
- switch (field->type()->element()) {
- case reflection::BaseType::Obj: {
- const reflection::Object *object =
- schema_->objects()->Get(field->type()->index());
- if (object->is_struct()) {
- // Vector of structs
- for (size_t i = 0; i < vector_length.value(); ++i) {
- // Structs are inline to the vector.
- const uint64_t next_offset =
- BuildStruct(offset, regions, "[" + NumToString(i) + "]", object);
- if (next_offset == offset) { break; }
- offset = next_offset;
- }
- } else {
- // Vector of objects
- for (size_t i = 0; i < vector_length.value(); ++i) {
- BinaryRegionComment vector_object_comment;
- vector_object_comment.type =
- BinaryRegionCommentType::VectorTableValue;
- vector_object_comment.index = i;
- const auto table_relative_offset = ReadScalar<uint32_t>(offset);
- if (!table_relative_offset.has_value()) {
- const uint64_t remaining = RemainingBytes(offset);
- SetError(vector_object_comment,
- BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
- regions.push_back(
- MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
- remaining, 0, vector_object_comment));
- break;
- }
- // The table offset is relative from the offset location itself.
- const uint64_t table_offset = offset + table_relative_offset.value();
- if (!IsValidOffset(table_offset)) {
- SetError(vector_object_comment,
- BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
- regions.push_back(MakeBinaryRegion(
- offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
- table_offset, vector_object_comment));
- offset += sizeof(uint32_t);
- continue;
- }
- if (table_offset == parent_table_offset) {
- SetError(vector_object_comment,
- BinaryRegionStatus::ERROR_CYCLE_DETECTED);
- // A cycle detected where a table vector field is pointing to
- // itself. This should only happen in corrupted files.
- regions.push_back(MakeBinaryRegion(
- offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
- table_offset, vector_object_comment));
- offset += sizeof(uint32_t);
- continue;
- }
- regions.push_back(MakeBinaryRegion(
- offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
- table_offset, vector_object_comment));
- // Consume the offset to the table.
- offset += sizeof(uint32_t);
- BuildTable(table_offset, BinarySectionType::Table, object);
- }
- }
- } break;
- case reflection::BaseType::String: {
- // Vector of strings
- for (size_t i = 0; i < vector_length.value(); ++i) {
- BinaryRegionComment vector_object_comment;
- vector_object_comment.type = BinaryRegionCommentType::VectorStringValue;
- vector_object_comment.index = i;
- const auto string_relative_offset = ReadScalar<uint32_t>(offset);
- if (!string_relative_offset.has_value()) {
- const uint64_t remaining = RemainingBytes(offset);
- SetError(vector_object_comment,
- BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
- regions.push_back(
- MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
- remaining, 0, vector_object_comment));
- break;
- }
- // The string offset is relative from the offset location itself.
- const uint64_t string_offset = offset + string_relative_offset.value();
- if (!IsValidOffset(string_offset)) {
- SetError(vector_object_comment,
- BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
- regions.push_back(MakeBinaryRegion(
- offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
- string_offset, vector_object_comment));
- offset += sizeof(uint32_t);
- continue;
- }
- regions.push_back(MakeBinaryRegion(
- offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
- string_offset, vector_object_comment));
- BuildString(string_offset, table, field);
- offset += sizeof(uint32_t);
- }
- } break;
- case reflection::BaseType::Union: {
- // Vector of unions
- // Unions have both their realized type (uint8_t for now) that are
- // stored separately. These are stored in the field->index() - 1
- // location.
- const uint16_t union_type_vector_id = field->id() - 1;
- auto vtable_entry = vtable_fields.find(union_type_vector_id);
- if (vtable_entry == vtable_fields.end()) {
- // TODO(dbaileychess): need to capture this error condition.
- break;
- }
- const uint64_t union_type_vector_field_offset =
- parent_table_offset + vtable_entry->second.offset_from_table;
- const auto union_type_vector_field_relative_offset =
- ReadScalar<uint16_t>(union_type_vector_field_offset);
- if (!union_type_vector_field_relative_offset.has_value()) {
- const uint64_t remaining = RemainingBytes(offset);
- BinaryRegionComment vector_union_comment;
- vector_union_comment.type = BinaryRegionCommentType::VectorUnionValue;
- SetError(vector_union_comment,
- BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
- regions.push_back(MakeBinaryRegion(offset, remaining,
- BinaryRegionType::Unknown, remaining,
- 0, vector_union_comment));
- break;
- }
- // Get the offset to the first type (the + sizeof(uint32_t) is to skip
- // over the vector length which we already know). Validation happens
- // within the loop below.
- const uint64_t union_type_vector_data_offset =
- union_type_vector_field_offset +
- union_type_vector_field_relative_offset.value() + sizeof(uint32_t);
- for (size_t i = 0; i < vector_length.value(); ++i) {
- BinaryRegionComment comment;
- comment.type = BinaryRegionCommentType::VectorUnionValue;
- comment.index = i;
- const auto union_relative_offset = ReadScalar<uint32_t>(offset);
- if (!union_relative_offset.has_value()) {
- const uint64_t remaining = RemainingBytes(offset);
- SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
- regions.push_back(MakeBinaryRegion(offset, remaining,
- BinaryRegionType::Unknown,
- remaining, 0, comment));
- break;
- }
- // The union offset is relative from the offset location itself.
- const uint64_t union_offset = offset + union_relative_offset.value();
- if (!IsValidOffset(union_offset)) {
- SetError(comment, BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
- regions.push_back(MakeBinaryRegion(offset, sizeof(uint32_t),
- BinaryRegionType::UOffset, 0,
- union_offset, comment));
- continue;
- }
- const auto realized_type =
- ReadScalar<uint8_t>(union_type_vector_data_offset + i);
- if (!realized_type.has_value()) {
- SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "1");
- regions.push_back(MakeBinaryRegion(
- offset, 0, BinaryRegionType::Unknown, 0, 0, comment));
- continue;
- }
- if (!IsValidUnionValue(vtable_entry->second.field->type()->index(),
- realized_type.value())) {
- // We already export an error in the union type field, so just skip
- // building the union itself and it will default to an unreference
- // Binary section.
- offset += sizeof(uint32_t);
- continue;
- }
- const std::string enum_type =
- BuildUnion(union_offset, realized_type.value(), field);
- comment.default_value = "(`" + enum_type + "`)";
- regions.push_back(MakeBinaryRegion(offset, sizeof(uint32_t),
- BinaryRegionType::UOffset, 0,
- union_offset, comment));
- offset += sizeof(uint32_t);
- }
- } break;
- default: {
- if (IsScalar(field->type()->element())) {
- const BinaryRegionType binary_region_type =
- GetRegionType(field->type()->element());
- const uint64_t type_size = GetTypeSize(field->type()->element());
- // TODO(dbaileychess): It might be nicer to user the
- // BinaryRegion.array_length field to indicate this.
- for (size_t i = 0; i < vector_length.value(); ++i) {
- BinaryRegionComment vector_scalar_comment;
- vector_scalar_comment.type = BinaryRegionCommentType::VectorValue;
- vector_scalar_comment.index = i;
- if (!IsValidRead(offset, type_size)) {
- const uint64_t remaining = RemainingBytes(offset);
- SetError(vector_scalar_comment,
- BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
- std::to_string(type_size));
- regions.push_back(
- MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
- remaining, 0, vector_scalar_comment));
- break;
- }
- if (IsUnionType(field->type()->element())) {
- // This is a type for a union. Validate the value
- const auto enum_value = ReadScalar<uint8_t>(offset);
- // This should always have a value, due to the IsValidRead check
- // above.
- if (!IsValidUnionValue(field->type()->index(),
- enum_value.value())) {
- SetError(vector_scalar_comment,
- BinaryRegionStatus::ERROR_INVALID_UNION_TYPE);
- regions.push_back(MakeBinaryRegion(offset, type_size,
- binary_region_type, 0, 0,
- vector_scalar_comment));
- offset += type_size;
- continue;
- }
- }
- regions.push_back(MakeBinaryRegion(offset, type_size,
- binary_region_type, 0, 0,
- vector_scalar_comment));
- offset += type_size;
- }
- }
- } break;
- }
- AddSection(vector_offset,
- MakeBinarySection(std::string(table->name()->c_str()) + "." +
- field->name()->c_str(),
- section_type, std::move(regions)));
- }
- std::string BinaryAnnotator::BuildUnion(const uint64_t union_offset,
- const uint8_t realized_type,
- const reflection::Field *const field) {
- const reflection::Enum *next_enum =
- schema_->enums()->Get(field->type()->index());
- const reflection::EnumVal *enum_val = next_enum->values()->Get(realized_type);
- if (ContainsSection(union_offset)) { return enum_val->name()->c_str(); }
- const reflection::Type *union_type = enum_val->union_type();
- if (union_type->base_type() == reflection::BaseType::Obj) {
- const reflection::Object *object =
- schema_->objects()->Get(union_type->index());
- if (object->is_struct()) {
- // Union of vectors point to a new Binary section
- std::vector<BinaryRegion> regions;
- BuildStruct(union_offset, regions, field->name()->c_str(), object);
- AddSection(
- union_offset,
- MakeBinarySection(std::string(object->name()->c_str()) + "." +
- field->name()->c_str(),
- BinarySectionType::Union, std::move(regions)));
- } else {
- BuildTable(union_offset, BinarySectionType::Table, object);
- }
- }
- // TODO(dbaileychess): handle the other union types.
- return enum_val->name()->c_str();
- }
- void BinaryAnnotator::FixMissingRegions() {
- std::vector<BinaryRegion> regions_to_insert;
- for (auto ¤t_section : sections_) {
- BinarySection §ion = current_section.second;
- if (section.regions.empty()) {
- // TODO(dbaileychess): is this possible?
- continue;
- }
- uint64_t offset = section.regions[0].offset + section.regions[0].length;
- for (size_t i = 1; i < section.regions.size(); ++i) {
- BinaryRegion ®ion = section.regions[i];
- const uint64_t next_offset = region.offset;
- if (!IsValidOffset(next_offset)) {
- // TODO(dbaileychess): figure out how we get into this situation.
- continue;
- }
- if (offset < next_offset) {
- const uint64_t padding_bytes = next_offset - offset;
- BinaryRegionComment comment;
- comment.type = BinaryRegionCommentType::Padding;
- if (IsNonZeroRegion(offset, padding_bytes, binary_)) {
- SetError(comment, BinaryRegionStatus::WARN_NO_REFERENCES);
- regions_to_insert.push_back(
- MakeBinaryRegion(offset, padding_bytes, BinaryRegionType::Unknown,
- padding_bytes, 0, comment));
- } else {
- regions_to_insert.push_back(
- MakeBinaryRegion(offset, padding_bytes, BinaryRegionType::Uint8,
- padding_bytes, 0, comment));
- }
- }
- offset = next_offset + region.length;
- }
- if (!regions_to_insert.empty()) {
- section.regions.insert(section.regions.end(), regions_to_insert.begin(),
- regions_to_insert.end());
- std::stable_sort(section.regions.begin(), section.regions.end(),
- BinaryRegionSort);
- regions_to_insert.clear();
- }
- }
- }
- void BinaryAnnotator::FixMissingSections() {
- uint64_t offset = 0;
- std::vector<BinarySection> sections_to_insert;
- for (auto ¤t_section : sections_) {
- BinarySection §ion = current_section.second;
- const uint64_t section_start_offset = current_section.first;
- const uint64_t section_end_offset =
- section.regions.back().offset + section.regions.back().length;
- if (offset < section_start_offset) {
- // We are at an offset that is less then the current section.
- const uint64_t pad_bytes = section_start_offset - offset + 1;
- sections_to_insert.push_back(
- GenerateMissingSection(offset - 1, pad_bytes, binary_));
- }
- offset = section_end_offset + 1;
- }
- // Handle the case where there are still bytes left in the binary that are
- // unaccounted for.
- if (offset < binary_length_) {
- const uint64_t pad_bytes = binary_length_ - offset + 1;
- sections_to_insert.push_back(
- GenerateMissingSection(offset - 1, pad_bytes, binary_));
- }
- for (const BinarySection §ion_to_insert : sections_to_insert) {
- AddSection(section_to_insert.regions[0].offset, section_to_insert);
- }
- }
- bool BinaryAnnotator::ContainsSection(const uint64_t offset) {
- auto it = sections_.lower_bound(offset);
- // If the section is found, check that it is exactly equal its offset.
- if (it != sections_.end() && it->first == offset) { return true; }
- // If this was the first section, there are no other previous sections to
- // check.
- if (it == sections_.begin()) { return false; }
- // Go back one section.
- --it;
- // And check that if the offset is covered by the section.
- return offset >= it->first && offset < it->second.regions.back().offset +
- it->second.regions.back().length;
- }
- } // namespace flatbuffers
|