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
12namespace v8 {
13namespace internal {
14
15class CodeReference;
16class 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).
23enum 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
35class 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 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 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 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 return mode == EXTERNAL_REFERENCE || mode == OFF_HEAP_TARGET;
176#endif
177 }
178
179 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 void Visit(ObjectVisitor* visitor) {
282 Mode mode = rmode();
283 if (IsEmbeddedObject(mode)) {
284 visitor->VisitEmbeddedPointer(host(), this);
285 } else if (IsCodeTargetMode(mode)) {
286 visitor->VisitCodeTarget(host(), this);
287 } else if (IsExternalReference(mode)) {
288 visitor->VisitExternalReference(host(), this);
289 } else if (IsInternalReference(mode) || IsInternalReferenceEncoded(mode)) {
290 visitor->VisitInternalReference(host(), this);
291 } else if (IsRuntimeEntry(mode)) {
292 visitor->VisitRuntimeEntry(host(), this);
293 } else if (IsOffHeapTarget(mode)) {
294 visitor->VisitOffHeapTarget(host(), this);
295 }
296 }
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 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.
337class RelocInfoWriter {
338 public:
339 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 pos_ = pos;
350 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.
382class 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 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 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 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_
448