-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ELF] Postpone "unable to move location counter backward" error #66854
Conversation
@llvm/pr-subscribers-lld-elf @llvm/pr-subscribers-lld ChangesThe size of .ARM.exidx may shrink across Other sections like .relr.dyn may change sizes across Similar to https://reviews.llvm.org/D152170, postpone the error reporting. Full diff: https://github.com/llvm/llvm-project/pull/66854.diff 5 Files Affected:
diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp
index 943fd865ae0c15c..14a89c79a65ecf8 100644
--- a/lld/ELF/LinkerScript.cpp
+++ b/lld/ELF/LinkerScript.cpp
@@ -169,9 +169,16 @@ void LinkerScript::expandOutputSection(uint64_t size) {
void LinkerScript::setDot(Expr e, const Twine &loc, bool inSec) {
uint64_t val = e().getValue();
- if (val < dot && inSec)
- error(loc + ": unable to move location counter backward for: " +
- state->outSec->name);
+ // If val is smaller and we are in an output section, record the error and
+ // report it if this is the last assignAddresses iteration. dot may be smaller
+ // if there is another assignAddresses iteration.
+ if (val < dot && inSec) {
+ backwardDotErr =
+ (loc + ": unable to move location counter (0x" + Twine::utohexstr(dot) +
+ ") backward (0x" + Twine::utohexstr(val) + ") for section '" +
+ state->outSec->name + "'")
+ .str();
+ }
// Update to location counter means update to section size.
if (inSec)
@@ -1343,6 +1350,7 @@ const Defined *LinkerScript::assignAddresses() {
state = &st;
errorOnMissingSection = true;
st.outSec = aether;
+ backwardDotErr.clear();
SymbolAssignmentMap oldValues = getSymbolAssignmentValues(sectionCommands);
for (SectionCommand *cmd : sectionCommands) {
@@ -1494,7 +1502,9 @@ static void checkMemoryRegion(const MemoryRegion *region,
}
}
-void LinkerScript::checkMemoryRegions() const {
+void LinkerScript::checkDotAndMemoryRegions() const {
+ if (backwardDotErr.size())
+ errorOrWarn(backwardDotErr);
for (const OutputSection *sec : outputSections) {
if (const MemoryRegion *memoryRegion = sec->memRegion)
checkMemoryRegion(memoryRegion, sec, sec->addr);
diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h
index 89780bb60f48244..a731610e3fdd504 100644
--- a/lld/ELF/LinkerScript.h
+++ b/lld/ELF/LinkerScript.h
@@ -345,8 +345,8 @@ class LinkerScript final {
// Describe memory region usage.
void printMemoryUsage(raw_ostream &os);
- // Verify memory/lma overflows.
- void checkMemoryRegions() const;
+ // Check backward location counter assignment and memory region/LMA overflows.
+ void checkDotAndMemoryRegions() const;
// SECTIONS command list.
SmallVector<SectionCommand *, 0> sectionCommands;
@@ -358,6 +358,7 @@ class LinkerScript final {
bool seenDataAlign = false;
bool seenRelroEnd = false;
bool errorOnMissingSection = false;
+ std::string backwardDotErr;
// List of section patterns specified with KEEP commands. They will
// be kept even if they are unused and --gc-sections is specified.
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 40f7d7981d9d441..4c82967b0a47d25 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -2187,7 +2187,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
for (OutputSection *sec : outputSections)
sec->finalize();
- script->checkMemoryRegions();
+ script->checkDotAndMemoryRegions();
if (config->emachine == EM_ARM && !config->isLE && config->armBe8) {
addArmInputSectionMappingSymbols();
diff --git a/lld/test/ELF/linkerscript/locationcountererr-arm-exidx.test b/lld/test/ELF/linkerscript/locationcountererr-arm-exidx.test
new file mode 100644
index 000000000000000..cea9d3de2264c96
--- /dev/null
+++ b/lld/test/ELF/linkerscript/locationcountererr-arm-exidx.test
@@ -0,0 +1,53 @@
+# REQUIRES: arm
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=armv7-linux-gnueabi a.s -o a.o
+# RUN: llvm-mc -filetype=obj -triple=armv7-linux-gnueabi a.s -o a.o
+
+## If we don't merge adjacent duplicate entries, __code_size will be negative and
+## . += __code_size will trigger an error.
+# RUN: not ld.lld -z norelro -T a.t a.o -o /dev/null --no-merge-exidx-entries 2>&1 | FileCheck %s --check-prefix=ERR
+
+# ERR: a.t:10: unable to move location counter (0x4104) backward (0x4070) for section 'code.unused_space'
+
+## If we merge adjacent duplicate entries, we will have enough space.
+## https://github.com/llvm/llvm-project/issues/66836
+# RUN: ld.lld -z norelro -T a.t a.o -o a
+# RUN: llvm-readelf -S a | FileCheck %s
+
+# CHECK: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
+# CHECK-NEXT: [ 0] NULL 00000000 000000 000000 00 0 0 0
+# CHECK-NEXT: [ 1] .text PROGBITS 00004000 004000 000054 00 AX 0 0 4
+# CHECK-NEXT: [ 2] .ARM.exidx ARM_EXIDX 00004054 004054 000010 00 AL 1 0 4
+# CHECK-NEXT: [ 3] code.unused_space NOBITS 00004064 004064 00000c 00 A 0 0 1
+
+#--- a.s
+.globl _start
+_start:
+ bx lr
+
+.macro A
+.section .text.f\@,"ax"
+.globl f\@
+f\@:
+.fnstart
+ bx lr
+.cantunwind
+.fnend
+.endm
+
+.rept 20
+ A
+.endr
+
+#--- a.t
+MEMORY {
+ CODE (RX) : ORIGIN = 0x4000, LENGTH = 0x70
+}
+__code_end = ORIGIN(CODE) + LENGTH(CODE);
+
+SECTIONS {
+ .text : { *(.text .text.*) } > CODE
+ .ARM.exidx : { *(.ARM.exidx .ARM.exidx.*) } > CODE
+ __code_size = __code_end - .;
+ code.unused_space (NOLOAD) : { . += __code_size; } > CODE
+}
diff --git a/lld/test/ELF/linkerscript/locationcountererr.test b/lld/test/ELF/linkerscript/locationcountererr.test
index 086bfeba1e3d455..181bc1f8ea00fa4 100644
--- a/lld/test/ELF/linkerscript/locationcountererr.test
+++ b/lld/test/ELF/linkerscript/locationcountererr.test
@@ -1,7 +1,7 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t
# RUN: not ld.lld %t --script %s -o /dev/null 2>&1 | FileCheck %s
-# CHECK: {{.*}}.test:8: unable to move location counter backward for: .text
+# CHECK: {{.*}}.test:8: unable to move location counter (0x2000) backward (0x10) for section '.text'
SECTIONS {
.text 0x2000 : {
|
5871e26
to
363c4f3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks sensible to me but probably a good idea to get another review.
lld/ELF/LinkerScript.cpp
Outdated
if (val < dot && inSec) { | ||
backwardDotErr = | ||
(loc + ": unable to move location counter (0x" + Twine::utohexstr(dot) + | ||
") backward (0x" + Twine::utohexstr(val) + ") for section '" + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Much prefer the new error message but I noticed that it's ambiguous if this means we are moving it backwards by a number of bytes or to given address. How about the following:
") backward (0x" + Twine::utohexstr(val) + ") for section '" + | |
") backward to (0x" + Twine::utohexstr(val) + ") for section '" + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in 54daea8e15f7e5e95e6531d34616062af81c4d84 to backward to 0x... for section 'xxx'
@@ -1,7 +1,7 @@ | |||
# REQUIRES: x86 | |||
# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux /dev/null -o %t | |||
# RUN: not ld.lld %t --script %s -o /dev/null 2>&1 | FileCheck %s | |||
# CHECK: {{.*}}.test:8: unable to move location counter backward for: .text | |||
# CHECK: {{.*}}.test:8: unable to move location counter (0x2000) backward (0x10) for section '.text' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe worth adding a test with more than one backwards error? Previously we'd get multiple reports now just the first one. There probably isn't much value in reporting further errors, but I think it does make sense to document this behaviour in a test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. 54daea8e15f7e5e95e6531d34616062af81c4d84 added two more memory regions to demonstrate the full set of errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM too.
lld/ELF/LinkerScript.h
Outdated
// Verify memory/lma overflows. | ||
void checkMemoryRegions() const; | ||
// Check backward location counter assignment and memory region/LMA overflows. | ||
void checkDotAndMemoryRegions() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be better to have a more general name such as checkStableAddressScript() or checkFinalScriptConditions() rather than detail each check.
Reason I mention it is that there may be more checks that are worth delaying. For example in https://github.com/kernkonzept/fiasco/blob/master/src/kernel.arm.ld#L60 the assert will fail in LLD but not in GNU ld because the assert is evaluated eagerly before any addresses are assigned, wheras GNU ld evaluates them once at the end. In this case the assert can be moved to the end of the script.
Not sure that this particular function would be the place to do that though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion. I remember that I've seen similar ASSERT
issues. We can postpone the check when the problem arises...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to checkFinalScriptConditions in 2bf25dc88bfa848ca520578ce2c0e6bb15d1a1a9
The size of .ARM.exidx may shrink across `assignAddress` calls. It is possible that the initial iteration has a larger location counter, causing `__code_size = __code_end - .; osec : { . += __code_size; }` to report an error, while the error would have been suppressed for subsequent `assignAddress` iterations. Other sections like .relr.dyn may change sizes across `assignAddress` calls as well. However, their initial size is zero, so it is difficiult to trigger a similar error. Similar to https://reviews.llvm.org/D152170, postpone the error reporting. Fix llvm#66836. While here, add more information to the error message.
Since `assignAddresses` is executed more than once, error reporting during `assignAddresses` would be duplicated. Generalize llvm#66854 to cover more errors. Note: address-related errors exposed in one invocation might not be errors in another invocation. Pull Request: llvm#96361
The size of .ARM.exidx may shrink across
assignAddress
calls. It is possiblethat the initial iteration has a larger location counter, causing
__code_size = __code_end - .; osec : { . += __code_size; }
to report an error, while the error wouldhave been suppressed for subsequent
assignAddress
iterations.Other sections like .relr.dyn may change sizes across
assignAddress
calls aswell. However, their initial size is zero, so it is difficiult to trigger a
similar error.
Similar to https://reviews.llvm.org/D152170, postpone the error reporting.
Fix #66836. While here, add more information to the error message.