Line data Source code
1 : // Copyright 2018 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #ifndef V8_RELOC_INFO_H_
6 : #define V8_RELOC_INFO_H_
7 :
8 : #include "src/flush-instruction-cache.h"
9 : #include "src/globals.h"
10 : #include "src/objects/code.h"
11 :
12 : namespace v8 {
13 : namespace internal {
14 :
15 : class CodeReference;
16 : class EmbeddedData;
17 :
18 : // Specifies whether to perform icache flush operations on RelocInfo updates.
19 : // If FLUSH_ICACHE_IF_NEEDED, the icache will always be flushed if an
20 : // instruction was modified. If SKIP_ICACHE_FLUSH the flush will always be
21 : // skipped (only use this if you will flush the icache manually before it is
22 : // executed).
23 : enum ICacheFlushMode { FLUSH_ICACHE_IF_NEEDED, SKIP_ICACHE_FLUSH };
24 :
25 : // -----------------------------------------------------------------------------
26 : // Relocation information
27 :
28 : // Relocation information consists of the address (pc) of the datum
29 : // to which the relocation information applies, the relocation mode
30 : // (rmode), and an optional data field. The relocation mode may be
31 : // "descriptive" and not indicate a need for relocation, but simply
32 : // describe a property of the datum. Such rmodes are useful for GC
33 : // and nice disassembly output.
34 :
35 : class RelocInfo {
36 : public:
37 : // This string is used to add padding comments to the reloc info in cases
38 : // where we are not sure to have enough space for patching in during
39 : // lazy deoptimization. This is the case if we have indirect calls for which
40 : // we do not normally record relocation info.
41 : static const char* const kFillerCommentString;
42 :
43 : // The minimum size of a comment is equal to two bytes for the extra tagged
44 : // pc and kSystemPointerSize for the actual pointer to the comment.
45 : static const int kMinRelocCommentSize = 2 + kSystemPointerSize;
46 :
47 : // The maximum size for a call instruction including pc-jump.
48 : static const int kMaxCallSize = 6;
49 :
50 : // The maximum pc delta that will use the short encoding.
51 : static const int kMaxSmallPCDelta;
52 :
53 : enum Mode : int8_t {
54 : // Please note the order is important (see IsRealRelocMode, IsGCRelocMode,
55 : // and IsShareableRelocMode predicates below).
56 :
57 : CODE_TARGET,
58 : RELATIVE_CODE_TARGET, // LAST_CODE_TARGET_MODE
59 : EMBEDDED_OBJECT, // LAST_GCED_ENUM
60 :
61 : WASM_CALL, // FIRST_SHAREABLE_RELOC_MODE
62 : WASM_STUB_CALL,
63 :
64 : RUNTIME_ENTRY,
65 :
66 : EXTERNAL_REFERENCE, // The address of an external C++ function.
67 : INTERNAL_REFERENCE, // An address inside the same function.
68 :
69 : // Encoded internal reference, used only on MIPS, MIPS64 and PPC.
70 : INTERNAL_REFERENCE_ENCODED,
71 :
72 : // An off-heap instruction stream target. See http://goo.gl/Z2HUiM.
73 : OFF_HEAP_TARGET,
74 :
75 : // Marks constant and veneer pools. Only used on ARM and ARM64.
76 : // They use a custom noncompact encoding.
77 : CONST_POOL,
78 : VENEER_POOL,
79 :
80 : DEOPT_SCRIPT_OFFSET,
81 : DEOPT_INLINING_ID, // Deoptimization source position.
82 : DEOPT_REASON, // Deoptimization reason index.
83 : DEOPT_ID, // Deoptimization inlining id.
84 :
85 : // This is not an actual reloc mode, but used to encode a long pc jump that
86 : // cannot be encoded as part of another record.
87 : PC_JUMP,
88 :
89 : // Pseudo-types
90 : NUMBER_OF_MODES,
91 : NONE, // never recorded value
92 :
93 : LAST_CODE_TARGET_MODE = RELATIVE_CODE_TARGET,
94 : FIRST_REAL_RELOC_MODE = CODE_TARGET,
95 : LAST_REAL_RELOC_MODE = VENEER_POOL,
96 : LAST_GCED_ENUM = EMBEDDED_OBJECT,
97 : FIRST_SHAREABLE_RELOC_MODE = WASM_CALL,
98 : };
99 :
100 : STATIC_ASSERT(NUMBER_OF_MODES <= kBitsPerInt);
101 :
102 415569348 : RelocInfo() = default;
103 :
104 : RelocInfo(Address pc, Mode rmode, intptr_t data, Code host,
105 : Address constant_pool = kNullAddress)
106 : : pc_(pc),
107 : rmode_(rmode),
108 : data_(data),
109 : host_(host),
110 12267584 : constant_pool_(constant_pool) {}
111 :
112 : static constexpr bool IsRealRelocMode(Mode mode) {
113 : return mode >= FIRST_REAL_RELOC_MODE && mode <= LAST_REAL_RELOC_MODE;
114 : }
115 : // Is the relocation mode affected by GC?
116 : static constexpr bool IsGCRelocMode(Mode mode) {
117 : return mode <= LAST_GCED_ENUM;
118 : }
119 : static constexpr bool IsShareableRelocMode(Mode mode) {
120 : static_assert(RelocInfo::NONE >= RelocInfo::FIRST_SHAREABLE_RELOC_MODE,
121 : "Users of this function rely on NONE being a sharable "
122 : "relocation mode.");
123 : return mode >= RelocInfo::FIRST_SHAREABLE_RELOC_MODE;
124 : }
125 : static constexpr bool IsCodeTarget(Mode mode) { return mode == CODE_TARGET; }
126 : static constexpr bool IsCodeTargetMode(Mode mode) {
127 : return mode <= LAST_CODE_TARGET_MODE;
128 : }
129 : static constexpr bool IsRelativeCodeTarget(Mode mode) {
130 : return mode == RELATIVE_CODE_TARGET;
131 : }
132 : static constexpr bool IsEmbeddedObject(Mode mode) {
133 : return mode == EMBEDDED_OBJECT;
134 : }
135 : static constexpr bool IsRuntimeEntry(Mode mode) {
136 : return mode == RUNTIME_ENTRY;
137 : }
138 : static constexpr bool IsWasmCall(Mode mode) { return mode == WASM_CALL; }
139 : static constexpr bool IsWasmReference(Mode mode) { return mode == WASM_CALL; }
140 : static constexpr bool IsWasmStubCall(Mode mode) {
141 : return mode == WASM_STUB_CALL;
142 : }
143 : static constexpr bool IsConstPool(Mode mode) { return mode == CONST_POOL; }
144 : static constexpr bool IsVeneerPool(Mode mode) { return mode == VENEER_POOL; }
145 : static constexpr bool IsDeoptPosition(Mode mode) {
146 215651616 : return mode == DEOPT_SCRIPT_OFFSET || mode == DEOPT_INLINING_ID;
147 : }
148 : static constexpr bool IsDeoptReason(Mode mode) {
149 : return mode == DEOPT_REASON;
150 : }
151 : static constexpr bool IsDeoptId(Mode mode) { return mode == DEOPT_ID; }
152 : static constexpr bool IsExternalReference(Mode mode) {
153 : return mode == EXTERNAL_REFERENCE;
154 : }
155 : static constexpr bool IsInternalReference(Mode mode) {
156 : return mode == INTERNAL_REFERENCE;
157 : }
158 : static constexpr bool IsInternalReferenceEncoded(Mode mode) {
159 : return mode == INTERNAL_REFERENCE_ENCODED;
160 : }
161 : static constexpr bool IsOffHeapTarget(Mode mode) {
162 : return mode == OFF_HEAP_TARGET;
163 : }
164 : static constexpr bool IsNone(Mode mode) { return mode == NONE; }
165 :
166 : static bool IsOnlyForSerializer(Mode mode) {
167 : #ifdef V8_TARGET_ARCH_IA32
168 : // On ia32, inlined off-heap trampolines must be relocated.
169 : DCHECK_NE((kApplyMask & ModeMask(OFF_HEAP_TARGET)), 0);
170 : DCHECK_EQ((kApplyMask & ModeMask(EXTERNAL_REFERENCE)), 0);
171 : return mode == EXTERNAL_REFERENCE;
172 : #else
173 : DCHECK_EQ((kApplyMask & ModeMask(OFF_HEAP_TARGET)), 0);
174 : DCHECK_EQ((kApplyMask & ModeMask(EXTERNAL_REFERENCE)), 0);
175 17861280 : return mode == EXTERNAL_REFERENCE || mode == OFF_HEAP_TARGET;
176 : #endif
177 : }
178 :
179 0 : static constexpr int ModeMask(Mode mode) { return 1 << mode; }
180 :
181 : // Accessors
182 : Address pc() const { return pc_; }
183 : Mode rmode() const { return rmode_; }
184 : intptr_t data() const { return data_; }
185 : Code host() const { return host_; }
186 : Address constant_pool() const { return constant_pool_; }
187 :
188 : // Apply a relocation by delta bytes. When the code object is moved, PC
189 : // relative addresses have to be updated as well as absolute addresses
190 : // inside the code (internal references).
191 : // Do not forget to flush the icache afterwards!
192 : V8_INLINE void apply(intptr_t delta);
193 :
194 : // Is the pointer this relocation info refers to coded like a plain pointer
195 : // or is it strange in some way (e.g. relative or patched into a series of
196 : // instructions).
197 : bool IsCodedSpecially();
198 :
199 : // The static pendant to IsCodedSpecially, just for off-heap targets. Used
200 : // during deserialization, when we don't actually have a RelocInfo handy.
201 : static bool OffHeapTargetIsCodedSpecially();
202 :
203 : // If true, the pointer this relocation info refers to is an entry in the
204 : // constant pool, otherwise the pointer is embedded in the instruction stream.
205 : bool IsInConstantPool();
206 :
207 : Address wasm_call_address() const;
208 : Address wasm_stub_call_address() const;
209 :
210 : uint32_t wasm_call_tag() const;
211 :
212 : void set_wasm_call_address(
213 : Address, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
214 : void set_wasm_stub_call_address(
215 : Address, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
216 :
217 : void set_target_address(
218 : Address target,
219 : WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
220 : ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
221 :
222 : // this relocation applies to;
223 : // can only be called if IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
224 : V8_INLINE Address target_address();
225 : V8_INLINE HeapObject target_object();
226 : V8_INLINE Handle<HeapObject> target_object_handle(Assembler* origin);
227 : V8_INLINE void set_target_object(
228 : Heap* heap, HeapObject target,
229 : WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
230 : ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
231 : V8_INLINE Address target_runtime_entry(Assembler* origin);
232 : V8_INLINE void set_target_runtime_entry(
233 : Address target,
234 : WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
235 : ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
236 : V8_INLINE Address target_off_heap_target();
237 : V8_INLINE void set_target_external_reference(
238 : Address, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
239 :
240 : // Returns the address of the constant pool entry where the target address
241 : // is held. This should only be called if IsInConstantPool returns true.
242 : V8_INLINE Address constant_pool_entry_address();
243 :
244 : // Read the address of the word containing the target_address in an
245 : // instruction stream. What this means exactly is architecture-independent.
246 : // The only architecture-independent user of this function is the serializer.
247 : // The serializer uses it to find out how many raw bytes of instruction to
248 : // output before the next target. Architecture-independent code shouldn't
249 : // dereference the pointer it gets back from this.
250 : V8_INLINE Address target_address_address();
251 : bool HasTargetAddressAddress() const;
252 :
253 : // This indicates how much space a target takes up when deserializing a code
254 : // stream. For most architectures this is just the size of a pointer. For
255 : // an instruction like movw/movt where the target bits are mixed into the
256 : // instruction bits the size of the target will be zero, indicating that the
257 : // serializer should not step forwards in memory after a target is resolved
258 : // and written. In this case the target_address_address function above
259 : // should return the end of the instructions to be patched, allowing the
260 : // deserializer to deserialize the instructions as raw bytes and put them in
261 : // place, ready to be patched with the target.
262 : V8_INLINE int target_address_size();
263 :
264 : // Read the reference in the instruction this relocation
265 : // applies to; can only be called if rmode_ is EXTERNAL_REFERENCE.
266 : V8_INLINE Address target_external_reference();
267 :
268 : // Read the reference in the instruction this relocation
269 : // applies to; can only be called if rmode_ is INTERNAL_REFERENCE.
270 : V8_INLINE Address target_internal_reference();
271 :
272 : // Return the reference address this relocation applies to;
273 : // can only be called if rmode_ is INTERNAL_REFERENCE.
274 : V8_INLINE Address target_internal_reference_address();
275 :
276 : // Wipe out a relocation to a fixed value, used for making snapshots
277 : // reproducible.
278 : V8_INLINE void WipeOut();
279 :
280 : template <typename ObjectVisitor>
281 211138474 : void Visit(ObjectVisitor* visitor) {
282 : Mode mode = rmode();
283 211138474 : if (IsEmbeddedObject(mode)) {
284 4042390 : visitor->VisitEmbeddedPointer(host(), this);
285 207096084 : } else if (IsCodeTargetMode(mode)) {
286 1014186 : visitor->VisitCodeTarget(host(), this);
287 206081898 : } else if (IsExternalReference(mode)) {
288 2442 : visitor->VisitExternalReference(host(), this);
289 206079456 : } else if (IsInternalReference(mode) || IsInternalReferenceEncoded(mode)) {
290 42 : visitor->VisitInternalReference(host(), this);
291 206079414 : } else if (IsRuntimeEntry(mode)) {
292 2264926 : visitor->VisitRuntimeEntry(host(), this);
293 203814488 : } else if (IsOffHeapTarget(mode)) {
294 203879850 : visitor->VisitOffHeapTarget(host(), this);
295 : }
296 210903239 : }
297 :
298 : // Check whether the given code contains relocation information that
299 : // either is position-relative or movable by the garbage collector.
300 : static bool RequiresRelocationAfterCodegen(const CodeDesc& desc);
301 : static bool RequiresRelocation(Code code);
302 :
303 : #ifdef ENABLE_DISASSEMBLER
304 : // Printing
305 : static const char* RelocModeName(Mode rmode);
306 : void Print(Isolate* isolate, std::ostream& os); // NOLINT
307 : #endif // ENABLE_DISASSEMBLER
308 : #ifdef VERIFY_HEAP
309 : void Verify(Isolate* isolate);
310 : #endif
311 :
312 : static const int kApplyMask; // Modes affected by apply. Depends on arch.
313 :
314 : // In addition to modes covered by the apply mask (which is applied at GC
315 : // time, among others), this covers all modes that are relocated by
316 : // Code::CopyFromNoFlush after code generation.
317 : static int PostCodegenRelocationMask() {
318 : return ModeMask(RelocInfo::CODE_TARGET) |
319 : ModeMask(RelocInfo::EMBEDDED_OBJECT) |
320 : ModeMask(RelocInfo::RUNTIME_ENTRY) |
321 1906418 : ModeMask(RelocInfo::RELATIVE_CODE_TARGET) | kApplyMask;
322 : }
323 :
324 : private:
325 : // On ARM/ARM64, note that pc_ is the address of the instruction referencing
326 : // the constant pool and not the address of the constant pool entry.
327 : Address pc_;
328 : Mode rmode_;
329 : intptr_t data_ = 0;
330 : Code host_;
331 : Address constant_pool_ = kNullAddress;
332 : friend class RelocIterator;
333 : };
334 :
335 : // RelocInfoWriter serializes a stream of relocation info. It writes towards
336 : // lower addresses.
337 : class RelocInfoWriter {
338 : public:
339 41305634 : RelocInfoWriter() : pos_(nullptr), last_pc_(nullptr) {}
340 :
341 : byte* pos() const { return pos_; }
342 : byte* last_pc() const { return last_pc_; }
343 :
344 : void Write(const RelocInfo* rinfo);
345 :
346 : // Update the state of the stream after reloc info buffer
347 : // and/or code is moved while the stream is active.
348 : void Reposition(byte* pos, byte* pc) {
349 41356403 : pos_ = pos;
350 41356403 : last_pc_ = pc;
351 : }
352 :
353 : // Max size (bytes) of a written RelocInfo. Longest encoding is
354 : // ExtraTag, VariableLengthPCJump, ExtraTag, pc_delta, data_delta.
355 : static constexpr int kMaxSize = 1 + 4 + 1 + 1 + kSystemPointerSize;
356 :
357 : private:
358 : inline uint32_t WriteLongPCJump(uint32_t pc_delta);
359 :
360 : inline void WriteShortTaggedPC(uint32_t pc_delta, int tag);
361 : inline void WriteShortData(intptr_t data_delta);
362 :
363 : inline void WriteMode(RelocInfo::Mode rmode);
364 : inline void WriteModeAndPC(uint32_t pc_delta, RelocInfo::Mode rmode);
365 : inline void WriteIntData(int data_delta);
366 : inline void WriteData(intptr_t data_delta);
367 :
368 : byte* pos_;
369 : byte* last_pc_;
370 :
371 : DISALLOW_COPY_AND_ASSIGN(RelocInfoWriter);
372 : };
373 :
374 : // A RelocIterator iterates over relocation information.
375 : // Typical use:
376 : //
377 : // for (RelocIterator it(code); !it.done(); it.next()) {
378 : // // do something with it.rinfo() here
379 : // }
380 : //
381 : // A mask can be specified to skip unwanted modes.
382 : class V8_EXPORT_PRIVATE RelocIterator : public Malloced {
383 : public:
384 : // Create a new iterator positioned at
385 : // the beginning of the reloc info.
386 : // Relocation information with mode k is included in the
387 : // iteration iff bit k of mode_mask is set.
388 : explicit RelocIterator(Code code, int mode_mask = -1);
389 : explicit RelocIterator(Code code, ByteArray relocation_info, int mode_mask);
390 : explicit RelocIterator(EmbeddedData* embedded_data, Code code, int mode_mask);
391 : explicit RelocIterator(const CodeDesc& desc, int mode_mask = -1);
392 : explicit RelocIterator(const CodeReference code_reference,
393 : int mode_mask = -1);
394 : explicit RelocIterator(Vector<byte> instructions,
395 : Vector<const byte> reloc_info, Address const_pool,
396 : int mode_mask = -1);
397 : RelocIterator(RelocIterator&&) V8_NOEXCEPT = default;
398 :
399 : // Iteration
400 : bool done() const { return done_; }
401 : void next();
402 :
403 : // Return pointer valid until next next().
404 : RelocInfo* rinfo() {
405 : DCHECK(!done());
406 116066301 : return &rinfo_;
407 : }
408 :
409 : private:
410 : RelocIterator(Code host, Address pc, Address constant_pool, const byte* pos,
411 : const byte* end, int mode_mask);
412 :
413 : // Advance* moves the position before/after reading.
414 : // *Read* reads from current byte(s) into rinfo_.
415 : // *Get* just reads and returns info on current byte.
416 1503666 : void Advance(int bytes = 1) { pos_ -= bytes; }
417 : int AdvanceGetTag();
418 : RelocInfo::Mode GetMode();
419 :
420 : void AdvanceReadLongPCJump();
421 :
422 : void ReadShortTaggedPC();
423 : void ReadShortData();
424 :
425 : void AdvanceReadPC();
426 : void AdvanceReadInt();
427 : void AdvanceReadData();
428 :
429 : // If the given mode is wanted, set it in rinfo_ and return true.
430 : // Else return false. Used for efficiently skipping unwanted modes.
431 : bool SetMode(RelocInfo::Mode mode) {
432 231669401 : return (mode_mask_ & (1 << mode)) ? (rinfo_.rmode_ = mode, true) : false;
433 : }
434 :
435 : const byte* pos_;
436 : const byte* end_;
437 : RelocInfo rinfo_;
438 : bool done_ = false;
439 : const int mode_mask_;
440 :
441 : DISALLOW_COPY_AND_ASSIGN(RelocIterator);
442 : };
443 :
444 : } // namespace internal
445 : } // namespace v8
446 :
447 : #endif // V8_RELOC_INFO_H_
|