[v4] elf: Set image base address to the maximum page size

Message ID CAMe9rOoW4dnsHBBLqAbHS_6fTaDwmSbjw=Ah2M0SaZVWei7i4Q@mail.gmail.com
State New
Headers
Series [v4] elf: Set image base address to the maximum page size |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

H.J. Lu June 26, 2026, 4:06 a.m. UTC
  On Thu, Jun 25, 2026 at 11:46 PM H.J. Lu <hjl.tools@gmail.com> wrote:
>
> On Thu, Jun 25, 2026 at 10:42 PM Nick Clifton <nickc@redhat.com> wrote:
> >
> > Hi H.J.
> >
> > >       PR ld/34184
> > >       * ldexp.c (fold_binary): Set text-segment address to the maximum
> > >       page size for ELF PDE output with -z max-page-size=SIZE.
> > >       * testsuite/ld-elf/elf.exp: Add ld/34184 test.
> > >       * testsuite/ld-elf/pr34184.c: New file.
> >
> > I like this patch, but I do have a couple of comments:
> >
> > > +      /* When generating Position Dependent Executable for ELF with the
> > > +      maximum page size set on command-line, if text-segment address
> > > +      is lower than the maximum page size, set text-segment address
> > > +      to the maximum page size.  */
> > > +      if ((bfd_get_flavour (link_info.output_bfd)
> > > +        == bfd_target_elf_flavour)
> > > +       && bfd_link_pde (&link_info)
> > > +       && link_info.maxpagesize_is_set
> > > +       && link_info.maxpagesize > value
> > > +       && strcmp (segment_name, "text-segment") == 0)
> > > +     value = link_info.maxpagesize;
> >
> > I think that it would be helpful to inform the user that the text
> > segment address is being changed - *if* they have tried to set it
> > using --image-base or --Ttext-segment.  Otherwise they may wonder
> > why their command line option is being ignored.
>
> Will do.

Fixed in the v4 patch.

> >
> > > +    [list \
> > > +     "Run pr34184" \
> > > +     "-Wl,-z,max-page-size=0x800000" \
> > > +     "" \
> > > +     {pr34184.c} \
> > > +     "pr34184" \
> > > +     "pass.out" \
> > > +    ] \
> >
> > As far as I can tell, this test does not actually check that
> > the text segment's address is now a multiple of the max page
> > size...
>
> This is a run-time test.  Without the fix, we get segfault:
>
> [hjl@gnu-tgl-3 tmp]$ cat x.c
> int
> main ()
> {
>   return 0;
> }
> [hjl@gnu-tgl-3 tmp]$ gcc -Wl,-z,max-page-size=0x800000 x.c
> [hjl@gnu-tgl-3 tmp]$ ./a.out
> Segmentation fault         ./a.out
> [hjl@gnu-tgl-3 tmp]$
>
>
> --
> H.J.

Here is the v4 patch.
  

Comments

Alan Modra June 29, 2026, 1:24 a.m. UTC | #1
On Fri, Jun 26, 2026 at 12:06:50PM +0800, H.J. Lu wrote:
> Here is the v4 patch.

On x86_64-linux I see FAIL: ld-elf/textaddr8 due to not matching the
expected header.

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  LOAD           0x000000 0x0000000000800000 0x0000000000800000 0x0000e8 0x0000e8 R   0x800000
  LOAD           0x800000 0x0000000001000000 0x0000000001000000 0x000004 0x000004 R E 0x800000
  LOAD           0x1000000 0x0000000001800000 0x0000000001800000 0x000004 0x000004 RW  0x800000

aarch64-linux-gnu  +FAIL: ld-elf/textaddr8
alpha-linux-gnu  +FAIL: ld-elf/textaddr8
am33_2.0-linux-gnu  +FAIL: ld-elf/textaddr8
armeb-linux-gnueabi  +FAIL: ld-elf/textaddr8
arm-linux-gnueabi  +FAIL: ld-elf/textaddr8
cris-linux-gnu  +FAIL: ld-elf/textaddr8
crisv32-linux-gnu  +FAIL: ld-elf/textaddr8
csky-linux-gnu  +FAIL: ld-elf/textaddr8
frv-linux-gnu  +FAIL: ld-elf/textaddr8
h8300-linux-gnu  +FAIL: ld-elf/textaddr8
hppa64-linux-gnu  +FAIL: ld-elf/textaddr8
hppa-linux-gnu  +FAIL: ld-elf/textaddr8
i586-linux-gnu  +FAIL: ld-elf/textaddr8
ia64-linux-gnu  +FAIL: ld-elf/textaddr8
kvx-linux-gnu  +FAIL: ld-elf/textaddr8
lm32-linux-gnu  +FAIL: ld-elf/textaddr8
loongarch64-linux-gnu  +FAIL: ld-elf/textaddr8
m32r-linux-gnu  +FAIL: ld-elf/textaddr8
m68k-linux-gnu  +FAIL: ld-elf/textaddr8
metag-linux-gnu  +FAIL: ld-elf/textaddr8
microblaze-linux-gnu  +FAIL: ld-elf/textaddr8
microblaze-linux-gnu  +FAIL: ld-elf/textaddr9
mips64-linux-gnuabi64  +FAIL: ld-elf/textaddr8
mipsel-linux-gnu  +FAIL: ld-elf/textaddr8
mipsisa32el-linux-gnu  +FAIL: ld-elf/textaddr8
mips-linux-gnu  +FAIL: ld-elf/textaddr8
nds32le-linux-gnu  +FAIL: ld-elf/textaddr8
nds32le-linux-gnu  +FAIL: ld-elf/textaddr9
or1k-linux-gnu  +FAIL: ld-elf/textaddr8
powerpc64le-linux-gnu  +FAIL: ld-elf/textaddr8
powerpc64-linux-gnu  +FAIL: ld-elf/textaddr8
powerpc-linux-gnu  +FAIL: ld-elf/textaddr8
riscv64-linux-gnu  +FAIL: ld-elf/textaddr8
s390-linux-gnu  +FAIL: ld-elf/textaddr8
s390x-linux-gnu  +FAIL: ld-elf/textaddr8
sh4-linux-gnu  +FAIL: ld-elf/textaddr8
sparc64-linux-gnu  +FAIL: ld-elf/textaddr8
sparc-linux-gnu  +FAIL: ld-elf/textaddr8
tilegx-linux-gnu  +FAIL: ld-elf/textaddr8
tilepro-linux-gnu  +FAIL: ld-elf/textaddr8
x86_64-pc-linux-gnu  +FAIL: ld-elf/textaddr8
x86_64-pc-linux-gnux32  +FAIL: ld-elf/textaddr8
  

Patch

From 71b78427774b7a8e2971cef7eddfe84e84753d8e Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Thu, 28 May 2026 10:47:37 +0800
Subject: [PATCH v4] elf: Set image base address to the maximum page size

When generating Position Dependent Executable for ELF with the maximum
page size set on command-line, set the default image base address to the
maximum page size to avoid segfault if it is lower than the maximum page
size and issue an warning if the image base address set on command-line
is lower than the maximum page size.

	PR ld/34184
	* ldexp.c (fold_binary): Set the default image base address to
	the maximum page size for ELF PDE output if it is lower than
	the maximum page size set on command-line and issue an warning
	if the image base address set on command-line is lower than the
	maximum page size.
	* testsuite/ld-elf/elf.exp: Add ld/34184 tests for PDE, PIE
	static PDE and static PIE.
	* testsuite/ld-elf/pr34184.c: New file.
	* testsuite/ld-elf/textaddr3.d: Change -Ttext-segment adddress
	to 0x200100 so that it > the maximum page size.
	* testsuite/ld-elf/textaddr5.d: Likewise.
	* testsuite/ld-elf/textaddr8.d: New test.
	* testsuite/ld-elf/textaddr9.d: Likewise.

Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
---
 ld/ldexp.c                      | 44 +++++++++++++++++++++++++++++----
 ld/testsuite/ld-elf/elf.exp     | 36 +++++++++++++++++++++++++++
 ld/testsuite/ld-elf/pr34184.c   |  8 ++++++
 ld/testsuite/ld-elf/textaddr3.d |  2 +-
 ld/testsuite/ld-elf/textaddr5.d |  2 +-
 ld/testsuite/ld-elf/textaddr8.d |  9 +++++++
 ld/testsuite/ld-elf/textaddr9.d |  9 +++++++
 7 files changed, 103 insertions(+), 7 deletions(-)
 create mode 100644 ld/testsuite/ld-elf/pr34184.c
 create mode 100644 ld/testsuite/ld-elf/textaddr8.d
 create mode 100644 ld/testsuite/ld-elf/textaddr9.d

diff --git a/ld/ldexp.c b/ld/ldexp.c
index 7bda6acc4c9..19542ad99c6 100644
--- a/ld/ldexp.c
+++ b/ld/ldexp.c
@@ -552,20 +552,54 @@  fold_binary (etree_type *tree)
       /* Check to see if the user has overridden the default
 	 value.  */
       segment_name = tree->binary.rhs->name.name;
+
+      bool update_image_base
+	= ((bfd_get_flavour (link_info.output_bfd)
+	    == bfd_target_elf_flavour)
+	   && bfd_link_pde (&link_info)
+	   && link_info.maxpagesize_is_set);
+
       for (seg = segments; seg; seg = seg->next)
 	if (strcmp (seg->name, segment_name) == 0)
 	  {
 	    if (!seg->used
 		&& config.magic_demand_paged
-		&& link_info.maxpagesize != 0
-		&& (seg->value % link_info.maxpagesize) != 0)
-	      einfo (_("%P: warning: address of `%s' "
-		       "isn't multiple of maximum page size\n"),
-		     segment_name);
+		&& link_info.maxpagesize != 0)
+	      {
+		if (seg->value < link_info.maxpagesize)
+		  {
+		    if (update_image_base)
+		      einfo (_("%P: warning: image base (0x%llx) < "
+			       "maximum page size (0x%llx)\n"),
+			     (unsigned long long) seg->value,
+			     (unsigned long long) link_info.maxpagesize);
+		  }
+		else if ((seg->value % link_info.maxpagesize) != 0)
+		  einfo (_("%P: warning: address of `%s' "
+			   "isn't multiple of maximum page size\n"),
+			 segment_name);
+
+		/* Don't override image base from command-line.  */
+		if (strcmp (segment_name, "text-segment") == 0)
+		  update_image_base = false;
+
+	      }
 	    seg->used = true;
 	    value = seg->value;
 	    break;
 	  }
+
+      /* When generating Position Dependent Executable for ELF with the
+	 maximum page size set on command-line, if image base address
+	 is lower than the maximum page size, set image base address
+	 to the maximum page size to avoid segfault.  NB: For ELF,
+	 -Ttext-segment=ADDR is an alias of --image-base=ADDR, which
+	 sets the base address of the ELF executable.  */
+      if (update_image_base
+	  && link_info.maxpagesize > value
+	  && strcmp (segment_name, "text-segment") == 0)
+	value = link_info.maxpagesize;
+
       new_rel_from_abs (value);
       return;
     }
diff --git a/ld/testsuite/ld-elf/elf.exp b/ld/testsuite/ld-elf/elf.exp
index 8fca30dbeeb..51b8c942648 100644
--- a/ld/testsuite/ld-elf/elf.exp
+++ b/ld/testsuite/ld-elf/elf.exp
@@ -535,6 +535,15 @@  if { [istarget *-*-linux*]
 		"pr14525.out" \
 		"-fPIE" \
 	    ] \
+	    [list \
+		"Run PR ld/34184 test (static PIE)" \
+		"-pie -Wl,-z,max-page-size=0x800000" \
+		"" \
+		{pr34184.c} \
+		"pr34184-static-pie" \
+		"pass.out" \
+		"-fPIE" \
+	    ] \
 	]
     }
 
@@ -580,6 +589,33 @@  if { [istarget *-*-linux*]
 	    {} \
 	    "-Wl,-R,tmpdir tmpdir/pr32690.so" \
 	] \
+	[list \
+	    "Run PR ld/34184 test (PDE)" \
+	    "$NOPIE_LDFLAGS -Wl,-z,max-page-size=0x800000" \
+	    "" \
+	    {pr34184.c} \
+	    "pr34184-pde" \
+	    "pass.out" \
+	    "$NOPIE_CFLAGS" \
+	] \
+	[list \
+	    "Run PR ld/34184 test (static PDE)" \
+	    "-static $NOPIE_LDFLAGS -Wl,-z,max-page-size=0x800000" \
+	    "" \
+	    {pr34184.c} \
+	    "pr34184-static-pde" \
+	    "pass.out" \
+	    "$NOPIE_CFLAGS" \
+	] \
+	[list \
+	    "Run PR ld/34184 test (PIE)" \
+	    "-pie -Wl,-z,max-page-size=0x800000" \
+	    "" \
+	    {pr34184.c} \
+	    "pr34184-pie" \
+	    "pass.out" \
+	    "-fPIE" \
+	] \
     ]
 }
 
diff --git a/ld/testsuite/ld-elf/pr34184.c b/ld/testsuite/ld-elf/pr34184.c
new file mode 100644
index 00000000000..8fb892c6aaa
--- /dev/null
+++ b/ld/testsuite/ld-elf/pr34184.c
@@ -0,0 +1,8 @@ 
+#include <stdio.h>
+
+int
+main ()
+{
+  printf ("PASS\n");
+  return 0;
+}
diff --git a/ld/testsuite/ld-elf/textaddr3.d b/ld/testsuite/ld-elf/textaddr3.d
index 3796b7dcb98..284d5a47015 100644
--- a/ld/testsuite/ld-elf/textaddr3.d
+++ b/ld/testsuite/ld-elf/textaddr3.d
@@ -1,4 +1,4 @@ 
 #source: maxpage1.s
-#ld: -Ttext-segment 0x10000 -z max-page-size=0x200000
+#ld: -Ttext-segment 0x200100 -z max-page-size=0x200000
 #target: *-*-linux-gnu *-*-gnu* arm*-*-uclinuxfdpiceabi
 #warning: .*address of `text-segment' isn't multiple of maximum page size
diff --git a/ld/testsuite/ld-elf/textaddr5.d b/ld/testsuite/ld-elf/textaddr5.d
index ab1097f40cf..1f65c5136e0 100644
--- a/ld/testsuite/ld-elf/textaddr5.d
+++ b/ld/testsuite/ld-elf/textaddr5.d
@@ -1,5 +1,5 @@ 
 #source: maxpage1.s
-#ld: -shared -z max-page-size=0x200000 -Ttext-segment 0x10000
+#ld: -shared -z max-page-size=0x200000 -Ttext-segment 0x200100
 #target: *-*-linux-gnu *-*-gnu* arm*-*-uclinuxfdpiceabi
 #xfail: ![check_shared_lib_support]
 #warning: .*address of `text-segment' isn't multiple of maximum page size
diff --git a/ld/testsuite/ld-elf/textaddr8.d b/ld/testsuite/ld-elf/textaddr8.d
new file mode 100644
index 00000000000..df0bdc63f08
--- /dev/null
+++ b/ld/testsuite/ld-elf/textaddr8.d
@@ -0,0 +1,9 @@ 
+#source: maxpage1.s
+#ld: -z max-page-size=0x800000 --image-base 0x40000 -z separate-code
+#warning: image base \(0x40000\) < maximum page size \(0x800000\)
+#readelf: -l --wide
+#target: *-*-linux-gnu *-*-gnu* arm*-*-uclinuxfdpiceabi
+
+#...
+  LOAD +0x0* 0x0*800000 0x0*800000 0x[0-9a-f]+ 0x[0-9a-f]+ R E 0x800000
+#pass
diff --git a/ld/testsuite/ld-elf/textaddr9.d b/ld/testsuite/ld-elf/textaddr9.d
new file mode 100644
index 00000000000..147ee7967c2
--- /dev/null
+++ b/ld/testsuite/ld-elf/textaddr9.d
@@ -0,0 +1,9 @@ 
+#source: maxpage1.s
+#ld: -shared -z max-page-size=0x800000 --image-base 0x40000 -z separate-code
+#readelf: -l --wide
+#target: *-*-linux-gnu *-*-gnu* arm*-*-uclinuxfdpiceabi
+#xfail: ![check_shared_lib_support]
+
+#...
+  LOAD +0x800000 0x0*800000 0x0*800000 0x[0-9a-f]+ 0x[0-9a-f]+ R E 0x800000
+#pass
-- 
2.54.0