OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. |
| 14 |
| 15 #include "minidump/minidump_writable.h" |
| 16 |
| 17 #include "base/logging.h" |
| 18 #include "util/numeric/safe_assignment.h" |
| 19 |
| 20 namespace { |
| 21 |
| 22 const size_t kMaximumAlignment = 16; |
| 23 |
| 24 } // namespace |
| 25 |
| 26 namespace crashpad { |
| 27 namespace internal { |
| 28 |
| 29 bool MinidumpWritable::WriteEverything(FileWriterInterface* file_writer) { |
| 30 DCHECK_EQ(state_, kStateMutable); |
| 31 |
| 32 if (!Freeze()) { |
| 33 return false; |
| 34 } |
| 35 |
| 36 DCHECK_EQ(state_, kStateFrozen); |
| 37 |
| 38 off_t offset = 0; |
| 39 std::vector<MinidumpWritable*> write_sequence; |
| 40 size_t size = WillWriteAtOffset(kPhaseEarly, &offset, &write_sequence); |
| 41 if (size == kInvalidSize) { |
| 42 return false; |
| 43 } |
| 44 |
| 45 offset += size; |
| 46 if (WillWriteAtOffset(kPhaseLate, &offset, &write_sequence) == kInvalidSize) { |
| 47 return false; |
| 48 } |
| 49 |
| 50 DCHECK_EQ(state_, kStateWritable); |
| 51 DCHECK_EQ(write_sequence.front(), this); |
| 52 |
| 53 for (MinidumpWritable* writable : write_sequence) { |
| 54 if (!writable->WritePaddingAndObject(file_writer)) { |
| 55 return false; |
| 56 } |
| 57 } |
| 58 |
| 59 DCHECK_EQ(state_, kStateWritten); |
| 60 |
| 61 return true; |
| 62 } |
| 63 |
| 64 void MinidumpWritable::RegisterRVA(RVA* rva) { |
| 65 DCHECK_LE(state_, kStateFrozen); |
| 66 |
| 67 registered_rvas_.push_back(rva); |
| 68 } |
| 69 |
| 70 void MinidumpWritable::RegisterLocationDescriptor( |
| 71 MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor) { |
| 72 DCHECK_LE(state_, kStateFrozen); |
| 73 |
| 74 registered_location_descriptors_.push_back(location_descriptor); |
| 75 } |
| 76 |
| 77 const size_t MinidumpWritable::kInvalidSize = |
| 78 std::numeric_limits<size_t>::max(); |
| 79 |
| 80 MinidumpWritable::MinidumpWritable() |
| 81 : registered_rvas_(), |
| 82 registered_location_descriptors_(), |
| 83 leading_pad_bytes_(0), |
| 84 state_(kStateMutable) { |
| 85 } |
| 86 |
| 87 MinidumpWritable::~MinidumpWritable() { |
| 88 } |
| 89 |
| 90 bool MinidumpWritable::Freeze() { |
| 91 DCHECK_EQ(state_, kStateMutable); |
| 92 state_ = kStateFrozen; |
| 93 |
| 94 std::vector<MinidumpWritable*> children = Children(); |
| 95 for (MinidumpWritable* child : children) { |
| 96 if (!child->Freeze()) { |
| 97 return false; |
| 98 } |
| 99 } |
| 100 |
| 101 return true; |
| 102 } |
| 103 |
| 104 size_t MinidumpWritable::Alignment() { |
| 105 DCHECK_GE(state_, kStateFrozen); |
| 106 |
| 107 return 4; |
| 108 } |
| 109 |
| 110 std::vector<MinidumpWritable*> MinidumpWritable::Children() { |
| 111 DCHECK_GE(state_, kStateFrozen); |
| 112 |
| 113 return std::vector<MinidumpWritable*>(); |
| 114 } |
| 115 |
| 116 MinidumpWritable::Phase MinidumpWritable::WritePhase() { |
| 117 return kPhaseEarly; |
| 118 } |
| 119 |
| 120 size_t MinidumpWritable::WillWriteAtOffset( |
| 121 Phase phase, |
| 122 off_t* offset, |
| 123 std::vector<MinidumpWritable*>* write_sequence) { |
| 124 off_t local_offset = *offset; |
| 125 CHECK_GE(local_offset, 0); |
| 126 |
| 127 size_t leading_pad_bytes_this_phase; |
| 128 size_t size; |
| 129 if (phase == WritePhase()) { |
| 130 DCHECK_EQ(state_, kStateFrozen); |
| 131 |
| 132 // Add this object to the sequence of MinidumpWritable objects to be |
| 133 // written. |
| 134 write_sequence->push_back(this); |
| 135 |
| 136 size = SizeOfObject(); |
| 137 |
| 138 if (size > 0) { |
| 139 // Honor this object’s request to be aligned to a specific byte boundary. |
| 140 // Once the alignment is corrected, this object knows exactly what file |
| 141 // offset it will be written at. |
| 142 size_t alignment = Alignment(); |
| 143 CHECK_LE(alignment, kMaximumAlignment); |
| 144 |
| 145 leading_pad_bytes_this_phase = |
| 146 (alignment - (local_offset % alignment)) % alignment; |
| 147 local_offset += leading_pad_bytes_this_phase; |
| 148 *offset = local_offset; |
| 149 } else { |
| 150 // If the object is size 0, alignment is of no concern. |
| 151 leading_pad_bytes_this_phase = 0; |
| 152 } |
| 153 leading_pad_bytes_ = leading_pad_bytes_this_phase; |
| 154 |
| 155 // Now that the file offset that this object will be written at is known, |
| 156 // let the subclass implementation know in case it’s interested. |
| 157 if (!WillWriteAtOffsetImpl(local_offset)) { |
| 158 return kInvalidSize; |
| 159 } |
| 160 |
| 161 // Populate the RVA fields in other objects that have registered to point to |
| 162 // this one. Typically, a parent object will have registered to point to its |
| 163 // children, but this can also occur where no parent-child relationship |
| 164 // exists. |
| 165 if (!registered_rvas_.empty() || |
| 166 !registered_location_descriptors_.empty()) { |
| 167 RVA local_rva; |
| 168 if (!AssignIfInRange(&local_rva, local_offset)) { |
| 169 LOG(ERROR) << "offset " << local_offset << " out of range"; |
| 170 return kInvalidSize; |
| 171 } |
| 172 |
| 173 for (RVA* rva : registered_rvas_) { |
| 174 *rva = local_rva; |
| 175 } |
| 176 |
| 177 if (!registered_location_descriptors_.empty()) { |
| 178 typeof(registered_location_descriptors_[0]->DataSize) local_size; |
| 179 if (!AssignIfInRange(&local_size, size)) { |
| 180 LOG(ERROR) << "size " << size << " out of range"; |
| 181 return kInvalidSize; |
| 182 } |
| 183 |
| 184 for (MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor : |
| 185 registered_location_descriptors_) { |
| 186 location_descriptor->DataSize = local_size; |
| 187 location_descriptor->Rva = local_rva; |
| 188 } |
| 189 } |
| 190 } |
| 191 |
| 192 // This object is now considered writable. However, if it contains RVA or |
| 193 // MINIDUMP_LOCATION_DESCRIPTOR fields, they may not be fully updated yet, |
| 194 // because it’s the repsonsibility of these fields’ pointees to update them. |
| 195 // Once WillWriteAtOffset has completed running for both phases on an entire |
| 196 // tree, and the entire tree has moved into kStateFrozen, all RVA and |
| 197 // MINIDUMP_LOCATION_DESCRIPTOR fields within that tree will be populated. |
| 198 state_ = kStateWritable; |
| 199 } else { |
| 200 if (phase == kPhaseEarly) { |
| 201 DCHECK_EQ(state_, kStateFrozen); |
| 202 } else { |
| 203 DCHECK_EQ(state_, kStateWritable); |
| 204 } |
| 205 |
| 206 size = 0; |
| 207 leading_pad_bytes_this_phase = 0; |
| 208 } |
| 209 |
| 210 // Loop over children regardless of whether this object itself will write |
| 211 // during this phase. An object’s children are not required to be written |
| 212 // during the same phase as their parent. |
| 213 std::vector<MinidumpWritable*> children = Children(); |
| 214 for (MinidumpWritable* child : children) { |
| 215 // Use “auto” here because it’s impossible to know whether size_t (size) or |
| 216 // off_t (local_offset) is the wider type, and thus what type the result of |
| 217 // adding these two variables will have. |
| 218 auto unaligned_child_offset = local_offset + size; |
| 219 off_t child_offset; |
| 220 if (!AssignIfInRange(&child_offset, unaligned_child_offset)) { |
| 221 LOG(ERROR) << "offset " << unaligned_child_offset << " out of range"; |
| 222 return kInvalidSize; |
| 223 } |
| 224 |
| 225 size_t child_size = |
| 226 child->WillWriteAtOffset(phase, &child_offset, write_sequence); |
| 227 if (child_size == kInvalidSize) { |
| 228 return kInvalidSize; |
| 229 } |
| 230 |
| 231 size += child_size; |
| 232 } |
| 233 |
| 234 return leading_pad_bytes_this_phase + size; |
| 235 } |
| 236 |
| 237 bool MinidumpWritable::WillWriteAtOffsetImpl(off_t offset) { |
| 238 return true; |
| 239 } |
| 240 |
| 241 bool MinidumpWritable::WritePaddingAndObject(FileWriterInterface* file_writer) { |
| 242 DCHECK_EQ(state_, kStateWritable); |
| 243 |
| 244 // The number of elements in kZeroes must be at least one less than the |
| 245 // maximum Alignment() ever encountered. |
| 246 const uint8_t kZeroes[kMaximumAlignment - 1] = {}; |
| 247 DCHECK_LE(leading_pad_bytes_, arraysize(kZeroes)); |
| 248 |
| 249 if (leading_pad_bytes_) { |
| 250 if (!file_writer->Write(&kZeroes, leading_pad_bytes_)) { |
| 251 return false; |
| 252 } |
| 253 } |
| 254 |
| 255 if (!WriteObject(file_writer)) { |
| 256 return false; |
| 257 } |
| 258 |
| 259 state_ = kStateWritten; |
| 260 return true; |
| 261 } |
| 262 |
| 263 } // namespace internal |
| 264 } // namespace crashpad |
OLD | NEW |