Skip to content

Commit 4689b49

Browse files
sstricklCommit Queue
authored andcommitted
[vm] Generate __eh_frame section in non-MacOS Mach-O snapshots.
Factor out the .eh_frame section content generation from ElfWriter, putting in as a static method in Dwarf instead. Create a __eh_frame section in the __TEXT section of non-MacOS Mach-O snapshots with the same information as .eh_frame in ELF snapshots. TEST=ci Issue: #60307 Change-Id: I4e123f586e59e1263385c838b755c1575a6330eb 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-fuchsia-release-x64-try,vm-fuchsia-release-arm64-try,vm-linux-debug-ia32-try,vm-aot-linux-debug-simarm_x64-try,vm-aot-linux-debug-simriscv32-try,vm-aot-linux-debug-simriscv64-try,vm-aot-linux-release-simarm_x64-try,vm-gcc-linux-try,vm-ubsan-linux-release-arm64-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/+/431600 Reviewed-by: Ryan Macnak <[email protected]> Reviewed-by: Slava Egorov <[email protected]> Commit-Queue: Tess Strickland <[email protected]>
1 parent f769974 commit 4689b49

File tree

5 files changed

+161
-102
lines changed

5 files changed

+161
-102
lines changed

runtime/platform/mach_o.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ static constexpr char SEG_TEXT[] = "__TEXT";
248248
static constexpr char SECT_TEXT[] = "__text";
249249
static constexpr char SECT_CONST[] = "__const";
250250
static constexpr char SECT_UNWIND_INFO[] = "__unwind_info";
251+
static constexpr char SECT_EH_FRAME[] = "__eh_frame";
251252

252253
// Segment and section names for the data segment, which contains
253254
// non-constant data (like the BSS section).

runtime/vm/dwarf.cc

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "vm/code_comments.h"
88
#include "vm/code_descriptors.h"
9+
#include "vm/dwarf_so_writer.h"
910
#include "vm/elf.h"
1011
#include "vm/image_snapshot.h"
1112
#include "vm/object_store.h"
@@ -939,6 +940,88 @@ void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
939940
});
940941
}
941942

943+
#if !defined(TARGET_ARCH_IA32)
944+
void Dwarf::WriteCallFrameInformationRecords(
945+
DwarfSharedObjectStream* stream,
946+
const GrowableArray<FrameDescriptionEntry>& fdes) {
947+
ASSERT(!fdes.is_empty());
948+
949+
#if defined(TARGET_ARCH_X64)
950+
// The x86_64 psABI defines the DWARF register numbers, which differ from
951+
// the registers' usual encoding within instructions.
952+
const intptr_t DWARF_RA = 16; // No corresponding register.
953+
const intptr_t DWARF_FP = 6; // RBP
954+
#else
955+
const intptr_t DWARF_RA = ConcreteRegister(LINK_REGISTER);
956+
const intptr_t DWARF_FP = FP;
957+
#endif
958+
959+
// Multiplier which will be used to scale operands of DW_CFA_offset and
960+
// DW_CFA_val_offset.
961+
const intptr_t kDataAlignment = -compiler::target::kWordSize;
962+
963+
// Used to calculate offset to CIE in FDEs.
964+
const intptr_t cie_start = stream->Position();
965+
stream->WritePrefixedLength([&] {
966+
stream->u4(0); // CIE
967+
stream->u1(1); // Version (must be 1 or 3)
968+
// Augmentation String
969+
stream->string("zR"); // NOLINT
970+
stream->uleb128(1); // Code alignment (must be 1).
971+
stream->sleb128(kDataAlignment); // Data alignment
972+
stream->u1(DWARF_RA); // Return address register
973+
stream->uleb128(1); // Augmentation size
974+
stream->u1(DW_EH_PE_pcrel | DW_EH_PE_sdata4); // FDE encoding.
975+
// CFA is caller's SP (FP+kCallerSpSlotFromFp*kWordSize)
976+
stream->u1(Dwarf::DW_CFA_def_cfa);
977+
stream->uleb128(DWARF_FP);
978+
stream->uleb128(kCallerSpSlotFromFp * compiler::target::kWordSize);
979+
});
980+
981+
// Emit rule defining that |reg| value is stored at CFA+offset.
982+
const auto cfa_offset = [&](intptr_t reg, intptr_t offset) {
983+
const intptr_t scaled_offset = offset / kDataAlignment;
984+
RELEASE_ASSERT(scaled_offset >= 0);
985+
stream->u1(Dwarf::DW_CFA_offset | reg);
986+
stream->uleb128(scaled_offset);
987+
};
988+
989+
// Emit an FDE covering each .text section.
990+
for (const auto& fde : fdes) {
991+
#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
992+
if (fde.label == 0) {
993+
// Unwinding instructions sections doesn't have label, doesn't dwarf
994+
continue;
995+
}
996+
#endif
997+
ASSERT(fde.label != 0); // Needed for relocations.
998+
stream->WritePrefixedLength([&]() {
999+
// Offset to CIE. Note that unlike pcrel this offset is encoded
1000+
// backwards: it will be subtracted from the current position.
1001+
stream->u4(stream->Position() - cie_start);
1002+
// Start address as a PC relative reference.
1003+
stream->RelativeSymbolOffset<int32_t>(fde.label);
1004+
stream->u4(fde.size); // Size.
1005+
stream->u1(0); // Augmentation Data length.
1006+
1007+
// Caller FP at FP+kSavedCallerPcSlotFromFp*kWordSize,
1008+
// where FP is CFA - kCallerSpSlotFromFp*kWordSize.
1009+
COMPILE_ASSERT((kSavedCallerFpSlotFromFp - kCallerSpSlotFromFp) <= 0);
1010+
cfa_offset(DWARF_FP, (kSavedCallerFpSlotFromFp - kCallerSpSlotFromFp) *
1011+
compiler::target::kWordSize);
1012+
1013+
// Caller LR at FP+kSavedCallerPcSlotFromFp*kWordSize,
1014+
// where FP is CFA - kCallerSpSlotFromFp*kWordSize
1015+
COMPILE_ASSERT((kSavedCallerPcSlotFromFp - kCallerSpSlotFromFp) <= 0);
1016+
cfa_offset(DWARF_RA, (kSavedCallerPcSlotFromFp - kCallerSpSlotFromFp) *
1017+
compiler::target::kWordSize);
1018+
});
1019+
}
1020+
1021+
stream->u4(0); // end of section (FDE with zero length)
1022+
}
1023+
#endif
1024+
9421025
#endif // DART_PRECOMPILER
9431026

9441027
} // namespace dart

runtime/vm/dwarf.h

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ class Dwarf : public ZoneAllocated {
162162
const Trie<const char>* deobfuscation_trie,
163163
const char* compilation_unit_name = nullptr);
164164

165-
const ZoneGrowableArray<const Code*>& codes() const { return codes_; }
165+
const GrowableArray<const Code*>& codes() const { return codes_; }
166166

167167
// Stores the code object for later creating the line number program.
168168
void AddCode(const Code& code, intptr_t label);
@@ -176,6 +176,24 @@ class Dwarf : public ZoneAllocated {
176176
void WriteDebugInfo(DwarfWriteStream* stream);
177177
void WriteLineNumberProgram(DwarfWriteStream* stream);
178178

179+
#if !defined(TARGET_ARCH_IA32)
180+
// A frame description entry (used in the call frame information records)
181+
// needs two pieces of information from the SharedObjectWriter:
182+
struct FrameDescriptionEntry {
183+
// * The starting address (here, represented by the label of the symbol
184+
// corresponding to the starting address).
185+
intptr_t label;
186+
// * The size of the memory space to cover from the starting address.
187+
intptr_t size;
188+
};
189+
190+
// Only used for non-assembly outputs; the AssemblyImageWriter inserts
191+
// approrpiate CFI directives directly into the generated assembly.
192+
static void WriteCallFrameInformationRecords(
193+
class DwarfSharedObjectStream* stream,
194+
const GrowableArray<FrameDescriptionEntry>& fdes);
195+
#endif
196+
179197
private:
180198
friend class LineNumberProgramWriter;
181199

@@ -223,13 +241,13 @@ class Dwarf : public ZoneAllocated {
223241
static constexpr intptr_t DW_LNE_end_sequence = 0x01;
224242
static constexpr intptr_t DW_LNE_set_address = 0x02;
225243

226-
public:
227-
// Public because they're also used in constructing .eh_frame ELF sections.
228244
static constexpr intptr_t DW_CFA_offset = 0x80;
229245
static constexpr intptr_t DW_CFA_val_offset = 0x14;
230246
static constexpr intptr_t DW_CFA_def_cfa = 0x0c;
231247

232-
private:
248+
static constexpr uint8_t DW_EH_PE_pcrel = 0x10;
249+
static constexpr uint8_t DW_EH_PE_sdata4 = 0x0b;
250+
233251
enum {
234252
kCompilationUnit = 1,
235253
kAbstractFunction,
@@ -252,11 +270,11 @@ class Dwarf : public ZoneAllocated {
252270
Zone* const zone_;
253271
const char* const compilation_unit_name_;
254272
const Trie<const char>* const deobfuscation_trie_;
255-
ZoneGrowableArray<const Code*> codes_;
273+
GrowableArray<const Code*> codes_;
256274
DwarfCodeMap<intptr_t> code_to_label_;
257-
ZoneGrowableArray<const Function*> functions_;
275+
GrowableArray<const Function*> functions_;
258276
FunctionIndexMap function_to_index_;
259-
ZoneGrowableArray<const Script*> scripts_;
277+
GrowableArray<const Script*> scripts_;
260278
ScriptIndexMap script_to_index_;
261279
};
262280

runtime/vm/elf.cc

Lines changed: 16 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,20 +1275,26 @@ void ElfWriter::InitializeSymbolTables() {
12751275

12761276
void ElfWriter::FinalizeEhFrame() {
12771277
#if !defined(TARGET_ARCH_IA32)
1278-
#if defined(TARGET_ARCH_X64)
1279-
// The x86_64 psABI defines the DWARF register numbers, which differ from
1280-
// the registers' usual encoding within instructions.
1281-
const intptr_t DWARF_RA = 16; // No corresponding register.
1282-
const intptr_t DWARF_FP = 6; // RBP
1283-
#else
1284-
const intptr_t DWARF_RA = ConcreteRegister(LINK_REGISTER);
1285-
const intptr_t DWARF_FP = FP;
1286-
#endif
1287-
12881278
auto text_section = section_table_->FindTextSection();
12891279
// No text section added means no .eh_frame.
12901280
if (text_section == nullptr) return;
12911281

1282+
GrowableArray<Dwarf::FrameDescriptionEntry> fdes(zone_, 0);
1283+
for (const auto& portion : text_section->portions()) {
1284+
ASSERT(portion.label != 0);
1285+
fdes.Add({portion.label, portion.size});
1286+
}
1287+
1288+
ZoneWriteStream stream(zone(), DwarfSharedObjectStream::kInitialBufferSize);
1289+
DwarfSharedObjectStream dwarf_stream(zone_, &stream);
1290+
Dwarf::WriteCallFrameInformationRecords(&dwarf_stream, fdes);
1291+
1292+
auto* const eh_frame = new (zone_)
1293+
BitsContainer(type_, /*writable=*/false, /*executable=*/false);
1294+
eh_frame->AddPortion(dwarf_stream.buffer(), dwarf_stream.bytes_written(),
1295+
dwarf_stream.relocations());
1296+
section_table_->Add(eh_frame, ".eh_frame");
1297+
12921298
#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
12931299
// Append Windows unwinding instructions to the end of .text section.
12941300
{ // NOLINT
@@ -1310,84 +1316,6 @@ void ElfWriter::FinalizeEhFrame() {
13101316
section_table_->Add(unwinding_instructions_frame, kTextName);
13111317
}
13121318
#endif
1313-
1314-
// Multiplier which will be used to scale operands of DW_CFA_offset and
1315-
// DW_CFA_val_offset.
1316-
const intptr_t kDataAlignment = -compiler::target::kWordSize;
1317-
1318-
static constexpr uint8_t DW_EH_PE_pcrel = 0x10;
1319-
static constexpr uint8_t DW_EH_PE_sdata4 = 0x0b;
1320-
1321-
ZoneWriteStream stream(zone(), DwarfSharedObjectStream::kInitialBufferSize);
1322-
DwarfSharedObjectStream dwarf_stream(zone_, &stream);
1323-
1324-
// Emit CIE.
1325-
1326-
// Used to calculate offset to CIE in FDEs.
1327-
const intptr_t cie_start = dwarf_stream.Position();
1328-
dwarf_stream.WritePrefixedLength([&] {
1329-
dwarf_stream.u4(0); // CIE
1330-
dwarf_stream.u1(1); // Version (must be 1 or 3)
1331-
// Augmentation String
1332-
dwarf_stream.string("zR"); // NOLINT
1333-
dwarf_stream.uleb128(1); // Code alignment (must be 1).
1334-
dwarf_stream.sleb128(kDataAlignment); // Data alignment
1335-
dwarf_stream.u1(DWARF_RA); // Return address register
1336-
dwarf_stream.uleb128(1); // Augmentation size
1337-
dwarf_stream.u1(DW_EH_PE_pcrel | DW_EH_PE_sdata4); // FDE encoding.
1338-
// CFA is caller's SP (FP+kCallerSpSlotFromFp*kWordSize)
1339-
dwarf_stream.u1(Dwarf::DW_CFA_def_cfa);
1340-
dwarf_stream.uleb128(DWARF_FP);
1341-
dwarf_stream.uleb128(kCallerSpSlotFromFp * compiler::target::kWordSize);
1342-
});
1343-
1344-
// Emit rule defining that |reg| value is stored at CFA+offset.
1345-
const auto cfa_offset = [&](intptr_t reg, intptr_t offset) {
1346-
const intptr_t scaled_offset = offset / kDataAlignment;
1347-
RELEASE_ASSERT(scaled_offset >= 0);
1348-
dwarf_stream.u1(Dwarf::DW_CFA_offset | reg);
1349-
dwarf_stream.uleb128(scaled_offset);
1350-
};
1351-
1352-
// Emit an FDE covering each .text section.
1353-
for (const auto& portion : text_section->portions()) {
1354-
#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
1355-
if (portion.label == 0) {
1356-
// Unwinding instructions sections doesn't have label, doesn't dwarf
1357-
continue;
1358-
}
1359-
#endif
1360-
ASSERT(portion.label != 0); // Needed for relocations.
1361-
dwarf_stream.WritePrefixedLength([&]() {
1362-
// Offset to CIE. Note that unlike pcrel this offset is encoded
1363-
// backwards: it will be subtracted from the current position.
1364-
dwarf_stream.u4(stream.Position() - cie_start);
1365-
// Start address as a PC relative reference.
1366-
dwarf_stream.RelativeSymbolOffset<int32_t>(portion.label);
1367-
dwarf_stream.u4(portion.size); // Size.
1368-
dwarf_stream.u1(0); // Augmentation Data length.
1369-
1370-
// Caller FP at FP+kSavedCallerPcSlotFromFp*kWordSize,
1371-
// where FP is CFA - kCallerSpSlotFromFp*kWordSize.
1372-
COMPILE_ASSERT((kSavedCallerFpSlotFromFp - kCallerSpSlotFromFp) <= 0);
1373-
cfa_offset(DWARF_FP, (kSavedCallerFpSlotFromFp - kCallerSpSlotFromFp) *
1374-
compiler::target::kWordSize);
1375-
1376-
// Caller LR at FP+kSavedCallerPcSlotFromFp*kWordSize,
1377-
// where FP is CFA - kCallerSpSlotFromFp*kWordSize
1378-
COMPILE_ASSERT((kSavedCallerPcSlotFromFp - kCallerSpSlotFromFp) <= 0);
1379-
cfa_offset(DWARF_RA, (kSavedCallerPcSlotFromFp - kCallerSpSlotFromFp) *
1380-
compiler::target::kWordSize);
1381-
});
1382-
}
1383-
1384-
dwarf_stream.u4(0); // end of section (FDE with zero length)
1385-
1386-
auto* const eh_frame = new (zone_)
1387-
BitsContainer(type_, /*writable=*/false, /*executable=*/false);
1388-
eh_frame->AddPortion(dwarf_stream.buffer(), dwarf_stream.bytes_written(),
1389-
dwarf_stream.relocations());
1390-
section_table_->Add(eh_frame, ".eh_frame");
13911319
#endif // !defined(TARGET_ARCH_IA32)
13921320
}
13931321

runtime/vm/mach_o.cc

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,18 +2103,48 @@ void MachOHeader::GenerateUnwindingInformation() {
21032103
}
21042104
#endif
21052105

2106+
#if defined(DART_TARGET_OS_MACOS)
2107+
// TODO(dartbug.com/60307): Add compact unwind information.
2108+
USE(section_type);
2109+
#else
2110+
if (auto* const text_section =
2111+
text_segment_->FindSection(mach_o::SECT_TEXT)) {
2112+
// For the __eh_frame section, the easiest way to determine the size is to
2113+
// generate the contents and just discard them if using zerofill.
2114+
GrowableArray<Dwarf::FrameDescriptionEntry> fdes(zone_, 0);
2115+
for (const auto& portion : text_section->portions()) {
2116+
ASSERT(portion.label != 0);
2117+
fdes.Add({portion.label, portion.size});
2118+
}
2119+
2120+
ZoneWriteStream stream(zone(), DwarfSharedObjectStream::kInitialBufferSize);
2121+
DwarfSharedObjectStream dwarf_stream(zone_, &stream);
2122+
Dwarf::WriteCallFrameInformationRecords(&dwarf_stream, fdes);
2123+
2124+
ASSERT(!text_segment_->FindSection(mach_o::SECT_EH_FRAME));
2125+
auto* const eh_frame = new (zone())
2126+
MachOSection(zone(), mach_o::SECT_EH_FRAME, section_type,
2127+
mach_o::S_NO_ATTRIBUTES, /*has_contents=*/!use_zerofill,
2128+
/*alignment=*/compiler::target::kWordSize);
2129+
eh_frame->AddPortion(use_zerofill ? nullptr : dwarf_stream.buffer(),
2130+
dwarf_stream.bytes_written(),
2131+
use_zerofill ? nullptr : dwarf_stream.relocations());
2132+
text_segment_->AddContents(eh_frame);
2133+
}
2134+
#endif // defined(DART_TARGET_OS_MACOS)
2135+
21062136
#if defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
21072137
// Append Windows unwinding instructions as another section at the end of
21082138
// the text segment.
2109-
auto* const section = new (zone()) MachOSection(
2139+
auto* const unwinding_records = new (zone()) MachOSection(
21102140
zone(), mach_o::SECT_UNWIND_INFO, section_type, mach_o::S_NO_ATTRIBUTES,
21112141
/*has_contents=*/!use_zerofill, compiler::target::kWordSize);
21122142
const intptr_t records_size = UnwindingRecordsPlatform::SizeInBytes();
21132143
// The memory space of the text segment is padded to the alignment size, so
21142144
// we need to make sure the resulting data has initial padding so that the
21152145
// records are at the end of the segment without extra padding.
2116-
const intptr_t section_start =
2117-
Utils::RoundUp(text_segment_->UnpaddedMemorySize(), section->Alignment());
2146+
const intptr_t section_start = Utils::RoundUp(
2147+
text_segment_->UnpaddedMemorySize(), unwinding_records->Alignment());
21182148
const intptr_t section_size =
21192149
Utils::RoundUp(section_start + records_size, text_segment_->Alignment()) -
21202150
section_start;
@@ -2130,11 +2160,9 @@ void MachOHeader::GenerateUnwindingInformation() {
21302160
ASSERT_EQUAL(section_size, stream.Position());
21312161
bytes = stream.buffer();
21322162
}
2133-
section->AddPortion(bytes, section_size);
2134-
text_segment_->AddContents(section);
2163+
unwinding_records->AddPortion(bytes, section_size);
2164+
text_segment_->AddContents(unwinding_records);
21352165
ASSERT_EQUAL(section_start + section_size, text_segment_->MemorySize());
2136-
#else
2137-
USE(section_type);
21382166
#endif // defined(DART_TARGET_OS_WINDOWS) && defined(TARGET_ARCH_IS_64_BIT)
21392167
#endif // !defined(TARGET_ARCH_IA32)
21402168
}
@@ -2213,6 +2241,7 @@ void MachOHeader::FinalizeDwarfSections() {
22132241
const intptr_t alignment = 1; // No extra padding.
22142242
auto add_debug = [&](const char* name,
22152243
const DwarfSharedObjectStream& stream) {
2244+
ASSERT(!dwarf_segment->FindSection(name));
22162245
auto* const section = new (zone())
22172246
MachOSection(zone(), name, mach_o::S_REGULAR, mach_o::S_ATTR_DEBUG,
22182247
/*has_contents=*/true, alignment);

0 commit comments

Comments
 (0)