Exclude zero size section for RELRO segment match

Message ID 20180215115816.GA17271@gmail.com
State New
Headers show
Series
  • Exclude zero size section for RELRO segment match
Related show

Commit Message

H.J. Lu Feb. 15, 2018, 11:58 a.m.
When seaching a LOAD segment which overlaps with the RELRO segment, we
need to check the size of the last non-zero size section in the LOAD
segment.

OK for master?

H.J.
---
bfd/

	PR ld/22845
	* elf.c (assign_file_positions_for_non_load_sections): Exclude
	zero size section when seaching a LOAD segment containing a
	section in the RELRO segment.

ld/

	PR ld/22845
	* testsuite/ld-elf/binutils.exp (tls_opts): Add tests for
	"-z noseparate-code" and "-z separate-code".
---
 bfd/elf.c                        | 27 ++++++++++++++++++++-------
 ld/testsuite/ld-elf/binutils.exp |  6 ++++--
 2 files changed, 24 insertions(+), 9 deletions(-)

-- 
2.14.3

Comments

Alan Modra Feb. 16, 2018, 12:08 p.m. | #1
On Thu, Feb 15, 2018 at 03:58:16AM -0800, H.J. Lu wrote:
> When seaching a LOAD segment which overlaps with the RELRO segment, we

> need to check the size of the last non-zero size section in the LOAD

> segment.


Why?  Do you have a case where the current algorithm goes wrong?

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 16, 2018, 12:13 p.m. | #2
On Fri, Feb 16, 2018 at 4:08 AM, Alan Modra <amodra@gmail.com> wrote:
> On Thu, Feb 15, 2018 at 03:58:16AM -0800, H.J. Lu wrote:

>> When seaching a LOAD segment which overlaps with the RELRO segment, we

>> need to check the size of the last non-zero size section in the LOAD

>> segment.

>

> Why?  Do you have a case where the current algorithm goes wrong?

>


Yes, apply this patch and I get assert in
assign_file_positions_for_non_load_sections
on x86-64:

diff --git a/ld/testsuite/ld-elf/binutils.exp b/ld/testsuite/ld-elf/binutils.exp
index 748699d5d1..290f158710 100644
--- a/ld/testsuite/ld-elf/binutils.exp
+++ b/ld/testsuite/ld-elf/binutils.exp
@@ -186,9 +186,11 @@ if { ![istarget "hppa64-*-*"] } {
     lappend tls_tests "tdata3" "tbss1" "tbss2" "tbss3"
 }
 set tls_opts {
-    ""
+    "-z noseparate-code"
+    "-z separate-code"
     "-z relro"
-    "-shared"
+    "-shared -z noseparate-code"
+    "-shared -z separate-code"
     "-shared -z relro"
     "-z max-page-size=0x100000"
     "-z max-page-size=0x100000 -z common-page-size=0x1000"


-- 
H.J.
Alan Modra Feb. 16, 2018, 11:44 p.m. | #3
On Fri, Feb 16, 2018 at 04:13:04AM -0800, H.J. Lu wrote:
> On Fri, Feb 16, 2018 at 4:08 AM, Alan Modra <amodra@gmail.com> wrote:

> > On Thu, Feb 15, 2018 at 03:58:16AM -0800, H.J. Lu wrote:

> >> When seaching a LOAD segment which overlaps with the RELRO segment, we

> >> need to check the size of the last non-zero size section in the LOAD

> >> segment.

> >

> > Why?  Do you have a case where the current algorithm goes wrong?

> >

> 

> Yes, apply this patch and I get assert in

> assign_file_positions_for_non_load_sections

> on x86-64:


On looking at the tbss1 fail, I see a PT_LOAD with a single
zero length .eh_frame section.  So we have a degenerate PT_LOAD.
I think the following simpler patch is sufficient.

Your testsuite change is OK, but please check a large set of targets
before committing in case some targets won't support -z separate-code.

	PR 22845
	* elf.c (assign_file_positions_for_non_load_sections): Ignore
	degenerate zero size PT_LOAD segments when finding one overlapping
	the PT_GNU_RELRO segment.

diff --git a/bfd/elf.c b/bfd/elf.c
index 397fa62..b069b59 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -5897,6 +5897,7 @@ assign_file_positions_for_non_load_sections (bfd *abfd,
 		   lm = lm->next, lp++)
 		{
 		  if (lp->p_type == PT_LOAD
+		      && lp->p_memsz != 0
 		      && lm->count != 0
 		      && lm->sections[lm->count - 1]->vma >= start
 		      && lm->sections[0]->vma < end)

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 17, 2018, 1:45 p.m. | #4
On Fri, Feb 16, 2018 at 3:44 PM, Alan Modra <amodra@gmail.com> wrote:
> On Fri, Feb 16, 2018 at 04:13:04AM -0800, H.J. Lu wrote:

>> On Fri, Feb 16, 2018 at 4:08 AM, Alan Modra <amodra@gmail.com> wrote:

>> > On Thu, Feb 15, 2018 at 03:58:16AM -0800, H.J. Lu wrote:

>> >> When seaching a LOAD segment which overlaps with the RELRO segment, we

>> >> need to check the size of the last non-zero size section in the LOAD

>> >> segment.

>> >

>> > Why?  Do you have a case where the current algorithm goes wrong?

>> >

>>

>> Yes, apply this patch and I get assert in

>> assign_file_positions_for_non_load_sections

>> on x86-64:

>

> On looking at the tbss1 fail, I see a PT_LOAD with a single

> zero length .eh_frame section.  So we have a degenerate PT_LOAD.

> I think the following simpler patch is sufficient.

>

> Your testsuite change is OK, but please check a large set of targets

> before committing in case some targets won't support -z separate-code.


These tests are only run on Linux:

# Make sure that binutils can correctly handle ld output in ELF.

if { ![istarget *-*-linux*]
     && ![istarget *-*-nacl*]
     && ![istarget *-*-gnu*]} {
    return
}

if { [istarget *-*-linux*aout*]
     || [istarget *-*-linux*oldld*] } {
    return
}

I tested a couple Linux targets and checked it in.

Thanks.

>         PR 22845

>         * elf.c (assign_file_positions_for_non_load_sections): Ignore

>         degenerate zero size PT_LOAD segments when finding one overlapping

>         the PT_GNU_RELRO segment.

>

> diff --git a/bfd/elf.c b/bfd/elf.c

> index 397fa62..b069b59 100644

> --- a/bfd/elf.c

> +++ b/bfd/elf.c

> @@ -5897,6 +5897,7 @@ assign_file_positions_for_non_load_sections (bfd *abfd,

>                    lm = lm->next, lp++)

>                 {

>                   if (lp->p_type == PT_LOAD

> +                     && lp->p_memsz != 0

>                       && lm->count != 0

>                       && lm->sections[lm->count - 1]->vma >= start

>                       && lm->sections[0]->vma < end)

>

> --

> Alan Modra

> Australia Development Lab, IBM




-- 
H.J.
H.J. Lu Feb. 17, 2018, 11:19 p.m. | #5
On Fri, Feb 16, 2018 at 3:44 PM, Alan Modra <amodra@gmail.com> wrote:
> On Fri, Feb 16, 2018 at 04:13:04AM -0800, H.J. Lu wrote:

>> On Fri, Feb 16, 2018 at 4:08 AM, Alan Modra <amodra@gmail.com> wrote:

>> > On Thu, Feb 15, 2018 at 03:58:16AM -0800, H.J. Lu wrote:

>> >> When seaching a LOAD segment which overlaps with the RELRO segment, we

>> >> need to check the size of the last non-zero size section in the LOAD

>> >> segment.

>> >

>> > Why?  Do you have a case where the current algorithm goes wrong?

>> >

>>

>> Yes, apply this patch and I get assert in

>> assign_file_positions_for_non_load_sections

>> on x86-64:

>

> On looking at the tbss1 fail, I see a PT_LOAD with a single

> zero length .eh_frame section.  So we have a degenerate PT_LOAD.

> I think the following simpler patch is sufficient.

>

> Your testsuite change is OK, but please check a large set of targets

> before committing in case some targets won't support -z separate-code.

>

>         PR 22845

>         * elf.c (assign_file_positions_for_non_load_sections): Ignore

>         degenerate zero size PT_LOAD segments when finding one overlapping

>         the PT_GNU_RELRO segment.

>

> diff --git a/bfd/elf.c b/bfd/elf.c

> index 397fa62..b069b59 100644

> --- a/bfd/elf.c

> +++ b/bfd/elf.c

> @@ -5897,6 +5897,7 @@ assign_file_positions_for_non_load_sections (bfd *abfd,

>                    lm = lm->next, lp++)

>                 {

>                   if (lp->p_type == PT_LOAD

> +                     && lp->p_memsz != 0

>                       && lm->count != 0

>                       && lm->sections[lm->count - 1]->vma >= start

>                       && lm->sections[0]->vma < end)

>


This patch isn't sufficient.  On x86, with this patch, I got

[hjl@gnu-tools-1 ld]$  ../gas/as-new
-I/export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf   -o
tmpdir/tbss.o /export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf/tbss.s
[hjl@gnu-tools-1 ld]$ ../gas/as-new
-I/export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf   -o
tmpdir/frame.o /export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf/frame.s
[hjl@gnu-tools-1 ld]$ ./ld-new   -o tmpdir/frame.so -z separate-code
--shared tmpdir/frame.o tmpdir/tbss.o
./ld-new: BFD (GNU Binutils) 2.30.51.20180217 assertion fail
/export/gnu/import/git/sources/binutils-gdb/bfd/elf.c:5917
[hjl@gnu-tools-1 ld]$

My original patch fixes them.

-- 
H.J.
From 04a1a6e280aeada4031b1f89c2c4c35ade8f3679 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 17 Feb 2018 15:06:32 -0800
Subject: [PATCH] ld: Add -z separate-code tests to frame.exp

On x86, "-z separate-code" leads to assertion fail at bfd/elf.c:5917.

	PR ld/22845
	* testsuite/ld-elf/frame.exp: Add tests for "-z noseparate-code"
	and "-z separate-code".
---
 ld/testsuite/ld-elf/frame.exp | 32 ++++++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/ld/testsuite/ld-elf/frame.exp b/ld/testsuite/ld-elf/frame.exp
index 6cce42deb3..55e6cba529 100644
--- a/ld/testsuite/ld-elf/frame.exp
+++ b/ld/testsuite/ld-elf/frame.exp
@@ -64,13 +64,23 @@ if { ![ld_assemble_flags $as $as_opt $srcdir/$subdir/tbss.s tmpdir/tbss.o ]
     return
 }
 
-if { [ld_link $ld tmpdir/frame.so "--shared tmpdir/frame.o tmpdir/tbss.o"] } {
-    pass "$test1"
+if { [ld_link $ld tmpdir/frame.so "-z noseparate-code --shared tmpdir/frame.o tmpdir/tbss.o"] } {
+    pass "$test1 -z noseparate-code"
 } else {
     if [string match "*shared not supported*" $link_output] {
 	unsupported "-shared is not supported by this target"
     } else {
-	fail "$test1"
+	fail "$test1 -z noseparate-code"
+    }
+}
+
+if { [ld_link $ld tmpdir/frame.so "-z separate-code --shared tmpdir/frame.o tmpdir/tbss.o"] } {
+    pass "$test1 -z separate-code"
+} else {
+    if [string match "*shared not supported*" $link_output] {
+	unsupported "-shared is not supported by this target"
+    } else {
+	fail "$test1 -z separate-code"
     }
 }
 
@@ -79,12 +89,22 @@ if ![ld_assemble_flags $as $as_opt $srcdir/$subdir/table.s tmpdir/table.o ] {
     return
 }
 
-if { [ld_link $ld tmpdir/table.so "--shared tmpdir/table.o tmpdir/tbss.o"] } {
-    pass "$test2"
+if { [ld_link $ld tmpdir/table.so "-z noseparate-code --shared tmpdir/table.o tmpdir/tbss.o"] } {
+    pass "$test2 -z noseparate-code"
+} else {
+    if [string match "*shared not supported*" $link_output] {
+	unsupported "-shared is not supported by this target"
+    } else {
+	fail "$test2 -z noseparate-code"
+    }
+}
+
+if { [ld_link $ld tmpdir/table.so "-z separate-code --shared tmpdir/table.o tmpdir/tbss.o"] } {
+    pass "$test2 -z separate-code"
 } else {
     if [string match "*shared not supported*" $link_output] {
 	unsupported "-shared is not supported by this target"
     } else {
-	fail "$test2"
+	fail "$test2 -z separate-code"
     }
 }
Alan Modra Feb. 19, 2018, 3:21 a.m. | #6
On Sat, Feb 17, 2018 at 03:19:26PM -0800, H.J. Lu wrote:
> On Fri, Feb 16, 2018 at 3:44 PM, Alan Modra <amodra@gmail.com> wrote:

> > --- a/bfd/elf.c

> > +++ b/bfd/elf.c

> > @@ -5897,6 +5897,7 @@ assign_file_positions_for_non_load_sections (bfd *abfd,

> >                    lm = lm->next, lp++)

> >                 {

> >                   if (lp->p_type == PT_LOAD

> > +                     && lp->p_memsz != 0

> >                       && lm->count != 0

> >                       && lm->sections[lm->count - 1]->vma >= start

> >                       && lm->sections[0]->vma < end)

> >

> 

> This patch isn't sufficient.  On x86, with this patch, I got

> 

> [hjl@gnu-tools-1 ld]$  ../gas/as-new

> -I/export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf   -o

> tmpdir/tbss.o /export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf/tbss.s

> [hjl@gnu-tools-1 ld]$ ../gas/as-new

> -I/export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf   -o

> tmpdir/frame.o /export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf/frame.s

> [hjl@gnu-tools-1 ld]$ ./ld-new   -o tmpdir/frame.so -z separate-code

> --shared tmpdir/frame.o tmpdir/tbss.o

> ./ld-new: BFD (GNU Binutils) 2.30.51.20180217 assertion fail

> /export/gnu/import/git/sources/binutils-gdb/bfd/elf.c:5917

> [hjl@gnu-tools-1 ld]$

> 

> My original patch fixes them.


You're correct, but the real underlying problem is that my load
section end calculation was wrong.  That can be fixed without a loop
scanning back through sections.

BTW, the testsuite changes will need some tweaks before they can be
committed, eg.
/home/alan/build/gas/d30v-elf/ld/ld-new: unrecognized option '-z'

	PR 22845
	* elf.c (IS_TBSS): Define.
	(_bfd_elf_map_sections_to_segments): Use IS_TBSS.
	(assign_file_positions_for_non_load_sections): Revert last change.
	Properly calculate load segment limits to compare against relro limits.

diff --git a/bfd/elf.c b/bfd/elf.c
index b069b59..f3a70f1 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -4542,6 +4542,9 @@ elf_modify_segment_map (bfd *abfd,
   return TRUE;
 }
 
+#define IS_TBSS(s) \
+  ((s->flags & (SEC_THREAD_LOCAL | SEC_LOAD)) == SEC_THREAD_LOCAL)
+
 /* Set up a mapping from BFD sections to program segments.  */
 
 bfd_boolean
@@ -4801,11 +4804,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
 		executable = TRUE;
 	      last_hdr = hdr;
 	      /* .tbss sections effectively have zero size.  */
-	      if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD))
-		  != SEC_THREAD_LOCAL)
-		last_size = hdr->size;
-	      else
-		last_size = 0;
+	      last_size = !IS_TBSS (hdr) ? hdr->size : 0;
 	      continue;
 	    }
 
@@ -4831,10 +4830,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
 
 	  last_hdr = hdr;
 	  /* .tbss sections effectively have zero size.  */
-	  if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD)) != SEC_THREAD_LOCAL)
-	    last_size = hdr->size;
-	  else
-	    last_size = 0;
+	  last_size = !IS_TBSS (hdr) ? hdr->size : 0;
 	  phdr_index = i;
 	  phdr_in_segment = FALSE;
 	}
@@ -4843,8 +4839,7 @@ _bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
 	 for .tbss.  */
       if (last_hdr != NULL
 	  && (i - phdr_index != 1
-	      || ((last_hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD))
-		  != SEC_THREAD_LOCAL)))
+	      || !IS_TBSS (last_hdr)))
 	{
 	  m = make_mapping (abfd, sections, phdr_index, i, phdr_in_segment);
 	  if (m == NULL)
@@ -5897,9 +5892,11 @@ assign_file_positions_for_non_load_sections (bfd *abfd,
 		   lm = lm->next, lp++)
 		{
 		  if (lp->p_type == PT_LOAD
-		      && lp->p_memsz != 0
 		      && lm->count != 0
-		      && lm->sections[lm->count - 1]->vma >= start
+		      && (lm->sections[lm->count - 1]->vma
+			  + (!IS_TBSS (lm->sections[lm->count - 1])
+			     ? lm->sections[lm->count - 1]->size
+			     : 0)) > start
 		      && lm->sections[0]->vma < end)
 		    break;
 		}

-- 
Alan Modra
Australia Development Lab, IBM
H.J. Lu Feb. 19, 2018, 1:06 p.m. | #7
On Sun, Feb 18, 2018 at 7:21 PM, Alan Modra <amodra@gmail.com> wrote:
> On Sat, Feb 17, 2018 at 03:19:26PM -0800, H.J. Lu wrote:

>> On Fri, Feb 16, 2018 at 3:44 PM, Alan Modra <amodra@gmail.com> wrote:

>> > --- a/bfd/elf.c

>> > +++ b/bfd/elf.c

>> > @@ -5897,6 +5897,7 @@ assign_file_positions_for_non_load_sections (bfd *abfd,

>> >                    lm = lm->next, lp++)

>> >                 {

>> >                   if (lp->p_type == PT_LOAD

>> > +                     && lp->p_memsz != 0

>> >                       && lm->count != 0

>> >                       && lm->sections[lm->count - 1]->vma >= start

>> >                       && lm->sections[0]->vma < end)

>> >

>>

>> This patch isn't sufficient.  On x86, with this patch, I got

>>

>> [hjl@gnu-tools-1 ld]$  ../gas/as-new

>> -I/export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf   -o

>> tmpdir/tbss.o /export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf/tbss.s

>> [hjl@gnu-tools-1 ld]$ ../gas/as-new

>> -I/export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf   -o

>> tmpdir/frame.o /export/gnu/import/git/sources/binutils-gdb/ld/testsuite/ld-elf/frame.s

>> [hjl@gnu-tools-1 ld]$ ./ld-new   -o tmpdir/frame.so -z separate-code

>> --shared tmpdir/frame.o tmpdir/tbss.o

>> ./ld-new: BFD (GNU Binutils) 2.30.51.20180217 assertion fail

>> /export/gnu/import/git/sources/binutils-gdb/bfd/elf.c:5917

>> [hjl@gnu-tools-1 ld]$

>>

>> My original patch fixes them.

>

> You're correct, but the real underlying problem is that my load

> section end calculation was wrong.  That can be fixed without a loop

> scanning back through sections.

>

> BTW, the testsuite changes will need some tweaks before they can be

> committed, eg.

> /home/alan/build/gas/d30v-elf/ld/ld-new: unrecognized option '-z'


This is the patch I am checking in.   It skips frame tests if -shared isn't
supported.

>         PR 22845

>         * elf.c (IS_TBSS): Define.

>         (_bfd_elf_map_sections_to_segments): Use IS_TBSS.

>         (assign_file_positions_for_non_load_sections): Revert last change.

>         Properly calculate load segment limits to compare against relro limits.

>


Thanks.

-- 
H.J.
From 5231ad1399e8794864891969f7de7cd5383bfe67 Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.tools@gmail.com>
Date: Sat, 17 Feb 2018 15:06:32 -0800
Subject: [PATCH] ld: Add -z separate-code tests to frame.exp

On x86, "-z separate-code" leads to assertion fail at bfd/elf.c:5917.
Alsp skip tests if -shared isn't supported.

	PR ld/22845
	* testsuite/ld-elf/frame.exp: Skip if -shared isn't supported.
	Add tests for "-z noseparate-code" and "-z separate-code".
	Remove unsupported -shared check.
---
 ld/testsuite/ld-elf/frame.exp | 34 +++++++++++++++++++---------------
 1 file changed, 19 insertions(+), 15 deletions(-)

diff --git a/ld/testsuite/ld-elf/frame.exp b/ld/testsuite/ld-elf/frame.exp
index 6cce42deb3..8862ff14a5 100644
--- a/ld/testsuite/ld-elf/frame.exp
+++ b/ld/testsuite/ld-elf/frame.exp
@@ -29,7 +29,7 @@ if ![is_elf_format] {
 }
 
 # No shared lib support on this target.
-if { [istarget "mcore-*-*"] } {
+if ![istarget shared] {
     return
 }
 
@@ -64,14 +64,16 @@ if { ![ld_assemble_flags $as $as_opt $srcdir/$subdir/tbss.s tmpdir/tbss.o ]
     return
 }
 
-if { [ld_link $ld tmpdir/frame.so "--shared tmpdir/frame.o tmpdir/tbss.o"] } {
-    pass "$test1"
+if { [ld_link $ld tmpdir/frame.so "-z noseparate-code --shared tmpdir/frame.o tmpdir/tbss.o"] } {
+    pass "$test1 -z noseparate-code"
 } else {
-    if [string match "*shared not supported*" $link_output] {
-	unsupported "-shared is not supported by this target"
-    } else {
-	fail "$test1"
-    }
+   fail "$test1 -z noseparate-code"
+}
+
+if { [ld_link $ld tmpdir/frame.so "-z separate-code --shared tmpdir/frame.o tmpdir/tbss.o"] } {
+    pass "$test1 -z separate-code"
+} else {
+    fail "$test1 -z separate-code"
 }
 
 if ![ld_assemble_flags $as $as_opt $srcdir/$subdir/table.s tmpdir/table.o ] {
@@ -79,12 +81,14 @@ if ![ld_assemble_flags $as $as_opt $srcdir/$subdir/table.s tmpdir/table.o ] {
     return
 }
 
-if { [ld_link $ld tmpdir/table.so "--shared tmpdir/table.o tmpdir/tbss.o"] } {
-    pass "$test2"
+if { [ld_link $ld tmpdir/table.so "-z noseparate-code --shared tmpdir/table.o tmpdir/tbss.o"] } {
+    pass "$test2 -z noseparate-code"
+} else {
+    fail "$test2 -z noseparate-code"
+}
+
+if { [ld_link $ld tmpdir/table.so "-z separate-code --shared tmpdir/table.o tmpdir/tbss.o"] } {
+    pass "$test2 -z separate-code"
 } else {
-    if [string match "*shared not supported*" $link_output] {
-	unsupported "-shared is not supported by this target"
-    } else {
-	fail "$test2"
-    }
+    fail "$test2 -z separate-code"
 }

Patch

diff --git a/bfd/elf.c b/bfd/elf.c
index 934052d7fd..e3fd56ad77 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -5895,13 +5895,26 @@  assign_file_positions_for_non_load_sections (bfd *abfd,
 	      for (lm = elf_seg_map (abfd), lp = phdrs;
 		   lm != NULL;
 		   lm = lm->next, lp++)
-		{
-		  if (lp->p_type == PT_LOAD
-		      && lm->count != 0
-		      && lm->sections[lm->count - 1]->vma >= start
-		      && lm->sections[0]->vma < end)
-		    break;
-		}
+		if (lp->p_type == PT_LOAD && lm->count != 0)
+		  {
+		    /* Find the last non-zero size section.  */
+		    int last = lm->count - 1;
+		    for (; last >= 0; last--)
+		      {
+			asection *s = lm->sections[last];
+			/* .tbss sections effectively have zero size.  */
+			if (s->size != 0
+			    && ((s->flags
+				 & (SEC_LOAD | SEC_THREAD_LOCAL))
+				!= SEC_THREAD_LOCAL))
+			  break;
+		      }
+
+		    if (last >= 0
+			&& lm->sections[last]->vma >= start
+			&& lm->sections[0]->vma < end)
+		      break;
+		  }
 	      BFD_ASSERT (lm != NULL);
 
 	      /* Find the section starting the RELRO segment.  */
diff --git a/ld/testsuite/ld-elf/binutils.exp b/ld/testsuite/ld-elf/binutils.exp
index 748699d5d1..290f158710 100644
--- a/ld/testsuite/ld-elf/binutils.exp
+++ b/ld/testsuite/ld-elf/binutils.exp
@@ -186,9 +186,11 @@  if { ![istarget "hppa64-*-*"] } {
     lappend tls_tests "tdata3" "tbss1" "tbss2" "tbss3"
 }
 set tls_opts {
-    ""
+    "-z noseparate-code"
+    "-z separate-code"
     "-z relro"
-    "-shared"
+    "-shared -z noseparate-code"
+    "-shared -z separate-code"
     "-shared -z relro"
     "-z max-page-size=0x100000"
     "-z max-page-size=0x100000 -z common-page-size=0x1000"