This is the mail archive of the binutils@sources.redhat.com 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]

[PATCH] SHF_MERGE support for gas and ld (was RFC: .gnu.strtab ...)


Hi!

Here is a second version of the section merging patch, this time using the
standard ELF SHF_MERGE and SHF_STRINGS flags to mark mergeable sections.
For SHF_MERGE sections, one needs to specify entity size (what will end up
in sh_entsize), which is either the constant size of each entity or size of
each character the (wide) string consists of.
The patch allows this to be set as an additional argument to .section, ie.:

	.section .rodata.strtab, "ams", @progbits, 1
is SHF_ALLOC|SHF_MERGE|SHF_STRINGS section with sh_entsize 1 (ie. consists
of normal zero-terminated strings to be merged), while
	.section .rodata.wstrtab, "ams", @progbits, 4
consists of wide zero-terminated strings where wchar_t is 4 bytes long and
finally
	.section .rodata.double, "am", @progbits, 8
consists of fixed-size 8 byte long constants to be merged.
Is this syntax ok or should entity size be specified separately from the
.section directive?

This patch does not need any modifications to port elf*.c files, but one
pays for this:
assembler has to keep local symbols in SHF_MERGE sections so that
relocations are against them and not against STT_SECTION symbols. But as gas
has -L by default turned off and ld does not have -X by default turned on
(ie. gas defaults to different behaviour than ld), the .L symbols will wind
up in the resulting executable/DSO unless -X is explicitely given or the
thing is stripped.
The patch could perhaps check local relocations against SHF_MERGE sections
and if they cross object boundaries, simply disable the optimization for the
current section, but it would be harder to do this for global symbols,
because at bfd_final_link time it is too late to change section sizes.
So I wonder whether:

	.section .rodata.strtab, "ams", @progbits, 1
.LC0:
	.string "foo"
	.globl	BAR
BAR:
	.string "bar"
.LC2:
	.string "baz"

	.data
	.long	.LC0+8

other file:
	.data
	.long	BAR+5

should have defined behaviour or not.

2001-04-02  Jakub Jelinek  <jakub@redhat.com>

	* libbfd-in.h (_bfd_merge_section): New.
	(_bfd_write_merged_section): New.
	(_bfd_merged_section_offset): New.
	* libbfd.h: Rebuilt.
	* elflink.h (elf_link_add_object_symbols): Call _bfd_merge_section.
	(elf_bfd_final_link): Adjust global symbols pointing into SEC_MERGE
	sections.
	(elf_link_sec_merge_syms): New.
	(elf_link_input_bfd): Call _bfd_merged_section_offset
	and _bfd_write_merged_section.
	* elf-bfd.h (struct elf_link_hash_table): Add merge_info
	field.
	(struct bfd_elf_section_data): Likewise.
	* elf.c (_bfd_elf_make_section_from_shdr): Set SEC_MERGE and
	SEC_STRINGS section flags and entsize from their ELF counterparts.
	(_bfd_elf_link_hash_table_init): Initialize merge_info.
	(elf_fake_sections): Set SHF_MERGE, SHF_STRINGS and sh_entsize
	from their BFD counterparts.
	* merge.c: New file.
	* Makefile.am: Add strtab.lo.
	* Makefile.in: Rebuilt.

	* config/obj-elf.c (obj_elf_change_section): Add entsize argument,
	handle SHF_MERGE and SHF_STRINGS.
	(obj_elf_parse_section_letters): Set SHF_MERGE and SHF_STRINGS.
	(obj_elf_section): Allow additional argument specifying entity size.
	* write.c (adjust_reloc_syms): Keep relocations against local symbols
	in SEC_MERGE sections.

--- bfd/libbfd-in.h.jj	Mon Mar 26 19:44:22 2001
+++ bfd/libbfd-in.h	Fri Mar 30 18:40:04 2001
@@ -462,6 +462,21 @@ extern boolean _bfd_write_stab_strings P
 extern bfd_vma _bfd_stab_section_offset
   PARAMS ((bfd *, PTR *, asection *, PTR *, bfd_vma));
 
+/* Attempt to merge a SEC_MERGE section.  */
+
+extern boolean _bfd_merge_section
+  PARAMS ((bfd *, PTR *, asection *, PTR *));
+
+/* Write out a merged section.  */
+
+extern boolean _bfd_write_merged_section
+  PARAMS ((bfd *, asection *, PTR));
+
+/* Find an offset within a modified SEC_MERGE section.  */
+
+extern bfd_vma _bfd_merged_section_offset
+  PARAMS ((bfd *, asection **, PTR, bfd_vma, bfd_vma));
+
 /* Create a string table.  */
 extern struct bfd_strtab_hash *_bfd_stringtab_init PARAMS ((void));
 
--- bfd/elflink.h.jj	Mon Mar 26 19:44:22 2001
+++ bfd/elflink.h	Mon Apr  2 11:54:26 2001
@@ -2167,6 +2167,18 @@ elf_link_add_object_symbols (abfd, info)
 	}
     }
 
+  if (! info->relocateable)
+    {
+      asection *s;
+
+      for (s = abfd->sections; s != NULL; s = s->next)
+	if ((s->flags & SEC_MERGE)
+	    && ! _bfd_merge_section (abfd,
+				     &elf_hash_table (info)->merge_info,
+				     s, &elf_section_data (s)->merge_info))
+	  goto error_return;
+    }
+
   return true;
 
  error_return:
@@ -4067,6 +4079,8 @@ static boolean elf_link_flush_output_sym
   PARAMS ((struct elf_final_link_info *));
 static boolean elf_link_output_extsym
   PARAMS ((struct elf_link_hash_entry *, PTR));
+static boolean elf_link_sec_merge_syms
+  PARAMS ((struct elf_link_hash_entry *, PTR));
 static boolean elf_link_input_bfd
   PARAMS ((struct elf_final_link_info *, bfd *));
 static boolean elf_reloc_link_order
@@ -4216,6 +4230,7 @@ elf_bfd_final_link (abfd, info)
   Elf_Internal_Shdr *symstrtab_hdr;
   struct elf_backend_data *bed = get_elf_backend_data (abfd);
   struct elf_outext_info eoinfo;
+  boolean merged;
 
   if (info->shared)
     abfd->flags |= DYNAMIC;
@@ -4261,6 +4276,7 @@ elf_bfd_final_link (abfd, info)
   max_external_reloc_size = 0;
   max_internal_reloc_count = 0;
   max_sym_count = 0;
+  merged = false;
   for (o = abfd->sections; o != (asection *) NULL; o = o->next)
     {
       o->reloc_count = 0;
@@ -4282,6 +4298,9 @@ elf_bfd_final_link (abfd, info)
 		 the linker has decided to not include.  */
 	      sec->linker_mark = true;
 
+	      if (sec->flags & SEC_MERGE)
+		merged = true;
+
 	      if (info->relocateable || info->emitrelocations)
 		o->reloc_count += sec->reloc_count;
 
@@ -4339,6 +4358,10 @@ elf_bfd_final_link (abfd, info)
 	o->vma = 0;
     }
 
+  if (! info->relocateable && merged)
+    elf_link_hash_traverse (elf_hash_table (info),
+			    elf_link_sec_merge_syms, (PTR) abfd);
+
   /* Figure out the file positions for everything but the symbol table
      and the relocs.  We set symcount to force assign_section_numbers
      to create a symbol table.  */
@@ -5058,6 +5081,33 @@ elf_link_flush_output_syms (finfo)
   return true;
 }
 
+/* Adjust all external symbols pointing into SEC_MERGE sections
+   to reflect the object merging within the sections.  */
+
+static boolean
+elf_link_sec_merge_syms (h, data)
+     struct elf_link_hash_entry *h;
+     PTR data;
+{
+  asection *sec;
+
+  if ((h->root.type == bfd_link_hash_defined
+       || h->root.type == bfd_link_hash_defweak)
+      && ((sec = h->root.u.def.section)->flags & SEC_MERGE)
+      && elf_section_data (sec)->merge_info)
+    {
+      bfd *output_bfd = (bfd *) data;
+
+      h->root.u.def.value =
+	_bfd_merged_section_offset (output_bfd,
+				    &h->root.u.def.section,
+				    elf_section_data (sec)->merge_info,
+				    h->root.u.def.value, (bfd_vma) 0);
+    }
+
+  return true;
+}
+
 /* Add an external symbol to the symbol table.  This is called from
    the hash table traversal routine.  When generating a shared object,
    we go through the symbol table twice.  The first time we output
@@ -5523,7 +5573,15 @@ elf_link_input_bfd (finfo, input_bfd)
 	  name = isec->name;
 	}
       else if (isym->st_shndx > 0 && isym->st_shndx < SHN_LORESERVE)
-	isec = section_from_elf_index (input_bfd, isym->st_shndx);
+	{
+	  isec = section_from_elf_index (input_bfd, isym->st_shndx);
+	  if (elf_section_data (isec)->merge_info
+	      && ELF_ST_TYPE (isym->st_info) != STT_SECTION)
+	    isym->st_value =
+	      _bfd_merged_section_offset (output_bfd, &isec,
+					  elf_section_data (isec)->merge_info,
+					  isym->st_value, (bfd_vma) 0);
+	}
       else if (isym->st_shndx == SHN_ABS)
 	{
 	  isec = bfd_abs_section_ptr;
@@ -5887,7 +5945,20 @@ elf_link_input_bfd (finfo, input_bfd)
 	}
 
       /* Write out the modified section contents.  */
-      if (elf_section_data (o)->stab_info == NULL)
+      if (elf_section_data (o)->stab_info)
+	{
+	  if (! (_bfd_write_section_stabs
+		 (output_bfd, &elf_hash_table (finfo->info)->stab_info,
+		  o, &elf_section_data (o)->stab_info, contents)))
+	    return false;
+	}
+      else if (elf_section_data (o)->merge_info)
+	{
+	  if (! (_bfd_write_merged_section
+		 (output_bfd, o, elf_section_data (o)->merge_info)))
+	    return false;
+	}
+      else
 	{
 	  if (! (o->flags & SEC_EXCLUDE) &&
 	      ! bfd_set_section_contents (output_bfd, o->output_section,
@@ -5895,13 +5966,6 @@ elf_link_input_bfd (finfo, input_bfd)
 					  (o->_cooked_size != 0
 					   ? o->_cooked_size
 					   : o->_raw_size)))
-	    return false;
-	}
-      else
-	{
-	  if (! (_bfd_write_section_stabs
-		 (output_bfd, &elf_hash_table (finfo->info)->stab_info,
-		  o, &elf_section_data (o)->stab_info, contents)))
 	    return false;
 	}
     }
--- bfd/elf-bfd.h.jj	Mon Mar 26 19:44:08 2001
+++ bfd/elf-bfd.h	Thu Mar 29 14:30:59 2001
@@ -241,6 +241,8 @@ struct elf_link_hash_table
   struct elf_link_hash_entry *hgot;
   /* A pointer to information used to link stabs in sections.  */
   PTR stab_info;
+  /* A pointer to information used to merge SEC_MERGE sections.  */
+  PTR merge_info;
   /* A linked list of local symbols to be added to .dynsym.  */
   struct elf_link_local_dynamic_entry *dynlocal;
   /* A linked list of DT_RPATH/DT_RUNPATH names found in dynamic
@@ -712,6 +714,8 @@ struct bfd_elf_section_data
   long dynindx;
   /* A pointer used for .stab linking optimizations.  */
   PTR stab_info;
+  /* A pointer used for SEC_MERGE optimizations.  */
+  PTR merge_info;
   /* A pointer available for the processor specific ELF backend.  */
   PTR tdata;
   /* Nonzero if this section uses RELA relocations, rather than REL.  */
--- bfd/elf.c.jj	Mon Mar 26 19:44:08 2001
+++ bfd/elf.c	Thu Mar 29 15:08:47 2001
@@ -386,6 +386,13 @@ _bfd_elf_make_section_from_shdr (abfd, h
     flags |= SEC_CODE;
   else if ((flags & SEC_LOAD) != 0)
     flags |= SEC_DATA;
+  if ((hdr->sh_flags & SHF_MERGE) != 0)
+    {
+      flags |= SEC_MERGE;
+      newsect->entsize = hdr->sh_entsize;
+      if ((hdr->sh_flags & SHF_STRINGS) != 0)
+	flags |= SEC_STRINGS;
+    }
 
   /* The debugging sections appear to be recognized only by name, not
      any sort of flag.  */
@@ -1031,6 +1038,7 @@ _bfd_elf_link_hash_table_init (table, ab
   table->runpath = NULL;
   table->hgot = NULL;
   table->stab_info = NULL;
+  table->merge_info = NULL;
   table->dynlocal = NULL;
   return _bfd_link_hash_table_init (&table->root, abfd, newfunc);
 }
@@ -1809,6 +1817,13 @@ elf_fake_sections (abfd, asect, failedpt
     this_hdr->sh_flags |= SHF_WRITE;
   if ((asect->flags & SEC_CODE) != 0)
     this_hdr->sh_flags |= SHF_EXECINSTR;
+  if ((asect->flags & SEC_MERGE) != 0)
+    {
+      this_hdr->sh_flags |= SHF_MERGE;
+      this_hdr->sh_entsize = asect->entsize;
+      if ((asect->flags & SEC_STRINGS) != 0)
+	this_hdr->sh_flags |= SHF_STRINGS;
+    }
 
   /* Check for processor-specific section types.  */
   if (bed->elf_backend_fake_sections)
--- bfd/Makefile.am.jj	Mon Mar 26 19:44:02 2001
+++ bfd/Makefile.am	Thu Mar 29 13:56:23 2001
@@ -30,14 +30,14 @@ BFD_LIBS = \
 	format.lo init.lo libbfd.lo opncls.lo reloc.lo \
 	section.lo syms.lo targets.lo hash.lo linker.lo \
 	srec.lo binary.lo tekhex.lo ihex.lo stabs.lo stab-syms.lo \
-	dwarf2.lo
+	merge.lo dwarf2.lo
 
 BFD_LIBS_CFILES = \
 	archive.c archures.c bfd.c cache.c coffgen.c corefile.c \
 	format.c init.c libbfd.c opncls.c reloc.c \
 	section.c syms.c targets.c hash.c linker.c \
 	srec.c binary.c tekhex.c ihex.c stabs.c stab-syms.c \
-	dwarf2.c
+	merge.c dwarf2.c
 
 # This list is alphabetized to make it easier to keep in sync
 # with the decls and initializer in archures.c.
@@ -780,6 +780,7 @@ stabs.lo: stabs.c $(INCDIR)/filenames.h 
   $(INCDIR)/aout/stab.def
 stab-syms.lo: stab-syms.c libaout.h $(INCDIR)/bfdlink.h \
   $(INCDIR)/aout/aout64.h $(INCDIR)/aout/stab.def
+merge.lo: merge.c $(INCDIR)/filenames.h
 dwarf2.lo: dwarf2.c $(INCDIR)/filenames.h $(INCDIR)/libiberty.h \
   elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \
   $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(INCDIR)/elf/dwarf2.h
--- bfd/Makefile.in.jj	Mon Mar 26 19:44:02 2001
+++ bfd/Makefile.in	Thu Mar 29 13:56:34 2001
@@ -144,10 +144,10 @@ BFD_H = bfd.h
 # for the debugger, so if you are downloading things as S-records you
 # need two copies of the executable, one to download and one for the
 # debugger).
-BFD_LIBS =  	archive.lo archures.lo bfd.lo cache.lo coffgen.lo corefile.lo 	format.lo init.lo libbfd.lo opncls.lo reloc.lo 	section.lo syms.lo targets.lo hash.lo linker.lo 	srec.lo binary.lo tekhex.lo ihex.lo stabs.lo stab-syms.lo 	dwarf2.lo
+BFD_LIBS =  	archive.lo archures.lo bfd.lo cache.lo coffgen.lo corefile.lo 	format.lo init.lo libbfd.lo opncls.lo reloc.lo 	section.lo syms.lo targets.lo hash.lo linker.lo 	srec.lo binary.lo tekhex.lo ihex.lo stabs.lo stab-syms.lo 	merge.lo dwarf2.lo
 
 
-BFD_LIBS_CFILES =  	archive.c archures.c bfd.c cache.c coffgen.c corefile.c 	format.c init.c libbfd.c opncls.c reloc.c 	section.c syms.c targets.c hash.c linker.c 	srec.c binary.c tekhex.c ihex.c stabs.c stab-syms.c 	dwarf2.c
+BFD_LIBS_CFILES =  	archive.c archures.c bfd.c cache.c coffgen.c corefile.c 	format.c init.c libbfd.c opncls.c reloc.c 	section.c syms.c targets.c hash.c linker.c 	srec.c binary.c tekhex.c ihex.c stabs.c stab-syms.c 	merge.c dwarf2.c
 
 
 # This list is alphabetized to make it easier to keep in sync
@@ -261,7 +261,7 @@ LTLIBRARIES =  $(lib_LTLIBRARIES)
 libbfd_la_OBJECTS =  archive.lo archures.lo bfd.lo cache.lo coffgen.lo \
 corefile.lo format.lo init.lo libbfd.lo opncls.lo reloc.lo section.lo \
 syms.lo targets.lo hash.lo linker.lo srec.lo binary.lo tekhex.lo \
-ihex.lo stabs.lo stab-syms.lo dwarf2.lo
+ihex.lo stabs.lo stab-syms.lo merge.lo dwarf2.lo
 CFLAGS = @CFLAGS@
 COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
 LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -887,6 +887,7 @@ stabs.lo: stabs.c $(INCDIR)/filenames.h 
   $(INCDIR)/aout/stab.def
 stab-syms.lo: stab-syms.c libaout.h $(INCDIR)/bfdlink.h \
   $(INCDIR)/aout/aout64.h $(INCDIR)/aout/stab.def
+merge.lo: merge.c $(INCDIR)/filenames.h
 dwarf2.lo: dwarf2.c $(INCDIR)/filenames.h $(INCDIR)/libiberty.h \
   elf-bfd.h $(INCDIR)/elf/common.h $(INCDIR)/elf/internal.h \
   $(INCDIR)/elf/external.h $(INCDIR)/bfdlink.h $(INCDIR)/elf/dwarf2.h
--- bfd/libbfd.h.jj	Mon Mar 26 19:44:22 2001
+++ bfd/libbfd.h	Fri Mar 30 18:40:37 2001
@@ -462,6 +462,21 @@ extern boolean _bfd_write_stab_strings P
 extern bfd_vma _bfd_stab_section_offset
   PARAMS ((bfd *, PTR *, asection *, PTR *, bfd_vma));
 
+/* Attempt to merge a SEC_MERGE section.  */
+
+extern boolean _bfd_merge_section
+  PARAMS ((bfd *, PTR *, asection *, PTR *));
+
+/* Write out a merged section.  */
+
+extern boolean _bfd_write_merged_section
+  PARAMS ((bfd *, asection *, PTR));
+
+/* Find an offset within a modified SEC_MERGE section.  */
+
+extern bfd_vma _bfd_merged_section_offset
+  PARAMS ((bfd *, asection **, PTR, bfd_vma, bfd_vma));
+
 /* Create a string table.  */
 extern struct bfd_strtab_hash *_bfd_stringtab_init PARAMS ((void));
 
--- bfd/bfd-in2.h.jj	Mon Mar 26 19:44:02 2001
+++ bfd/bfd-in2.h	Thu Mar 29 12:22:13 2001
@@ -1102,6 +1102,14 @@ typedef struct sec
      references found to any symbol in the section.  */
 #define SEC_CLINK 0x10000000
 
+  /* Attempt to merge identical entities in the section.
+     Entity size is given in the entsize field.  */
+#define SEC_MERGE 0x20000000
+
+  /* If given with SEC_MERGE, entities to merge are zero terminated strings
+     where entsize specifies character size instead of fixed size entries.  */
+#define SEC_STRINGS 0x40000000
+
   /*  End of section flags.  */
 
   /* Some internal packed boolean fields.  */
@@ -1213,6 +1221,10 @@ typedef struct sec
   /* Number of line number records.  */
 
   unsigned int lineno_count;
+
+  /* Entity size for merging purposes.  */
+
+  unsigned int entsize;
 
   /* Optional information about a COMDAT entry; NULL if not COMDAT.  */
 
--- bfd/merge.c.jj	Tue Mar 27 16:16:20 2001
+++ bfd/merge.c	Mon Apr  2 13:06:30 2001
@@ -0,0 +1,606 @@
+/* SEC_MERGE support.
+   Copyright 2001 Free Software Foundation, Inc.
+   Written by Jakub Jelinek <jakub@redhat.com>.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* This file contains support for merging duplicate entities within sections,
+   as used in ELF SHF_MERGE.  */
+
+#include "bfd.h"
+#include "sysdep.h"
+#include "libbfd.h"
+
+#include <ctype.h>
+
+/* An entry in the section merge hash table.  */
+
+struct sec_merge_hash_entry
+{
+  struct bfd_hash_entry root;
+  /* Length of this entry.  */
+  unsigned int len;
+  /* Index within the merged section.  */
+  bfd_size_type index;
+  /* Which section is it in.  */
+  asection *sec;
+  /* Next entity in the hash table.  */
+  struct sec_merge_hash_entry *next;
+};
+
+/* The section merge hash table.  */
+
+struct sec_merge_hash
+{
+  struct bfd_hash_table table;
+  /* Next available index.  */
+  bfd_size_type size;
+  /* First entity in the SEC_MERGE sections of this type.  */
+  struct sec_merge_hash_entry *first;
+  /* Last entity in the SEC_MERGE sections of this type.  */
+  struct sec_merge_hash_entry *last;
+  /* Entity size.  */
+  unsigned int entsize;
+  /* Start of each string needs to be aligned to 1 << alignment_power
+     octets.  */
+  unsigned int alignment_power;
+  /* Are entries fixed size or zero terminated strings?  */
+  boolean strings;
+};
+
+struct sec_merge_info
+{
+  /* Chain of sec_merge_infos.  */
+  struct sec_merge_info *next;
+  /* A hash table used to hold section content.  */
+  struct sec_merge_hash *htab;
+  /* The last section attempted for merge.  */
+  asection *last;
+};
+
+struct sec_merge_sec_info
+{
+  /* A hash table used to hold section content.  */
+  struct sec_merge_hash *htab;
+  /* First string in this section.  */
+  struct sec_merge_hash_entry *first;
+  /* Original section content.  */
+  unsigned char contents[1];
+};
+
+static struct bfd_hash_entry *sec_merge_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+static struct sec_merge_hash_entry *sec_merge_hash_lookup
+  PARAMS ((struct sec_merge_hash *, const char *, boolean));
+static struct sec_merge_hash *sec_merge_init
+  PARAMS ((unsigned int, unsigned int, boolean));
+static struct sec_merge_hash_entry *sec_merge_add
+  PARAMS ((struct sec_merge_hash *, const char *));
+static boolean sec_merge_emit
+  PARAMS ((bfd *, struct sec_merge_hash *, struct sec_merge_hash_entry *));
+
+/* Routine to create an entry in a section merge hashtab.  */
+
+static struct bfd_hash_entry *
+sec_merge_hash_newfunc (entry, table, string)
+     struct bfd_hash_entry *entry;
+     struct bfd_hash_table *table;
+     const char *string;
+{
+  struct sec_merge_hash_entry *ret = (struct sec_merge_hash_entry *) entry;
+
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (ret == (struct sec_merge_hash_entry *) NULL)
+    ret = ((struct sec_merge_hash_entry *)
+	   bfd_hash_allocate (table, sizeof (struct sec_merge_hash_entry)));
+  if (ret == (struct sec_merge_hash_entry *) NULL)
+    return NULL;
+
+  /* Call the allocation method of the superclass.  */
+  ret = ((struct sec_merge_hash_entry *)
+	 bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string));
+
+  if (ret)
+    {
+      /* Initialize the local fields.  */
+      ret->index = (bfd_size_type) -1;
+      ret->sec = NULL;
+      ret->next = NULL;
+    }
+
+  return (struct bfd_hash_entry *)ret;
+}
+
+/* Look up an entry in a section merge hash table.  */
+
+static struct sec_merge_hash_entry *
+sec_merge_hash_lookup (table, string, create)
+     struct sec_merge_hash *table;
+     const char *string;
+     boolean create;
+{
+  register const unsigned char *s;
+  register unsigned long hash;
+  register unsigned int c;
+  struct sec_merge_hash_entry *hashp;
+  unsigned int len, i;
+  unsigned int index;
+
+  hash = 0;
+  len = 0;
+  s = (const unsigned char *) string;
+  if (table->strings)
+    {
+      if (table->entsize == 1)
+	{
+	  while ((c = *s++) != '\0')
+	    {
+	      hash += c + (c << 17);
+	      hash ^= hash >> 2;
+	      ++len;
+	    }
+	  hash += len + (len << 17);
+	}
+      else
+	{
+	  for (;;)
+	    {
+	      for (i = 0; i < table->entsize; ++i)
+		if (s[i] != '\0')
+		  break;
+	      if (i == table->entsize)
+		break;
+	      for (i = 0; i < table->entsize; ++i)
+		{
+		  c = *s++;
+		  hash += c + (c << 17);
+		  hash ^= hash >> 2;
+		}
+	      ++len;
+	    }
+	  hash += len + (len << 17);
+	  len *= table->entsize;
+	}
+      hash ^= hash >> 2;
+      len += table->entsize;
+    }      
+  else
+    {
+      for (i = 0; i < table->entsize; ++i)
+	{
+	  c = *s++;
+	  hash += c + (c << 17);
+	  hash ^= hash >> 2;
+	}
+      len = table->entsize;
+    }
+
+  index = hash % table->table.size;
+  for (hashp = (struct sec_merge_hash_entry *) table->table.table[index];
+       hashp != (struct sec_merge_hash_entry *) NULL;
+       hashp = (struct sec_merge_hash_entry *) hashp->next)
+    {
+      if (hashp->root.hash == hash
+	  && len == hashp->len
+	  && memcmp (hashp->root.string, string, len) == 0)
+	return hashp;
+    }
+
+  if (! create)
+    return (struct sec_merge_hash_entry *) NULL;
+
+  hashp = (struct sec_merge_hash_entry *)
+	  sec_merge_hash_newfunc ((struct bfd_hash_entry *) NULL,
+				  (struct bfd_hash_table *) table, string);
+  if (hashp == (struct sec_merge_hash_entry *) NULL)
+    return (struct sec_merge_hash_entry *) NULL;
+  hashp->root.string = string;
+  hashp->root.hash = hash;
+  hashp->len = len;
+  hashp->root.next = table->table.table[index];
+  table->table.table[index] = (struct bfd_hash_entry *) hashp;
+
+  return hashp;
+}
+
+/* Create a new hash table.  */
+
+static struct sec_merge_hash *
+sec_merge_init (alignment_power, entsize, strings)
+     unsigned int alignment_power, entsize;
+     boolean strings;
+{
+  struct sec_merge_hash *table;
+
+  table = ((struct sec_merge_hash *)
+	   bfd_malloc (sizeof (struct sec_merge_hash)));
+  if (table == NULL)
+    return NULL;
+
+  if (! bfd_hash_table_init (&table->table, sec_merge_hash_newfunc))
+    {
+      free (table);
+      return NULL;
+    }
+
+  table->size = 0;
+  table->first = NULL;
+  table->last = NULL;
+  table->alignment_power = alignment_power;
+  table->entsize = entsize;
+  table->strings = strings;
+
+  return table;
+}
+
+/* Get the index of an entity in a hash table, adding it if it is not
+   already present.  */
+
+static struct sec_merge_hash_entry *
+sec_merge_add (tab, str)
+     struct sec_merge_hash *tab;
+     const char *str;
+{
+  register struct sec_merge_hash_entry *entry;
+
+  entry = sec_merge_hash_lookup (tab, str, true);
+  if (entry == NULL)
+    return NULL;
+
+  if (entry->index == (bfd_size_type) -1)
+    {
+      entry->index = tab->size;
+      tab->size += entry->len;
+      tab->size = align_power (tab->size, tab->alignment_power);
+      if (tab->first == NULL)
+	tab->first = entry;
+      else
+	tab->last->next = entry;
+      tab->last = entry;
+    }
+
+  return entry;
+}
+
+static boolean
+sec_merge_emit (abfd, tab, entry)
+     register bfd *abfd;
+     struct sec_merge_hash *tab;
+     struct sec_merge_hash_entry *entry;
+{
+  asection *sec = entry->sec;
+  char *pad = "";
+
+  if (tab->alignment_power)
+    pad = bfd_zmalloc (1 << tab->alignment_power);
+
+  for (; entry != NULL && entry->sec == sec; entry = entry->next)
+    {
+      register const char *str;
+      register size_t len;
+
+      str = entry->root.string;
+      len = entry->len;
+
+      if (bfd_write ((PTR) str, 1, len, abfd) != len)
+	break;
+
+      if (tab->alignment_power)
+	{
+	  len &= (1 << tab->alignment_power) - 1;
+	  if (len && bfd_write ((PTR) pad, 1,
+				(1 << tab->alignment_power) - len,
+				abfd) != (1 << tab->alignment_power) - len)
+	    break;
+	}
+    }
+
+  if (tab->alignment_power)
+    free (pad);
+
+  return entry == NULL || entry->sec != sec;
+}
+
+/* This function is called for each input file from the add_symbols
+   pass of the linker.  */
+
+boolean
+_bfd_merge_section (abfd, psinfo, sec, psecinfo)
+     bfd *abfd;
+     PTR *psinfo;
+     asection *sec;
+     PTR *psecinfo;
+{
+  boolean first, nul;
+  struct sec_merge_info *sinfo;
+  struct sec_merge_sec_info *secinfo;
+  unsigned char *p, *end;
+  bfd_vma mask;
+  unsigned int align;
+  unsigned int i;
+
+  if (sec->_raw_size == 0
+      || (sec->flags & SEC_MERGE) == 0
+      || sec->entsize == 0)
+    return true;
+
+  if ((sec->flags & SEC_RELOC) != 0)
+    {
+      /* We aren't prepared to handle relocations in merged sections.  */
+      return true;
+    }
+
+  if (sec->output_section != NULL
+      && bfd_is_abs_section (sec->output_section))
+    {
+      /* The section is being discarded from the link, so we should
+	 just ignore it.  */
+      return true;
+    }
+
+  align = bfd_get_section_alignment (abfd, sec);
+  if ((sec->flags & SEC_STRINGS)
+      && ((sec->entsize < (unsigned int)(1 << align)
+	   && (sec->entsize & (sec->entsize - 1)))
+	  || (sec->entsize > (unsigned int)(1 << align)
+	      && (sec->entsize & ((1 << align) - 1)))))
+    {
+      /* Sanity check.  If string character size is smaller than
+	 alignment, then we require character size to be a power
+	 of 2, otherwise character size must be integer multiple
+	 of alignment.  */
+      return true;
+    }
+
+  first = false;
+
+  for (sinfo = (struct sec_merge_info *) *psinfo; sinfo; sinfo = sinfo->next)
+    if (! ((sinfo->last->flags ^ sec->flags) & (SEC_MERGE | SEC_STRINGS))
+	&& sinfo->last->entsize == sec->entsize
+	&& ! strcmp (sinfo->last->name, sec->name)
+	&& bfd_get_section_alignment (abfd, sinfo->last) == align)
+      break;
+
+  if (sinfo == NULL)
+    {
+      /* Initialize the information we need to keep track of.  */
+      first = true;
+      sinfo = (struct sec_merge_info *)
+	      bfd_alloc (abfd, sizeof (struct sec_merge_info));
+      if (sinfo == NULL)
+	goto error_return;
+      sinfo->next = (struct sec_merge_info *) *psinfo;
+      *psinfo = (PTR) sinfo;
+      sinfo->htab =
+	sec_merge_init ((sec->flags & SEC_STRINGS) ? align : 0,
+			sec->entsize, (sec->flags & SEC_STRINGS));
+      if (sinfo->htab == NULL)
+	goto error_return;
+    }
+
+  /* Read the section from abfd.  */
+
+  *psecinfo = bfd_alloc (abfd, sizeof (struct sec_merge_sec_info)
+			       + sec->_raw_size - 1);
+  if (*psecinfo == NULL)
+    goto error_return;
+
+  secinfo = (struct sec_merge_sec_info *)*psecinfo;
+  secinfo->htab = sinfo->htab;
+  sinfo->htab->size = 0;
+  secinfo->first = NULL;
+
+  if (! bfd_get_section_contents (abfd, sec, secinfo->contents, 0,
+				  sec->_raw_size))
+    goto error_return;
+
+  end = secinfo->contents + sec->_raw_size;
+  nul = false;
+  mask = ((bfd_vma)1 << sinfo->htab->alignment_power) - 1;
+  if (sec->flags & SEC_STRINGS)
+    {
+      for (p = secinfo->contents; p < end;)
+	{
+	  struct sec_merge_hash_entry *entry;
+
+	  entry = sec_merge_add (sinfo->htab, p);
+	  if (entry->sec == NULL)
+	    {
+	      if (secinfo->first == NULL)
+		secinfo->first = entry;
+	      entry->sec = sec;
+	    }
+	  p += entry->len;
+	  if (sec->entsize == 1)
+	    {
+	      while (p < end && *p == 0)
+		{
+		  if (!nul && !((p - secinfo->contents) & mask))
+		    {
+		      nul = true;
+		      entry = sec_merge_add (sinfo->htab, "");
+		      if (entry->sec == NULL)
+		        {
+			  if (secinfo->first == NULL)
+			    secinfo->first = entry;
+			  entry->sec = sec;
+		        }
+		    }
+		  p++;
+	        }
+	    }
+	  else
+	    {
+	      while (p < end)
+		{
+		  for (i = 0; i < sec->entsize; i++)
+		    if (p[i] != '\0')
+		      break;
+		  if (i != sec->entsize)
+		    break;
+		  if (!nul && !((p - secinfo->contents) & mask))
+		    {
+		      nul = true;
+		      entry = sec_merge_add (sinfo->htab, p);
+		      if (entry->sec == NULL)
+			{
+			  if (secinfo->first == NULL)
+			    secinfo->first = entry;
+			  entry->sec = sec;
+			}
+		    }
+		  p += sec->entsize;
+		}
+	    }
+	}
+    }
+  else
+    {
+      for (p = secinfo->contents; p < end; p += sec->entsize)
+	{
+	  struct sec_merge_hash_entry *entry;
+
+	  entry = sec_merge_add (sinfo->htab, p);
+	  if (entry->sec == NULL)
+	    {
+	      if (secinfo->first == NULL)
+		secinfo->first = entry;
+	      entry->sec = sec;
+	    }
+	}
+    }
+
+  sec->_cooked_size = sinfo->htab->size;
+  if (!sec->_cooked_size)
+    sec->flags |= SEC_EXCLUDE;
+  sinfo->last = sec;
+  return true;
+
+ error_return:
+  if (*psecinfo != NULL)
+    free (*psecinfo);
+  *psecinfo = NULL;
+  return false;
+}
+
+/* Write out the merged section.  */
+
+boolean
+_bfd_write_merged_section (output_bfd, sec, psecinfo)
+     bfd *output_bfd;
+     asection *sec;
+     PTR psecinfo;
+{
+  struct sec_merge_sec_info *secinfo;
+
+  secinfo = (struct sec_merge_sec_info *) psecinfo;
+
+  if (!secinfo->first)
+    return true;
+
+  if (bfd_seek (output_bfd,
+		(sec->output_section->filepos + sec->output_offset),
+		SEEK_SET) != 0)
+    return false;
+
+  if (! sec_merge_emit (output_bfd, secinfo->htab, secinfo->first))
+    return false;
+
+  return true;
+}
+
+/* Adjust an address in the SEC_MERGE section.  Given OFFSET within
+   *PSEC, this returns the new offset in the adjusted SEC_MERGE
+   section and writes the new section back into *PSEC.  */
+
+bfd_vma
+_bfd_merged_section_offset (output_bfd, psec, psecinfo, offset, addend)
+     bfd *output_bfd ATTRIBUTE_UNUSED;
+     asection **psec;
+     PTR psecinfo;
+     bfd_vma offset, addend;
+{
+  struct sec_merge_sec_info *secinfo;
+  struct sec_merge_hash_entry *entry;
+  unsigned char *p;
+  asection *sec = *psec;
+
+  secinfo = (struct sec_merge_sec_info *) psecinfo;
+
+  if (offset + addend >= sec->_raw_size)
+    {
+      if (offset + addend > sec->_raw_size)
+	(*_bfd_error_handler) (_("%s: access beyond end of merged section (%ld + %ld)"),
+			       bfd_get_filename (sec->owner), (long)offset,
+			       (long) addend);
+      return (secinfo->first ? sec->_cooked_size : 0);
+    }
+
+  if (secinfo->htab->strings)
+    {
+      if (sec->entsize == 1)
+	{
+	  p = secinfo->contents + offset + addend - 1;
+	  while (*p && p >= secinfo->contents)
+	    --p;
+	  ++p;
+	}
+      else
+	{
+	  p = secinfo->contents
+	      + ((offset + addend) / sec->entsize) * sec->entsize;
+	  p -= sec->entsize;
+	  while (p >= secinfo->contents)
+	    {
+	      unsigned int i;
+
+	      for (i = 0; i < sec->entsize; ++i)
+		if (p[i] != '\0')
+		  break;
+	      if (i == sec->entsize)
+		break;
+	      p -= sec->entsize;
+	    }
+	  p += sec->entsize;
+	}
+    }
+  else
+    {
+      p = secinfo->contents
+	  + ((offset + addend) / sec->entsize) * sec->entsize;
+    }
+  entry = sec_merge_hash_lookup (secinfo->htab, p, false);
+  if (!entry)
+    {
+      if (! secinfo->htab->strings)
+	abort ();
+      /* This should only happen if somebody points into the padding
+	 after a NUL character but before next entity.  */
+      if (*p)
+	abort ();
+      if (! secinfo->htab->first)
+	abort ();
+      entry = secinfo->htab->first;
+      p = secinfo->contents
+	  + ((offset + addend) / sec->entsize + 1) * sec->entsize
+	  - entry->len;
+    }
+
+  *psec = entry->sec;
+  return entry->index + (secinfo->contents + offset - p);
+}
--- gas/config/obj-elf.c.jj	Mon Mar 26 19:46:15 2001
+++ gas/config/obj-elf.c	Thu Mar 29 13:52:57 2001
@@ -620,9 +620,9 @@ static struct special_section const spec
 };
 
 void
-obj_elf_change_section (name, type, attr, push)
+obj_elf_change_section (name, type, attr, entsize, push)
      char *name;
-     int type, attr, push;
+     int type, attr, entsize, push;
 {
   asection *old_sec;
   segT sec;
@@ -690,7 +690,9 @@ obj_elf_change_section (name, type, attr
 	   | ((attr & SHF_WRITE) ? 0 : SEC_READONLY)
 	   | ((attr & SHF_ALLOC) ? SEC_ALLOC : 0)
 	   | (((attr & SHF_ALLOC) && type != SHT_NOBITS) ? SEC_LOAD : 0)
-	   | ((attr & SHF_EXECINSTR) ? SEC_CODE : 0));
+	   | ((attr & SHF_EXECINSTR) ? SEC_CODE : 0)
+	   | ((attr & SHF_MERGE) ? SEC_MERGE : 0)
+	   | ((attr & SHF_STRINGS) ? SEC_STRINGS : 0));
 #ifdef md_elf_section_flags
   flags = md_elf_section_flags (flags, attr, type);
 #endif
@@ -704,6 +706,8 @@ obj_elf_change_section (name, type, attr
         seg_info (sec)->bss = 1;
 
       bfd_set_section_flags (stdoutput, sec, flags);
+      if (flags & SEC_MERGE)
+	sec->entsize = entsize;
 
       /* Add a symbol for this section to the symbol table.  */
       secsym = symbol_find (name);
@@ -719,8 +723,10 @@ obj_elf_change_section (name, type, attr
 	 saw the first time.  */
       if ((old_sec->flags ^ flags)
 	  & (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
-	     | SEC_EXCLUDE | SEC_SORT_ENTRIES))
+	     | SEC_EXCLUDE | SEC_SORT_ENTRIES | SEC_MERGE | SEC_STRINGS))
 	as_warn (_("Ignoring changed section attributes for %s"), name);
+      else if ((flags & SEC_MERGE) && old_sec->entsize != entsize)
+	as_warn (_("Ignoring changed section entity size for %s"), name);
     }
 
 #ifdef md_elf_section_change_hook
@@ -748,9 +754,15 @@ obj_elf_parse_section_letters (str, len)
 	case 'x':
 	  attr |= SHF_EXECINSTR;
 	  break;
+	case 'm':
+	  attr |= SHF_MERGE;
+	  break;
+	case 's':
+	  attr |= SHF_STRINGS;
+	  break;
 	default:
 	  {
-	    char *bad_msg = _("Unrecognized .section attribute: want a,w,x");
+	    char *bad_msg = _("Unrecognized .section attribute: want a,m,s,w,x");
 #ifdef md_elf_section_letter
 	    int md_attr = md_elf_section_letter (*str, &bad_msg);
 	    if (md_attr >= 0)
@@ -822,6 +834,7 @@ obj_elf_section (push)
 {
   char *name, *beg, *end;
   int type, attr, dummy;
+  int entsize;
 
 #ifndef TC_I370
   if (flag_mri)
@@ -877,6 +890,7 @@ obj_elf_section (push)
 
   type = SHT_NULL;
   attr = 0;
+  entsize = 0;
 
   if (*input_line_pointer == ',')
     {
@@ -919,6 +933,20 @@ obj_elf_section (push)
 		  type = obj_elf_section_type (beg, input_line_pointer - beg);
 		}
 	    }
+
+	  SKIP_WHITESPACE ();
+	  if ((attr & SHF_MERGE) && *input_line_pointer == ',')
+	    {
+	      ++input_line_pointer;
+	      SKIP_WHITESPACE ();
+	      entsize = get_absolute_expression ();
+	      if (entsize < 0)
+		{
+		  as_warn (_("Bad .section directive - invalid merge entity size"));
+		  attr &= ~SHF_MERGE;
+		  entsize = 0;
+		}
+	    }
 	}
       else
 	{
@@ -948,7 +976,13 @@ obj_elf_section (push)
 
   demand_empty_rest_of_line ();
 
-  obj_elf_change_section (name, type, attr, push);
+  if ((attr & SHF_MERGE) && entsize == 0)
+    {
+      as_warn (_("Entity size for SHF_MERGE not specified.\nSpecify entity size as 4th argument"));
+      attr &= SHF_MERGE;
+    }
+
+  obj_elf_change_section (name, type, attr, entsize, push);
 }
 
 /* Change to the .data section.  */
--- gas/write.c.jj	Mon Mar 26 19:46:14 2001
+++ gas/write.c	Thu Mar 29 15:32:26 2001
@@ -851,6 +851,13 @@ adjust_reloc_syms (abfd, sec, xxx)
 	    symbol_mark_used_in_reloc (fixp->fx_addsy);
 	    goto done;
 	  }
+
+	/* Never adjust a reloc against local symbol in a merge section.  */
+	if (symsec->flags & SEC_MERGE)
+	  {
+	    symbol_mark_used_in_reloc (fixp->fx_addsy);
+	    goto done;
+	  }
 #endif
 
 	/* Is there some other reason we can't adjust this one?  (E.g.,

	Jakub


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