Skip to content

Commit 14889cc

Browse files
committed
[vm] Handle Windows unwinding records better in the Mach-O loader.
The Mach-O writer puts the unwinding records in a __unwind_info section, so just find the appropriate section and use its contents instead of forcing it to be at the end of the segment's memory space. Create an overload of UnwindingRecordsPlatform::RegisterExecutableMemory that takes a pointer to the start of the unwinding records for use by the Mach-O loader. Add __unwind_info for any executable segments that are not the text segment as well. Instead of testing host/target windows plus 64-bit arch, use the defines provided by platform/unwinding_records.h in the Mach-O writer and the ELF and Mach-O loaders. TEST=ci on Windows trybots Issue: #60307 Change-Id: Iee53c6725a681f44ac98ed9c9aecf1b75853ece5 Cq-Include-Trybots: luci.dart.try:vm-aot-linux-debug-x64-try,vm-mac-release-arm64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-dwarf-linux-product-x64-try,vm-linux-debug-x64-try,vm-mac-debug-arm64-try,vm-gcc-linux-try,vm-aot-win-release-arm64-try,vm-aot-win-release-x64-try,vm-win-release-x64-try,vm-win-release-arm64-try,vm-aot-win-debug-arm64-try,vm-win-debug-arm64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/432960 Commit-Queue: Tess Strickland <[email protected]> Reviewed-by: Ryan Macnak <[email protected]> Reviewed-by: Slava Egorov <[email protected]>
1 parent b67482d commit 14889cc

File tree

6 files changed

+138
-60
lines changed

6 files changed

+138
-60
lines changed

runtime/bin/elf_loader.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class LoadedElf {
104104
const dart::elf::Symbol* dynamic_symbol_table_ = nullptr;
105105
uword dynamic_symbol_count_ = 0;
106106

107-
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
107+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
108108
// Dynamic table for looking up unwinding exceptions info.
109109
// Initialized by LoadSegments as we load executable segment.
110110
MallocGrowableArray<void*> dynamic_runtime_function_tables_;
@@ -157,7 +157,7 @@ bool LoadedElf::Load() {
157157
}
158158

159159
LoadedElf::~LoadedElf() {
160-
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
160+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
161161
for (intptr_t i = 0; i < dynamic_runtime_function_tables_.length(); i++) {
162162
UnwindingRecordsPlatform::UnregisterDynamicTable(
163163
dynamic_runtime_function_tables_[i]);
@@ -326,7 +326,7 @@ bool LoadedElf::LoadSegments() {
326326
CHECK_ERROR(memory != nullptr, "Could not map segment.");
327327
CHECK_ERROR(memory->address() == memory_start,
328328
"Mapping not at requested address.");
329-
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
329+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
330330
// For executable pages register unwinding information that should be
331331
// present on the page.
332332
if (map_type == File::kReadExecute) {

runtime/bin/macho_loader.cc

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace bin {
2525

2626
namespace mach_o {
2727

28-
class LoadCommandIterator {
28+
class LoadCommandIterator : public ValueObject {
2929
public:
3030
LoadCommandIterator(const void* start, size_t size)
3131
: end_(reinterpret_cast<const void*>(reinterpret_cast<uword>(start) +
@@ -48,6 +48,29 @@ class LoadCommandIterator {
4848
private:
4949
const void* end_;
5050
const void* current_;
51+
52+
DISALLOW_COPY_AND_ASSIGN(LoadCommandIterator);
53+
};
54+
55+
// For MachO structs of constant size that are contiguous in memory.
56+
template <typename T>
57+
class MachOStructIterator : public ValueObject {
58+
public:
59+
MachOStructIterator(const T* start, size_t size_in_bytes)
60+
: start_(start),
61+
end_(reinterpret_cast<const T*>(reinterpret_cast<uword>(start) +
62+
size_in_bytes)) {
63+
ASSERT_EQUAL(0, size_in_bytes % sizeof(T));
64+
}
65+
66+
const T* begin() const { return start_; }
67+
const T* end() const { return end_; }
68+
69+
private:
70+
const T* start_;
71+
const T* end_;
72+
73+
DISALLOW_COPY_AND_ASSIGN(MachOStructIterator);
5174
};
5275

5376
/// A loader for a subset of Mach-O which may be used to load objects produced
@@ -118,7 +141,7 @@ class LoadedMachODylib {
118141
uword external_symbol_count_ = 0;
119142
std::unique_ptr<MappedMemory> external_symbols_mapping_;
120143

121-
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
144+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
122145
// Dynamic table for looking up unwinding exceptions info.
123146
// Initialized by LoadSegments as we load executable segment.
124147
MallocGrowableArray<void*> dynamic_runtime_function_tables_;
@@ -169,7 +192,7 @@ bool LoadedMachODylib::Load() {
169192
}
170193

171194
LoadedMachODylib::~LoadedMachODylib() {
172-
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
195+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
173196
for (intptr_t i = 0; i < dynamic_runtime_function_tables_.length(); i++) {
174197
UnwindingRecordsPlatform::UnregisterDynamicTable(
175198
dynamic_runtime_function_tables_[i]);
@@ -272,6 +295,9 @@ bool LoadedMachODylib::LoadSegments() {
272295
auto* const current = it.current();
273296
uint64_t memory_offset, memory_size, file_offset, file_size;
274297
dart::mach_o::vm_prot_t initprot;
298+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
299+
uint64_t records_address = 0, records_size = 0;
300+
#endif
275301
if (current->cmd == dart::mach_o::LC_SEGMENT) {
276302
auto* const segment =
277303
reinterpret_cast<const dart::mach_o::segment_command*>(current);
@@ -280,6 +306,21 @@ bool LoadedMachODylib::LoadSegments() {
280306
file_offset = segment->fileoff;
281307
file_size = segment->filesize;
282308
initprot = segment->initprot;
309+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
310+
if ((initprot & dart::mach_o::VM_PROT_EXECUTE) != 0) {
311+
auto* const start =
312+
reinterpret_cast<const dart::mach_o::section*>(segment + 1);
313+
const size_t size_in_bytes = segment->cmdsize - sizeof(*segment);
314+
MachOStructIterator sections(start, size_in_bytes);
315+
for (const auto& section : sections) {
316+
if (strcmp(section.sectname, dart::mach_o::SECT_UNWIND_INFO) == 0) {
317+
records_address = section.addr;
318+
records_size = section.size;
319+
break;
320+
}
321+
}
322+
}
323+
#endif
283324
} else if (current->cmd == dart::mach_o::LC_SEGMENT_64) {
284325
auto* const segment_64 =
285326
reinterpret_cast<const dart::mach_o::segment_command_64*>(current);
@@ -288,6 +329,22 @@ bool LoadedMachODylib::LoadSegments() {
288329
file_offset = segment_64->fileoff;
289330
file_size = segment_64->filesize;
290331
initprot = segment_64->initprot;
332+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
333+
if ((initprot & dart::mach_o::VM_PROT_EXECUTE) != 0) {
334+
auto* const start =
335+
reinterpret_cast<const dart::mach_o::section_64*>(segment_64 + 1);
336+
const size_t size_in_bytes =
337+
segment_64->cmdsize - sizeof(*segment_64);
338+
MachOStructIterator sections(start, size_in_bytes);
339+
for (const auto& section : sections) {
340+
if (strcmp(section.sectname, dart::mach_o::SECT_UNWIND_INFO) == 0) {
341+
records_address = section.addr;
342+
records_size = section.size;
343+
break;
344+
}
345+
}
346+
}
347+
#endif
291348
} else {
292349
it.Advance();
293350
continue;
@@ -344,18 +401,22 @@ bool LoadedMachODylib::LoadSegments() {
344401
CHECK_ERROR(memory != nullptr, "Could not map segment.");
345402
CHECK_ERROR(memory->address() == memory_start,
346403
"Mapping not at requested address.");
347-
#if defined(DART_HOST_OS_WINDOWS) && defined(ARCH_IS_64_BIT)
404+
#if defined(UNWINDING_RECORDS_WINDOWS_HOST)
348405
// For executable pages register unwinding information that should be
349406
// present on the page.
350407
if (map_type == File::kReadExecute) {
351-
// RegisterExecutableMemory checks the end of the memory space, so
352-
// if there are zerofill sections or the like in this segment, then
353-
// the offset of the unwinding records is incorrectly calculated.
354-
CHECK_ERROR(memory_size == file_size,
355-
"Executable segment contains zerofill sections.");
408+
CHECK_ERROR(records_address != 0,
409+
"No __unwind_info section found in segment");
410+
CHECK_ERROR(records_size == UnwindingRecordsPlatform::SizeInBytes(),
411+
"__unwind_info section does not contain expected "
412+
"unwinding records");
356413
void* ptable = nullptr;
357-
UnwindingRecordsPlatform::RegisterExecutableMemory(memory->address(),
358-
length, &ptable);
414+
void* start = memory->address();
415+
void* records_start = reinterpret_cast<void*>(
416+
reinterpret_cast<uword>(memory->address()) + adjustment +
417+
(records_address - memory_offset));
418+
UnwindingRecordsPlatform::RegisterExecutableMemory(
419+
start, length, records_start, &ptable);
359420
dynamic_runtime_function_tables_.Add(ptable);
360421
}
361422
#else

runtime/platform/unwinding_records.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ void UnwindingRecordsPlatform::RegisterExecutableMemory(
2525
intptr_t size,
2626
void** pp_dynamic_table) {}
2727

28+
void UnwindingRecordsPlatform::RegisterExecutableMemory(
29+
void* start,
30+
intptr_t size,
31+
void* records_start,
32+
void** pp_dynamic_table) {}
33+
2834
void UnwindingRecordsPlatform::UnregisterDynamicTable(void* p_dynamic_table) {}
2935

3036
#endif // !defined(UNWINDING_RECORDS_WINDOWS_HOST)

runtime/platform/unwinding_records.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ class UnwindingRecordsPlatform : public AllStatic {
1717
static void RegisterExecutableMemory(void* start,
1818
intptr_t size,
1919
void** pp_dynamic_table);
20+
static void RegisterExecutableMemory(void* start,
21+
intptr_t size,
22+
void* records_start,
23+
void** pp_dynamic_table);
2024
static void UnregisterDynamicTable(void* p_dynamic_table);
2125
};
2226

runtime/platform/unwinding_records_win.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,16 @@ void UnwindingRecordsPlatform::RegisterExecutableMemory(
3535
void** pp_dynamic_table) {
3636
intptr_t unwinding_record_offset = size - kReservedUnwindingRecordsSizeBytes;
3737
uint8_t* record_ptr = static_cast<uint8_t*>(start) + unwinding_record_offset;
38+
RegisterExecutableMemory(start, size, record_ptr, pp_dynamic_table);
39+
}
40+
41+
void UnwindingRecordsPlatform::RegisterExecutableMemory(
42+
void* start,
43+
intptr_t size,
44+
void* records_start,
45+
void** pp_dynamic_table) {
3846
CodeRangeUnwindingRecord* record =
39-
reinterpret_cast<CodeRangeUnwindingRecord*>(record_ptr);
47+
reinterpret_cast<CodeRangeUnwindingRecord*>(records_start);
4048
RELEASE_ASSERT(record->magic == kUnwindingRecordMagic);
4149
uword start_num = reinterpret_cast<intptr_t>(start);
4250
uword end_num = start_num + size;

runtime/vm/mach_o.cc

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,10 @@ class MachOSegment : public MachOCommand {
744744
bool HasContents() const override { return next_contents_index_ > 0; }
745745
bool IsAllocated() const override { return true; }
746746

747+
bool HasZerofillSections() const {
748+
return next_contents_index_ != contents_.length();
749+
}
750+
747751
uint32_t cmdsize() const override {
748752
uword size = sizeof(SegmentCommandType);
749753
// The header information for sections is nested within the
@@ -2075,7 +2079,6 @@ void MachOHeader::CreateBSS() {
20752079
}
20762080

20772081
void MachOHeader::GenerateUnwindingInformation() {
2078-
ASSERT(text_segment_ != nullptr);
20792082
#if !defined(TARGET_ARCH_IA32)
20802083
// Unwinding information is added to the text segment in Mach-O files.
20812084
// Thus, we need the size of the unwinding information even for debugging
@@ -2089,26 +2092,18 @@ void MachOHeader::GenerateUnwindingInformation() {
20892092
const bool use_zerofill = type_ == SnapshotType::DebugInfo;
20902093
auto const section_type =
20912094
use_zerofill ? mach_o::S_ZEROFILL : mach_o::S_REGULAR;
2092-
#if defined(DEBUG)
2093-
for (auto* const c : text_segment_->contents()) {
2094-
// The header always has contents, but the restriction on zerofill
2095-
// sections coming after content-containing sections only matters with
2096-
// respect to sections, not other contents.
2097-
if (c->IsMachOHeader()) continue;
2098-
// RegisterExecutablePages looks at the end of the loaded segment for
2099-
// the unwinding information, which means the unwinding info needs to be
2100-
// the last section, but any sections without file contents are ordered
2101-
// after any sections with file contents in a segment.
2102-
ASSERT_EQUAL(use_zerofill, !c->HasContents());
2103-
}
2104-
#endif
21052095

21062096
#if defined(DART_TARGET_OS_MACOS)
21072097
// TODO(dartbug.com/60307): Add compact unwind information.
21082098
USE(section_type);
21092099
#else
2100+
ASSERT(text_segment_ != nullptr);
21102101
if (auto* const text_section =
21112102
text_segment_->FindSection(mach_o::SECT_TEXT)) {
2103+
ASSERT(use_zerofill || !text_segment_->HasZerofillSections());
2104+
// Not idempotent.
2105+
ASSERT(text_segment_->FindSection(mach_o::SECT_EH_FRAME) == nullptr);
2106+
21122107
// For the __eh_frame section, the easiest way to determine the size is to
21132108
// generate the contents and just discard them if using zerofill.
21142109
GrowableArray<Dwarf::FrameDescriptionEntry> fdes(zone_, 0);
@@ -2121,7 +2116,6 @@ void MachOHeader::GenerateUnwindingInformation() {
21212116
DwarfSharedObjectStream dwarf_stream(zone_, &stream);
21222117
Dwarf::WriteCallFrameInformationRecords(&dwarf_stream, fdes);
21232118

2124-
ASSERT(!text_segment_->FindSection(mach_o::SECT_EH_FRAME));
21252119
auto* const eh_frame = new (zone())
21262120
MachOSection(zone(), mach_o::SECT_EH_FRAME, section_type,
21272121
mach_o::S_NO_ATTRIBUTES, /*has_contents=*/!use_zerofill,
@@ -2133,36 +2127,41 @@ void MachOHeader::GenerateUnwindingInformation() {
21332127
}
21342128
#endif // defined(DART_TARGET_OS_MACOS)
21352129

2136-
#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
2137-
// Append Windows unwinding instructions as another section at the end of
2138-
// the text segment.
2139-
auto* const unwinding_records = new (zone()) MachOSection(
2140-
zone(), mach_o::SECT_UNWIND_INFO, section_type, mach_o::S_NO_ATTRIBUTES,
2141-
/*has_contents=*/!use_zerofill, compiler::target::kWordSize);
2142-
const intptr_t records_size = UnwindingRecordsPlatform::SizeInBytes();
2143-
// The memory space of the text segment is padded to the alignment size, so
2144-
// we need to make sure the resulting data has initial padding so that the
2145-
// records are at the end of the segment without extra padding.
2146-
const intptr_t section_start = Utils::RoundUp(
2147-
text_segment_->UnpaddedMemorySize(), unwinding_records->Alignment());
2148-
const intptr_t section_size =
2149-
Utils::RoundUp(section_start + records_size, text_segment_->Alignment()) -
2150-
section_start;
2151-
const uint8_t* bytes = nullptr;
2152-
if (!use_zerofill) {
2153-
ZoneWriteStream stream(zone(), /*initial_size=*/section_size);
2154-
uint8_t* unwinding_instructions = zone()->Alloc<uint8_t>(records_size);
2155-
intptr_t records_start = section_size - records_size;
2156-
stream.SetPosition(records_start);
2157-
stream.WriteBytes(UnwindingRecords::GenerateRecordsInto(
2158-
records_start, unwinding_instructions),
2159-
records_size);
2160-
ASSERT_EQUAL(section_size, stream.Position());
2161-
bytes = stream.buffer();
2162-
}
2163-
unwinding_records->AddPortion(bytes, section_size);
2164-
text_segment_->AddContents(unwinding_records);
2165-
ASSERT_EQUAL(section_start + section_size, text_segment_->MemorySize());
2130+
#if defined(UNWINDING_RECORDS_WINDOWS_PRECOMPILER)
2131+
// Append Windows unwinding instructions as a __unwind_info section at
2132+
// the end of any executable segments.
2133+
for (auto* const command : commands_) {
2134+
if (auto* const segment = command->AsMachOSegment()) {
2135+
if (segment->IsExecutable()) {
2136+
ASSERT(use_zerofill || !segment->HasZerofillSections());
2137+
// Not idempotent.
2138+
ASSERT(segment->FindSection(mach_o::SECT_UNWIND_INFO) == nullptr);
2139+
2140+
auto* const unwinding_records = new (zone()) MachOSection(
2141+
zone(), mach_o::SECT_UNWIND_INFO, section_type,
2142+
mach_o::S_NO_ATTRIBUTES,
2143+
/*has_contents=*/!use_zerofill, compiler::target::kWordSize);
2144+
const intptr_t records_size = UnwindingRecordsPlatform::SizeInBytes();
2145+
const intptr_t section_start = Utils::RoundUp(
2146+
segment->UnpaddedMemorySize(), unwinding_records->Alignment());
2147+
const uint8_t* bytes = nullptr;
2148+
if (!use_zerofill) {
2149+
ZoneWriteStream stream(zone(), /*initial_size=*/records_size);
2150+
uint8_t* unwinding_instructions =
2151+
zone()->Alloc<uint8_t>(records_size);
2152+
stream.WriteBytes(UnwindingRecords::GenerateRecordsInto(
2153+
section_start, unwinding_instructions),
2154+
records_size);
2155+
ASSERT_EQUAL(records_size, stream.Position());
2156+
bytes = stream.buffer();
2157+
}
2158+
unwinding_records->AddPortion(bytes, records_size);
2159+
segment->AddContents(unwinding_records);
2160+
ASSERT_EQUAL(section_start + records_size,
2161+
segment->UnpaddedMemorySize());
2162+
}
2163+
}
2164+
}
21662165
#endif // defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
21672166
#endif // !defined(TARGET_ARCH_IA32)
21682167
}

0 commit comments

Comments
 (0)