This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[11/21] Cope with TOCs bigger than 32k, and other TC0 fixes


xcoff_link_input_bfd decides on the fly whether to keep each input
XCOFF symbol.  This leads to a serious bug that I'll come to in a
few patches' time.

This patch deals with the handling of XMC_TC0 (TOC anchor) symbols.
At the moment, the output XMC_TC0 symbol is taken from one of the
input XMC_TC0 symbols.  One problem with this is that we can create
TOC entries and TC0 references automatically, so we should be prepared
to create the TC0 symbol automatically too.  More importantly, the current
code tries to cope with TOCs bigger than 32k:

      /* We skip all but the first TOC anchor.  */
      if (! skip
	  && isymp->n_sclass == C_HIDEXT
	  && aux.x_csect.x_smclas == XMC_TC0)
	{
	  if (finfo->toc_symindx != -1)
	    skip = TRUE;
	  else
	    {
	      bfd_vma tocval, tocend;
	      bfd *inp;

	      tocval = ((*csectpp)->output_section->vma
			+ (*csectpp)->output_offset
			+ isym.n_value
			- (*csectpp)->vma);

	      /* We want to find out if tocval is a good value to use
		 as the TOC anchor--that is, whether we can access all
		 of the TOC using a 16 bit offset from tocval.  This
		 test assumes that the TOC comes at the end of the
		 output section, as it does in the default linker
		 script.  */
	      tocend = ((*csectpp)->output_section->vma
			+ (*csectpp)->output_section->size);
	      for (inp = finfo->info->input_bfds;
		   inp != NULL;
		   inp = inp->link_next)
		{

		  for (o = inp->sections; o != NULL; o = o->next)
		    if (strcmp (o->name, ".tocbss") == 0)
		      {
			bfd_vma new_toc_end;
			new_toc_end = (o->output_section->vma
				       + o->output_offset
				       + o->size);
			if (new_toc_end > tocend)
			  tocend = new_toc_end;
		      }

		}

	      if (tocval + 0x10000 < tocend)
		{
		  (*_bfd_error_handler)
		    (_("TOC overflow: 0x%lx > 0x10000; try -mminimal-toc when compiling"),
		     (unsigned long) (tocend - tocval));
		  bfd_set_error (bfd_error_file_too_big);
		  return FALSE;
		}

	      if (tocval + 0x8000 < tocend)
		{
		  bfd_vma tocadd;

		  tocadd = tocend - (tocval + 0x8000);
		  tocval += tocadd;
		  isym.n_value += tocadd;
		}

	      finfo->toc_symindx = output_index;
	      xcoff_data (finfo->output_bfd)->toc = tocval;
	      xcoff_data (finfo->output_bfd)->sntoc =
		(*csectpp)->output_section->target_index;
	      require = TRUE;

	    }
	}

But the default linker script links sections in this order:

    *(.tc0)
    *(.tc)
    *(.td)

so all the XMC_TC0 csects will be linked at the same address, at the
beginning of the TOC.  We therefore fail to link when the TOC is
bigger than 32k.

We could probably improve things by using:

    *(.tc0 .tc)
    *(.td)

or:

    *(.tc0 .tc .td)

instead of the current section list, but in general, the best place
for the anchor might be _between_ the TOC csects of an input object.

This patch uses a separate function to create a TOC anchor from scratch.

OK to install?

Richard


bfd/
	* xcofflink.c: (xcoff_mark_symbol): Mark the TOC section when
	creating a descriptor.
	(xcoff_sweep): Don't mark toc_section unless it's needed.
	(bfd_xcoff_size_dynamic_sections): Skip the toc_section
	when marking every bfd.
	(xcoff_link_input_bfd): Skip all TOC anchors.
	(xcoff_toc_section_p, xcoff_find_tc0): New functions.
	(_bfd_xcoff_bfd_final_link): Don't set the output bfd's TOC anchor
	to -1; call xcoff_find_tc0 instead.

ld/testsuite/
	* ld-powerpc/aix-toc-1.ex, ld-powerpc/aix-toc-1a.s,
	ld-powerpc/aix-toc-1b.s, ld-powerpc/aix-toc-1-32.dd,
	ld-powerpc/aix-toc-1-64.dd: New tests.
	* ld-powerpc/aix52.exp: Run them.

Index: bfd/xcofflink.c
===================================================================
--- bfd/xcofflink.c	2009-03-10 13:46:38.000000000 +0000
+++ bfd/xcofflink.c	2009-03-10 13:46:58.000000000 +0000
@@ -2331,6 +2331,11 @@ xcoff_mark_symbol (struct bfd_link_info 
 	  if (!xcoff_mark_symbol (info, h->descriptor))
 	    return FALSE;
 
+	  /* Mark the TOC section, so that we get an anchor
+	     to relocate against.  */
+	  if (!xcoff_mark (info, xcoff_hash_table (info)->toc_section))
+	    return FALSE;
+
 	  /* We handle writing out the contents of the descriptor in
 	     xcoff_write_global_symbol.  */
 	}
@@ -2590,7 +2595,6 @@ xcoff_sweep (struct bfd_link_info *info)
 		  || o == xcoff_hash_table (info)->debug_section
 		  || o == xcoff_hash_table (info)->loader_section
 		  || o == xcoff_hash_table (info)->linkage_section
-		  || o == xcoff_hash_table (info)->toc_section
 		  || o == xcoff_hash_table (info)->descriptor_section
 		  || strcmp (o->name, ".debug") == 0)
 		o->flags |= SEC_MARK;
@@ -3126,7 +3130,12 @@ bfd_xcoff_size_dynamic_sections (bfd *ou
 
 	  for (o = sub->sections; o != NULL; o = o->next)
 	    {
-	      if ((o->flags & SEC_MARK) == 0)
+	      /* We shouldn't unconditionaly mark the TOC section.
+		 The output file should only have a TOC if either
+		 (a) one of the input files did or (b) we end up
+		 creating TOC references as part of the link process.  */
+	      if (o != xcoff_hash_table (info)->toc_section
+		  && (o->flags & SEC_MARK) == 0)
 		{
 		  if (! xcoff_mark (info, o))
 		    goto error_return;
@@ -3504,7 +3513,6 @@ #define N_BTSHFT n_btshft
       union internal_auxent aux;
       int smtyp = 0;
       bfd_boolean skip;
-      bfd_boolean require;
       int add;
 
       bfd_coff_swap_sym_in (input_bfd, (void *) esym, (void *) isymp);
@@ -3623,7 +3631,6 @@ #define N_BTSHFT n_btshft
       *indexp = -1;
 
       skip = FALSE;
-      require = FALSE;
       add = 1 + isym.n_numaux;
 
       /* If we are skipping this csect, we want to skip this symbol.  */
@@ -3644,75 +3651,11 @@ #define N_BTSHFT n_btshft
 	  && isymp->n_sclass == C_STAT)
 	skip = TRUE;
 
-      /* We skip all but the first TOC anchor.  */
+      /* We generate the TOC anchor separately.  */
       if (! skip
 	  && isymp->n_sclass == C_HIDEXT
 	  && aux.x_csect.x_smclas == XMC_TC0)
-	{
-	  if (finfo->toc_symindx != -1)
-	    skip = TRUE;
-	  else
-	    {
-	      bfd_vma tocval, tocend;
-	      bfd *inp;
-
-	      tocval = ((*csectpp)->output_section->vma
-			+ (*csectpp)->output_offset
-			+ isym.n_value
-			- (*csectpp)->vma);
-
-	      /* We want to find out if tocval is a good value to use
-		 as the TOC anchor--that is, whether we can access all
-		 of the TOC using a 16 bit offset from tocval.  This
-		 test assumes that the TOC comes at the end of the
-		 output section, as it does in the default linker
-		 script.  */
-	      tocend = ((*csectpp)->output_section->vma
-			+ (*csectpp)->output_section->size);
-	      for (inp = finfo->info->input_bfds;
-		   inp != NULL;
-		   inp = inp->link_next)
-		{
-
-		  for (o = inp->sections; o != NULL; o = o->next)
-		    if (strcmp (o->name, ".tocbss") == 0)
-		      {
-			bfd_vma new_toc_end;
-			new_toc_end = (o->output_section->vma
-				       + o->output_offset
-				       + o->size);
-			if (new_toc_end > tocend)
-			  tocend = new_toc_end;
-		      }
-
-		}
-
-	      if (tocval + 0x10000 < tocend)
-		{
-		  (*_bfd_error_handler)
-		    (_("TOC overflow: 0x%lx > 0x10000; try -mminimal-toc when compiling"),
-		     (unsigned long) (tocend - tocval));
-		  bfd_set_error (bfd_error_file_too_big);
-		  return FALSE;
-		}
-
-	      if (tocval + 0x8000 < tocend)
-		{
-		  bfd_vma tocadd;
-
-		  tocadd = tocend - (tocval + 0x8000);
-		  tocval += tocadd;
-		  isym.n_value += tocadd;
-		}
-
-	      finfo->toc_symindx = output_index;
-	      xcoff_data (finfo->output_bfd)->toc = tocval;
-	      xcoff_data (finfo->output_bfd)->sntoc =
-		(*csectpp)->output_section->target_index;
-	      require = TRUE;
-
-	    }
-	}
+	skip = TRUE;
 
       /* If we are stripping all symbols, we want to skip this one.  */
       if (! skip
@@ -3781,12 +3724,6 @@ #define N_BTSHFT n_btshft
 	    skip = TRUE;
 	}
 
-      /* We can not skip the first TOC anchor.  */
-      if (skip
-	  && require
-	  && finfo->info->strip != strip_all)
-	skip = FALSE;
-
       /* We now know whether we are to skip this symbol or not.  */
       if (! skip)
 	{
@@ -4616,6 +4553,144 @@ xcoff_sort_relocs (const void * p1, cons
     return 0;
 }
 
+/* Return true if section SEC is a TOC section.  */
+
+static inline bfd_boolean
+xcoff_toc_section_p (asection *sec)
+{
+  const char *name;
+
+  name = sec->name;
+  if (name[0] == '.' && name[1] == 't')
+    {
+      if (name[2] == 'c')
+	{
+	  if (name[3] == '0' && name[4] == 0)
+	    return TRUE;
+	  if (name[3] == 0)
+	    return TRUE;
+	}
+      if (name[2] == 'd' && name[3] == 0)
+	return TRUE;
+    }
+  return FALSE;
+}
+
+/* See if the link requires a TOC (it usually does!).  If so, find a
+   good place to put the TOC anchor csect, and write out the associated
+   symbol.  */
+
+static bfd_boolean
+xcoff_find_tc0 (bfd *output_bfd, struct xcoff_final_link_info *finfo)
+{
+  bfd_vma toc_start, toc_end, start, end, best_address;
+  asection *sec;
+  bfd *input_bfd;
+  int section_index;
+  struct internal_syment irsym;
+  union internal_auxent iraux;
+  file_ptr pos;
+  size_t size;
+
+  /* Set [TOC_START, TOC_END) to the range of the TOC.  Record the
+     index of a csect at the beginning of the TOC.  */
+  toc_start = ~(bfd_vma) 0;
+  toc_end = 0;
+  section_index = -1;
+  for (input_bfd = finfo->info->input_bfds;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next)
+    for (sec = input_bfd->sections; sec != NULL; sec = sec->next)
+      if ((sec->flags & SEC_MARK) != 0 && xcoff_toc_section_p (sec))
+	{
+	  start = sec->output_section->vma + sec->output_offset;
+	  if (toc_start > start)
+	    {
+	      toc_start = start;
+	      section_index = sec->output_section->target_index;
+	    }
+
+	  end = start + sec->size;
+	  if (toc_end < end)
+	    toc_end = end;
+	}
+
+  /* There's no need for a TC0 symbol if we don't have a TOC.  */
+  if (toc_end < toc_start)
+    {
+      xcoff_data (output_bfd)->toc = toc_start;
+      return TRUE;
+    }
+
+  if (toc_end - toc_start < 0x8000)
+    /* Every TOC csect can be accessed from TOC_START.  */
+    best_address = toc_start;
+  else
+    {
+      /* Find the lowest TOC csect that is still within range of TOC_END.  */
+      best_address = toc_end;
+      for (input_bfd = finfo->info->input_bfds;
+	   input_bfd != NULL;
+	   input_bfd = input_bfd->link_next)
+	for (sec = input_bfd->sections; sec != NULL; sec = sec->next)
+	  if ((sec->flags & SEC_MARK) != 0 && xcoff_toc_section_p (sec))
+	    {
+	      start = sec->output_section->vma + sec->output_offset;
+	      if (start < best_address
+		  && start + 0x8000 >= toc_end)
+		{
+		  best_address = start;
+		  section_index = sec->output_section->target_index;
+		}
+	    }
+
+      /* Make sure that the start of the TOC is also within range.  */
+      if (best_address > toc_start + 0x8000)
+	{
+	  (*_bfd_error_handler)
+	    (_("TOC overflow: 0x%lx > 0x10000; try -mminimal-toc "
+	       "when compiling"),
+	     (unsigned long) (toc_end - toc_start));
+	  bfd_set_error (bfd_error_file_too_big);
+	  return FALSE;
+	}
+    }
+
+  /* Record the chosen TOC value.  */
+  finfo->toc_symindx = obj_raw_syment_count (output_bfd);
+  xcoff_data (output_bfd)->toc = best_address;
+  xcoff_data (output_bfd)->sntoc = section_index;
+
+  /* Fill out the TC0 symbol.  */
+  if (!bfd_xcoff_put_symbol_name (output_bfd, finfo->strtab, &irsym, "TOC"))
+    return FALSE;
+  irsym.n_value = best_address;
+  irsym.n_scnum = section_index;
+  irsym.n_sclass = C_HIDEXT;
+  irsym.n_type = T_NULL;
+  irsym.n_numaux = 1;
+  bfd_coff_swap_sym_out (output_bfd, &irsym, finfo->outsyms);
+
+  /* Fill out the auxillary csect information.  */
+  memset (&iraux, 0, sizeof iraux);
+  iraux.x_csect.x_smtyp = XTY_SD;
+  iraux.x_csect.x_smclas = XMC_TC0;
+  iraux.x_csect.x_scnlen.l = 0;
+  bfd_coff_swap_aux_out (output_bfd, &iraux, T_NULL, C_HIDEXT, 0, 1,
+			 finfo->outsyms + bfd_coff_symesz (output_bfd));
+
+  /* Write the contents to the file.  */
+  pos = obj_sym_filepos (output_bfd);
+  pos += obj_raw_syment_count (output_bfd) * bfd_coff_symesz (output_bfd);
+  size = 2 * bfd_coff_symesz (output_bfd);
+  if (bfd_seek (output_bfd, pos, SEEK_SET) != 0
+      || bfd_bwrite (finfo->outsyms, size, output_bfd) != size)
+    return FALSE;
+  obj_raw_syment_count (output_bfd) += 2;
+
+  return TRUE;
+}
+
 /* Write out a non-XCOFF global symbol.  */
 
 static bfd_boolean
@@ -5692,7 +5767,10 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, st
     goto error_return;
 
   obj_raw_syment_count (abfd) = 0;
-  xcoff_data (abfd)->toc = (bfd_vma) -1;
+
+  /* Find a TOC symbol, if we need one.  */
+  if (!xcoff_find_tc0 (abfd, &finfo))
+    goto error_return;
 
   /* We now know the position of everything in the file, except that
      we don't know the size of the symbol table and therefore we don't
Index: ld/testsuite/ld-powerpc/aix-toc-1.ex
===================================================================
--- /dev/null	2009-02-06 09:11:03.343159000 +0000
+++ ld/testsuite/ld-powerpc/aix-toc-1.ex	2009-03-10 13:46:58.000000000 +0000
@@ -0,0 +1,2 @@
+f1
+f2
Index: ld/testsuite/ld-powerpc/aix-toc-1a.s
===================================================================
--- /dev/null	2009-02-06 09:11:03.343159000 +0000
+++ ld/testsuite/ld-powerpc/aix-toc-1a.s	2009-03-10 13:46:58.000000000 +0000
@@ -0,0 +1,23 @@
+	.macro  loadtoc
+	.toc
+	.tc	sym\@[TC], \@
+
+	.csect	.f1[PR]
+	.if     size == 32
+	lwz     1,sym\@[TC](2)
+	.else
+	ld      1,sym\@[TC](2)
+	.endif
+	.endm
+
+	.globl	.f1
+	.csect	.f1[PR]
+.f1:
+	.rept	0x7ffc * 8 / size
+	loadtoc
+	.endr
+
+	.globl	f1
+	.csect	f1[DS]
+f1:
+	.long	.f1[PR],TOC[TC0],0
Index: ld/testsuite/ld-powerpc/aix-toc-1b.s
===================================================================
--- /dev/null	2009-02-06 09:11:03.343159000 +0000
+++ ld/testsuite/ld-powerpc/aix-toc-1b.s	2009-03-10 13:46:58.000000000 +0000
@@ -0,0 +1,23 @@
+	.macro  loadtoc
+	.toc
+	.tc	asym\@[TC], \@ | 0x10000
+
+	.csect	.f2[PR]
+	.if     size == 32
+	lwz     1,asym\@[TC](2)
+	.else
+	ld      1,asym\@[TC](2)
+	.endif
+	.endm
+
+	.globl	.f2
+	.csect	.f2[PR]
+.f2:
+	.rept	0x7ffc * 8 / size
+	loadtoc
+	.endr
+
+	.globl	f2
+	.csect	f2[DS]
+f2:
+	.long	.f2[PR],TOC[TC0],0
Index: ld/testsuite/ld-powerpc/aix-toc-1-32.dd
===================================================================
--- /dev/null	2009-02-06 09:11:03.343159000 +0000
+++ ld/testsuite/ld-powerpc/aix-toc-1-32.dd	2009-03-10 13:46:58.000000000 +0000
@@ -0,0 +1,12 @@
+
+.*
+
+
+Disassembly of section \.text:
+
+10000000 <\.f1>:
+10000000:	80 22 80 08 	l       r1,-32760\(r2\)
+			10000002: R_TOC	sym0.*
+#...
+1000fff4:	80 22 7f fc 	l       r1,32764\(r2\)
+			1000fff6: R_TOC	asym8190.*
Index: ld/testsuite/ld-powerpc/aix-toc-1-64.dd
===================================================================
--- /dev/null	2009-02-06 09:11:03.343159000 +0000
+++ ld/testsuite/ld-powerpc/aix-toc-1-64.dd	2009-03-10 13:46:58.000000000 +0000
@@ -0,0 +1,12 @@
+
+.*
+
+
+Disassembly of section \.text:
+
+0000000010000000 <.f1>:
+    10000000:	e8 22 80 10 	ld      r1,-32752\(r2\)
+			10000002: R_TOC	sym0.*
+#...
+    10007ff4:	e8 22 7f f8 	ld      r1,32760\(r2\)
+			10007ff6: R_TOC	asym4094.*
Index: ld/testsuite/ld-powerpc/aix52.exp
===================================================================
--- ld/testsuite/ld-powerpc/aix52.exp	2009-03-10 13:46:23.000000000 +0000
+++ ld/testsuite/ld-powerpc/aix52.exp	2009-03-10 13:46:58.000000000 +0000
@@ -97,6 +97,11 @@ set aix52tests {
      "" {aix-glink-1.s}
      {{objdump {-D -j.text -j.data} aix-glink-1-SIZE.dd}}
      "aix-glink-1.so"}
+
+    {"TOC test 1" "-shared -bE:aix-toc-1.ex"
+     "" {aix-toc-1a.s aix-toc-1b.s}
+     {{objdump -dr aix-toc-1-SIZE.dd}}
+     "aix-toc-1.so"}
 }
 
 foreach test $aix52tests {


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]