--- /dev/null
+#!/bin/sh -e
+## 204-hjl-binutils-tls-relro.dpatch
+##
+## DP: Description: PR binutils/3281; objcopy changes PT_GNU_RELRO when there is PT_TLS
+## DP: Author: H.J. Lu <hongjiu.lu@intel.com>
+## DP: Upstream status: hjl 2.17.50.0.18
+## DP: Original patch: binutils-tls-relro-14.patch
+
+if [ $# -ne 1 ]; then
+ echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
+ exit 1
+fi
+
+[ -f debian/patches/00patch-opts ] && . debian/patches/00patch-opts
+patch_opts="${patch_opts:--f --no-backup-if-mismatch}"
+
+case "$1" in
+ -patch) patch $patch_opts -p1 < $0;;
+ -unpatch) patch $patch_opts -p1 -R < $0;;
+ *)
+ echo >&2 "`basename $0`: script expects -patch|-unpatch as argument"
+ exit 1;;
+esac
+
+exit 0
+
+bfd/
+
+2007-01-23 H.J. Lu <hongjiu.lu@intel.com>
+
+ PR binutils/3281
+ * elf-bfd.h (elf_obj_tdata): Remove relro.
+
+ * elf.c (get_program_header_size): Check info->relro instead
+ of elf_tdata (abfd)->relro.
+ (_bfd_elf_map_sections_to_segments): Likewise.
+ (assign_file_positions_for_load_sections): Don't set
+ PT_GNU_RELRO segment alignment here.
+ (assign_file_positions_for_non_load_sections): Properly set up
+ PT_GNU_RELRO segment for copying executable/shared library.
+ (elf_section_status): New enum.
+ (rewrite_elf_program_header): Add elf_section_status. Remove
+ PT_GNU_RELRO segment if a section is modified.
+ (copy_private_bfd_data): Updated rewrite_elf_program_header
+ call.
+
+include/elf/
+
+2006-10-20 H.J. Lu <hongjiu.lu@intel.com>
+
+ PR binutils/3281
+ * internal.h (ELF_IS_SECTION_IN_SEGMENT): Allow SHF_TLS
+ sections in PT_GNU_RELRO segments.
+
+ld/
+
+2006-10-20 H.J. Lu <hongjiu.lu@intel.com>
+
+ PR binutils/3281
+ * ldexp.h (ldexp_control): Add relro, relro_start_stat and
+ relro_end_stat.
+
+ * ldexp.c (fold_binary): Set expld.dataseg.relro to
+ exp_dataseg_relro_start or exp_dataseg_relro_end when
+ seeing DATA_SEGMENT_ALIGN or DATA_SEGMENT_RELRO_END,
+ respectively.
+
+ * ldlang.c (lang_size_sections_1): Properly set
+ expld.dataseg.relro_start_stat and
+ expld.dataseg.relro_end_stat.
+ (find_relro_section_callback): New function.
+ (lang_find_relro_sections_1): Likewise.
+ (lang_find_relro_sections): Likewise.
+ (lang_process): Call lang_find_relro_sections for
+ non-relocatable link.
+
+ld/testsuite/
+
+2006-10-20 H.J. Lu <hongjiu.lu@intel.com>
+
+ PR binutils/3281
+ * ld-elf/binutils.exp: Update "-z relro" tests to use relro.s.
+ Add "-z relro" tests with TLS for objcopy.
+
+ * ld-elf/relro.s: New file.
+
+@DPATCH@
+diff -urNad binutils-2.18~cvs20070812~/bfd/elf-bfd.h binutils-2.18~cvs20070812/bfd/elf-bfd.h
+--- binutils-2.18~cvs20070812~/bfd/elf-bfd.h 2007-08-04 18:31:00.000000000 +0200
++++ binutils-2.18~cvs20070812/bfd/elf-bfd.h 2007-08-12 13:31:23.000000000 +0200
+@@ -1428,9 +1428,6 @@
+ /* Segment flags for the PT_GNU_STACK segment. */
+ unsigned int stack_flags;
+
+- /* Should the PT_GNU_RELRO segment be emitted? */
+- bfd_boolean relro;
+-
+ /* Symbol version definitions in external objects. */
+ Elf_Internal_Verdef *verdef;
+
+diff -urNad binutils-2.18~cvs20070812~/bfd/elf.c binutils-2.18~cvs20070812/bfd/elf.c
+--- binutils-2.18~cvs20070812~/bfd/elf.c 2007-08-07 02:06:14.000000000 +0200
++++ binutils-2.18~cvs20070812/bfd/elf.c 2007-08-12 13:31:23.000000000 +0200
+@@ -3321,7 +3321,7 @@
+ /* We need a PT_DYNAMIC segment. */
+ ++segs;
+
+- if (elf_tdata (abfd)->relro)
++ if (info->relro)
+ {
+ /* We need a PT_GNU_RELRO segment only when there is a
+ PT_DYNAMIC segment. */
+@@ -3848,7 +3848,7 @@
+ pm = &m->next;
+ }
+
+- if (dynsec != NULL && elf_tdata (abfd)->relro)
++ if (dynsec != NULL && info->relro)
+ {
+ /* We make a PT_GNU_RELRO segment only when there is a
+ PT_DYNAMIC segment. */
+@@ -4323,12 +4323,10 @@
+ p->p_memsz += this_hdr->sh_size;
+ }
+
+- if (p->p_type == PT_GNU_RELRO)
+- p->p_align = 1;
+- else if (align > p->p_align
+- && !m->p_align_valid
+- && (p->p_type != PT_LOAD
+- || (abfd->flags & D_PAGED) == 0))
++ if (align > p->p_align
++ && !m->p_align_valid
++ && (p->p_type != PT_LOAD
++ || (abfd->flags & D_PAGED) == 0))
+ p->p_align = align;
+ }
+
+@@ -4477,18 +4475,53 @@
+ if (m->count != 0)
+ {
+ if (p->p_type != PT_LOAD
+- && (p->p_type != PT_NOTE || bfd_get_format (abfd) != bfd_core))
++ && (p->p_type != PT_NOTE
++ || bfd_get_format (abfd) != bfd_core))
+ {
+ Elf_Internal_Shdr *hdr;
++ asection *sect;
++
+ BFD_ASSERT (!m->includes_filehdr && !m->includes_phdrs);
+
+- hdr = &elf_section_data (m->sections[m->count - 1])->this_hdr;
+- p->p_filesz = (m->sections[m->count - 1]->filepos
+- - m->sections[0]->filepos);
++ sect = m->sections[m->count - 1];
++ hdr = &elf_section_data (sect)->this_hdr;
++ p->p_filesz = sect->filepos - m->sections[0]->filepos;
+ if (hdr->sh_type != SHT_NOBITS)
+ p->p_filesz += hdr->sh_size;
+
+- p->p_offset = m->sections[0]->filepos;
++ if (p->p_type == PT_GNU_RELRO)
++ {
++ /* When we get here, we are copying executable
++ or shared library. But we need to use the same
++ linker logic. */
++ Elf_Internal_Phdr *lp;
++
++ for (lp = phdrs; lp < phdrs + count; ++lp)
++ {
++ if (lp->p_type == PT_LOAD
++ && lp->p_paddr == p->p_paddr)
++ break;
++ }
++
++ if (lp < phdrs + count)
++ {
++ /* The end of PT_GNU_RELRO segment is the next
++ SEC_ALLOC section after it if it exists. */
++ if (sect->next != NULL
++ && (sect->next->flags & SEC_ALLOC) != 0)
++ p->p_filesz = sect->next->lma - lp->p_vaddr;
++ else
++ p->p_filesz += p->p_vaddr - lp->p_vaddr;
++ p->p_vaddr = lp->p_vaddr;
++ p->p_offset = lp->p_offset;
++ p->p_memsz = p->p_filesz;
++ p->p_align = 1;
++ }
++ else
++ abort ();
++ }
++ else
++ p->p_offset = m->sections[0]->filepos;
+ }
+ }
+ else
+@@ -4923,8 +4956,17 @@
+
+ /* Rewrite program header information. */
+
++enum elf_section_status
++{
++ unknown,
++ added,
++ removed,
++ modified
++};
++
+ static bfd_boolean
+-rewrite_elf_program_header (bfd *ibfd, bfd *obfd)
++rewrite_elf_program_header (bfd *ibfd, bfd *obfd,
++ enum elf_section_status status)
+ {
+ Elf_Internal_Ehdr *iehdr;
+ struct elf_segment_map *map;
+@@ -5077,7 +5119,14 @@
+ }
+
+ if (segment->p_type != PT_LOAD)
+- continue;
++ {
++ /* If a section is added or mofied, remove PT_GNU_RELRO
++ segment. */
++ if (status != removed &&
++ segment->p_type == PT_GNU_RELRO)
++ segment->p_type = PT_NULL;
++ continue;
++ }
+
+ /* Determine if this segment overlaps any previous segments. */
+ for (j = 0, segment2 = elf_tdata (ibfd)->phdr; j < i; j++, segment2 ++)
+@@ -5657,6 +5706,12 @@
+ static bfd_boolean
+ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
+ {
++ enum elf_section_status status;
++ Elf_Internal_Phdr *segment;
++ asection *section, *osec;
++ unsigned int i, num_segments;
++ Elf_Internal_Shdr *this_hdr;
++
+ if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+ || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
+ return TRUE;
+@@ -5664,16 +5719,13 @@
+ if (elf_tdata (ibfd)->phdr == NULL)
+ return TRUE;
+
++ status = unknown;
+ if (ibfd->xvec == obfd->xvec)
+ {
+- /* Check to see if any sections in the input BFD
+- covered by ELF program header have changed. */
+- Elf_Internal_Phdr *segment;
+- asection *section, *osec;
+- unsigned int i, num_segments;
+- Elf_Internal_Shdr *this_hdr;
++ /* Check to see if any sections in the input BFD covered by ELF
++ program header have changed.
+
+- /* Initialize the segment mark field. */
++ Initialize the segment mark field. */
+ for (section = obfd->sections; section != NULL;
+ section = section->next)
+ section->segment_mark = FALSE;
+@@ -5725,7 +5777,10 @@
+ section = section->next)
+ {
+ if (section->segment_mark == FALSE)
+- goto rewrite;
++ {
++ status = added;
++ goto rewrite;
++ }
+ else
+ section->segment_mark = FALSE;
+ }
+@@ -5734,7 +5789,42 @@
+ }
+
+ rewrite:
+- return rewrite_elf_program_header (ibfd, obfd);
++ if (status == unknown)
++ {
++ /* We need to find out how we are changed. */
++ num_segments = elf_elfheader (ibfd)->e_phnum;
++ for (i = 0, segment = elf_tdata (ibfd)->phdr;
++ i < num_segments;
++ i++, segment++)
++ {
++ for (section = ibfd->sections;
++ section != NULL; section = section->next)
++ {
++ osec = section->output_section;
++
++ /* Check if this section is covered by the segment. */
++ this_hdr = &(elf_section_data(section)->this_hdr);
++ if (ELF_IS_SECTION_IN_SEGMENT_FILE (this_hdr, segment))
++ {
++ if (osec == NULL)
++ status = removed;
++ else if (section->flags != osec->flags
++ || section->lma != osec->lma
++ || section->vma != osec->vma
++ || section->size != osec->size
++ || section->rawsize != osec->rawsize
++ || section->alignment_power != osec->alignment_power)
++ {
++ /* Stop if a section is modified. */
++ status = modified;
++ break;
++ }
++ }
++ }
++ }
++ }
++ BFD_ASSERT (status != unknown);
++ return rewrite_elf_program_header (ibfd, obfd, status);
+ }
+
+ /* Initialize private output section information from input section. */
+diff -urNad binutils-2.18~cvs20070812~/bfd/elflink.c binutils-2.18~cvs20070812/bfd/elflink.c
+--- binutils-2.18~cvs20070812~/bfd/elflink.c 2007-08-12 13:30:46.000000000 +0200
++++ binutils-2.18~cvs20070812/bfd/elflink.c 2007-08-12 13:31:23.000000000 +0200
+@@ -5364,7 +5364,6 @@
+ return TRUE;
+
+ bed = get_elf_backend_data (output_bfd);
+- elf_tdata (output_bfd)->relro = info->relro;
+ if (info->execstack)
+ elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
+ else if (info->noexecstack)
+diff -urNad binutils-2.18~cvs20070812~/include/elf/internal.h binutils-2.18~cvs20070812/include/elf/internal.h
+--- binutils-2.18~cvs20070812~/include/elf/internal.h 2007-05-02 15:44:36.000000000 +0200
++++ binutils-2.18~cvs20070812/include/elf/internal.h 2007-08-12 13:31:23.000000000 +0200
+@@ -266,11 +266,12 @@
+ || segment->p_type == PT_TLS) ? sec_hdr->sh_size : 0)
+
+ /* Decide if the given sec_hdr is in the given segment. PT_TLS segment
+- contains only SHF_TLS sections. Only PT_LOAD and PT_TLS segments
+- can contain SHF_TLS sections. */
++ contains only SHF_TLS sections. Only PT_LOAD, PT_GNU_RELRO and
++ and PT_TLS segments can contain SHF_TLS sections. */
+ #define ELF_IS_SECTION_IN_SEGMENT(sec_hdr, segment) \
+ (((((sec_hdr->sh_flags & SHF_TLS) != 0) \
+ && (segment->p_type == PT_TLS \
++ || segment->p_type == PT_GNU_RELRO \
+ || segment->p_type == PT_LOAD)) \
+ || ((sec_hdr->sh_flags & SHF_TLS) == 0 \
+ && segment->p_type != PT_TLS)) \
+diff -urNad binutils-2.18~cvs20070812~/ld/ldexp.c binutils-2.18~cvs20070812/ld/ldexp.c
+--- binutils-2.18~cvs20070812~/ld/ldexp.c 2007-07-06 16:09:41.000000000 +0200
++++ binutils-2.18~cvs20070812/ld/ldexp.c 2007-08-12 13:31:23.000000000 +0200
+@@ -390,6 +390,7 @@
+ break;
+
+ case DATA_SEGMENT_ALIGN:
++ expld.dataseg.relro = exp_dataseg_relro_start;
+ if (expld.phase != lang_first_phase_enum
+ && expld.section == bfd_abs_section_ptr
+ && (expld.dataseg.phase == exp_dataseg_none
+@@ -425,6 +426,7 @@
+ break;
+
+ case DATA_SEGMENT_RELRO_END:
++ expld.dataseg.relro = exp_dataseg_relro_end;
+ if (expld.phase != lang_first_phase_enum
+ && (expld.dataseg.phase == exp_dataseg_align_seen
+ || expld.dataseg.phase == exp_dataseg_adjust
+diff -urNad binutils-2.18~cvs20070812~/ld/ldexp.h binutils-2.18~cvs20070812/ld/ldexp.h
+--- binutils-2.18~cvs20070812~/ld/ldexp.h 2007-07-06 16:09:41.000000000 +0200
++++ binutils-2.18~cvs20070812/ld/ldexp.h 2007-08-12 13:31:23.000000000 +0200
+@@ -98,6 +98,8 @@
+ lang_final_phase_enum
+ } lang_phase_type;
+
++union lang_statement_union;
++
+ struct ldexp_control {
+ /* Modify expression evaluation depending on this. */
+ lang_phase_type phase;
+@@ -125,6 +127,15 @@
+ } phase;
+
+ bfd_vma base, min_base, relro_end, end, pagesize, maxpagesize;
++
++ enum {
++ exp_dataseg_relro_none,
++ exp_dataseg_relro_start,
++ exp_dataseg_relro_end,
++ } relro;
++
++ union lang_statement_union *relro_start_stat;
++ union lang_statement_union *relro_end_stat;
+ } dataseg;
+ };
+
+diff -urNad binutils-2.18~cvs20070812~/ld/ldlang.c binutils-2.18~cvs20070812/ld/ldlang.c
+--- binutils-2.18~cvs20070812~/ld/ldlang.c 2007-07-29 14:33:37.000000000 +0200
++++ binutils-2.18~cvs20070812/ld/ldlang.c 2007-08-12 13:31:23.000000000 +0200
+@@ -4631,10 +4631,32 @@
+ bfd_vma newdot = dot;
+ etree_type *tree = s->assignment_statement.exp;
+
++ expld.dataseg.relro = exp_dataseg_relro_none;
++
+ exp_fold_tree (tree,
+ output_section_statement->bfd_section,
+ &newdot);
+
++ if (expld.dataseg.relro == exp_dataseg_relro_start)
++ {
++ if (!expld.dataseg.relro_start_stat)
++ expld.dataseg.relro_start_stat = s;
++ else
++ {
++ ASSERT (expld.dataseg.relro_start_stat == s);
++ }
++ }
++ else if (expld.dataseg.relro == exp_dataseg_relro_end)
++ {
++ if (!expld.dataseg.relro_end_stat)
++ expld.dataseg.relro_end_stat = s;
++ else
++ {
++ ASSERT (expld.dataseg.relro_end_stat == s);
++ }
++ }
++ expld.dataseg.relro = exp_dataseg_relro_none;
++
+ /* This symbol is relative to this section. */
+ if ((tree->type.node_class == etree_provided
+ || tree->type.node_class == etree_assign)
+@@ -5660,6 +5682,81 @@
+ bfd_gc_sections (output_bfd, &link_info);
+ }
+
++/* Worker for lang_find_relro_sections_1. */
++
++static void
++find_relro_section_callback (lang_wild_statement_type *ptr ATTRIBUTE_UNUSED,
++ struct wildcard_list *sec ATTRIBUTE_UNUSED,
++ asection *section,
++ lang_input_statement_type *file ATTRIBUTE_UNUSED,
++ void *data)
++{
++ /* Discarded, excluded and ignored sections effectively have zero
++ size. */
++ if (section->output_section != NULL
++ && section->output_section->owner == output_bfd
++ && (section->output_section->flags & SEC_EXCLUDE) == 0
++ && !IGNORE_SECTION (section)
++ && section->size != 0)
++ {
++ bfd_boolean *has_relro_section = (bfd_boolean *) data;
++ *has_relro_section = TRUE;
++ }
++}
++
++/* Iterate over sections for relro sections. */
++
++static void
++lang_find_relro_sections_1 (lang_statement_union_type *s,
++ bfd_boolean *has_relro_section)
++{
++ if (*has_relro_section)
++ return;
++
++ for (; s != NULL; s = s->header.next)
++ {
++ if (s == expld.dataseg.relro_end_stat)
++ break;
++
++ switch (s->header.type)
++ {
++ case lang_wild_statement_enum:
++ walk_wild (&s->wild_statement,
++ find_relro_section_callback,
++ has_relro_section);
++ break;
++ case lang_constructors_statement_enum:
++ lang_find_relro_sections_1 (constructor_list.head,
++ has_relro_section);
++ break;
++ case lang_output_section_statement_enum:
++ lang_find_relro_sections_1 (s->output_section_statement.children.head,
++ has_relro_section);
++ break;
++ case lang_group_statement_enum:
++ lang_find_relro_sections_1 (s->group_statement.children.head,
++ has_relro_section);
++ break;
++ default:
++ break;
++ }
++ }
++}
++
++static void
++lang_find_relro_sections (void)
++{
++ bfd_boolean has_relro_section = FALSE;
++
++ /* Check all sections in the link script. */
++
++ lang_find_relro_sections_1 (expld.dataseg.relro_start_stat,
++ &has_relro_section);
++
++ if (!has_relro_section)
++ link_info.relro = FALSE;
++}
++
+ /* Relax all sections until bfd_relax_section gives up. */
+
+ static void
+@@ -5787,6 +5884,10 @@
+ section positions, since they will affect SIZEOF_HEADERS. */
+ lang_record_phdrs ();
+
++ /* Check relro sections. */
++ if (link_info.relro && ! link_info.relocatable)
++ lang_find_relro_sections ();
++
+ /* Size up the sections. */
+ lang_size_sections (NULL, !command_line.relax);
+
+diff -urNad binutils-2.18~cvs20070812~/ld/testsuite/ld-elf/binutils.exp binutils-2.18~cvs20070812/ld/testsuite/ld-elf/binutils.exp
+--- binutils-2.18~cvs20070812~/ld/testsuite/ld-elf/binutils.exp 2007-07-06 16:09:43.000000000 +0200
++++ binutils-2.18~cvs20070812/ld/testsuite/ld-elf/binutils.exp 2007-08-12 13:31:23.000000000 +0200
+@@ -104,24 +104,33 @@
+ binutils_test objcopy "" maxpage1
+ binutils_test objcopy "-shared" maxpage1
+
+-binutils_test strip "-z relro" maxpage1
+-binutils_test strip "-z relro -shared" maxpage1
+-binutils_test objcopy "-z relro" maxpage1
+-binutils_test objcopy "-z relro -shared" maxpage1
++binutils_test strip "-z relro" relro
++binutils_test strip "-z relro -shared" relro
++binutils_test objcopy "-z relro" relro
++binutils_test objcopy "-z relro -shared" relro
+
+ binutils_test objcopy "" tbss1
++binutils_test objcopy "-z relro" tbss1
+ binutils_test objcopy "-shared" tbss1
++binutils_test objcopy "-shared -z relro" tbss1
+ binutils_test objcopy "-z max-page-size=0x100000" tbss1
+ binutils_test objcopy "-z max-page-size=0x100000 -z common-page-size=0x1000" tbss1
+ binutils_test objcopy "" tdata1
++binutils_test objcopy "-z relro" tdata1
+ binutils_test objcopy "-shared" tdata1
++binutils_test objcopy "-shared -z relro" tdata1
+ binutils_test objcopy "-z max-page-size=0x100000" tdata1
+ binutils_test objcopy "-z max-page-size=0x100000 -z common-page-size=0x1000" tdata1
+ binutils_test objcopy "" tbss2
++binutils_test objcopy "-z relro" tbss2
+ binutils_test objcopy "-shared" tbss2
++binutils_test objcopy "-shared -z relro" tbss2
+ binutils_test objcopy "-z max-page-size=0x100000" tbss2
+ binutils_test objcopy "-z max-page-size=0x100000 -z common-page-size=0x1000" tbss2
+-binutils_test objcopy "-z max-page-size=0x100000" tdata2
++
+ binutils_test objcopy "" tdata2
++binutils_test objcopy "-z relro" tdata2
+ binutils_test objcopy "-shared" tdata2
++binutils_test objcopy "-shared -z relro" tdata2
++binutils_test objcopy "-z max-page-size=0x100000" tdata2
+ binutils_test objcopy "-z max-page-size=0x100000 -z common-page-size=0x1000" tdata2
+diff -urNad binutils-2.18~cvs20070812~/ld/testsuite/ld-elf/relro.s binutils-2.18~cvs20070812/ld/testsuite/ld-elf/relro.s
+--- binutils-2.18~cvs20070812~/ld/testsuite/ld-elf/relro.s 1970-01-01 01:00:00.000000000 +0100
++++ binutils-2.18~cvs20070812/ld/testsuite/ld-elf/relro.s 2007-08-12 13:31:23.000000000 +0200
+@@ -0,0 +1,14 @@
++ .globl main
++ .globl start
++ .globl _start
++ .globl __start
++ .text
++main:
++start:
++_start:
++__start:
++ .long 0
++ .data
++ .long 0
++ .section .data.rel.ro,"aw",%progbits
++ .long 0