xref: /aosp_15_r20/external/flatbuffers/src/binary_annotator.cpp (revision 890232f25432b36107d06881e0a25aaa6b473652)
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 &region) {
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> &regions,
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 &current_section : sections_) {
1322*890232f2SAndroid Build Coastguard Worker     BinarySection &section = 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 &region = 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 &current_section : sections_) {
1374*890232f2SAndroid Build Coastguard Worker     BinarySection &section = 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 &section_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