Skip to content

fix: improve linker script expression section semantics - #2731

Open
vishruth-thimmaiah wants to merge 6 commits into
wild-linker:mainfrom
vishruth-thimmaiah:dyn_expressions
Open

vishruth-thimmaiah wants to merge 6 commits into
wild-linker:mainfrom
vishruth-thimmaiah:dyn_expressions

Conversation

@vishruth-thimmaiah

Copy link
Copy Markdown
Member

This PR make a bunch of improvements to how different types of expressions(absolute, section relative, numbers) interact with each other. Fixes:

Symbols preserve their section indexes across redefinition.
Preserving section relativity across ternary, MIN, and MAX expressions
Logical, bitwise, and comparison operators producing absolute symbols

Signed-off-by: Vishruth Thimmaiah <vishruththimmaiah@gmail.com>
@vishruth-thimmaiah
vishruth-thimmaiah marked this pull request as ready for review October 15, 2026 01:29

@davidlattimore davidlattimore left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some extra related tests that currently fail:

diff --git a/wild/tests/sources/elf/linker-script-expression-sections/absolute-arithmetic.ld b/wild/tests/sources/elf/linker-script-expression-sections/absolute-arithmetic.ld
new file mode 100644
index 0000000000..d221ad17da
--- /dev/null
+++ b/wild/tests/sources/elf/linker-script-expression-sections/absolute-arithmetic.ld
@@ -0,0 +1,13 @@
+ENTRY(_start)
+SECTIONS {
+    .text 0x10000 : {
+        section_start = .;
+        *(.text*)
+    }
+}
+
+/* These operations produce absolute values even with a section operand. */
+product = section_start * 2;
+quotient = section_start / 2;
+shifted = section_start >> 1;
+reverse_difference = 0x20000 - section_start;
diff --git a/wild/tests/sources/elf/linker-script-expression-sections/addr-alias.ld b/wild/tests/sources/elf/linker-script-expression-sections/addr-alias.ld
new file mode 100644
index 0000000000..7463c19edc
--- /dev/null
+++ b/wild/tests/sources/elf/linker-script-expression-sections/addr-alias.ld
@@ -0,0 +1,9 @@
+ENTRY(_start)
+SECTIONS {
+    .text 0x10000 : {
+        *(.text*)
+        /* ADDR retains the referenced section, including through arithmetic. */
+        section_addr = ADDR(.text);
+        after_addr = section_addr + 0x10;
+    }
+}
diff --git a/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c b/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c
new file mode 100644
index 0000000000..114b4c89d5
--- /dev/null
+++ b/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c
@@ -0,0 +1,37 @@ void _start(void) {}
+//#AbstractConfig:base
+//#RunEnabled:false
+//#DiffEnabled:false
+//#ReferenceLinkers:bfd
+
+//#Config:addr-alias:base
+//#ReferenceLinkers:bfd,lld
+//#LinkArgs:-T ./addr-alias.ld
+//#ExpectSym:after_addr section=".text",address=0x10010
+//#ExpectSym:section_addr section=".text",address=0x10000
+
+//#Config:min-max-number:base
+//#LinkArgs:-T ./min-max-number.ld
+//#ExpectSym:min_right section=".text",address=0x10000
+//#ExpectSym:min_left section=".text",address=0x10000
+//#ExpectSym:max_right section=".text",address=0x10010
+//#ExpectSym:max_left section=".text",address=0x10010
+
+//#Config:min-max-location:base
+//#LinkArgs:-T ./min-max-location.ld
+//#ExpectSym:padded_end section=".text",address=0x10100
+
+//#Config:absolute-arithmetic:base
+//#ReferenceLinkers:bfd,lld
+//#LinkArgs:-T ./absolute-arithmetic.ld
+//#ExpectSym:product is_abs=true,address=0x20000
+//#ExpectSym:quotient is_abs=true,address=0x8000
+//#ExpectSym:shifted is_abs=true,address=0x8000
+//#ExpectSym:reverse_difference is_abs=true,address=0x10000
+
+//#Config:relative-plus-absolute:base
+//#LinkArgs:-T ./relative-plus-absolute.ld
+//#ExpectSym:absolute_offset is_abs=true,address=0x10
+//#ExpectSym:derived is_abs=true,address=0x10010
+//#ExpectSym:derived_reverse is_abs=true,address=0x10010
+
+void _start(void) {}
diff --git a/wild/tests/sources/elf/linker-script-expression-sections/min-max-location.ld b/wild/tests/sources/elf/linker-script-expression-sections/min-max-location.ld
new file mode 100644
index 0000000000..fb8e396591
--- /dev/null
+++ b/wild/tests/sources/elf/linker-script-expression-sections/min-max-location.ld
@@ -0,0 +1,9 @@
+ENTRY(_start)
+SECTIONS {
+    .text 0x10000 : {
+        *(.text*)
+        /* The same conversion must work during early layout evaluation. */
+        . = MAX(., 0x100);
+        padded_end = .;
+    }
+}
diff --git a/wild/tests/sources/elf/linker-script-expression-sections/min-max-number.ld b/wild/tests/sources/elf/linker-script-expression-sections/min-max-number.ld
new file mode 100644
index 0000000000..edfcd004b3
--- /dev/null
+++ b/wild/tests/sources/elf/linker-script-expression-sections/min-max-number.ld
@@ -0,0 +1,12 @@
+ENTRY(_start)
+SECTIONS {
+    .text 0x10000 : {
+        section_start = .;
+        *(.text*)
+        /* GNU ld compares section offsets with numeric operands here. */
+        min_right = MIN(section_start, 0x10);
+        min_left = MIN(0x10, section_start);
+        max_right = MAX(section_start, 0x10);
+        max_left = MAX(0x10, section_start);
+    }
+}
diff --git a/wild/tests/sources/elf/linker-script-expression-sections/relative-plus-absolute.ld b/wild/tests/sources/elf/linker-script-expression-sections/relative-plus-absolute.ld
new file mode 100644
index 0000000000..7a3aa3490b
--- /dev/null
+++ b/wild/tests/sources/elf/linker-script-expression-sections/relative-plus-absolute.ld
@@ -0,0 +1,14 @@
+ENTRY(_start)
+absolute_offset = 0x10;
+
+SECTIONS {
+    .text 0x10000 : {
+        section_start = .;
+        *(.text*)
+    }
+}
+
+/* A named absolute symbol exercises Relative/Absolute, rather than
+   Relative/Number. GNU ld makes both sums absolute outside a section. */
+derived = section_start + absolute_offset;
+derived_reverse = absolute_offset + section_start;

Comment thread libwild/src/expression_eval.rs Outdated
Comment thread libwild/src/layout.rs Outdated
let resolution = resolutions[canonical_symbol_id.as_usize()]
.as_mut()
.ok_or_else(|| redirect.missing_resolution(def_info.name))?;
if is_absolute && redirect.loc.relative_section_id().is_some() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function both reads and writes redirect.loc, making it a little hard to follow what's going on. Is there an alternative that would be more readable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a Redirect::update_for_evaluated_value method here.

Comment thread libwild/src/expression_eval.rs
Signed-off-by: Vishruth Thimmaiah <vishruththimmaiah@gmail.com>
}
}

if exp.assertions.is_abs {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the tests have is_abs=false, but it looks like this will skip any check if that's the case

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, I've replaced any is_abs=false checks with an explicit section check.

Comment thread libwild/src/expression_eval.rs Outdated
_section_layouts: &OutputSectionMap<OutputRecordLayout>,
op: impl FnOnce(u64, u64) -> Result<u64>,
) -> Result<EvaluatedValue> {
let value_kind = if is_in_section {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter what type we produce for expressions like LOADADDR(.text) & 0xffff? Do we agree with the other linkers in that case with regard to whether the result is absolute to section-relative?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lld and ld deviate on how they handle absolute results, and I was previously matching whichever linkers' behavior was more convenient. I've updated it to match bfd more closely.

Signed-off-by: Vishruth Thimmaiah <vishruththimmaiah@gmail.com>
(ExpressionValueKind::Number, ExpressionValueKind::Number) => {
ExpressionValueKind::Number
}
(ExpressionValueKind::Relative(sec), _) | (_, ExpressionValueKind::Relative(sec)) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like subtracting a relative offset from another relative offset should produce an absolute:

diff --git a/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c b/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c
index 086c2547af..41623d5217 100644
--- a/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c
+++ b/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c
@@ -17,6 +17,11 @@
 //#ExpectSym:max_right section=".text",address=0x10010
 //#ExpectSym:max_left section=".text",address=0x10010
 
+//#Config:nested-subtraction:base
+//#ReferenceLinkers:bfd,lld
+//#LinkArgs:-T ./nested-subtraction.ld
+//#ExpectSym:difference is_abs=true,address=0x20
+
 //#Config:min-max-location:base
 //#LinkArgs:-T ./min-max-location.ld
 //#ExpectSym:padded_end section=".text",address=0x10100
diff --git a/wild/tests/sources/elf/linker-script-expression-sections/nested-subtraction.ld b/wild/tests/sources/elf/linker-script-expression-sections/nested-subtraction.ld
new file mode 100644
index 0000000000..702b662e0d
--- /dev/null
+++ b/wild/tests/sources/elf/linker-script-expression-sections/nested-subtraction.ld
@@ -0,0 +1,9 @@
+ENTRY(_start)
+SECTIONS {
+    .text 0x10000 : {
+        section_start = .;
+        section_end = section_start + 0x20;
+        difference = MIN(section_end - section_start, LOADADDR(.text));
+        *(.text*)
+    }
+}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, along with a few other previously failing tests.

Signed-off-by: Vishruth Thimmaiah <vishruththimmaiah@gmail.com>
Signed-off-by: Vishruth Thimmaiah <vishruththimmaiah@gmail.com>
Signed-off-by: Vishruth Thimmaiah <vishruththimmaiah@gmail.com>

@davidlattimore davidlattimore left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like cross-section expressions aren't doing the right thing:

diff --git a/wild/tests/sources/elf/linker-script-expression-sections/cross-section-offsets.ld b/wild/tests/sources/elf/linker-script-expression-sections/cross-section-offsets.ld
new file mode 100644
index 0000000000..fa2f4bcf68
--- /dev/null
+++ b/wild/tests/sources/elf/linker-script-expression-sections/cross-section-offsets.ld
@@ -0,0 +1,12 @@
+ENTRY(_start)
+SECTIONS {
+    .data 0x20000 : {
+        data_start = .;
+        KEEP(*(.data*))
+    }
+    .text 0x10000 : {
+        cross_min = MIN(data_start, 0x10);
+        cross_mul = data_start * 2;
+        *(.text*)
+    }
+}
diff --git a/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c b/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c
index 28e5cb80b3..f989ee66c0 100644
--- a/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c
+++ b/wild/tests/sources/elf/linker-script-expression-sections/linker-script-expression-sections.c
@@ -17,6 +17,11 @@
 //#ExpectSym:max_right section=".text",address=0x10010
 //#ExpectSym:max_left section=".text",address=0x10010
 
+//#Config:cross-section-offsets:base
+//#LinkArgs:-T ./cross-section-offsets.ld
+//#ExpectSym:cross_min section=".data",address=0x20000
+//#ExpectSym:cross_mul section=".data",address=0x20000
+
 //#Config:nested-subtraction:base
 //#ReferenceLinkers:bfd,lld
 //#LinkArgs:-T ./nested-subtraction.ld

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants