1*890232f2SAndroid Build Coastguard Worker #include "binary_annotator.h"
2*890232f2SAndroid Build Coastguard Worker
3*890232f2SAndroid Build Coastguard Worker #include <limits>
4*890232f2SAndroid Build Coastguard Worker #include <string>
5*890232f2SAndroid Build Coastguard Worker #include <vector>
6*890232f2SAndroid Build Coastguard Worker
7*890232f2SAndroid Build Coastguard Worker #include "flatbuffers/reflection.h"
8*890232f2SAndroid Build Coastguard Worker #include "flatbuffers/verifier.h"
9*890232f2SAndroid Build Coastguard Worker
10*890232f2SAndroid Build Coastguard Worker namespace flatbuffers {
11*890232f2SAndroid Build Coastguard Worker namespace {
12*890232f2SAndroid Build Coastguard Worker
BinaryRegionSort(const BinaryRegion & a,const BinaryRegion & b)13*890232f2SAndroid Build Coastguard Worker static bool BinaryRegionSort(const BinaryRegion &a, const BinaryRegion &b) {
14*890232f2SAndroid Build Coastguard Worker return a.offset < b.offset;
15*890232f2SAndroid Build Coastguard Worker }
16*890232f2SAndroid Build Coastguard Worker
SetError(BinaryRegionComment & comment,BinaryRegionStatus status,std::string message="")17*890232f2SAndroid Build Coastguard Worker static void SetError(BinaryRegionComment &comment, BinaryRegionStatus status,
18*890232f2SAndroid Build Coastguard Worker std::string message = "") {
19*890232f2SAndroid Build Coastguard Worker comment.status = status;
20*890232f2SAndroid Build Coastguard Worker comment.status_message = message;
21*890232f2SAndroid Build Coastguard Worker }
22*890232f2SAndroid Build Coastguard Worker
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,const BinaryRegionComment comment={})23*890232f2SAndroid Build Coastguard Worker static BinaryRegion MakeBinaryRegion(
24*890232f2SAndroid Build Coastguard Worker const uint64_t offset = 0, const uint64_t length = 0,
25*890232f2SAndroid Build Coastguard Worker const BinaryRegionType type = BinaryRegionType::Unknown,
26*890232f2SAndroid Build Coastguard Worker const uint64_t array_length = 0, const uint64_t points_to_offset = 0,
27*890232f2SAndroid Build Coastguard Worker const BinaryRegionComment comment = {}) {
28*890232f2SAndroid Build Coastguard Worker BinaryRegion region;
29*890232f2SAndroid Build Coastguard Worker region.offset = offset;
30*890232f2SAndroid Build Coastguard Worker region.length = length;
31*890232f2SAndroid Build Coastguard Worker region.type = type;
32*890232f2SAndroid Build Coastguard Worker region.array_length = array_length;
33*890232f2SAndroid Build Coastguard Worker region.points_to_offset = points_to_offset;
34*890232f2SAndroid Build Coastguard Worker region.comment = std::move(comment);
35*890232f2SAndroid Build Coastguard Worker return region;
36*890232f2SAndroid Build Coastguard Worker }
37*890232f2SAndroid Build Coastguard Worker
MakeBinarySection(const std::string & name,const BinarySectionType type,const std::vector<BinaryRegion> regions)38*890232f2SAndroid Build Coastguard Worker static BinarySection MakeBinarySection(
39*890232f2SAndroid Build Coastguard Worker const std::string &name, const BinarySectionType type,
40*890232f2SAndroid Build Coastguard Worker const std::vector<BinaryRegion> regions) {
41*890232f2SAndroid Build Coastguard Worker BinarySection section;
42*890232f2SAndroid Build Coastguard Worker section.name = name;
43*890232f2SAndroid Build Coastguard Worker section.type = type;
44*890232f2SAndroid Build Coastguard Worker section.regions = std::move(regions);
45*890232f2SAndroid Build Coastguard Worker return section;
46*890232f2SAndroid Build Coastguard Worker }
47*890232f2SAndroid Build Coastguard Worker
MakeSingleRegionBinarySection(const std::string & name,const BinarySectionType type,const BinaryRegion & region)48*890232f2SAndroid Build Coastguard Worker static BinarySection MakeSingleRegionBinarySection(const std::string &name,
49*890232f2SAndroid Build Coastguard Worker const BinarySectionType type,
50*890232f2SAndroid Build Coastguard Worker const BinaryRegion ®ion) {
51*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions;
52*890232f2SAndroid Build Coastguard Worker regions.push_back(region);
53*890232f2SAndroid Build Coastguard Worker return MakeBinarySection(name, type, std::move(regions));
54*890232f2SAndroid Build Coastguard Worker }
55*890232f2SAndroid Build Coastguard Worker
IsNonZeroRegion(const uint64_t offset,const uint64_t length,const uint8_t * const binary)56*890232f2SAndroid Build Coastguard Worker static bool IsNonZeroRegion(const uint64_t offset, const uint64_t length,
57*890232f2SAndroid Build Coastguard Worker const uint8_t *const binary) {
58*890232f2SAndroid Build Coastguard Worker for (uint64_t i = offset; i < offset + length; ++i) {
59*890232f2SAndroid Build Coastguard Worker if (binary[i] != 0) { return true; }
60*890232f2SAndroid Build Coastguard Worker }
61*890232f2SAndroid Build Coastguard Worker return false;
62*890232f2SAndroid Build Coastguard Worker }
63*890232f2SAndroid Build Coastguard Worker
IsPrintableRegion(const uint64_t offset,const uint64_t length,const uint8_t * const binary)64*890232f2SAndroid Build Coastguard Worker static bool IsPrintableRegion(const uint64_t offset, const uint64_t length,
65*890232f2SAndroid Build Coastguard Worker const uint8_t *const binary) {
66*890232f2SAndroid Build Coastguard Worker for (uint64_t i = offset; i < offset + length; ++i) {
67*890232f2SAndroid Build Coastguard Worker if (!isprint(binary[i])) { return false; }
68*890232f2SAndroid Build Coastguard Worker }
69*890232f2SAndroid Build Coastguard Worker return true;
70*890232f2SAndroid Build Coastguard Worker }
71*890232f2SAndroid Build Coastguard Worker
GenerateMissingSection(const uint64_t offset,const uint64_t length,const uint8_t * const binary)72*890232f2SAndroid Build Coastguard Worker static BinarySection GenerateMissingSection(const uint64_t offset,
73*890232f2SAndroid Build Coastguard Worker const uint64_t length,
74*890232f2SAndroid Build Coastguard Worker const uint8_t *const binary) {
75*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions;
76*890232f2SAndroid Build Coastguard Worker
77*890232f2SAndroid Build Coastguard Worker // Check if the region is all zeros or not, as that can tell us if it is
78*890232f2SAndroid Build Coastguard Worker // padding or not.
79*890232f2SAndroid Build Coastguard Worker if (IsNonZeroRegion(offset, length, binary)) {
80*890232f2SAndroid Build Coastguard Worker // Some of the padding bytes are non-zero, so this might be an unknown
81*890232f2SAndroid Build Coastguard Worker // section of the binary.
82*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): We could be a bit smarter with different sized
83*890232f2SAndroid Build Coastguard Worker // alignments. For now, the 8 byte check encompasses all the smaller
84*890232f2SAndroid Build Coastguard Worker // alignments.
85*890232f2SAndroid Build Coastguard Worker BinaryRegionComment comment;
86*890232f2SAndroid Build Coastguard Worker comment.type = BinaryRegionCommentType::Unknown;
87*890232f2SAndroid Build Coastguard Worker if (length >= 8) {
88*890232f2SAndroid Build Coastguard Worker SetError(comment, BinaryRegionStatus::WARN_NO_REFERENCES);
89*890232f2SAndroid Build Coastguard Worker } else {
90*890232f2SAndroid Build Coastguard Worker SetError(comment, BinaryRegionStatus::WARN_CORRUPTED_PADDING);
91*890232f2SAndroid Build Coastguard Worker }
92*890232f2SAndroid Build Coastguard Worker
93*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, length * sizeof(uint8_t),
94*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, length, 0,
95*890232f2SAndroid Build Coastguard Worker comment));
96*890232f2SAndroid Build Coastguard Worker
97*890232f2SAndroid Build Coastguard Worker return MakeBinarySection("no known references", BinarySectionType::Unknown,
98*890232f2SAndroid Build Coastguard Worker std::move(regions));
99*890232f2SAndroid Build Coastguard Worker }
100*890232f2SAndroid Build Coastguard Worker
101*890232f2SAndroid Build Coastguard Worker BinaryRegionComment comment;
102*890232f2SAndroid Build Coastguard Worker comment.type = BinaryRegionCommentType::Padding;
103*890232f2SAndroid Build Coastguard Worker if (length >= 8) {
104*890232f2SAndroid Build Coastguard Worker SetError(comment, BinaryRegionStatus::WARN_PADDING_LENGTH);
105*890232f2SAndroid Build Coastguard Worker }
106*890232f2SAndroid Build Coastguard Worker
107*890232f2SAndroid Build Coastguard Worker // This region is most likely padding.
108*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, length * sizeof(uint8_t),
109*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint8, length, 0,
110*890232f2SAndroid Build Coastguard Worker comment));
111*890232f2SAndroid Build Coastguard Worker
112*890232f2SAndroid Build Coastguard Worker return MakeBinarySection("", BinarySectionType::Padding, std::move(regions));
113*890232f2SAndroid Build Coastguard Worker }
114*890232f2SAndroid Build Coastguard Worker
115*890232f2SAndroid Build Coastguard Worker } // namespace
116*890232f2SAndroid Build Coastguard Worker
Annotate()117*890232f2SAndroid Build Coastguard Worker std::map<uint64_t, BinarySection> BinaryAnnotator::Annotate() {
118*890232f2SAndroid Build Coastguard Worker flatbuffers::Verifier verifier(bfbs_, static_cast<size_t>(bfbs_length_));
119*890232f2SAndroid Build Coastguard Worker if (!reflection::VerifySchemaBuffer(verifier)) { return {}; }
120*890232f2SAndroid Build Coastguard Worker
121*890232f2SAndroid Build Coastguard Worker // The binary is too short to read as a flatbuffers.
122*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): We could spit out the annotated buffer sections, but
123*890232f2SAndroid Build Coastguard Worker // I'm not sure if it is worth it.
124*890232f2SAndroid Build Coastguard Worker if (binary_length_ < 4) { return {}; }
125*890232f2SAndroid Build Coastguard Worker
126*890232f2SAndroid Build Coastguard Worker // Make sure we start with a clean slate.
127*890232f2SAndroid Build Coastguard Worker vtables_.clear();
128*890232f2SAndroid Build Coastguard Worker sections_.clear();
129*890232f2SAndroid Build Coastguard Worker
130*890232f2SAndroid Build Coastguard Worker // First parse the header region which always start at offset 0.
131*890232f2SAndroid Build Coastguard Worker // The returned offset will point to the root_table location.
132*890232f2SAndroid Build Coastguard Worker const uint64_t root_table_offset = BuildHeader(0);
133*890232f2SAndroid Build Coastguard Worker
134*890232f2SAndroid Build Coastguard Worker if (IsValidOffset(root_table_offset)) {
135*890232f2SAndroid Build Coastguard Worker // Build the root table, and all else will be referenced from it.
136*890232f2SAndroid Build Coastguard Worker BuildTable(root_table_offset, BinarySectionType::RootTable,
137*890232f2SAndroid Build Coastguard Worker schema_->root_table());
138*890232f2SAndroid Build Coastguard Worker }
139*890232f2SAndroid Build Coastguard Worker
140*890232f2SAndroid Build Coastguard Worker // Now that all the sections are built, make sure the binary sections are
141*890232f2SAndroid Build Coastguard Worker // contiguous.
142*890232f2SAndroid Build Coastguard Worker FixMissingRegions();
143*890232f2SAndroid Build Coastguard Worker
144*890232f2SAndroid Build Coastguard Worker // Then scan the area between BinarySections insert padding sections that are
145*890232f2SAndroid Build Coastguard Worker // implied.
146*890232f2SAndroid Build Coastguard Worker FixMissingSections();
147*890232f2SAndroid Build Coastguard Worker
148*890232f2SAndroid Build Coastguard Worker return sections_;
149*890232f2SAndroid Build Coastguard Worker }
150*890232f2SAndroid Build Coastguard Worker
BuildHeader(const uint64_t header_offset)151*890232f2SAndroid Build Coastguard Worker uint64_t BinaryAnnotator::BuildHeader(const uint64_t header_offset) {
152*890232f2SAndroid Build Coastguard Worker const auto root_table_offset = ReadScalar<uint32_t>(header_offset);
153*890232f2SAndroid Build Coastguard Worker
154*890232f2SAndroid Build Coastguard Worker if (!root_table_offset.has_value()) {
155*890232f2SAndroid Build Coastguard Worker // This shouldn't occur, since we validate the min size of the buffer
156*890232f2SAndroid Build Coastguard Worker // before. But for completion sake, we shouldn't read passed the binary end.
157*890232f2SAndroid Build Coastguard Worker return std::numeric_limits<uint64_t>::max();
158*890232f2SAndroid Build Coastguard Worker }
159*890232f2SAndroid Build Coastguard Worker
160*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions;
161*890232f2SAndroid Build Coastguard Worker uint64_t offset = header_offset;
162*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): sized prefixed value
163*890232f2SAndroid Build Coastguard Worker
164*890232f2SAndroid Build Coastguard Worker BinaryRegionComment root_offset_comment;
165*890232f2SAndroid Build Coastguard Worker root_offset_comment.type = BinaryRegionCommentType::RootTableOffset;
166*890232f2SAndroid Build Coastguard Worker root_offset_comment.name = schema_->root_table()->name()->str();
167*890232f2SAndroid Build Coastguard Worker
168*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(root_table_offset.value())) {
169*890232f2SAndroid Build Coastguard Worker SetError(root_offset_comment,
170*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
171*890232f2SAndroid Build Coastguard Worker }
172*890232f2SAndroid Build Coastguard Worker
173*890232f2SAndroid Build Coastguard Worker regions.push_back(
174*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
175*890232f2SAndroid Build Coastguard Worker root_table_offset.value(), root_offset_comment));
176*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint32_t);
177*890232f2SAndroid Build Coastguard Worker
178*890232f2SAndroid Build Coastguard Worker if (IsValidRead(offset, flatbuffers::kFileIdentifierLength) &&
179*890232f2SAndroid Build Coastguard Worker IsPrintableRegion(offset, flatbuffers::kFileIdentifierLength, binary_)) {
180*890232f2SAndroid Build Coastguard Worker BinaryRegionComment comment;
181*890232f2SAndroid Build Coastguard Worker comment.type = BinaryRegionCommentType::FileIdentifier;
182*890232f2SAndroid Build Coastguard Worker // Check if the file identifier region has non-zero data, and assume its
183*890232f2SAndroid Build Coastguard Worker // the file identifier. Otherwise, it will get filled in with padding
184*890232f2SAndroid Build Coastguard Worker // later.
185*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
186*890232f2SAndroid Build Coastguard Worker offset, flatbuffers::kFileIdentifierLength * sizeof(uint8_t),
187*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Char, flatbuffers::kFileIdentifierLength, 0,
188*890232f2SAndroid Build Coastguard Worker comment));
189*890232f2SAndroid Build Coastguard Worker }
190*890232f2SAndroid Build Coastguard Worker
191*890232f2SAndroid Build Coastguard Worker AddSection(header_offset, MakeBinarySection("", BinarySectionType::Header,
192*890232f2SAndroid Build Coastguard Worker std::move(regions)));
193*890232f2SAndroid Build Coastguard Worker
194*890232f2SAndroid Build Coastguard Worker return root_table_offset.value();
195*890232f2SAndroid Build Coastguard Worker }
196*890232f2SAndroid Build Coastguard Worker
BuildVTable(const uint64_t vtable_offset,const reflection::Object * const table,const uint64_t offset_of_referring_table)197*890232f2SAndroid Build Coastguard Worker void BinaryAnnotator::BuildVTable(const uint64_t vtable_offset,
198*890232f2SAndroid Build Coastguard Worker const reflection::Object *const table,
199*890232f2SAndroid Build Coastguard Worker const uint64_t offset_of_referring_table) {
200*890232f2SAndroid Build Coastguard Worker // First see if we have used this vtable before, if so skip building it again.
201*890232f2SAndroid Build Coastguard Worker auto it = vtables_.find(vtable_offset);
202*890232f2SAndroid Build Coastguard Worker if (it != vtables_.end()) { return; }
203*890232f2SAndroid Build Coastguard Worker
204*890232f2SAndroid Build Coastguard Worker if (ContainsSection(vtable_offset)) { return; }
205*890232f2SAndroid Build Coastguard Worker
206*890232f2SAndroid Build Coastguard Worker BinaryRegionComment vtable_size_comment;
207*890232f2SAndroid Build Coastguard Worker vtable_size_comment.type = BinaryRegionCommentType::VTableSize;
208*890232f2SAndroid Build Coastguard Worker
209*890232f2SAndroid Build Coastguard Worker const auto vtable_length = ReadScalar<uint16_t>(vtable_offset);
210*890232f2SAndroid Build Coastguard Worker if (!vtable_length.has_value()) {
211*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(vtable_offset);
212*890232f2SAndroid Build Coastguard Worker
213*890232f2SAndroid Build Coastguard Worker SetError(vtable_size_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
214*890232f2SAndroid Build Coastguard Worker "2");
215*890232f2SAndroid Build Coastguard Worker
216*890232f2SAndroid Build Coastguard Worker AddSection(vtable_offset,
217*890232f2SAndroid Build Coastguard Worker MakeSingleRegionBinarySection(
218*890232f2SAndroid Build Coastguard Worker table->name()->str(), BinarySectionType::VTable,
219*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(vtable_offset, remaining,
220*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining, 0,
221*890232f2SAndroid Build Coastguard Worker vtable_size_comment)));
222*890232f2SAndroid Build Coastguard Worker return;
223*890232f2SAndroid Build Coastguard Worker }
224*890232f2SAndroid Build Coastguard Worker
225*890232f2SAndroid Build Coastguard Worker // Vtables start with the size of the vtable
226*890232f2SAndroid Build Coastguard Worker const uint16_t vtable_size = vtable_length.value();
227*890232f2SAndroid Build Coastguard Worker
228*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(vtable_offset + vtable_size - 1)) {
229*890232f2SAndroid Build Coastguard Worker SetError(vtable_size_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
230*890232f2SAndroid Build Coastguard Worker // The vtable_size points to off the end of the binary.
231*890232f2SAndroid Build Coastguard Worker AddSection(vtable_offset,
232*890232f2SAndroid Build Coastguard Worker MakeSingleRegionBinarySection(
233*890232f2SAndroid Build Coastguard Worker table->name()->str(), BinarySectionType::VTable,
234*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
235*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint16, 0, 0,
236*890232f2SAndroid Build Coastguard Worker vtable_size_comment)));
237*890232f2SAndroid Build Coastguard Worker
238*890232f2SAndroid Build Coastguard Worker return;
239*890232f2SAndroid Build Coastguard Worker } else if (vtable_size < 2 * sizeof(uint16_t)) {
240*890232f2SAndroid Build Coastguard Worker SetError(vtable_size_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_SHORT,
241*890232f2SAndroid Build Coastguard Worker "4");
242*890232f2SAndroid Build Coastguard Worker // The size includes itself and the table size which are both uint16_t.
243*890232f2SAndroid Build Coastguard Worker AddSection(vtable_offset,
244*890232f2SAndroid Build Coastguard Worker MakeSingleRegionBinarySection(
245*890232f2SAndroid Build Coastguard Worker table->name()->str(), BinarySectionType::VTable,
246*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
247*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint16, 0, 0,
248*890232f2SAndroid Build Coastguard Worker vtable_size_comment)));
249*890232f2SAndroid Build Coastguard Worker return;
250*890232f2SAndroid Build Coastguard Worker }
251*890232f2SAndroid Build Coastguard Worker
252*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions;
253*890232f2SAndroid Build Coastguard Worker
254*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(vtable_offset, sizeof(uint16_t),
255*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint16, 0, 0,
256*890232f2SAndroid Build Coastguard Worker vtable_size_comment));
257*890232f2SAndroid Build Coastguard Worker uint64_t offset = vtable_offset + sizeof(uint16_t);
258*890232f2SAndroid Build Coastguard Worker
259*890232f2SAndroid Build Coastguard Worker BinaryRegionComment ref_table_len_comment;
260*890232f2SAndroid Build Coastguard Worker ref_table_len_comment.type =
261*890232f2SAndroid Build Coastguard Worker BinaryRegionCommentType::VTableRefferingTableLength;
262*890232f2SAndroid Build Coastguard Worker
263*890232f2SAndroid Build Coastguard Worker // Ensure we can read the next uint16_t field, which is the size of the
264*890232f2SAndroid Build Coastguard Worker // referring table.
265*890232f2SAndroid Build Coastguard Worker const auto table_length = ReadScalar<uint16_t>(offset);
266*890232f2SAndroid Build Coastguard Worker
267*890232f2SAndroid Build Coastguard Worker if (!table_length.has_value()) {
268*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(offset);
269*890232f2SAndroid Build Coastguard Worker SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
270*890232f2SAndroid Build Coastguard Worker "2");
271*890232f2SAndroid Build Coastguard Worker
272*890232f2SAndroid Build Coastguard Worker AddSection(offset, MakeSingleRegionBinarySection(
273*890232f2SAndroid Build Coastguard Worker table->name()->str(), BinarySectionType::VTable,
274*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(
275*890232f2SAndroid Build Coastguard Worker offset, remaining, BinaryRegionType::Unknown,
276*890232f2SAndroid Build Coastguard Worker remaining, 0, ref_table_len_comment)));
277*890232f2SAndroid Build Coastguard Worker return;
278*890232f2SAndroid Build Coastguard Worker }
279*890232f2SAndroid Build Coastguard Worker
280*890232f2SAndroid Build Coastguard Worker // Then they have the size of the table they reference.
281*890232f2SAndroid Build Coastguard Worker const uint16_t table_size = table_length.value();
282*890232f2SAndroid Build Coastguard Worker
283*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(offset_of_referring_table + table_size - 1)) {
284*890232f2SAndroid Build Coastguard Worker SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
285*890232f2SAndroid Build Coastguard Worker } else if (table_size < 4) {
286*890232f2SAndroid Build Coastguard Worker SetError(ref_table_len_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_SHORT,
287*890232f2SAndroid Build Coastguard Worker "4");
288*890232f2SAndroid Build Coastguard Worker }
289*890232f2SAndroid Build Coastguard Worker
290*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, sizeof(uint16_t),
291*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint16, 0, 0,
292*890232f2SAndroid Build Coastguard Worker ref_table_len_comment));
293*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint16_t);
294*890232f2SAndroid Build Coastguard Worker
295*890232f2SAndroid Build Coastguard Worker const uint64_t offset_start = offset;
296*890232f2SAndroid Build Coastguard Worker
297*890232f2SAndroid Build Coastguard Worker // A mapping between field (and its id) to the relative offset (uin16_t) from
298*890232f2SAndroid Build Coastguard Worker // the start of the table.
299*890232f2SAndroid Build Coastguard Worker std::map<uint16_t, VTable::Entry> fields;
300*890232f2SAndroid Build Coastguard Worker
301*890232f2SAndroid Build Coastguard Worker // Counter for determining if the binary has more vtable entries than the
302*890232f2SAndroid Build Coastguard Worker // schema provided. This can occur if the binary was created at a newer schema
303*890232f2SAndroid Build Coastguard Worker // version and is being processed with an older one.
304*890232f2SAndroid Build Coastguard Worker uint16_t fields_processed = 0;
305*890232f2SAndroid Build Coastguard Worker
306*890232f2SAndroid Build Coastguard Worker // Loop over all the fields.
307*890232f2SAndroid Build Coastguard Worker ForAllFields(table, /*reverse=*/false, [&](const reflection::Field *field) {
308*890232f2SAndroid Build Coastguard Worker const uint64_t field_offset = offset_start + field->id() * sizeof(uint16_t);
309*890232f2SAndroid Build Coastguard Worker
310*890232f2SAndroid Build Coastguard Worker if (field_offset >= vtable_offset + vtable_size) {
311*890232f2SAndroid Build Coastguard Worker // This field_offset is too large for this vtable, so it must come from a
312*890232f2SAndroid Build Coastguard Worker // newer schema than the binary was create with or the binary writer did
313*890232f2SAndroid Build Coastguard Worker // not write it. For either case, it is safe to ignore.
314*890232f2SAndroid Build Coastguard Worker
315*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): We could show which fields are not set an their
316*890232f2SAndroid Build Coastguard Worker // default values if we want. We just need a way to make it obvious that
317*890232f2SAndroid Build Coastguard Worker // it isn't part of the buffer.
318*890232f2SAndroid Build Coastguard Worker return;
319*890232f2SAndroid Build Coastguard Worker }
320*890232f2SAndroid Build Coastguard Worker
321*890232f2SAndroid Build Coastguard Worker BinaryRegionComment field_comment;
322*890232f2SAndroid Build Coastguard Worker field_comment.type = BinaryRegionCommentType::VTableFieldOffset;
323*890232f2SAndroid Build Coastguard Worker field_comment.name = std::string(field->name()->c_str()) +
324*890232f2SAndroid Build Coastguard Worker "` (id: " + std::to_string(field->id()) + ")";
325*890232f2SAndroid Build Coastguard Worker
326*890232f2SAndroid Build Coastguard Worker const auto offset_from_table = ReadScalar<uint16_t>(field_offset);
327*890232f2SAndroid Build Coastguard Worker
328*890232f2SAndroid Build Coastguard Worker if (!offset_from_table.has_value()) {
329*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(field_offset);
330*890232f2SAndroid Build Coastguard Worker
331*890232f2SAndroid Build Coastguard Worker SetError(field_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
332*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, remaining,
333*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining,
334*890232f2SAndroid Build Coastguard Worker 0, field_comment));
335*890232f2SAndroid Build Coastguard Worker
336*890232f2SAndroid Build Coastguard Worker return;
337*890232f2SAndroid Build Coastguard Worker }
338*890232f2SAndroid Build Coastguard Worker
339*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(offset_of_referring_table + offset_from_table.value() -
340*890232f2SAndroid Build Coastguard Worker 1)) {
341*890232f2SAndroid Build Coastguard Worker SetError(field_comment, BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
342*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
343*890232f2SAndroid Build Coastguard Worker BinaryRegionType::VOffset, 0, 0,
344*890232f2SAndroid Build Coastguard Worker field_comment));
345*890232f2SAndroid Build Coastguard Worker return;
346*890232f2SAndroid Build Coastguard Worker }
347*890232f2SAndroid Build Coastguard Worker
348*890232f2SAndroid Build Coastguard Worker VTable::Entry entry;
349*890232f2SAndroid Build Coastguard Worker entry.field = field;
350*890232f2SAndroid Build Coastguard Worker entry.offset_from_table = offset_from_table.value();
351*890232f2SAndroid Build Coastguard Worker fields.insert(std::make_pair(field->id(), entry));
352*890232f2SAndroid Build Coastguard Worker
353*890232f2SAndroid Build Coastguard Worker std::string default_label;
354*890232f2SAndroid Build Coastguard Worker if (offset_from_table.value() == 0) {
355*890232f2SAndroid Build Coastguard Worker // Not present, so could be default or be optional.
356*890232f2SAndroid Build Coastguard Worker if (field->required()) {
357*890232f2SAndroid Build Coastguard Worker SetError(field_comment,
358*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_REQUIRED_FIELD_NOT_PRESENT);
359*890232f2SAndroid Build Coastguard Worker // If this is a required field, make it known this is an error.
360*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
361*890232f2SAndroid Build Coastguard Worker BinaryRegionType::VOffset, 0, 0,
362*890232f2SAndroid Build Coastguard Worker field_comment));
363*890232f2SAndroid Build Coastguard Worker return;
364*890232f2SAndroid Build Coastguard Worker } else {
365*890232f2SAndroid Build Coastguard Worker // Its an optional field, so get the default value and interpret and
366*890232f2SAndroid Build Coastguard Worker // provided an annotation for it.
367*890232f2SAndroid Build Coastguard Worker if (IsScalar(field->type()->base_type())) {
368*890232f2SAndroid Build Coastguard Worker default_label += "<defaults to ";
369*890232f2SAndroid Build Coastguard Worker default_label += IsFloat(field->type()->base_type())
370*890232f2SAndroid Build Coastguard Worker ? std::to_string(field->default_real())
371*890232f2SAndroid Build Coastguard Worker : std::to_string(field->default_integer());
372*890232f2SAndroid Build Coastguard Worker default_label += "> (";
373*890232f2SAndroid Build Coastguard Worker } else {
374*890232f2SAndroid Build Coastguard Worker default_label += "<null> (";
375*890232f2SAndroid Build Coastguard Worker }
376*890232f2SAndroid Build Coastguard Worker default_label +=
377*890232f2SAndroid Build Coastguard Worker reflection::EnumNameBaseType(field->type()->base_type());
378*890232f2SAndroid Build Coastguard Worker default_label += ")";
379*890232f2SAndroid Build Coastguard Worker }
380*890232f2SAndroid Build Coastguard Worker }
381*890232f2SAndroid Build Coastguard Worker field_comment.default_value = default_label;
382*890232f2SAndroid Build Coastguard Worker
383*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
384*890232f2SAndroid Build Coastguard Worker BinaryRegionType::VOffset, 0, 0,
385*890232f2SAndroid Build Coastguard Worker field_comment));
386*890232f2SAndroid Build Coastguard Worker
387*890232f2SAndroid Build Coastguard Worker fields_processed++;
388*890232f2SAndroid Build Coastguard Worker });
389*890232f2SAndroid Build Coastguard Worker
390*890232f2SAndroid Build Coastguard Worker // Check if we covered all the expectant fields. If not, we need to add them
391*890232f2SAndroid Build Coastguard Worker // as unknown fields.
392*890232f2SAndroid Build Coastguard Worker uint16_t expectant_vtable_fields =
393*890232f2SAndroid Build Coastguard Worker (vtable_size - sizeof(uint16_t) - sizeof(uint16_t)) / sizeof(uint16_t);
394*890232f2SAndroid Build Coastguard Worker
395*890232f2SAndroid Build Coastguard Worker // Prevent a bad binary from declaring a really large vtable_size, that we can
396*890232f2SAndroid Build Coastguard Worker // not indpendently verify.
397*890232f2SAndroid Build Coastguard Worker expectant_vtable_fields = std::min(
398*890232f2SAndroid Build Coastguard Worker static_cast<uint16_t>(fields_processed * 3), expectant_vtable_fields);
399*890232f2SAndroid Build Coastguard Worker
400*890232f2SAndroid Build Coastguard Worker for (uint16_t id = fields_processed; id < expectant_vtable_fields; ++id) {
401*890232f2SAndroid Build Coastguard Worker const uint64_t field_offset = offset_start + id * sizeof(uint16_t);
402*890232f2SAndroid Build Coastguard Worker
403*890232f2SAndroid Build Coastguard Worker const auto offset_from_table = ReadScalar<uint16_t>(field_offset);
404*890232f2SAndroid Build Coastguard Worker
405*890232f2SAndroid Build Coastguard Worker BinaryRegionComment field_comment;
406*890232f2SAndroid Build Coastguard Worker field_comment.type = BinaryRegionCommentType::VTableUnknownFieldOffset;
407*890232f2SAndroid Build Coastguard Worker field_comment.index = id;
408*890232f2SAndroid Build Coastguard Worker
409*890232f2SAndroid Build Coastguard Worker if (!offset_from_table.has_value()) {
410*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(field_offset);
411*890232f2SAndroid Build Coastguard Worker SetError(field_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
412*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, remaining,
413*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining,
414*890232f2SAndroid Build Coastguard Worker 0, field_comment));
415*890232f2SAndroid Build Coastguard Worker continue;
416*890232f2SAndroid Build Coastguard Worker }
417*890232f2SAndroid Build Coastguard Worker
418*890232f2SAndroid Build Coastguard Worker VTable::Entry entry;
419*890232f2SAndroid Build Coastguard Worker entry.field = nullptr; // No field to reference.
420*890232f2SAndroid Build Coastguard Worker entry.offset_from_table = offset_from_table.value();
421*890232f2SAndroid Build Coastguard Worker fields.insert(std::make_pair(id, entry));
422*890232f2SAndroid Build Coastguard Worker
423*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint16_t),
424*890232f2SAndroid Build Coastguard Worker BinaryRegionType::VOffset, 0, 0,
425*890232f2SAndroid Build Coastguard Worker field_comment));
426*890232f2SAndroid Build Coastguard Worker }
427*890232f2SAndroid Build Coastguard Worker
428*890232f2SAndroid Build Coastguard Worker sections_[vtable_offset] = MakeBinarySection(
429*890232f2SAndroid Build Coastguard Worker table->name()->str(), BinarySectionType::VTable, std::move(regions));
430*890232f2SAndroid Build Coastguard Worker
431*890232f2SAndroid Build Coastguard Worker VTable vtable;
432*890232f2SAndroid Build Coastguard Worker vtable.fields = std::move(fields);
433*890232f2SAndroid Build Coastguard Worker vtable.table_size = table_size;
434*890232f2SAndroid Build Coastguard Worker vtable.vtable_size = vtable_size;
435*890232f2SAndroid Build Coastguard Worker
436*890232f2SAndroid Build Coastguard Worker vtables_[vtable_offset] = vtable;
437*890232f2SAndroid Build Coastguard Worker }
438*890232f2SAndroid Build Coastguard Worker
BuildTable(const uint64_t table_offset,const BinarySectionType type,const reflection::Object * const table)439*890232f2SAndroid Build Coastguard Worker void BinaryAnnotator::BuildTable(const uint64_t table_offset,
440*890232f2SAndroid Build Coastguard Worker const BinarySectionType type,
441*890232f2SAndroid Build Coastguard Worker const reflection::Object *const table) {
442*890232f2SAndroid Build Coastguard Worker if (ContainsSection(table_offset)) { return; }
443*890232f2SAndroid Build Coastguard Worker
444*890232f2SAndroid Build Coastguard Worker BinaryRegionComment vtable_offset_comment;
445*890232f2SAndroid Build Coastguard Worker vtable_offset_comment.type = BinaryRegionCommentType::TableVTableOffset;
446*890232f2SAndroid Build Coastguard Worker
447*890232f2SAndroid Build Coastguard Worker const auto vtable_soffset = ReadScalar<int32_t>(table_offset);
448*890232f2SAndroid Build Coastguard Worker
449*890232f2SAndroid Build Coastguard Worker if (!vtable_soffset.has_value()) {
450*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(table_offset);
451*890232f2SAndroid Build Coastguard Worker SetError(vtable_offset_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
452*890232f2SAndroid Build Coastguard Worker "4");
453*890232f2SAndroid Build Coastguard Worker
454*890232f2SAndroid Build Coastguard Worker AddSection(
455*890232f2SAndroid Build Coastguard Worker table_offset,
456*890232f2SAndroid Build Coastguard Worker MakeSingleRegionBinarySection(
457*890232f2SAndroid Build Coastguard Worker table->name()->str(), type,
458*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(table_offset, remaining, BinaryRegionType::Unknown,
459*890232f2SAndroid Build Coastguard Worker remaining, 0, vtable_offset_comment)));
460*890232f2SAndroid Build Coastguard Worker
461*890232f2SAndroid Build Coastguard Worker // If there aren't enough bytes left to read the vtable offset, there is
462*890232f2SAndroid Build Coastguard Worker // nothing we can do.
463*890232f2SAndroid Build Coastguard Worker return;
464*890232f2SAndroid Build Coastguard Worker }
465*890232f2SAndroid Build Coastguard Worker
466*890232f2SAndroid Build Coastguard Worker // Tables start with the vtable
467*890232f2SAndroid Build Coastguard Worker const uint64_t vtable_offset = table_offset - vtable_soffset.value();
468*890232f2SAndroid Build Coastguard Worker
469*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(vtable_offset)) {
470*890232f2SAndroid Build Coastguard Worker SetError(vtable_offset_comment,
471*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
472*890232f2SAndroid Build Coastguard Worker
473*890232f2SAndroid Build Coastguard Worker AddSection(table_offset,
474*890232f2SAndroid Build Coastguard Worker MakeSingleRegionBinarySection(
475*890232f2SAndroid Build Coastguard Worker table->name()->str(), type,
476*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(table_offset, sizeof(int32_t),
477*890232f2SAndroid Build Coastguard Worker BinaryRegionType::SOffset, 0, vtable_offset,
478*890232f2SAndroid Build Coastguard Worker vtable_offset_comment)));
479*890232f2SAndroid Build Coastguard Worker
480*890232f2SAndroid Build Coastguard Worker // There isn't much to do with an invalid vtable offset, as we won't be able
481*890232f2SAndroid Build Coastguard Worker // to intepret the rest of the table fields.
482*890232f2SAndroid Build Coastguard Worker return;
483*890232f2SAndroid Build Coastguard Worker }
484*890232f2SAndroid Build Coastguard Worker
485*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions;
486*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(table_offset, sizeof(int32_t),
487*890232f2SAndroid Build Coastguard Worker BinaryRegionType::SOffset, 0,
488*890232f2SAndroid Build Coastguard Worker vtable_offset, vtable_offset_comment));
489*890232f2SAndroid Build Coastguard Worker
490*890232f2SAndroid Build Coastguard Worker // Parse the vtable first so we know what the rest of the fields in the table
491*890232f2SAndroid Build Coastguard Worker // are.
492*890232f2SAndroid Build Coastguard Worker BuildVTable(vtable_offset, table, table_offset);
493*890232f2SAndroid Build Coastguard Worker
494*890232f2SAndroid Build Coastguard Worker auto vtable_entry = vtables_.find(vtable_offset);
495*890232f2SAndroid Build Coastguard Worker if (vtable_entry == vtables_.end()) {
496*890232f2SAndroid Build Coastguard Worker // There is no valid vtable for this table, so we cannot process the rest of
497*890232f2SAndroid Build Coastguard Worker // the table entries.
498*890232f2SAndroid Build Coastguard Worker return;
499*890232f2SAndroid Build Coastguard Worker }
500*890232f2SAndroid Build Coastguard Worker
501*890232f2SAndroid Build Coastguard Worker const VTable &vtable = vtable_entry->second;
502*890232f2SAndroid Build Coastguard Worker
503*890232f2SAndroid Build Coastguard Worker // This is the size and length of this table.
504*890232f2SAndroid Build Coastguard Worker const uint16_t table_size = vtable.table_size;
505*890232f2SAndroid Build Coastguard Worker uint64_t table_end_offset = table_offset + table_size;
506*890232f2SAndroid Build Coastguard Worker
507*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(table_end_offset - 1)) {
508*890232f2SAndroid Build Coastguard Worker // We already validated the table size in BuildVTable, but we have to make
509*890232f2SAndroid Build Coastguard Worker // sure we don't use a bad value here.
510*890232f2SAndroid Build Coastguard Worker table_end_offset = binary_length_;
511*890232f2SAndroid Build Coastguard Worker }
512*890232f2SAndroid Build Coastguard Worker
513*890232f2SAndroid Build Coastguard Worker // We need to iterate over the vtable fields by their offset in the binary,
514*890232f2SAndroid Build Coastguard Worker // not by their IDs. So copy them over to another vector that we can sort on
515*890232f2SAndroid Build Coastguard Worker // the offset_from_table property.
516*890232f2SAndroid Build Coastguard Worker std::vector<VTable::Entry> fields;
517*890232f2SAndroid Build Coastguard Worker for (const auto &vtable_field : vtable.fields) {
518*890232f2SAndroid Build Coastguard Worker fields.push_back(vtable_field.second);
519*890232f2SAndroid Build Coastguard Worker }
520*890232f2SAndroid Build Coastguard Worker
521*890232f2SAndroid Build Coastguard Worker std::stable_sort(fields.begin(), fields.end(),
522*890232f2SAndroid Build Coastguard Worker [](const VTable::Entry &a, const VTable::Entry &b) {
523*890232f2SAndroid Build Coastguard Worker return a.offset_from_table < b.offset_from_table;
524*890232f2SAndroid Build Coastguard Worker });
525*890232f2SAndroid Build Coastguard Worker
526*890232f2SAndroid Build Coastguard Worker // Iterate over all the fields by order of their offset.
527*890232f2SAndroid Build Coastguard Worker for (size_t i = 0; i < fields.size(); ++i) {
528*890232f2SAndroid Build Coastguard Worker const reflection::Field *field = fields[i].field;
529*890232f2SAndroid Build Coastguard Worker const uint16_t offset_from_table = fields[i].offset_from_table;
530*890232f2SAndroid Build Coastguard Worker
531*890232f2SAndroid Build Coastguard Worker if (offset_from_table == 0) {
532*890232f2SAndroid Build Coastguard Worker // Skip non-present fields.
533*890232f2SAndroid Build Coastguard Worker continue;
534*890232f2SAndroid Build Coastguard Worker }
535*890232f2SAndroid Build Coastguard Worker
536*890232f2SAndroid Build Coastguard Worker // The field offsets are relative to the start of the table.
537*890232f2SAndroid Build Coastguard Worker const uint64_t field_offset = table_offset + offset_from_table;
538*890232f2SAndroid Build Coastguard Worker
539*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(field_offset)) {
540*890232f2SAndroid Build Coastguard Worker // The field offset is larger than the binary, nothing we can do.
541*890232f2SAndroid Build Coastguard Worker continue;
542*890232f2SAndroid Build Coastguard Worker }
543*890232f2SAndroid Build Coastguard Worker
544*890232f2SAndroid Build Coastguard Worker // We have a vtable entry for a non-existant field, that means its a binary
545*890232f2SAndroid Build Coastguard Worker // generated by a newer schema than we are currently processing.
546*890232f2SAndroid Build Coastguard Worker if (field == nullptr) {
547*890232f2SAndroid Build Coastguard Worker // Calculate the length of this unknown field.
548*890232f2SAndroid Build Coastguard Worker const uint64_t unknown_field_length =
549*890232f2SAndroid Build Coastguard Worker // Check if there is another unknown field after this one.
550*890232f2SAndroid Build Coastguard Worker ((i + 1 < fields.size())
551*890232f2SAndroid Build Coastguard Worker ? table_offset + fields[i + 1].offset_from_table
552*890232f2SAndroid Build Coastguard Worker // Otherwise use the known end of the table.
553*890232f2SAndroid Build Coastguard Worker : table_end_offset) -
554*890232f2SAndroid Build Coastguard Worker field_offset;
555*890232f2SAndroid Build Coastguard Worker
556*890232f2SAndroid Build Coastguard Worker if (unknown_field_length == 0) { continue; }
557*890232f2SAndroid Build Coastguard Worker
558*890232f2SAndroid Build Coastguard Worker std::string hint;
559*890232f2SAndroid Build Coastguard Worker
560*890232f2SAndroid Build Coastguard Worker if (unknown_field_length == 4) {
561*890232f2SAndroid Build Coastguard Worker const auto relative_offset = ReadScalar<uint32_t>(field_offset);
562*890232f2SAndroid Build Coastguard Worker if (relative_offset.has_value()) {
563*890232f2SAndroid Build Coastguard Worker // The field is 4 in length, so it could be an offset? Provide a hint.
564*890232f2SAndroid Build Coastguard Worker hint += "<possibly an offset? Check Loc: +0x";
565*890232f2SAndroid Build Coastguard Worker hint += ToHex(field_offset + relative_offset.value());
566*890232f2SAndroid Build Coastguard Worker hint += ">";
567*890232f2SAndroid Build Coastguard Worker }
568*890232f2SAndroid Build Coastguard Worker }
569*890232f2SAndroid Build Coastguard Worker
570*890232f2SAndroid Build Coastguard Worker BinaryRegionComment unknown_field_comment;
571*890232f2SAndroid Build Coastguard Worker unknown_field_comment.type = BinaryRegionCommentType::TableUnknownField;
572*890232f2SAndroid Build Coastguard Worker
573*890232f2SAndroid Build Coastguard Worker if (!IsValidRead(field_offset, unknown_field_length)) {
574*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(field_offset);
575*890232f2SAndroid Build Coastguard Worker
576*890232f2SAndroid Build Coastguard Worker SetError(unknown_field_comment,
577*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
578*890232f2SAndroid Build Coastguard Worker std::to_string(unknown_field_length));
579*890232f2SAndroid Build Coastguard Worker
580*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, remaining,
581*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining,
582*890232f2SAndroid Build Coastguard Worker 0, unknown_field_comment));
583*890232f2SAndroid Build Coastguard Worker continue;
584*890232f2SAndroid Build Coastguard Worker }
585*890232f2SAndroid Build Coastguard Worker
586*890232f2SAndroid Build Coastguard Worker unknown_field_comment.default_value = hint;
587*890232f2SAndroid Build Coastguard Worker
588*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
589*890232f2SAndroid Build Coastguard Worker field_offset, unknown_field_length, BinaryRegionType::Unknown,
590*890232f2SAndroid Build Coastguard Worker unknown_field_length, 0, unknown_field_comment));
591*890232f2SAndroid Build Coastguard Worker continue;
592*890232f2SAndroid Build Coastguard Worker }
593*890232f2SAndroid Build Coastguard Worker
594*890232f2SAndroid Build Coastguard Worker if (IsScalar(field->type()->base_type())) {
595*890232f2SAndroid Build Coastguard Worker // These are the raw values store in the table.
596*890232f2SAndroid Build Coastguard Worker const uint64_t type_size = GetTypeSize(field->type()->base_type());
597*890232f2SAndroid Build Coastguard Worker const BinaryRegionType region_type =
598*890232f2SAndroid Build Coastguard Worker GetRegionType(field->type()->base_type());
599*890232f2SAndroid Build Coastguard Worker
600*890232f2SAndroid Build Coastguard Worker BinaryRegionComment scalar_field_comment;
601*890232f2SAndroid Build Coastguard Worker scalar_field_comment.type = BinaryRegionCommentType::TableField;
602*890232f2SAndroid Build Coastguard Worker scalar_field_comment.name =
603*890232f2SAndroid Build Coastguard Worker std::string(field->name()->c_str()) + "` (" +
604*890232f2SAndroid Build Coastguard Worker reflection::EnumNameBaseType(field->type()->base_type()) + ")";
605*890232f2SAndroid Build Coastguard Worker
606*890232f2SAndroid Build Coastguard Worker if (!IsValidRead(field_offset, type_size)) {
607*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(field_offset);
608*890232f2SAndroid Build Coastguard Worker SetError(scalar_field_comment,
609*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
610*890232f2SAndroid Build Coastguard Worker std::to_string(type_size));
611*890232f2SAndroid Build Coastguard Worker
612*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, remaining,
613*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining,
614*890232f2SAndroid Build Coastguard Worker 0, scalar_field_comment));
615*890232f2SAndroid Build Coastguard Worker continue;
616*890232f2SAndroid Build Coastguard Worker }
617*890232f2SAndroid Build Coastguard Worker
618*890232f2SAndroid Build Coastguard Worker if (IsUnionType(field)) {
619*890232f2SAndroid Build Coastguard Worker // This is a type for a union. Validate the value
620*890232f2SAndroid Build Coastguard Worker const auto enum_value = ReadScalar<uint8_t>(field_offset);
621*890232f2SAndroid Build Coastguard Worker
622*890232f2SAndroid Build Coastguard Worker // This should always have a value, due to the IsValidRead check above.
623*890232f2SAndroid Build Coastguard Worker if (!IsValidUnionValue(field, enum_value.value())) {
624*890232f2SAndroid Build Coastguard Worker SetError(scalar_field_comment,
625*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INVALID_UNION_TYPE);
626*890232f2SAndroid Build Coastguard Worker
627*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, type_size,
628*890232f2SAndroid Build Coastguard Worker region_type, 0, 0,
629*890232f2SAndroid Build Coastguard Worker scalar_field_comment));
630*890232f2SAndroid Build Coastguard Worker continue;
631*890232f2SAndroid Build Coastguard Worker }
632*890232f2SAndroid Build Coastguard Worker }
633*890232f2SAndroid Build Coastguard Worker
634*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, type_size, region_type,
635*890232f2SAndroid Build Coastguard Worker 0, 0, scalar_field_comment));
636*890232f2SAndroid Build Coastguard Worker continue;
637*890232f2SAndroid Build Coastguard Worker }
638*890232f2SAndroid Build Coastguard Worker
639*890232f2SAndroid Build Coastguard Worker // Read the offset
640*890232f2SAndroid Build Coastguard Worker const auto offset_from_field = ReadScalar<uint32_t>(field_offset);
641*890232f2SAndroid Build Coastguard Worker uint64_t offset_of_next_item = 0;
642*890232f2SAndroid Build Coastguard Worker BinaryRegionComment offset_field_comment;
643*890232f2SAndroid Build Coastguard Worker offset_field_comment.type = BinaryRegionCommentType::TableOffsetField;
644*890232f2SAndroid Build Coastguard Worker offset_field_comment.name = field->name()->c_str();
645*890232f2SAndroid Build Coastguard Worker const std::string offset_prefix =
646*890232f2SAndroid Build Coastguard Worker "offset to field `" + std::string(field->name()->c_str()) + "`";
647*890232f2SAndroid Build Coastguard Worker
648*890232f2SAndroid Build Coastguard Worker // Validate any field that isn't inline (i.e., non-structs).
649*890232f2SAndroid Build Coastguard Worker if (!IsInlineField(field)) {
650*890232f2SAndroid Build Coastguard Worker if (!offset_from_field.has_value()) {
651*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(field_offset);
652*890232f2SAndroid Build Coastguard Worker
653*890232f2SAndroid Build Coastguard Worker SetError(offset_field_comment,
654*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
655*890232f2SAndroid Build Coastguard Worker
656*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, remaining,
657*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining,
658*890232f2SAndroid Build Coastguard Worker 0, offset_field_comment));
659*890232f2SAndroid Build Coastguard Worker continue;
660*890232f2SAndroid Build Coastguard Worker }
661*890232f2SAndroid Build Coastguard Worker
662*890232f2SAndroid Build Coastguard Worker offset_of_next_item = field_offset + offset_from_field.value();
663*890232f2SAndroid Build Coastguard Worker
664*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(offset_of_next_item)) {
665*890232f2SAndroid Build Coastguard Worker SetError(offset_field_comment,
666*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
667*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
668*890232f2SAndroid Build Coastguard Worker field_offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
669*890232f2SAndroid Build Coastguard Worker offset_of_next_item, offset_field_comment));
670*890232f2SAndroid Build Coastguard Worker continue;
671*890232f2SAndroid Build Coastguard Worker }
672*890232f2SAndroid Build Coastguard Worker }
673*890232f2SAndroid Build Coastguard Worker
674*890232f2SAndroid Build Coastguard Worker switch (field->type()->base_type()) {
675*890232f2SAndroid Build Coastguard Worker case reflection::BaseType::Obj: {
676*890232f2SAndroid Build Coastguard Worker const reflection::Object *next_object =
677*890232f2SAndroid Build Coastguard Worker schema_->objects()->Get(field->type()->index());
678*890232f2SAndroid Build Coastguard Worker
679*890232f2SAndroid Build Coastguard Worker if (next_object->is_struct()) {
680*890232f2SAndroid Build Coastguard Worker // Structs are stored inline.
681*890232f2SAndroid Build Coastguard Worker BuildStruct(field_offset, regions, next_object);
682*890232f2SAndroid Build Coastguard Worker } else {
683*890232f2SAndroid Build Coastguard Worker offset_field_comment.default_value = "(table)";
684*890232f2SAndroid Build Coastguard Worker
685*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
686*890232f2SAndroid Build Coastguard Worker field_offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
687*890232f2SAndroid Build Coastguard Worker offset_of_next_item, offset_field_comment));
688*890232f2SAndroid Build Coastguard Worker
689*890232f2SAndroid Build Coastguard Worker BuildTable(offset_of_next_item, BinarySectionType::Table,
690*890232f2SAndroid Build Coastguard Worker next_object);
691*890232f2SAndroid Build Coastguard Worker }
692*890232f2SAndroid Build Coastguard Worker } break;
693*890232f2SAndroid Build Coastguard Worker
694*890232f2SAndroid Build Coastguard Worker case reflection::BaseType::String: {
695*890232f2SAndroid Build Coastguard Worker offset_field_comment.default_value = "(string)";
696*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
697*890232f2SAndroid Build Coastguard Worker field_offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
698*890232f2SAndroid Build Coastguard Worker offset_of_next_item, offset_field_comment));
699*890232f2SAndroid Build Coastguard Worker BuildString(offset_of_next_item, table, field);
700*890232f2SAndroid Build Coastguard Worker } break;
701*890232f2SAndroid Build Coastguard Worker
702*890232f2SAndroid Build Coastguard Worker case reflection::BaseType::Vector: {
703*890232f2SAndroid Build Coastguard Worker offset_field_comment.default_value = "(vector)";
704*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
705*890232f2SAndroid Build Coastguard Worker field_offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
706*890232f2SAndroid Build Coastguard Worker offset_of_next_item, offset_field_comment));
707*890232f2SAndroid Build Coastguard Worker BuildVector(offset_of_next_item, table, field, table_offset, vtable);
708*890232f2SAndroid Build Coastguard Worker } break;
709*890232f2SAndroid Build Coastguard Worker
710*890232f2SAndroid Build Coastguard Worker case reflection::BaseType::Union: {
711*890232f2SAndroid Build Coastguard Worker const uint64_t union_offset = offset_of_next_item;
712*890232f2SAndroid Build Coastguard Worker
713*890232f2SAndroid Build Coastguard Worker // The union type field is always one less than the union itself.
714*890232f2SAndroid Build Coastguard Worker const uint16_t union_type_id = field->id() - 1;
715*890232f2SAndroid Build Coastguard Worker
716*890232f2SAndroid Build Coastguard Worker auto vtable_field = vtable.fields.find(union_type_id);
717*890232f2SAndroid Build Coastguard Worker if (vtable_field == vtable.fields.end()) {
718*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): need to capture this error condition.
719*890232f2SAndroid Build Coastguard Worker break;
720*890232f2SAndroid Build Coastguard Worker }
721*890232f2SAndroid Build Coastguard Worker offset_field_comment.default_value = "(union)";
722*890232f2SAndroid Build Coastguard Worker
723*890232f2SAndroid Build Coastguard Worker const uint64_t type_offset =
724*890232f2SAndroid Build Coastguard Worker table_offset + vtable_field->second.offset_from_table;
725*890232f2SAndroid Build Coastguard Worker
726*890232f2SAndroid Build Coastguard Worker const auto realized_type = ReadScalar<uint8_t>(type_offset);
727*890232f2SAndroid Build Coastguard Worker if (!realized_type.has_value()) {
728*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(type_offset);
729*890232f2SAndroid Build Coastguard Worker SetError(offset_field_comment,
730*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "1");
731*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
732*890232f2SAndroid Build Coastguard Worker type_offset, remaining, BinaryRegionType::Unknown, remaining, 0,
733*890232f2SAndroid Build Coastguard Worker offset_field_comment));
734*890232f2SAndroid Build Coastguard Worker continue;
735*890232f2SAndroid Build Coastguard Worker }
736*890232f2SAndroid Build Coastguard Worker
737*890232f2SAndroid Build Coastguard Worker if (!IsValidUnionValue(field, realized_type.value())) {
738*890232f2SAndroid Build Coastguard Worker // We already export an error in the union type field, so just skip
739*890232f2SAndroid Build Coastguard Worker // building the union itself and it will default to an unreference
740*890232f2SAndroid Build Coastguard Worker // Binary section.
741*890232f2SAndroid Build Coastguard Worker continue;
742*890232f2SAndroid Build Coastguard Worker }
743*890232f2SAndroid Build Coastguard Worker
744*890232f2SAndroid Build Coastguard Worker const std::string enum_type =
745*890232f2SAndroid Build Coastguard Worker BuildUnion(union_offset, realized_type.value(), field);
746*890232f2SAndroid Build Coastguard Worker
747*890232f2SAndroid Build Coastguard Worker offset_field_comment.default_value =
748*890232f2SAndroid Build Coastguard Worker "(union of type `" + enum_type + "`)";
749*890232f2SAndroid Build Coastguard Worker
750*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(field_offset, sizeof(uint32_t),
751*890232f2SAndroid Build Coastguard Worker BinaryRegionType::UOffset, 0,
752*890232f2SAndroid Build Coastguard Worker union_offset, offset_field_comment));
753*890232f2SAndroid Build Coastguard Worker
754*890232f2SAndroid Build Coastguard Worker } break;
755*890232f2SAndroid Build Coastguard Worker
756*890232f2SAndroid Build Coastguard Worker default: break;
757*890232f2SAndroid Build Coastguard Worker }
758*890232f2SAndroid Build Coastguard Worker }
759*890232f2SAndroid Build Coastguard Worker
760*890232f2SAndroid Build Coastguard Worker // Handle the case where there is padding after the last known binary
761*890232f2SAndroid Build Coastguard Worker // region. Calculate where we left off towards the expected end of the
762*890232f2SAndroid Build Coastguard Worker // table.
763*890232f2SAndroid Build Coastguard Worker const uint64_t i = regions.back().offset + regions.back().length + 1;
764*890232f2SAndroid Build Coastguard Worker
765*890232f2SAndroid Build Coastguard Worker if (i < table_end_offset) {
766*890232f2SAndroid Build Coastguard Worker const uint64_t pad_bytes = table_end_offset - i + 1;
767*890232f2SAndroid Build Coastguard Worker
768*890232f2SAndroid Build Coastguard Worker BinaryRegionComment padding_comment;
769*890232f2SAndroid Build Coastguard Worker padding_comment.type = BinaryRegionCommentType::Padding;
770*890232f2SAndroid Build Coastguard Worker
771*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(i - 1, pad_bytes * sizeof(uint8_t),
772*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint8, pad_bytes, 0,
773*890232f2SAndroid Build Coastguard Worker padding_comment));
774*890232f2SAndroid Build Coastguard Worker }
775*890232f2SAndroid Build Coastguard Worker
776*890232f2SAndroid Build Coastguard Worker AddSection(table_offset,
777*890232f2SAndroid Build Coastguard Worker MakeBinarySection(table->name()->str(), type, std::move(regions)));
778*890232f2SAndroid Build Coastguard Worker }
779*890232f2SAndroid Build Coastguard Worker
BuildStruct(const uint64_t struct_offset,std::vector<BinaryRegion> & regions,const reflection::Object * const object)780*890232f2SAndroid Build Coastguard Worker uint64_t BinaryAnnotator::BuildStruct(const uint64_t struct_offset,
781*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> ®ions,
782*890232f2SAndroid Build Coastguard Worker const reflection::Object *const object) {
783*890232f2SAndroid Build Coastguard Worker if (!object->is_struct()) { return struct_offset; }
784*890232f2SAndroid Build Coastguard Worker uint64_t offset = struct_offset;
785*890232f2SAndroid Build Coastguard Worker
786*890232f2SAndroid Build Coastguard Worker // Loop over all the fields in increasing order
787*890232f2SAndroid Build Coastguard Worker ForAllFields(object, /*reverse=*/false, [&](const reflection::Field *field) {
788*890232f2SAndroid Build Coastguard Worker if (IsScalar(field->type()->base_type())) {
789*890232f2SAndroid Build Coastguard Worker // Structure Field value
790*890232f2SAndroid Build Coastguard Worker const uint64_t type_size = GetTypeSize(field->type()->base_type());
791*890232f2SAndroid Build Coastguard Worker const BinaryRegionType region_type =
792*890232f2SAndroid Build Coastguard Worker GetRegionType(field->type()->base_type());
793*890232f2SAndroid Build Coastguard Worker
794*890232f2SAndroid Build Coastguard Worker BinaryRegionComment comment;
795*890232f2SAndroid Build Coastguard Worker comment.type = BinaryRegionCommentType::StructField;
796*890232f2SAndroid Build Coastguard Worker comment.name =
797*890232f2SAndroid Build Coastguard Worker std::string(object->name()->c_str()) + "." + field->name()->c_str();
798*890232f2SAndroid Build Coastguard Worker comment.default_value = "(" +
799*890232f2SAndroid Build Coastguard Worker std::string(reflection::EnumNameBaseType(
800*890232f2SAndroid Build Coastguard Worker field->type()->base_type())) +
801*890232f2SAndroid Build Coastguard Worker ")";
802*890232f2SAndroid Build Coastguard Worker
803*890232f2SAndroid Build Coastguard Worker if (!IsValidRead(offset, type_size)) {
804*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(offset);
805*890232f2SAndroid Build Coastguard Worker SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
806*890232f2SAndroid Build Coastguard Worker std::to_string(type_size));
807*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, remaining,
808*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining,
809*890232f2SAndroid Build Coastguard Worker 0, comment));
810*890232f2SAndroid Build Coastguard Worker
811*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): Should I bail out here? This sets offset to the
812*890232f2SAndroid Build Coastguard Worker // end of the binary. So all other reads in the loop should fail.
813*890232f2SAndroid Build Coastguard Worker offset += remaining;
814*890232f2SAndroid Build Coastguard Worker return;
815*890232f2SAndroid Build Coastguard Worker }
816*890232f2SAndroid Build Coastguard Worker
817*890232f2SAndroid Build Coastguard Worker regions.push_back(
818*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(offset, type_size, region_type, 0, 0, comment));
819*890232f2SAndroid Build Coastguard Worker offset += type_size;
820*890232f2SAndroid Build Coastguard Worker } else if (field->type()->base_type() == reflection::BaseType::Obj) {
821*890232f2SAndroid Build Coastguard Worker // Structs are stored inline, even when nested.
822*890232f2SAndroid Build Coastguard Worker offset = BuildStruct(offset, regions,
823*890232f2SAndroid Build Coastguard Worker schema_->objects()->Get(field->type()->index()));
824*890232f2SAndroid Build Coastguard Worker } else if (field->type()->base_type() == reflection::BaseType::Array) {
825*890232f2SAndroid Build Coastguard Worker const bool is_scalar = IsScalar(field->type()->element());
826*890232f2SAndroid Build Coastguard Worker const uint64_t type_size = GetTypeSize(field->type()->element());
827*890232f2SAndroid Build Coastguard Worker const BinaryRegionType region_type =
828*890232f2SAndroid Build Coastguard Worker GetRegionType(field->type()->element());
829*890232f2SAndroid Build Coastguard Worker
830*890232f2SAndroid Build Coastguard Worker // Arrays are just repeated structures.
831*890232f2SAndroid Build Coastguard Worker for (uint16_t i = 0; i < field->type()->fixed_length(); ++i) {
832*890232f2SAndroid Build Coastguard Worker if (is_scalar) {
833*890232f2SAndroid Build Coastguard Worker BinaryRegionComment array_comment;
834*890232f2SAndroid Build Coastguard Worker array_comment.type = BinaryRegionCommentType::ArrayField;
835*890232f2SAndroid Build Coastguard Worker array_comment.name = std::string(object->name()->c_str()) + "." +
836*890232f2SAndroid Build Coastguard Worker field->name()->c_str();
837*890232f2SAndroid Build Coastguard Worker array_comment.index = i;
838*890232f2SAndroid Build Coastguard Worker array_comment.default_value =
839*890232f2SAndroid Build Coastguard Worker "(" +
840*890232f2SAndroid Build Coastguard Worker std::string(
841*890232f2SAndroid Build Coastguard Worker reflection::EnumNameBaseType(field->type()->element())) +
842*890232f2SAndroid Build Coastguard Worker ")";
843*890232f2SAndroid Build Coastguard Worker
844*890232f2SAndroid Build Coastguard Worker if (!IsValidRead(offset, type_size)) {
845*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(offset);
846*890232f2SAndroid Build Coastguard Worker
847*890232f2SAndroid Build Coastguard Worker SetError(array_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
848*890232f2SAndroid Build Coastguard Worker std::to_string(type_size));
849*890232f2SAndroid Build Coastguard Worker
850*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, remaining,
851*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown,
852*890232f2SAndroid Build Coastguard Worker remaining, 0, array_comment));
853*890232f2SAndroid Build Coastguard Worker
854*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): Should I bail out here? This sets offset to
855*890232f2SAndroid Build Coastguard Worker // the end of the binary. So all other reads in the loop should
856*890232f2SAndroid Build Coastguard Worker // fail.
857*890232f2SAndroid Build Coastguard Worker offset += remaining;
858*890232f2SAndroid Build Coastguard Worker break;
859*890232f2SAndroid Build Coastguard Worker }
860*890232f2SAndroid Build Coastguard Worker
861*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, type_size, region_type, 0,
862*890232f2SAndroid Build Coastguard Worker 0, array_comment));
863*890232f2SAndroid Build Coastguard Worker
864*890232f2SAndroid Build Coastguard Worker offset += type_size;
865*890232f2SAndroid Build Coastguard Worker } else {
866*890232f2SAndroid Build Coastguard Worker // Array of Structs.
867*890232f2SAndroid Build Coastguard Worker //
868*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): This works, but the comments on the fields lose
869*890232f2SAndroid Build Coastguard Worker // some context. Need to figure a way how to plumb the nested arrays
870*890232f2SAndroid Build Coastguard Worker // comments together that isn't too confusing.
871*890232f2SAndroid Build Coastguard Worker offset = BuildStruct(offset, regions,
872*890232f2SAndroid Build Coastguard Worker schema_->objects()->Get(field->type()->index()));
873*890232f2SAndroid Build Coastguard Worker }
874*890232f2SAndroid Build Coastguard Worker }
875*890232f2SAndroid Build Coastguard Worker }
876*890232f2SAndroid Build Coastguard Worker
877*890232f2SAndroid Build Coastguard Worker // Insert any padding after this field.
878*890232f2SAndroid Build Coastguard Worker const uint16_t padding = field->padding();
879*890232f2SAndroid Build Coastguard Worker if (padding > 0 && IsValidOffset(offset + padding)) {
880*890232f2SAndroid Build Coastguard Worker BinaryRegionComment padding_comment;
881*890232f2SAndroid Build Coastguard Worker padding_comment.type = BinaryRegionCommentType::Padding;
882*890232f2SAndroid Build Coastguard Worker
883*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, padding,
884*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint8, padding, 0,
885*890232f2SAndroid Build Coastguard Worker padding_comment));
886*890232f2SAndroid Build Coastguard Worker offset += padding;
887*890232f2SAndroid Build Coastguard Worker }
888*890232f2SAndroid Build Coastguard Worker });
889*890232f2SAndroid Build Coastguard Worker
890*890232f2SAndroid Build Coastguard Worker return offset;
891*890232f2SAndroid Build Coastguard Worker }
892*890232f2SAndroid Build Coastguard Worker
BuildString(const uint64_t string_offset,const reflection::Object * const table,const reflection::Field * const field)893*890232f2SAndroid Build Coastguard Worker void BinaryAnnotator::BuildString(const uint64_t string_offset,
894*890232f2SAndroid Build Coastguard Worker const reflection::Object *const table,
895*890232f2SAndroid Build Coastguard Worker const reflection::Field *const field) {
896*890232f2SAndroid Build Coastguard Worker // Check if we have already generated this string section, and this is a
897*890232f2SAndroid Build Coastguard Worker // shared string instance.
898*890232f2SAndroid Build Coastguard Worker if (ContainsSection(string_offset)) { return; }
899*890232f2SAndroid Build Coastguard Worker
900*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions;
901*890232f2SAndroid Build Coastguard Worker const auto string_length = ReadScalar<uint32_t>(string_offset);
902*890232f2SAndroid Build Coastguard Worker
903*890232f2SAndroid Build Coastguard Worker BinaryRegionComment string_length_comment;
904*890232f2SAndroid Build Coastguard Worker string_length_comment.type = BinaryRegionCommentType::StringLength;
905*890232f2SAndroid Build Coastguard Worker
906*890232f2SAndroid Build Coastguard Worker if (!string_length.has_value()) {
907*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(string_offset);
908*890232f2SAndroid Build Coastguard Worker
909*890232f2SAndroid Build Coastguard Worker SetError(string_length_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
910*890232f2SAndroid Build Coastguard Worker "4");
911*890232f2SAndroid Build Coastguard Worker
912*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(string_offset, remaining,
913*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining, 0,
914*890232f2SAndroid Build Coastguard Worker string_length_comment));
915*890232f2SAndroid Build Coastguard Worker
916*890232f2SAndroid Build Coastguard Worker } else {
917*890232f2SAndroid Build Coastguard Worker const uint32_t string_size = string_length.value();
918*890232f2SAndroid Build Coastguard Worker const uint64_t string_end =
919*890232f2SAndroid Build Coastguard Worker string_offset + sizeof(uint32_t) + string_size + sizeof(char);
920*890232f2SAndroid Build Coastguard Worker
921*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(string_end - 1)) {
922*890232f2SAndroid Build Coastguard Worker SetError(string_length_comment,
923*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
924*890232f2SAndroid Build Coastguard Worker
925*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(string_offset, sizeof(uint32_t),
926*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint32, 0, 0,
927*890232f2SAndroid Build Coastguard Worker string_length_comment));
928*890232f2SAndroid Build Coastguard Worker } else {
929*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(string_offset, sizeof(uint32_t),
930*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint32, 0, 0,
931*890232f2SAndroid Build Coastguard Worker string_length_comment));
932*890232f2SAndroid Build Coastguard Worker
933*890232f2SAndroid Build Coastguard Worker BinaryRegionComment string_comment;
934*890232f2SAndroid Build Coastguard Worker string_comment.type = BinaryRegionCommentType::StringValue;
935*890232f2SAndroid Build Coastguard Worker
936*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(string_offset + sizeof(uint32_t),
937*890232f2SAndroid Build Coastguard Worker string_size, BinaryRegionType::Char,
938*890232f2SAndroid Build Coastguard Worker string_size, 0, string_comment));
939*890232f2SAndroid Build Coastguard Worker
940*890232f2SAndroid Build Coastguard Worker BinaryRegionComment string_terminator_comment;
941*890232f2SAndroid Build Coastguard Worker string_terminator_comment.type =
942*890232f2SAndroid Build Coastguard Worker BinaryRegionCommentType::StringTerminator;
943*890232f2SAndroid Build Coastguard Worker
944*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
945*890232f2SAndroid Build Coastguard Worker string_offset + sizeof(uint32_t) + string_size, sizeof(char),
946*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Char, 0, 0, string_terminator_comment));
947*890232f2SAndroid Build Coastguard Worker }
948*890232f2SAndroid Build Coastguard Worker }
949*890232f2SAndroid Build Coastguard Worker
950*890232f2SAndroid Build Coastguard Worker AddSection(string_offset,
951*890232f2SAndroid Build Coastguard Worker MakeBinarySection(std::string(table->name()->c_str()) + "." +
952*890232f2SAndroid Build Coastguard Worker field->name()->c_str(),
953*890232f2SAndroid Build Coastguard Worker BinarySectionType::String, std::move(regions)));
954*890232f2SAndroid Build Coastguard Worker }
955*890232f2SAndroid Build Coastguard Worker
BuildVector(const uint64_t vector_offset,const reflection::Object * const table,const reflection::Field * const field,const uint64_t parent_table_offset,const VTable & vtable)956*890232f2SAndroid Build Coastguard Worker void BinaryAnnotator::BuildVector(const uint64_t vector_offset,
957*890232f2SAndroid Build Coastguard Worker const reflection::Object *const table,
958*890232f2SAndroid Build Coastguard Worker const reflection::Field *const field,
959*890232f2SAndroid Build Coastguard Worker const uint64_t parent_table_offset,
960*890232f2SAndroid Build Coastguard Worker const VTable &vtable) {
961*890232f2SAndroid Build Coastguard Worker if (ContainsSection(vector_offset)) { return; }
962*890232f2SAndroid Build Coastguard Worker
963*890232f2SAndroid Build Coastguard Worker BinaryRegionComment vector_length_comment;
964*890232f2SAndroid Build Coastguard Worker vector_length_comment.type = BinaryRegionCommentType::VectorLength;
965*890232f2SAndroid Build Coastguard Worker
966*890232f2SAndroid Build Coastguard Worker const auto vector_length = ReadScalar<uint32_t>(vector_offset);
967*890232f2SAndroid Build Coastguard Worker if (!vector_length.has_value()) {
968*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(vector_offset);
969*890232f2SAndroid Build Coastguard Worker SetError(vector_length_comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
970*890232f2SAndroid Build Coastguard Worker "4");
971*890232f2SAndroid Build Coastguard Worker
972*890232f2SAndroid Build Coastguard Worker AddSection(
973*890232f2SAndroid Build Coastguard Worker vector_offset,
974*890232f2SAndroid Build Coastguard Worker MakeSingleRegionBinarySection(
975*890232f2SAndroid Build Coastguard Worker std::string(table->name()->c_str()) + "." + field->name()->c_str(),
976*890232f2SAndroid Build Coastguard Worker BinarySectionType::Vector,
977*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(vector_offset, remaining,
978*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining, 0,
979*890232f2SAndroid Build Coastguard Worker vector_length_comment)));
980*890232f2SAndroid Build Coastguard Worker return;
981*890232f2SAndroid Build Coastguard Worker }
982*890232f2SAndroid Build Coastguard Worker
983*890232f2SAndroid Build Coastguard Worker // Validate there are enough bytes left in the binary to process all the
984*890232f2SAndroid Build Coastguard Worker // items.
985*890232f2SAndroid Build Coastguard Worker const uint64_t last_item_offset =
986*890232f2SAndroid Build Coastguard Worker vector_offset + sizeof(uint32_t) +
987*890232f2SAndroid Build Coastguard Worker vector_length.value() * GetElementSize(field);
988*890232f2SAndroid Build Coastguard Worker
989*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(last_item_offset - 1)) {
990*890232f2SAndroid Build Coastguard Worker SetError(vector_length_comment, BinaryRegionStatus::ERROR_LENGTH_TOO_LONG);
991*890232f2SAndroid Build Coastguard Worker AddSection(
992*890232f2SAndroid Build Coastguard Worker vector_offset,
993*890232f2SAndroid Build Coastguard Worker MakeSingleRegionBinarySection(
994*890232f2SAndroid Build Coastguard Worker std::string(table->name()->c_str()) + "." + field->name()->c_str(),
995*890232f2SAndroid Build Coastguard Worker BinarySectionType::Vector,
996*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(vector_offset, sizeof(uint32_t),
997*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint32, 0, 0,
998*890232f2SAndroid Build Coastguard Worker vector_length_comment)));
999*890232f2SAndroid Build Coastguard Worker
1000*890232f2SAndroid Build Coastguard Worker return;
1001*890232f2SAndroid Build Coastguard Worker }
1002*890232f2SAndroid Build Coastguard Worker
1003*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions;
1004*890232f2SAndroid Build Coastguard Worker
1005*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(vector_offset, sizeof(uint32_t),
1006*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Uint32, 0, 0,
1007*890232f2SAndroid Build Coastguard Worker vector_length_comment));
1008*890232f2SAndroid Build Coastguard Worker
1009*890232f2SAndroid Build Coastguard Worker uint64_t offset = vector_offset + sizeof(uint32_t);
1010*890232f2SAndroid Build Coastguard Worker
1011*890232f2SAndroid Build Coastguard Worker switch (field->type()->element()) {
1012*890232f2SAndroid Build Coastguard Worker case reflection::BaseType::Obj: {
1013*890232f2SAndroid Build Coastguard Worker const reflection::Object *object =
1014*890232f2SAndroid Build Coastguard Worker schema_->objects()->Get(field->type()->index());
1015*890232f2SAndroid Build Coastguard Worker
1016*890232f2SAndroid Build Coastguard Worker if (object->is_struct()) {
1017*890232f2SAndroid Build Coastguard Worker // Vector of structs
1018*890232f2SAndroid Build Coastguard Worker for (size_t i = 0; i < vector_length.value(); ++i) {
1019*890232f2SAndroid Build Coastguard Worker // Structs are inline to the vector.
1020*890232f2SAndroid Build Coastguard Worker const uint64_t next_offset = BuildStruct(offset, regions, object);
1021*890232f2SAndroid Build Coastguard Worker if (next_offset == offset) { break; }
1022*890232f2SAndroid Build Coastguard Worker offset = next_offset;
1023*890232f2SAndroid Build Coastguard Worker }
1024*890232f2SAndroid Build Coastguard Worker } else {
1025*890232f2SAndroid Build Coastguard Worker // Vector of objects
1026*890232f2SAndroid Build Coastguard Worker for (size_t i = 0; i < vector_length.value(); ++i) {
1027*890232f2SAndroid Build Coastguard Worker BinaryRegionComment vector_object_comment;
1028*890232f2SAndroid Build Coastguard Worker vector_object_comment.type =
1029*890232f2SAndroid Build Coastguard Worker BinaryRegionCommentType::VectorTableValue;
1030*890232f2SAndroid Build Coastguard Worker vector_object_comment.index = i;
1031*890232f2SAndroid Build Coastguard Worker
1032*890232f2SAndroid Build Coastguard Worker const auto table_relative_offset = ReadScalar<uint32_t>(offset);
1033*890232f2SAndroid Build Coastguard Worker if (!table_relative_offset.has_value()) {
1034*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(offset);
1035*890232f2SAndroid Build Coastguard Worker SetError(vector_object_comment,
1036*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
1037*890232f2SAndroid Build Coastguard Worker
1038*890232f2SAndroid Build Coastguard Worker regions.push_back(
1039*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
1040*890232f2SAndroid Build Coastguard Worker remaining, 0, vector_object_comment));
1041*890232f2SAndroid Build Coastguard Worker break;
1042*890232f2SAndroid Build Coastguard Worker }
1043*890232f2SAndroid Build Coastguard Worker
1044*890232f2SAndroid Build Coastguard Worker // The table offset is relative from the offset location itself.
1045*890232f2SAndroid Build Coastguard Worker const uint64_t table_offset = offset + table_relative_offset.value();
1046*890232f2SAndroid Build Coastguard Worker
1047*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(table_offset)) {
1048*890232f2SAndroid Build Coastguard Worker SetError(vector_object_comment,
1049*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
1050*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
1051*890232f2SAndroid Build Coastguard Worker offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
1052*890232f2SAndroid Build Coastguard Worker table_offset, vector_object_comment));
1053*890232f2SAndroid Build Coastguard Worker
1054*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint32_t);
1055*890232f2SAndroid Build Coastguard Worker continue;
1056*890232f2SAndroid Build Coastguard Worker }
1057*890232f2SAndroid Build Coastguard Worker
1058*890232f2SAndroid Build Coastguard Worker if (table_offset == parent_table_offset) {
1059*890232f2SAndroid Build Coastguard Worker SetError(vector_object_comment,
1060*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_CYCLE_DETECTED);
1061*890232f2SAndroid Build Coastguard Worker // A cycle detected where a table vector field is pointing to
1062*890232f2SAndroid Build Coastguard Worker // itself. This should only happen in corrupted files.
1063*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
1064*890232f2SAndroid Build Coastguard Worker offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
1065*890232f2SAndroid Build Coastguard Worker table_offset, vector_object_comment));
1066*890232f2SAndroid Build Coastguard Worker
1067*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint32_t);
1068*890232f2SAndroid Build Coastguard Worker continue;
1069*890232f2SAndroid Build Coastguard Worker }
1070*890232f2SAndroid Build Coastguard Worker
1071*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
1072*890232f2SAndroid Build Coastguard Worker offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
1073*890232f2SAndroid Build Coastguard Worker table_offset, vector_object_comment));
1074*890232f2SAndroid Build Coastguard Worker
1075*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint32_t);
1076*890232f2SAndroid Build Coastguard Worker
1077*890232f2SAndroid Build Coastguard Worker BuildTable(table_offset, BinarySectionType::Table, object);
1078*890232f2SAndroid Build Coastguard Worker }
1079*890232f2SAndroid Build Coastguard Worker }
1080*890232f2SAndroid Build Coastguard Worker } break;
1081*890232f2SAndroid Build Coastguard Worker case reflection::BaseType::String: {
1082*890232f2SAndroid Build Coastguard Worker // Vector of strings
1083*890232f2SAndroid Build Coastguard Worker for (size_t i = 0; i < vector_length.value(); ++i) {
1084*890232f2SAndroid Build Coastguard Worker BinaryRegionComment vector_object_comment;
1085*890232f2SAndroid Build Coastguard Worker vector_object_comment.type = BinaryRegionCommentType::VectorStringValue;
1086*890232f2SAndroid Build Coastguard Worker vector_object_comment.index = i;
1087*890232f2SAndroid Build Coastguard Worker
1088*890232f2SAndroid Build Coastguard Worker const auto string_relative_offset = ReadScalar<uint32_t>(offset);
1089*890232f2SAndroid Build Coastguard Worker if (!string_relative_offset.has_value()) {
1090*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(offset);
1091*890232f2SAndroid Build Coastguard Worker
1092*890232f2SAndroid Build Coastguard Worker SetError(vector_object_comment,
1093*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
1094*890232f2SAndroid Build Coastguard Worker
1095*890232f2SAndroid Build Coastguard Worker regions.push_back(
1096*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
1097*890232f2SAndroid Build Coastguard Worker remaining, 0, vector_object_comment));
1098*890232f2SAndroid Build Coastguard Worker break;
1099*890232f2SAndroid Build Coastguard Worker }
1100*890232f2SAndroid Build Coastguard Worker
1101*890232f2SAndroid Build Coastguard Worker // The string offset is relative from the offset location itself.
1102*890232f2SAndroid Build Coastguard Worker const uint64_t string_offset = offset + string_relative_offset.value();
1103*890232f2SAndroid Build Coastguard Worker
1104*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(string_offset)) {
1105*890232f2SAndroid Build Coastguard Worker SetError(vector_object_comment,
1106*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
1107*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
1108*890232f2SAndroid Build Coastguard Worker offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
1109*890232f2SAndroid Build Coastguard Worker string_offset, vector_object_comment));
1110*890232f2SAndroid Build Coastguard Worker
1111*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint32_t);
1112*890232f2SAndroid Build Coastguard Worker continue;
1113*890232f2SAndroid Build Coastguard Worker }
1114*890232f2SAndroid Build Coastguard Worker
1115*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
1116*890232f2SAndroid Build Coastguard Worker offset, sizeof(uint32_t), BinaryRegionType::UOffset, 0,
1117*890232f2SAndroid Build Coastguard Worker string_offset, vector_object_comment));
1118*890232f2SAndroid Build Coastguard Worker
1119*890232f2SAndroid Build Coastguard Worker BuildString(string_offset, table, field);
1120*890232f2SAndroid Build Coastguard Worker
1121*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint32_t);
1122*890232f2SAndroid Build Coastguard Worker }
1123*890232f2SAndroid Build Coastguard Worker } break;
1124*890232f2SAndroid Build Coastguard Worker case reflection::BaseType::Union: {
1125*890232f2SAndroid Build Coastguard Worker // Vector of unions
1126*890232f2SAndroid Build Coastguard Worker // Unions have both their realized type (uint8_t for now) that are
1127*890232f2SAndroid Build Coastguard Worker // stored separately. These are stored in the field->index() - 1
1128*890232f2SAndroid Build Coastguard Worker // location.
1129*890232f2SAndroid Build Coastguard Worker const uint16_t union_type_vector_id = field->id() - 1;
1130*890232f2SAndroid Build Coastguard Worker
1131*890232f2SAndroid Build Coastguard Worker auto vtable_entry = vtable.fields.find(union_type_vector_id);
1132*890232f2SAndroid Build Coastguard Worker if (vtable_entry == vtable.fields.end()) {
1133*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): need to capture this error condition.
1134*890232f2SAndroid Build Coastguard Worker break;
1135*890232f2SAndroid Build Coastguard Worker }
1136*890232f2SAndroid Build Coastguard Worker
1137*890232f2SAndroid Build Coastguard Worker const uint64_t union_type_vector_field_offset =
1138*890232f2SAndroid Build Coastguard Worker parent_table_offset + vtable_entry->second.offset_from_table;
1139*890232f2SAndroid Build Coastguard Worker
1140*890232f2SAndroid Build Coastguard Worker const auto union_type_vector_field_relative_offset =
1141*890232f2SAndroid Build Coastguard Worker ReadScalar<uint16_t>(union_type_vector_field_offset);
1142*890232f2SAndroid Build Coastguard Worker
1143*890232f2SAndroid Build Coastguard Worker if (!union_type_vector_field_relative_offset.has_value()) {
1144*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(offset);
1145*890232f2SAndroid Build Coastguard Worker BinaryRegionComment vector_union_comment;
1146*890232f2SAndroid Build Coastguard Worker vector_union_comment.type = BinaryRegionCommentType::VectorUnionValue;
1147*890232f2SAndroid Build Coastguard Worker SetError(vector_union_comment,
1148*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "2");
1149*890232f2SAndroid Build Coastguard Worker
1150*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, remaining,
1151*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown, remaining,
1152*890232f2SAndroid Build Coastguard Worker 0, vector_union_comment));
1153*890232f2SAndroid Build Coastguard Worker
1154*890232f2SAndroid Build Coastguard Worker break;
1155*890232f2SAndroid Build Coastguard Worker }
1156*890232f2SAndroid Build Coastguard Worker
1157*890232f2SAndroid Build Coastguard Worker // Get the offset to the first type (the + sizeof(uint32_t) is to skip
1158*890232f2SAndroid Build Coastguard Worker // over the vector length which we already know). Validation happens
1159*890232f2SAndroid Build Coastguard Worker // within the loop below.
1160*890232f2SAndroid Build Coastguard Worker const uint64_t union_type_vector_data_offset =
1161*890232f2SAndroid Build Coastguard Worker union_type_vector_field_offset +
1162*890232f2SAndroid Build Coastguard Worker union_type_vector_field_relative_offset.value() + sizeof(uint32_t);
1163*890232f2SAndroid Build Coastguard Worker
1164*890232f2SAndroid Build Coastguard Worker for (size_t i = 0; i < vector_length.value(); ++i) {
1165*890232f2SAndroid Build Coastguard Worker BinaryRegionComment comment;
1166*890232f2SAndroid Build Coastguard Worker comment.type = BinaryRegionCommentType::VectorUnionValue;
1167*890232f2SAndroid Build Coastguard Worker comment.index = i;
1168*890232f2SAndroid Build Coastguard Worker
1169*890232f2SAndroid Build Coastguard Worker const auto union_relative_offset = ReadScalar<uint32_t>(offset);
1170*890232f2SAndroid Build Coastguard Worker if (!union_relative_offset.has_value()) {
1171*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(offset);
1172*890232f2SAndroid Build Coastguard Worker
1173*890232f2SAndroid Build Coastguard Worker SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "4");
1174*890232f2SAndroid Build Coastguard Worker
1175*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, remaining,
1176*890232f2SAndroid Build Coastguard Worker BinaryRegionType::Unknown,
1177*890232f2SAndroid Build Coastguard Worker remaining, 0, comment));
1178*890232f2SAndroid Build Coastguard Worker
1179*890232f2SAndroid Build Coastguard Worker break;
1180*890232f2SAndroid Build Coastguard Worker }
1181*890232f2SAndroid Build Coastguard Worker
1182*890232f2SAndroid Build Coastguard Worker // The union offset is relative from the offset location itself.
1183*890232f2SAndroid Build Coastguard Worker const uint64_t union_offset = offset + union_relative_offset.value();
1184*890232f2SAndroid Build Coastguard Worker
1185*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(union_offset)) {
1186*890232f2SAndroid Build Coastguard Worker SetError(comment, BinaryRegionStatus::ERROR_OFFSET_OUT_OF_BINARY);
1187*890232f2SAndroid Build Coastguard Worker
1188*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, sizeof(uint32_t),
1189*890232f2SAndroid Build Coastguard Worker BinaryRegionType::UOffset, 0,
1190*890232f2SAndroid Build Coastguard Worker union_offset, comment));
1191*890232f2SAndroid Build Coastguard Worker continue;
1192*890232f2SAndroid Build Coastguard Worker }
1193*890232f2SAndroid Build Coastguard Worker
1194*890232f2SAndroid Build Coastguard Worker const auto realized_type =
1195*890232f2SAndroid Build Coastguard Worker ReadScalar<uint8_t>(union_type_vector_data_offset + i);
1196*890232f2SAndroid Build Coastguard Worker
1197*890232f2SAndroid Build Coastguard Worker if (!realized_type.has_value()) {
1198*890232f2SAndroid Build Coastguard Worker SetError(comment, BinaryRegionStatus::ERROR_INCOMPLETE_BINARY, "1");
1199*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(
1200*890232f2SAndroid Build Coastguard Worker offset, 0, BinaryRegionType::Unknown, 0, 0, comment));
1201*890232f2SAndroid Build Coastguard Worker continue;
1202*890232f2SAndroid Build Coastguard Worker }
1203*890232f2SAndroid Build Coastguard Worker
1204*890232f2SAndroid Build Coastguard Worker if (!IsValidUnionValue(vtable_entry->second.field->type()->index(),
1205*890232f2SAndroid Build Coastguard Worker realized_type.value())) {
1206*890232f2SAndroid Build Coastguard Worker // We already export an error in the union type field, so just skip
1207*890232f2SAndroid Build Coastguard Worker // building the union itself and it will default to an unreference
1208*890232f2SAndroid Build Coastguard Worker // Binary section.
1209*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint32_t);
1210*890232f2SAndroid Build Coastguard Worker continue;
1211*890232f2SAndroid Build Coastguard Worker }
1212*890232f2SAndroid Build Coastguard Worker
1213*890232f2SAndroid Build Coastguard Worker const std::string enum_type =
1214*890232f2SAndroid Build Coastguard Worker BuildUnion(union_offset, realized_type.value(), field);
1215*890232f2SAndroid Build Coastguard Worker
1216*890232f2SAndroid Build Coastguard Worker comment.default_value = "(`" + enum_type + "`)";
1217*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, sizeof(uint32_t),
1218*890232f2SAndroid Build Coastguard Worker BinaryRegionType::UOffset, 0,
1219*890232f2SAndroid Build Coastguard Worker union_offset, comment));
1220*890232f2SAndroid Build Coastguard Worker
1221*890232f2SAndroid Build Coastguard Worker offset += sizeof(uint32_t);
1222*890232f2SAndroid Build Coastguard Worker }
1223*890232f2SAndroid Build Coastguard Worker } break;
1224*890232f2SAndroid Build Coastguard Worker default: {
1225*890232f2SAndroid Build Coastguard Worker if (IsScalar(field->type()->element())) {
1226*890232f2SAndroid Build Coastguard Worker const BinaryRegionType binary_region_type =
1227*890232f2SAndroid Build Coastguard Worker GetRegionType(field->type()->element());
1228*890232f2SAndroid Build Coastguard Worker
1229*890232f2SAndroid Build Coastguard Worker const uint64_t type_size = GetTypeSize(field->type()->element());
1230*890232f2SAndroid Build Coastguard Worker
1231*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): It might be nicer to user the
1232*890232f2SAndroid Build Coastguard Worker // BinaryRegion.array_length field to indicate this.
1233*890232f2SAndroid Build Coastguard Worker for (size_t i = 0; i < vector_length.value(); ++i) {
1234*890232f2SAndroid Build Coastguard Worker BinaryRegionComment vector_scalar_comment;
1235*890232f2SAndroid Build Coastguard Worker vector_scalar_comment.type = BinaryRegionCommentType::VectorValue;
1236*890232f2SAndroid Build Coastguard Worker vector_scalar_comment.index = i;
1237*890232f2SAndroid Build Coastguard Worker
1238*890232f2SAndroid Build Coastguard Worker if (!IsValidRead(offset, type_size)) {
1239*890232f2SAndroid Build Coastguard Worker const uint64_t remaining = RemainingBytes(offset);
1240*890232f2SAndroid Build Coastguard Worker
1241*890232f2SAndroid Build Coastguard Worker SetError(vector_scalar_comment,
1242*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INCOMPLETE_BINARY,
1243*890232f2SAndroid Build Coastguard Worker std::to_string(type_size));
1244*890232f2SAndroid Build Coastguard Worker
1245*890232f2SAndroid Build Coastguard Worker regions.push_back(
1246*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(offset, remaining, BinaryRegionType::Unknown,
1247*890232f2SAndroid Build Coastguard Worker remaining, 0, vector_scalar_comment));
1248*890232f2SAndroid Build Coastguard Worker break;
1249*890232f2SAndroid Build Coastguard Worker }
1250*890232f2SAndroid Build Coastguard Worker
1251*890232f2SAndroid Build Coastguard Worker if (IsUnionType(field->type()->element())) {
1252*890232f2SAndroid Build Coastguard Worker // This is a type for a union. Validate the value
1253*890232f2SAndroid Build Coastguard Worker const auto enum_value = ReadScalar<uint8_t>(offset);
1254*890232f2SAndroid Build Coastguard Worker
1255*890232f2SAndroid Build Coastguard Worker // This should always have a value, due to the IsValidRead check
1256*890232f2SAndroid Build Coastguard Worker // above.
1257*890232f2SAndroid Build Coastguard Worker if (!IsValidUnionValue(field->type()->index(),
1258*890232f2SAndroid Build Coastguard Worker enum_value.value())) {
1259*890232f2SAndroid Build Coastguard Worker SetError(vector_scalar_comment,
1260*890232f2SAndroid Build Coastguard Worker BinaryRegionStatus::ERROR_INVALID_UNION_TYPE);
1261*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, type_size,
1262*890232f2SAndroid Build Coastguard Worker binary_region_type, 0, 0,
1263*890232f2SAndroid Build Coastguard Worker vector_scalar_comment));
1264*890232f2SAndroid Build Coastguard Worker offset += type_size;
1265*890232f2SAndroid Build Coastguard Worker continue;
1266*890232f2SAndroid Build Coastguard Worker }
1267*890232f2SAndroid Build Coastguard Worker }
1268*890232f2SAndroid Build Coastguard Worker
1269*890232f2SAndroid Build Coastguard Worker regions.push_back(MakeBinaryRegion(offset, type_size,
1270*890232f2SAndroid Build Coastguard Worker binary_region_type, 0, 0,
1271*890232f2SAndroid Build Coastguard Worker vector_scalar_comment));
1272*890232f2SAndroid Build Coastguard Worker offset += type_size;
1273*890232f2SAndroid Build Coastguard Worker }
1274*890232f2SAndroid Build Coastguard Worker }
1275*890232f2SAndroid Build Coastguard Worker } break;
1276*890232f2SAndroid Build Coastguard Worker }
1277*890232f2SAndroid Build Coastguard Worker AddSection(vector_offset,
1278*890232f2SAndroid Build Coastguard Worker MakeBinarySection(std::string(table->name()->c_str()) + "." +
1279*890232f2SAndroid Build Coastguard Worker field->name()->c_str(),
1280*890232f2SAndroid Build Coastguard Worker BinarySectionType::Vector, std::move(regions)));
1281*890232f2SAndroid Build Coastguard Worker }
1282*890232f2SAndroid Build Coastguard Worker
BuildUnion(const uint64_t union_offset,const uint8_t realized_type,const reflection::Field * const field)1283*890232f2SAndroid Build Coastguard Worker std::string BinaryAnnotator::BuildUnion(const uint64_t union_offset,
1284*890232f2SAndroid Build Coastguard Worker const uint8_t realized_type,
1285*890232f2SAndroid Build Coastguard Worker const reflection::Field *const field) {
1286*890232f2SAndroid Build Coastguard Worker const reflection::Enum *next_enum =
1287*890232f2SAndroid Build Coastguard Worker schema_->enums()->Get(field->type()->index());
1288*890232f2SAndroid Build Coastguard Worker
1289*890232f2SAndroid Build Coastguard Worker const reflection::EnumVal *enum_val = next_enum->values()->Get(realized_type);
1290*890232f2SAndroid Build Coastguard Worker
1291*890232f2SAndroid Build Coastguard Worker if (ContainsSection(union_offset)) { return enum_val->name()->c_str(); }
1292*890232f2SAndroid Build Coastguard Worker
1293*890232f2SAndroid Build Coastguard Worker const reflection::Type *union_type = enum_val->union_type();
1294*890232f2SAndroid Build Coastguard Worker
1295*890232f2SAndroid Build Coastguard Worker if (union_type->base_type() == reflection::BaseType::Obj) {
1296*890232f2SAndroid Build Coastguard Worker const reflection::Object *object =
1297*890232f2SAndroid Build Coastguard Worker schema_->objects()->Get(union_type->index());
1298*890232f2SAndroid Build Coastguard Worker
1299*890232f2SAndroid Build Coastguard Worker if (object->is_struct()) {
1300*890232f2SAndroid Build Coastguard Worker // Union of vectors point to a new Binary section
1301*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions;
1302*890232f2SAndroid Build Coastguard Worker
1303*890232f2SAndroid Build Coastguard Worker BuildStruct(union_offset, regions, object);
1304*890232f2SAndroid Build Coastguard Worker
1305*890232f2SAndroid Build Coastguard Worker AddSection(
1306*890232f2SAndroid Build Coastguard Worker union_offset,
1307*890232f2SAndroid Build Coastguard Worker MakeBinarySection(std::string(object->name()->c_str()) + "." +
1308*890232f2SAndroid Build Coastguard Worker field->name()->c_str(),
1309*890232f2SAndroid Build Coastguard Worker BinarySectionType::Union, std::move(regions)));
1310*890232f2SAndroid Build Coastguard Worker } else {
1311*890232f2SAndroid Build Coastguard Worker BuildTable(union_offset, BinarySectionType::Table, object);
1312*890232f2SAndroid Build Coastguard Worker }
1313*890232f2SAndroid Build Coastguard Worker }
1314*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): handle the other union types.
1315*890232f2SAndroid Build Coastguard Worker
1316*890232f2SAndroid Build Coastguard Worker return enum_val->name()->c_str();
1317*890232f2SAndroid Build Coastguard Worker }
1318*890232f2SAndroid Build Coastguard Worker
FixMissingRegions()1319*890232f2SAndroid Build Coastguard Worker void BinaryAnnotator::FixMissingRegions() {
1320*890232f2SAndroid Build Coastguard Worker std::vector<BinaryRegion> regions_to_insert;
1321*890232f2SAndroid Build Coastguard Worker for (auto ¤t_section : sections_) {
1322*890232f2SAndroid Build Coastguard Worker BinarySection §ion = current_section.second;
1323*890232f2SAndroid Build Coastguard Worker if (section.regions.empty()) {
1324*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): is this possible?
1325*890232f2SAndroid Build Coastguard Worker continue;
1326*890232f2SAndroid Build Coastguard Worker }
1327*890232f2SAndroid Build Coastguard Worker
1328*890232f2SAndroid Build Coastguard Worker uint64_t offset = section.regions[0].offset + section.regions[0].length;
1329*890232f2SAndroid Build Coastguard Worker for (size_t i = 1; i < section.regions.size(); ++i) {
1330*890232f2SAndroid Build Coastguard Worker BinaryRegion ®ion = section.regions[i];
1331*890232f2SAndroid Build Coastguard Worker
1332*890232f2SAndroid Build Coastguard Worker const uint64_t next_offset = region.offset;
1333*890232f2SAndroid Build Coastguard Worker if (!IsValidOffset(next_offset)) {
1334*890232f2SAndroid Build Coastguard Worker // TODO(dbaileychess): figure out how we get into this situation.
1335*890232f2SAndroid Build Coastguard Worker continue;
1336*890232f2SAndroid Build Coastguard Worker }
1337*890232f2SAndroid Build Coastguard Worker
1338*890232f2SAndroid Build Coastguard Worker if (offset < next_offset) {
1339*890232f2SAndroid Build Coastguard Worker const uint64_t padding_bytes = next_offset - offset;
1340*890232f2SAndroid Build Coastguard Worker
1341*890232f2SAndroid Build Coastguard Worker BinaryRegionComment comment;
1342*890232f2SAndroid Build Coastguard Worker comment.type = BinaryRegionCommentType::Padding;
1343*890232f2SAndroid Build Coastguard Worker
1344*890232f2SAndroid Build Coastguard Worker if (IsNonZeroRegion(offset, padding_bytes, binary_)) {
1345*890232f2SAndroid Build Coastguard Worker SetError(comment, BinaryRegionStatus::WARN_NO_REFERENCES);
1346*890232f2SAndroid Build Coastguard Worker regions_to_insert.push_back(
1347*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(offset, padding_bytes, BinaryRegionType::Unknown,
1348*890232f2SAndroid Build Coastguard Worker padding_bytes, 0, comment));
1349*890232f2SAndroid Build Coastguard Worker } else {
1350*890232f2SAndroid Build Coastguard Worker regions_to_insert.push_back(
1351*890232f2SAndroid Build Coastguard Worker MakeBinaryRegion(offset, padding_bytes, BinaryRegionType::Uint8,
1352*890232f2SAndroid Build Coastguard Worker padding_bytes, 0, comment));
1353*890232f2SAndroid Build Coastguard Worker }
1354*890232f2SAndroid Build Coastguard Worker }
1355*890232f2SAndroid Build Coastguard Worker offset = next_offset + region.length;
1356*890232f2SAndroid Build Coastguard Worker }
1357*890232f2SAndroid Build Coastguard Worker
1358*890232f2SAndroid Build Coastguard Worker if (!regions_to_insert.empty()) {
1359*890232f2SAndroid Build Coastguard Worker section.regions.insert(section.regions.end(), regions_to_insert.begin(),
1360*890232f2SAndroid Build Coastguard Worker regions_to_insert.end());
1361*890232f2SAndroid Build Coastguard Worker std::stable_sort(section.regions.begin(), section.regions.end(),
1362*890232f2SAndroid Build Coastguard Worker BinaryRegionSort);
1363*890232f2SAndroid Build Coastguard Worker regions_to_insert.clear();
1364*890232f2SAndroid Build Coastguard Worker }
1365*890232f2SAndroid Build Coastguard Worker }
1366*890232f2SAndroid Build Coastguard Worker }
1367*890232f2SAndroid Build Coastguard Worker
FixMissingSections()1368*890232f2SAndroid Build Coastguard Worker void BinaryAnnotator::FixMissingSections() {
1369*890232f2SAndroid Build Coastguard Worker uint64_t offset = 0;
1370*890232f2SAndroid Build Coastguard Worker
1371*890232f2SAndroid Build Coastguard Worker std::vector<BinarySection> sections_to_insert;
1372*890232f2SAndroid Build Coastguard Worker
1373*890232f2SAndroid Build Coastguard Worker for (auto ¤t_section : sections_) {
1374*890232f2SAndroid Build Coastguard Worker BinarySection §ion = current_section.second;
1375*890232f2SAndroid Build Coastguard Worker const uint64_t section_start_offset = current_section.first;
1376*890232f2SAndroid Build Coastguard Worker const uint64_t section_end_offset =
1377*890232f2SAndroid Build Coastguard Worker section.regions.back().offset + section.regions.back().length;
1378*890232f2SAndroid Build Coastguard Worker
1379*890232f2SAndroid Build Coastguard Worker if (offset < section_start_offset) {
1380*890232f2SAndroid Build Coastguard Worker // We are at an offset that is less then the current section.
1381*890232f2SAndroid Build Coastguard Worker const uint64_t pad_bytes = section_start_offset - offset + 1;
1382*890232f2SAndroid Build Coastguard Worker
1383*890232f2SAndroid Build Coastguard Worker sections_to_insert.push_back(
1384*890232f2SAndroid Build Coastguard Worker GenerateMissingSection(offset - 1, pad_bytes, binary_));
1385*890232f2SAndroid Build Coastguard Worker }
1386*890232f2SAndroid Build Coastguard Worker offset = section_end_offset + 1;
1387*890232f2SAndroid Build Coastguard Worker }
1388*890232f2SAndroid Build Coastguard Worker
1389*890232f2SAndroid Build Coastguard Worker // Handle the case where there are still bytes left in the binary that are
1390*890232f2SAndroid Build Coastguard Worker // unaccounted for.
1391*890232f2SAndroid Build Coastguard Worker if (offset < binary_length_) {
1392*890232f2SAndroid Build Coastguard Worker const uint64_t pad_bytes = binary_length_ - offset + 1;
1393*890232f2SAndroid Build Coastguard Worker sections_to_insert.push_back(
1394*890232f2SAndroid Build Coastguard Worker GenerateMissingSection(offset - 1, pad_bytes, binary_));
1395*890232f2SAndroid Build Coastguard Worker }
1396*890232f2SAndroid Build Coastguard Worker
1397*890232f2SAndroid Build Coastguard Worker for (const BinarySection §ion_to_insert : sections_to_insert) {
1398*890232f2SAndroid Build Coastguard Worker AddSection(section_to_insert.regions[0].offset, section_to_insert);
1399*890232f2SAndroid Build Coastguard Worker }
1400*890232f2SAndroid Build Coastguard Worker }
1401*890232f2SAndroid Build Coastguard Worker
ContainsSection(const uint64_t offset)1402*890232f2SAndroid Build Coastguard Worker bool BinaryAnnotator::ContainsSection(const uint64_t offset) {
1403*890232f2SAndroid Build Coastguard Worker auto it = sections_.lower_bound(offset);
1404*890232f2SAndroid Build Coastguard Worker // If the section is found, check that it is exactly equal its offset.
1405*890232f2SAndroid Build Coastguard Worker if (it != sections_.end() && it->first == offset) { return true; }
1406*890232f2SAndroid Build Coastguard Worker
1407*890232f2SAndroid Build Coastguard Worker // If this was the first section, there are no other previous sections to
1408*890232f2SAndroid Build Coastguard Worker // check.
1409*890232f2SAndroid Build Coastguard Worker if (it == sections_.begin()) { return false; }
1410*890232f2SAndroid Build Coastguard Worker
1411*890232f2SAndroid Build Coastguard Worker // Go back one section.
1412*890232f2SAndroid Build Coastguard Worker --it;
1413*890232f2SAndroid Build Coastguard Worker
1414*890232f2SAndroid Build Coastguard Worker // And check that if the offset is covered by the section.
1415*890232f2SAndroid Build Coastguard Worker return offset >= it->first && offset < it->second.regions.back().offset +
1416*890232f2SAndroid Build Coastguard Worker it->second.regions.back().length;
1417*890232f2SAndroid Build Coastguard Worker }
1418*890232f2SAndroid Build Coastguard Worker
1419*890232f2SAndroid Build Coastguard Worker } // namespace flatbuffers