(root)/
binutils-2.41/
bfd/
reloc16.c
       1  /* 8 and 16 bit COFF relocation functions, for BFD.
       2     Copyright (C) 1990-2023 Free Software Foundation, Inc.
       3     Written by Cygnus Support.
       4  
       5     This file is part of BFD, the Binary File Descriptor library.
       6  
       7     This program is free software; you can redistribute it and/or modify
       8     it under the terms of the GNU General Public License as published by
       9     the Free Software Foundation; either version 3 of the License, or
      10     (at your option) any later version.
      11  
      12     This program is distributed in the hope that it will be useful,
      13     but WITHOUT ANY WARRANTY; without even the implied warranty of
      14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15     GNU General Public License for more details.
      16  
      17     You should have received a copy of the GNU General Public License
      18     along with this program; if not, write to the Free Software
      19     Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
      20     MA 02110-1301, USA.  */
      21  
      22  
      23  /* Most of this hacked by Steve Chamberlain <sac@cygnus.com>.  */
      24  
      25  /* These routines are used by coff-z8k to do relocation.
      26  
      27     FIXME: This code should be rewritten to support the new COFF
      28     linker.  Basically, they need to deal with COFF relocs rather than
      29     BFD generic relocs.  They should store the relocs in some location
      30     where coff_link_input_bfd can find them (and coff_link_input_bfd
      31     should be changed to use this location rather than rereading the
      32     file) (unless info->keep_memory is FALSE, in which case they should
      33     free up the relocs after dealing with them).  */
      34  
      35  #include "sysdep.h"
      36  #include "bfd.h"
      37  #include "libbfd.h"
      38  #include "bfdlink.h"
      39  #include "genlink.h"
      40  #include "coff/internal.h"
      41  #include "libcoff.h"
      42  
      43  bfd_vma
      44  bfd_coff_reloc16_get_value (arelent *reloc,
      45  			    struct bfd_link_info *link_info,
      46  			    asection *input_section)
      47  {
      48    bfd_vma value;
      49    asymbol *symbol = *(reloc->sym_ptr_ptr);
      50    /* A symbol holds a pointer to a section, and an offset from the
      51       base of the section.  To relocate, we find where the section will
      52       live in the output and add that in.  */
      53  
      54    if (bfd_is_und_section (symbol->section)
      55        || bfd_is_com_section (symbol->section))
      56      {
      57        struct bfd_link_hash_entry *h;
      58  
      59        /* The symbol is undefined in this BFD.  Look it up in the
      60  	 global linker hash table.  FIXME: This should be changed when
      61  	 we convert this stuff to use a specific final_link function
      62  	 and change the interface to bfd_relax_section to not require
      63  	 the generic symbols.  */
      64        h = bfd_wrapped_link_hash_lookup (input_section->owner, link_info,
      65  					bfd_asymbol_name (symbol),
      66  					false, false, true);
      67        if (h != (struct bfd_link_hash_entry *) NULL
      68  	  && (h->type == bfd_link_hash_defined
      69  	      || h->type == bfd_link_hash_defweak))
      70  	value = (h->u.def.value
      71  		 + h->u.def.section->output_section->vma
      72  		 + h->u.def.section->output_offset);
      73        else if (h != (struct bfd_link_hash_entry *) NULL
      74  	       && h->type == bfd_link_hash_common)
      75  	value = h->u.c.size;
      76        else if (h != (struct bfd_link_hash_entry *) NULL
      77  	       && h->type == bfd_link_hash_undefweak)
      78  	/* This is a GNU extension.  */
      79  	value = 0;
      80        else
      81  	{
      82  	  (*link_info->callbacks->undefined_symbol)
      83  	    (link_info, bfd_asymbol_name (symbol),
      84  	     input_section->owner, input_section, reloc->address, true);
      85  	  value = 0;
      86  	}
      87      }
      88    else
      89      {
      90        value = symbol->value
      91  	+ symbol->section->output_offset
      92  	+ symbol->section->output_section->vma;
      93      }
      94  
      95    /* Add the value contained in the relocation.  */
      96    value += reloc->addend;
      97  
      98    return value;
      99  }
     100  
     101  void
     102  bfd_perform_slip (bfd *abfd,
     103  		  unsigned int slip,
     104  		  asection *input_section,
     105  		  bfd_vma value)
     106  {
     107    asymbol **s;
     108  
     109    s = _bfd_generic_link_get_symbols (abfd);
     110    BFD_ASSERT (s != (asymbol **) NULL);
     111  
     112    /* Find all symbols past this point, and make them know
     113       what's happened.  */
     114    while (*s)
     115      {
     116        asymbol *p = *s;
     117        if (p->section == input_section)
     118  	{
     119  	  /* This was pointing into this section, so mangle it.  */
     120  	  if (p->value > value)
     121  	    {
     122  	      p->value -= slip;
     123  	      if (p->udata.p != NULL)
     124  		{
     125  		  struct generic_link_hash_entry *h;
     126  
     127  		  h = (struct generic_link_hash_entry *) p->udata.p;
     128  		  BFD_ASSERT (h->root.type == bfd_link_hash_defined
     129  			      || h->root.type == bfd_link_hash_defweak);
     130  		  h->root.u.def.value -= slip;
     131  		  BFD_ASSERT (h->root.u.def.value == p->value);
     132  		}
     133  	    }
     134  	}
     135        s++;
     136      }
     137  }
     138  
     139  bool
     140  bfd_coff_reloc16_relax_section (bfd *abfd,
     141  				asection *input_section,
     142  				struct bfd_link_info *link_info,
     143  				bool *again)
     144  {
     145    /* Get enough memory to hold the stuff.  */
     146    bfd *input_bfd = input_section->owner;
     147    unsigned *shrinks;
     148    unsigned shrink = 0;
     149    long reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
     150    arelent **reloc_vector = NULL;
     151    long reloc_count;
     152  
     153    if (bfd_link_relocatable (link_info))
     154      (*link_info->callbacks->einfo)
     155        (_("%P%F: --relax and -r may not be used together\n"));
     156  
     157    /* We only do global relaxation once.  It is not safe to do it multiple
     158       times (see discussion of the "shrinks" array below).  */
     159    *again = false;
     160  
     161    if (reloc_size < 0)
     162      return false;
     163  
     164    reloc_vector = (arelent **) bfd_malloc ((bfd_size_type) reloc_size);
     165    if (!reloc_vector && reloc_size > 0)
     166      return false;
     167  
     168    /* Get the relocs and think about them.  */
     169    reloc_count =
     170      bfd_canonicalize_reloc (input_bfd, input_section, reloc_vector,
     171  			    _bfd_generic_link_get_symbols (input_bfd));
     172    if (reloc_count < 0)
     173      {
     174        free (reloc_vector);
     175        return false;
     176      }
     177  
     178    /* The reloc16.c and related relaxing code is very simple, the price
     179       for that simplicity is we can only call this function once for
     180       each section.
     181  
     182       So, to get the best results within that limitation, we do multiple
     183       relaxing passes over each section here.  That involves keeping track
     184       of the "shrink" at each reloc in the section.  This allows us to
     185       accurately determine the relative location of two relocs within
     186       this section.
     187  
     188       In theory, if we kept the "shrinks" array for each section for the
     189       entire link, we could use the generic relaxing code in the linker
     190       and get better results, particularly for jsr->bsr and 24->16 bit
     191       memory reference relaxations.  */
     192  
     193    if (reloc_count > 0)
     194      {
     195        int another_pass = 0;
     196        bfd_size_type amt;
     197  
     198        /* Allocate and initialize the shrinks array for this section.
     199  	 The last element is used as an accumulator of shrinks.  */
     200        amt = reloc_count + 1;
     201        amt *= sizeof (unsigned);
     202        shrinks = (unsigned *) bfd_zmalloc (amt);
     203  
     204        /* Loop until nothing changes in this section.  */
     205        do
     206  	{
     207  	  arelent **parent;
     208  	  unsigned int i;
     209  	  long j;
     210  
     211  	  another_pass = 0;
     212  
     213  	  for (i = 0, parent = reloc_vector; *parent; parent++, i++)
     214  	    {
     215  	      /* Let the target/machine dependent code examine each reloc
     216  		 in this section and attempt to shrink it.  */
     217  	      shrink = bfd_coff_reloc16_estimate (abfd, input_section, *parent,
     218  						  shrinks[i], link_info);
     219  
     220  	      /* If it shrunk, note it in the shrinks array and set up for
     221  		 another pass.  */
     222  	      if (shrink != shrinks[i])
     223  		{
     224  		  another_pass = 1;
     225  		  for (j = i + 1; j <= reloc_count; j++)
     226  		    shrinks[j] += shrink - shrinks[i];
     227  		}
     228  	    }
     229  	}
     230        while (another_pass);
     231  
     232        shrink = shrinks[reloc_count];
     233        free ((char *) shrinks);
     234      }
     235  
     236    input_section->rawsize = input_section->size;
     237    input_section->size -= shrink;
     238    free ((char *) reloc_vector);
     239    return true;
     240  }
     241  
     242  bfd_byte *
     243  bfd_coff_reloc16_get_relocated_section_contents
     244    (bfd *in_abfd,
     245     struct bfd_link_info *link_info,
     246     struct bfd_link_order *link_order,
     247     bfd_byte *data,
     248     bool relocatable,
     249     asymbol **symbols)
     250  {
     251    /* Get enough memory to hold the stuff.  */
     252    bfd *input_bfd = link_order->u.indirect.section->owner;
     253    asection *input_section = link_order->u.indirect.section;
     254    long reloc_size;
     255    arelent **reloc_vector;
     256    long reloc_count;
     257  
     258    reloc_size = bfd_get_reloc_upper_bound (input_bfd, input_section);
     259    if (reloc_size < 0)
     260      return NULL;
     261  
     262    /* If producing relocatable output, don't bother to relax.  */
     263    if (relocatable)
     264      return bfd_generic_get_relocated_section_contents (in_abfd, link_info,
     265  						       link_order,
     266  						       data, relocatable,
     267  						       symbols);
     268  
     269    /* Read in the section.  */
     270    bfd_byte *orig_data = data;
     271    if (!bfd_get_full_section_contents (input_bfd, input_section, &data))
     272      return NULL;
     273  
     274    if (data == NULL)
     275      return NULL;
     276  
     277    if (reloc_size == 0)
     278      return data;
     279  
     280    reloc_vector = (arelent **) bfd_malloc (reloc_size);
     281    if (reloc_vector == NULL)
     282      goto error_return;
     283  
     284    reloc_count = bfd_canonicalize_reloc (input_bfd,
     285  					input_section,
     286  					reloc_vector,
     287  					symbols);
     288    if (reloc_count < 0)
     289      goto error_return;
     290  
     291    if (reloc_count > 0)
     292      {
     293        arelent **parent = reloc_vector;
     294        arelent *reloc;
     295        size_t dst_address = 0;
     296        size_t src_address = 0;
     297        size_t run;
     298        size_t idx;
     299  
     300        /* Find how long a run we can do.  */
     301        while (dst_address < link_order->size)
     302  	{
     303  	  reloc = *parent;
     304  	  if (reloc)
     305  	    {
     306  	      /* Note that the relaxing didn't tie up the addresses in the
     307  		 relocation, so we use the original address to work out the
     308  		 run of non-relocated data.  */
     309  	      if (reloc->address > link_order->size
     310  		  || reloc->address < src_address)
     311  		{
     312  		  link_info->callbacks->einfo
     313  		    /* xgettext:c-format */
     314  		    (_("%X%P: %pB(%pA): relocation \"%pR\" goes out of range\n"),
     315  		     input_bfd, input_section, reloc);
     316  		  goto error_return;
     317  		}
     318  	      run = reloc->address - src_address;
     319  	      parent++;
     320  	    }
     321  	  else
     322  	    {
     323  	      run = link_order->size - dst_address;
     324  	    }
     325  
     326  	  /* Copy the bytes.  */
     327  	  for (idx = 0; idx < run; idx++)
     328  	    data[dst_address++] = data[src_address++];
     329  
     330  	  /* Now do the relocation.  */
     331  	  if (reloc
     332  	      && !bfd_coff_reloc16_extra_cases (input_bfd, link_info,
     333  						link_order, reloc, data,
     334  						&src_address, &dst_address))
     335  	    goto error_return;
     336  	}
     337      }
     338    free (reloc_vector);
     339    return data;
     340  
     341   error_return:
     342    free (reloc_vector);
     343    if (orig_data == NULL)
     344      free (data);
     345    return NULL;
     346  }