Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/eld/Diagnostics/DiagLayouts.inc
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ DIAG(error_offset_not_assigned_for_output_section, DiagnosticEngine::Error,
DIAG(warn_empty_segment, DiagnosticEngine::Warning, "Empty segment: '%0'")
DIAG(note_section_address_not_converging, DiagnosticEngine::Note,
"Section '%0' address did not converge after %1 layout passes")
DIAG(note_symbol_value_not_converging, DiagnosticEngine::Note,
"Symbol '%0' value did not converge after %1 layout passes")
DIAG(note_assignment_value_not_converging, DiagnosticEngine::Note,
"%0: assignment '%1' did not converge after %2 layout passes")
2 changes: 2 additions & 0 deletions include/eld/Script/Assignment.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ class Assignment : public ScriptCommand {
bool EndWithNewLine = true, bool WithValues = false,
bool AddIndent = true) const override;

std::string getAsString(bool WithValues = false) const;

bool isDot() const;

// Does the assignment have any dot usage ?
Expand Down
16 changes: 13 additions & 3 deletions include/eld/Target/GNULDBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -772,13 +772,23 @@ class GNULDBackend {

struct LayoutSnapshot {
llvm::DenseMap<const OutputSectionEntry *, SectionAddrs> outSections;
std::vector<uint64_t> assignmentValues;
};

struct DivergenceResult {
const OutputSectionEntry *outputSection = nullptr;
const Assignment *assignment = nullptr;
};

// Capture current layout snapshot keyed by OutputSectionEntry*.
LayoutSnapshot captureLayoutSnapshot() const;

const OutputSectionEntry *findDivergence(const LayoutSnapshot &Prev,
const LayoutSnapshot &cur) const;
void captureAssignmentsSnapshot(LayoutSnapshot &S) const;

DivergenceResult findDivergence(const LayoutSnapshot &Prev,
const LayoutSnapshot &Cur) const;

const Assignment *findAssignmentDivergence(const LayoutSnapshot &Prev,
const LayoutSnapshot &Cur) const;

// --- Exclude symbol support
void markSymbolForRemoval(const ResolveInfo *S);
Expand Down
7 changes: 7 additions & 0 deletions lib/Script/Assignment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,13 @@ bool Assignment::checkLinkerScript(Module &CurModule) {

bool Assignment::isDot() const { return (Name.size() == 1 && Name[0] == '.'); }

std::string Assignment::getAsString(bool WithValues) const {
std::string Str;
llvm::raw_string_ostream SS(Str);
dumpMap(SS, false, false, WithValues);
return Str;
}

bool Assignment::hasDot() const {
return isDot() || ExpressionToEvaluate->hasDot();
}
Expand Down
72 changes: 57 additions & 15 deletions lib/Target/GNULDBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3318,25 +3318,65 @@ GNULDBackend::LayoutSnapshot GNULDBackend::captureLayoutSnapshot() const {
A.lma = Sec->pAddr();
S.outSections.insert({O, A});
}
captureAssignmentsSnapshot(S);
return S;
}

const OutputSectionEntry *
static bool shouldSkipAssignForLayoutConv(const Assignment *A) {
if (A->type() == Assignment::ASSERT || A->type() == Assignment::FILL)
return true;
if (A->isProvideOrProvideHidden() && !A->isUsed())
return true;
return false;
}

void GNULDBackend::captureAssignmentsSnapshot(LayoutSnapshot &S) const {
for (const Assignment *A : m_Module.getScript().assignments()) {
if (shouldSkipAssignForLayoutConv(A))
continue;
S.assignmentValues.push_back(A->value());
}
}

const Assignment *
GNULDBackend::findAssignmentDivergence(const LayoutSnapshot &Prev,
const LayoutSnapshot &Cur) const {
const auto &Assignments = m_Module.getScript().assignments();
size_t Idx = 0;
for (const Assignment *A : Assignments) {
if (shouldSkipAssignForLayoutConv(A))
continue;
if (Idx >= Prev.assignmentValues.size() ||
Idx >= Cur.assignmentValues.size() ||
Prev.assignmentValues[Idx] != Cur.assignmentValues[Idx])
return A;
++Idx;
}
return nullptr;
}

GNULDBackend::DivergenceResult
GNULDBackend::findDivergence(const LayoutSnapshot &Prev,
const LayoutSnapshot &cur) const {
const LayoutSnapshot &Cur) const {
DivergenceResult Result;
const SectionMap &SM = m_Module.getScript().sectionMap();
for (auto It = SM.begin(), End = SM.end(); It != End; ++It) {
const OutputSectionEntry *O = *It;
auto PrevIt = Prev.outSections.find(O);
auto CurIt = cur.outSections.find(O);
if (PrevIt == Prev.outSections.end() || CurIt == cur.outSections.end())
return O;
auto CurIt = Cur.outSections.find(O);
if (PrevIt == Prev.outSections.end() || CurIt == Cur.outSections.end()) {
Result.outputSection = O;
break;
}
const SectionAddrs &A = PrevIt->second;
const SectionAddrs &B = CurIt->second;
if (A.vma != B.vma || A.lma != B.lma)
return O;
if (A.vma != B.vma || A.lma != B.lma) {
Result.outputSection = O;
break;
}
}
return nullptr;
Result.assignment = findAssignmentDivergence(Prev, Cur);
return Result;
}

// Create or return an already created relocation output section for partial
Expand Down Expand Up @@ -4133,11 +4173,10 @@ bool GNULDBackend::relax() {
while (!finished) {
auto start = std::chrono::steady_clock::now();
{
// Bounded convergence loop over address assignment.
LayoutSnapshot prevSnap, curSnap;
prevSnap = captureLayoutSnapshot();
constexpr int maxIterations = 4;
const OutputSectionEntry *changed = nullptr;
DivergenceResult diverged;
for (int i = 0; i < maxIterations; ++i) {
eld::RegisterTimer T("Assign Address", "Establish Layout",
m_Module.getConfig().options().printTimingStats());
Expand All @@ -4150,16 +4189,19 @@ bool GNULDBackend::relax() {
m_Module.setFailure(true);
}
curSnap = captureLayoutSnapshot();
changed = findDivergence(prevSnap, curSnap);
if (!changed)
diverged = findDivergence(prevSnap, curSnap);
if (!diverged.outputSection && !diverged.assignment)
break;
prevSnap = std::move(curSnap);
}
if (changed) {
// Emit a note for the first observed changed section.
if (const ELFSection *S = changed->getSection())
if (diverged.outputSection) {
if (const ELFSection *S = diverged.outputSection->getSection())
config().raise(Diag::note_section_address_not_converging)
<< S->name() << maxIterations;
} else if (diverged.assignment) {
const Assignment &A = *diverged.assignment;
config().raise(Diag::note_assignment_value_not_converging)
<< A.getContext() << A.getAsString(true) << maxIterations;
}
if (LinkerConfig::Object != config().codeGenType()) {
if (!setupProgramHdrs()) {
Expand Down
3 changes: 3 additions & 0 deletions test/Common/standalone/SymbolForwReference/Inputs/1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int foo() { return 1; }

int val = 3;
7 changes: 7 additions & 0 deletions test/Common/standalone/SymbolForwReference/Inputs/script.1.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SECTIONS {
.text : { *(.text*) }
.data : { *(.data*) }
.comment : { *(.comment*) }
a = b + 0x100;
b = 0x1000;
}
8 changes: 8 additions & 0 deletions test/Common/standalone/SymbolForwReference/Inputs/script.2.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SECTIONS {
.text : { *(.text*) }
.data : { *(.data*) }
.comment : { *(.comment*) }
a = b + c;
b = c + 0x100;
c = 0x1000;
}
7 changes: 7 additions & 0 deletions test/Common/standalone/SymbolForwReference/Inputs/script.3.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
SECTIONS {
.text : { *(.text*) }
.data : { *(.data*) }
.comment : { *(.comment*) }
a = b + 0x100;
b = a + 0x100;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#---SymbolForwReference.test------------------ Executable,LS ------------------#
#BEGIN_COMMENT
# This test validates that the linker reiterates the layout computation
# until symbol values converge or the max layout iteration count is reached.
#END_COMMENT
#START_TEST
RUN: %clang %clangopts -o %t1.1.o %p/Inputs/1.c -c
RUN: %link %linkopts -o %t1.1.out %t1.1.o -T %p/Inputs/script.1.t
RUN: %readelf -s %t1.1.out | %filecheck %s --check-prefix=CHECK1
RUN: %link %linkopts -o %t1.2.out %t1.1.o -T %p/Inputs/script.2.t
RUN: %readelf -s %t1.2.out | %filecheck %s --check-prefix=CHECK2
RUN: %link %linkopts -o %t1.3.out %t1.1.o -T %p/Inputs/script.3.t 2>&1 \
RUN: | %filecheck %s --check-prefix=NON_CONVERGENCE
#END_TEST

CHECK1-DAG: {{0+}}1100 {{.*}} a
CHECK1-DAG: {{0+}}1000 {{.*}} b

CHECK2-DAG: {{0+}}2100 {{.*}} a
CHECK2-DAG: {{0+}}1100 {{.*}} b
CHECK2-DAG: {{0+}}1000 {{.*}} c

NON_CONVERGENCE: Note: {{.*}}script.3.t: assignment 'a({{.*}}) = b({{.*}}) + 0x100;' did not converge after 4 layout passes
Loading