[BOLT][DWARF] Properly emit of end-of-sequence entries for line tables

Summary:
When the compiler emits line table program, it emits EOS using the label
at the end of the containing code section. Since each compilation unit
has its own set of code sections it works as expected (* see the excerpt
from the standard below). However, in BOLT the code from many CUs is
combined into a common section, such as hot text or cold text.
As a result, the symbol at the end of the section may point way past the
code sequence for a given unit.

Since we can emit functions in any order, we conservatively emit
end-of-sequence at the end of every emitted function.

Fixes a problem while intermixing source code with disassembly in
binutils' objdump.

(*) DWARF v4 6.2.5.3:
"Every line number program sequence must end with a DW_LNE_end_sequence
instruction which creates a row whose address is that of the byte after
the last target machine instruction of the sequence."

(cherry picked from FBD31347870)
This commit is contained in:
Maksim Panchenko 2021-09-30 17:47:50 -07:00
parent 98bc9876fb
commit 8ef3b27834
4 changed files with 47 additions and 4 deletions

View File

@ -11,6 +11,7 @@
#include "BinaryEmitter.h"
#include "BinaryContext.h"
#include "BinaryFunction.h"
#include "DebugData.h"
#include "Utils.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCStreamer.h"
@ -179,6 +180,12 @@ private:
SMLoc emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc, SMLoc PrevLoc,
bool FirstInstr);
/// Use \p FunctionEndSymbol to mark the end of the line info sequence.
/// Note that it does not automatically result in the insertion of the EOS
/// marker in the line table program, but provides one to the DWARF generator
/// when it needs it.
void emitLineInfoEnd(const BinaryFunction &BF, MCSymbol *FunctionEndSymbol);
/// Emit debug line information for functions that were not emitted.
void emitDebugLineInfoForOriginalFunctions();
@ -385,6 +392,9 @@ bool BinaryEmitter::emitFunction(BinaryFunction &Function, bool EmitColdPart) {
Streamer.emitELFSize(StartSymbol, SizeExpr);
}
if (opts::UpdateDebugSections && Function.getDWARFUnit())
emitLineInfoEnd(Function, EndSymbol);
// Exception handling info for the function.
emitLSDA(Function, EmitColdPart);
@ -670,6 +680,19 @@ SMLoc BinaryEmitter::emitLineInfo(const BinaryFunction &BF, SMLoc NewLoc,
return NewLoc;
}
void BinaryEmitter::emitLineInfoEnd(const BinaryFunction &BF,
MCSymbol *FunctionEndLabel) {
DWARFUnit *FunctionCU = BF.getDWARFUnit();
assert(FunctionCU && "DWARF unit expected");
BC.Ctx->setCurrentDwarfLoc(0, 0, 0, DWARF2_FLAG_END_SEQUENCE, 0, 0);
const MCDwarfLoc &DwarfLoc = BC.Ctx->getCurrentDwarfLoc();
BC.Ctx->clearDwarfLocSeen();
BC.getDwarfLineTable(FunctionCU->getOffset())
.getMCLineSections()
.addLineEntry(MCDwarfLineEntry(FunctionEndLabel, DwarfLoc),
Streamer.getCurrentSectionOnly());
}
void BinaryEmitter::emitJumpTables(const BinaryFunction &BF) {
MCSection *ReadOnlySection = BC.MOFI->getReadOnlySection();
MCSection *ReadOnlyColdSection = BC.MOFI->getContext().getELFSection(

View File

@ -642,6 +642,9 @@ static inline void emitBinaryDwarfLineTable(
emitEndOfSequence(PrevEndOfSequence);
}
// This function is similar to the one from MCDwarfLineTable, except it handles
// end-of-sequence entries differently by utilizing line entries with
// DWARF2_FLAG_END_SEQUENCE flag.
static inline void emitDwarfLineTable(
MCStreamer *MCOS, MCSection *Section,
const MCLineSection::MCDwarfLineEntryCollection &LineEntries) {
@ -656,6 +659,19 @@ static inline void emitDwarfLineTable(
// Loop through each MCDwarfLineEntry and encode the dwarf line number table.
for (const MCDwarfLineEntry &LineEntry : LineEntries) {
if (LineEntry.getFlags() & DWARF2_FLAG_END_SEQUENCE) {
MCOS->emitDwarfAdvanceLineAddr(INT64_MAX, LastLabel, LineEntry.getLabel(),
AsmInfo->getCodePointerSize());
FileNum = 1;
LastLine = 1;
Column = 0;
Flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0;
Isa = 0;
Discriminator = 0;
LastLabel = nullptr;
continue;
}
int64_t LineDelta = static_cast<int64_t>(LineEntry.getLine()) - LastLine;
if (FileNum != LineEntry.getFileNum()) {
@ -705,8 +721,7 @@ static inline void emitDwarfLineTable(
LastLabel = Label;
}
// Generate DWARF line end entry.
MCOS->emitDwarfLineEndEntry(Section, LastLabel);
assert(LastLabel == nullptr && "end of sequence expected");
}
void DwarfLineTable::emitCU(MCStreamer *MCOS, MCDwarfLineTableParams Params,

View File

@ -28,6 +28,8 @@
#include <utility>
#include <vector>
#define DWARF2_FLAG_END_SEQUENCE (1 << 4)
namespace llvm {
class DWARFAbbreviationDeclarationSet;

View File

@ -1,3 +1,5 @@
## Check that debug_line offsets for the compile unit and for the type unit
## match.
# REQUIRES: system-linux
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown \
@ -8,6 +10,7 @@
# RUN: %p/Inputs/file2.s -o %tfile2.o
# RUN: %clangxx %cxxflags %tmain.o %tfile1.o %tfile2.o -o %t.exe -Wl,-q
# RUN: llvm-bolt %t.exe --reorder-blocks=reverse -update-debug-sections -o %t.out
# RUN: llvm-dwarfdump --debug-types=0x000005f7 %t.out | grep DW_AT_stmt_list | FileCheck %s --check-prefix=CHECK-OUTPUT
# RUN: llvm-dwarfdump --debug-info=0x00001c36 --debug-types=0x000005f7 %t.out | FileCheck %s --check-prefix=CHECK-OUTPUT
# CHECK-OUTPUT: DW_AT_stmt_list (0x000004d4)
# CHECK-OUTPUT: DW_AT_stmt_list ([[#%#.8x,OFFSET:]]
# CHECK-OUTPUT: DW_AT_stmt_list ([[#OFFSET]])