[arm] Add missing RELATIVE_CODE_TARGET iteration

Code object iteration was missing logic for RELATIVE_CODE_TARGET
reloc entries. Garbage collection could thus miss objects that were
referenced only as targets of pc-relative calls or jumps.

RELATIVE_CODE_TARGETs are only used on arm, mips, and s390 and only
at mksnapshot-time.

This exposed another issue in that the interpreter entry trampoline
copy we generate for profiling *did* contain relative calls in
runtime-accessible code. This is a problem, since code space on arm is,
by default, too large to be fully addressable through pc-relative
calls. This CL thus also disables the related
FLAG_interpreted_frames_native_stack feature on arm.

Drive-by: Ensure the builtins constants table does not contain Code
objects.

Bug: v8:8713,v8:6666
Change-Id: Idd914b46970ad08f9091fc72113fa7aed2732e71
Reviewed-on: https://chromium-review.googlesource.com/c/1424866
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59023}
This commit is contained in:
Jakob Gruber 2019-01-22 15:45:10 +01:00 committed by Commit Bot
parent ea513ab813
commit b766299d2c
13 changed files with 69 additions and 25 deletions

View File

@ -79,9 +79,12 @@ Address RelocInfo::target_address_address() {
DCHECK(HasTargetAddressAddress());
if (Assembler::IsMovW(Memory<int32_t>(pc_))) {
return pc_;
} else {
DCHECK(Assembler::IsLdrPcImmediateOffset(Memory<int32_t>(pc_)));
} else if (Assembler::IsLdrPcImmediateOffset(Memory<int32_t>(pc_))) {
return constant_pool_entry_address();
} else {
DCHECK(Assembler::IsBOrBlPcImmediateOffset(Memory<int32_t>(pc_)));
DCHECK(IsRelativeCodeTarget(rmode_));
return pc_;
}
}
@ -295,6 +298,7 @@ Address Assembler::return_address_from_call_start(Address pc) {
void Assembler::deserialization_set_special_target_at(
Address constant_pool_entry, Code code, Address target) {
DCHECK(!Builtins::IsIsolateIndependentBuiltin(code));
Memory<Address>(constant_pool_entry) = target;
}

View File

@ -488,6 +488,11 @@ const Instr kPopRegPattern = al | B26 | L | 4 | PostIndex | sp.code() * B16;
// ldr rd, [pc, #offset]
const Instr kLdrPCImmedMask = 15 * B24 | 7 * B20 | 15 * B16;
const Instr kLdrPCImmedPattern = 5 * B24 | L | pc.code() * B16;
// Pc-relative call or jump to a signed imm24 offset.
// bl pc + #offset
// b pc + #offset
const Instr kBOrBlPCImmedMask = 0xE * B24;
const Instr kBOrBlPCImmedPattern = 0xA * B24;
// vldr dd, [pc, #offset]
const Instr kVldrDPCMask = 15 * B24 | 3 * B20 | 15 * B16 | 15 * B8;
const Instr kVldrDPCPattern = 13 * B24 | L | pc.code() * B16 | 11 * B8;
@ -730,6 +735,9 @@ bool Assembler::IsLdrPcImmediateOffset(Instr instr) {
return (instr & kLdrPCImmedMask) == kLdrPCImmedPattern;
}
bool Assembler::IsBOrBlPcImmediateOffset(Instr instr) {
return (instr & kBOrBlPCImmedMask) == kBOrBlPCImmedPattern;
}
bool Assembler::IsVldrDPcImmediateOffset(Instr instr) {
// Check the instruction is indeed a

View File

@ -1157,6 +1157,7 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
static bool IsStrRegFpNegOffset(Instr instr);
static bool IsLdrRegFpNegOffset(Instr instr);
static bool IsLdrPcImmediateOffset(Instr instr);
static bool IsBOrBlPcImmediateOffset(Instr instr);
static bool IsVldrDPcImmediateOffset(Instr instr);
static bool IsBlxReg(Instr instr);
static bool IsBlxIp(Instr instr);

View File

@ -140,8 +140,8 @@ TF_BUILTIN(FastNewClosure, ConstructorBuiltinsAssembler) {
StoreObjectFieldNoWriteBarrier(result, JSFunction::kSharedFunctionInfoOffset,
shared_function_info);
StoreObjectFieldNoWriteBarrier(result, JSFunction::kContextOffset, context);
Handle<Code> lazy_builtin_handle(
isolate()->builtins()->builtin(Builtins::kCompileLazy), isolate());
Handle<Code> lazy_builtin_handle =
isolate()->builtins()->builtin_handle(Builtins::kCompileLazy);
Node* lazy_builtin = HeapConstant(lazy_builtin_handle);
StoreObjectFieldNoWriteBarrier(result, JSFunction::kCodeOffset, lazy_builtin);
Return(result);

View File

@ -40,6 +40,10 @@ uint32_t BuiltinsConstantsTableBuilder::AddObject(Handle<Object> object) {
// Must be generating embedded builtin code.
DCHECK(isolate_->ShouldLoadConstantsFromRootList());
// All code objects should be loaded through the root register or use
// pc-relative addressing.
DCHECK(!object->IsCode());
#endif
uint32_t* maybe_key = map_.Find(object);

View File

@ -1311,9 +1311,6 @@ DEFINE_BOOL(prof_browser_mode, true,
DEFINE_STRING(logfile, "v8.log", "Specify the name of the log file.")
DEFINE_BOOL(logfile_per_isolate, true, "Separate log files for each isolate.")
DEFINE_BOOL(ll_prof, false, "Enable low-level linux profiler.")
DEFINE_BOOL(interpreted_frames_native_stack, false,
"Show interpreted frames on the native stack (useful for external "
"profilers).")
DEFINE_BOOL(perf_basic_prof, false,
"Enable perf linux profiler (basic support).")
DEFINE_NEG_IMPLICATION(perf_basic_prof, compact_code_space)
@ -1351,6 +1348,18 @@ DEFINE_STRING(redirect_code_traces_to, nullptr,
DEFINE_BOOL(print_opt_source, false,
"print source code of optimized and inlined functions")
#ifdef V8_TARGET_ARCH_ARM
// Unsupported on arm. See https://crbug.com/v8/8713.
DEFINE_BOOL_READONLY(
interpreted_frames_native_stack, false,
"Show interpreted frames on the native stack (useful for external "
"profilers).")
#else
DEFINE_BOOL(interpreted_frames_native_stack, false,
"Show interpreted frames on the native stack (useful for external "
"profilers).")
#endif
//
// Disassembler only flags
//

View File

@ -3283,17 +3283,22 @@ bool Isolate::Init(StartupDeserializer* des) {
builtins_constants_table_builder_ = new BuiltinsConstantsTableBuilder(this);
}
setup_delegate_->SetupBuiltins(this);
#ifndef V8_TARGET_ARCH_ARM
if (create_heap_objects) {
// Create a copy of the the interpreter entry trampoline and store it
// on the root list. It is used as a template for further copies that
// may later be created to help profile interpreted code.
// TODO(jgruber): Merge this with the block below once
// FLAG_embedded_builtins is always true.
// We currently cannot do this on arm due to RELATIVE_CODE_TARGETs
// assuming that all possible Code targets may be addressed with an int24
// offset, effectively limiting code space size to 32MB. We can guarantee
// this at mksnapshot-time, but not at runtime.
// See also: https://crbug.com/v8/8713.
HandleScope handle_scope(this);
Handle<Code> code =
factory()->CopyCode(BUILTIN_CODE(this, InterpreterEntryTrampoline));
heap_.SetInterpreterEntryTrampolineForProfiling(*code);
}
#endif
if (FLAG_embedded_builtins && create_heap_objects) {
builtins_constants_table_builder_->Finalize();
delete builtins_constants_table_builder_;

View File

@ -593,18 +593,20 @@ class Code::BodyDescriptor final : public BodyDescriptorBase {
template <typename ObjectVisitor>
static inline void IterateBody(Map map, HeapObject obj, ObjectVisitor* v) {
int mode_mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) |
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
static constexpr int kModeMask =
RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET) |
RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::INTERNAL_REFERENCE_ENCODED) |
RelocInfo::ModeMask(RelocInfo::OFF_HEAP_TARGET) |
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
// GC does not visit data/code in the header and in the body directly.
IteratePointers(obj, kRelocationInfoOffset, kDataStart, v);
RelocIterator it(Code::cast(obj), mode_mask);
RelocIterator it(Code::cast(obj), kModeMask);
v->VisitRelocInfo(&it);
}

View File

@ -382,9 +382,9 @@ bool RelocInfo::HasTargetAddressAddress() const {
ModeMask(RUNTIME_ENTRY) | ModeMask(WASM_CALL) | ModeMask(WASM_STUB_CALL);
#else
static constexpr int kTargetAddressAddressModeMask =
ModeMask(CODE_TARGET) | ModeMask(EMBEDDED_OBJECT) |
ModeMask(EXTERNAL_REFERENCE) | ModeMask(OFF_HEAP_TARGET) |
ModeMask(RUNTIME_ENTRY) | ModeMask(WASM_CALL);
ModeMask(CODE_TARGET) | ModeMask(RELATIVE_CODE_TARGET) |
ModeMask(EMBEDDED_OBJECT) | ModeMask(EXTERNAL_REFERENCE) |
ModeMask(OFF_HEAP_TARGET) | ModeMask(RUNTIME_ENTRY) | ModeMask(WASM_CALL);
#endif
return (ModeMask(rmode_) & kTargetAddressAddressModeMask) != 0;
}

View File

@ -855,6 +855,9 @@ void Serializer::ObjectSerializer::VisitRelocInfo(RelocIterator* it) {
void Serializer::ObjectSerializer::VisitCodeTarget(Code host,
RelocInfo* rinfo) {
#ifdef V8_TARGET_ARCH_ARM
DCHECK(!RelocInfo::IsRelativeCodeTarget(rinfo->rmode()));
#endif
int skip = SkipTo(rinfo->target_address_address());
Code object = Code::GetCodeFromTargetAddress(rinfo->target_address());
serializer_->SerializeObject(object, kFromCode, kInnerPointer, skip);

View File

@ -5008,6 +5008,7 @@ TEST(InterpreterGenerators) {
}
}
#ifndef V8_TARGET_ARCH_ARM
TEST(InterpreterWithNativeStack) {
i::FLAG_interpreted_frames_native_stack = true;
@ -5029,6 +5030,7 @@ TEST(InterpreterWithNativeStack) {
CHECK(code->is_interpreter_trampoline_builtin());
CHECK_NE(code->address(), interpreter_entry_trampoline->address());
}
#endif // V8_TARGET_ARCH_ARM
TEST(InterpreterGetBytecodeHandler) {
HandleAndZoneScope handles;

View File

@ -636,6 +636,7 @@ TEST(LogAll) {
isolate->Dispose();
}
#ifndef V8_TARGET_ARCH_ARM
TEST(LogInterpretedFramesNativeStack) {
SETUP_FLAGS();
i::FLAG_interpreted_frames_native_stack = true;
@ -658,6 +659,7 @@ TEST(LogInterpretedFramesNativeStack) {
}
isolate->Dispose();
}
#endif // V8_TARGET_ARCH_ARM
TEST(ExternalCodeEventListener) {
i::FLAG_log = false;
@ -760,6 +762,7 @@ TEST(ExternalCodeEventListenerInnerFunctions) {
isolate2->Dispose();
}
#ifndef V8_TARGET_ARCH_ARM
TEST(ExternalCodeEventListenerWithInterpretedFramesNativeStack) {
i::FLAG_log = false;
i::FLAG_prof = false;
@ -809,6 +812,7 @@ TEST(ExternalCodeEventListenerWithInterpretedFramesNativeStack) {
}
isolate->Dispose();
}
#endif // V8_TARGET_ARCH_ARM
TEST(TraceMaps) {
SETUP_FLAGS();

View File

@ -65,11 +65,13 @@ bool IsInitiallyMutable(Factory* factory, Address object_address) {
// The CHECK_EQ line is there just to ensure that the root is publicly
// accessible from Heap, but ultimately the factory is used as it provides
// handles that have the address in the root table.
#define CHECK_NOT_IN_RO_SPACE(type, name, CamelName) \
Handle<Object> name = factory->name(); \
CHECK_EQ(*name, heap->name()); \
if (name->IsHeapObject() && IsInitiallyMutable(factory, name.address())) \
CHECK_NE(RO_SPACE, GetSpaceFromObject(HeapObject::cast(*name)));
#define CHECK_NOT_IN_RO_SPACE(type, name, CamelName) \
Handle<Object> name = factory->name(); \
CHECK_EQ(*name, heap->name()); \
if (name->IsHeapObject() && IsInitiallyMutable(factory, name.address()) && \
!name->IsUndefined(CcTest::i_isolate())) { \
CHECK_NE(RO_SPACE, GetSpaceFromObject(HeapObject::cast(*name))); \
}
// The following tests check that all the roots accessible via public Heap
// accessors are not in RO_SPACE with the exception of the objects listed in