[PATCH v4] elf/x86-64: Subtract __ImageBase for R_AMD64_IMAGEBASE

Alan Modra amodra@gmail.com
Fri Mar 5 13:57:15 GMT 2021


On Thu, Mar 04, 2021 at 09:29:48PM -0800, H.J. Lu wrote:
> --- a/bfd/coff-x86_64.c
> +++ b/bfd/coff-x86_64.c
> @@ -122,13 +122,39 @@ coff_amd64_reloc (bfd *abfd,
>  #if defined (COFF_WITH_PE)
>    if (output_bfd == NULL)
>      {
> -      /* PC relative relocations are off by their size.  */
> -      if (reloc_entry->howto->pc_relative)
> -	diff -= bfd_get_reloc_size (reloc_entry->howto);
> +      if ((bfd_get_flavour (input_section->output_section->owner)
> +	   == bfd_target_elf_flavour)
> +	  && reloc_entry->howto->type == R_AMD64_IMAGEBASE)
> +	{
> +	  /* Subtract __ImageBase.  */
> +	  struct bfd_link_info *link_info;
> +	  struct bfd_link_hash_entry *h;
> +	  link_info
> +	    = _bfd_get_link_info (input_section->output_section->owner);
> +	  if (link_info == NULL)
> +	    abort ();
> +	  h = bfd_link_hash_lookup (link_info->hash, "__ImageBase",
> +				    FALSE, FALSE, FALSE);
> +	  if (h == NULL)
> +	    abort ();
> +	  while (h->type == bfd_link_hash_indirect)
> +	    h = h->u.i.link;
> +	  /* ELF symbols in relocatable files are section relative,
> +	     but in nonrelocatable files they are virtual addresses.  */
> +	  diff -= (h->u.def.value
> +		   + h->u.def.section->output_offset
> +		   + h->u.def.section->output_section->vma);
> +	}
> +      else
> +	{
> +	  /* PC relative relocations are off by their size.  */
> +	  if (reloc_entry->howto->pc_relative)
> +	    diff -= bfd_get_reloc_size (reloc_entry->howto);
>  
> -      if (reloc_entry->howto->type >= R_AMD64_PCRLONG_1
> -	  && reloc_entry->howto->type <= R_AMD64_PCRLONG_5)
> -	diff -= reloc_entry->howto->type - R_AMD64_PCRLONG;
> +	  if (reloc_entry->howto->type >= R_AMD64_PCRLONG_1
> +	      && reloc_entry->howto->type <= R_AMD64_PCRLONG_5)
> +	    diff -= reloc_entry->howto->type - R_AMD64_PCRLONG;
> +	}
>      }

I think this would be better style if the pc_relative and PCRLONG
adjustments were left as they were, and then write

    if (reloc_entry->howto->type == R_AMD64_IMAGEBASE)
      {
	bfd *obfd = input_section->output_section->owner;

	switch (bfd_get_flavour (obfd))
	  {
	  case bfd_target_coff_flavour:
	    diff -= pe_data (obfd)->pe_opthdr.ImageBase;
	    break;

	  case bfd_target_elf_flavour:
	    {
	      your new code
	    }
	    break;
	  }
      }

That should get rid of the FIXME.

>  
>    /* FIXME: How should this case be handled?  */


> diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
> index dd66d98883e..c2aaab0e5bf 100644
> --- a/bfd/elf-bfd.h
> +++ b/bfd/elf-bfd.h
> @@ -1887,6 +1887,9 @@ struct output_elf_obj_tdata
>    /* Used when laying out sections.  */
>    file_ptr next_file_pos;
>  
> +  /* Optional linker information.  */
> +  struct bfd_link_info *link_info;
> +

That's a nice trick that might be handy elsewhere, if you always set
it rather than making it optional.

>    int num_section_syms;
>    unsigned int shstrtab_section, strtab_section;
>  
> @@ -2064,6 +2067,7 @@ struct elf_obj_tdata
>  #define elf_elfsections(bfd)	(elf_tdata(bfd) -> elf_sect_ptr)
>  #define elf_numsections(bfd)	(elf_tdata(bfd) -> num_elf_sections)
>  #define elf_seg_map(bfd)	(elf_tdata(bfd) -> o->seg_map)
> +#define elf_link_info(bfd)	(elf_tdata(bfd) -> o->link_info)
>  #define elf_next_file_pos(bfd)	(elf_tdata(bfd) -> o->next_file_pos)
>  #define elf_eh_frame_hdr(bfd)	(elf_tdata(bfd) -> o->eh_frame_hdr)
>  #define elf_stack_flags(bfd)	(elf_tdata(bfd) -> o->stack_flags)
> diff --git a/bfd/elfxx-x86.c b/bfd/elfxx-x86.c
> index 93ad38c5eb0..e60d87ff981 100644
> --- a/bfd/elfxx-x86.c
> +++ b/bfd/elfxx-x86.c
> @@ -3174,6 +3174,8 @@ _bfd_elf_linker_x86_set_options (struct bfd_link_info * info,
>      = get_elf_backend_data (info->output_bfd);
>    struct elf_x86_link_hash_table *htab
>      = elf_x86_hash_table (info, bed->target_id);
> +  if (info->output_bfd->xvec->flavour == bfd_target_elf_flavour)
> +    elf_link_info (info->output_bfd) = info;

Move this to somewhere all ELF targets will have elf_link_info, a new
ldelf.c:elf_set_output_arch perhaps?

>    if (htab != NULL)
>      htab->params = params;
>  }
> diff --git a/bfd/libbfd-in.h b/bfd/libbfd-in.h
> index 2dc20ec1b19..62b1cee0af0 100644
> --- a/bfd/libbfd-in.h
> +++ b/bfd/libbfd-in.h
> @@ -899,6 +899,8 @@ extern bfd_vma _bfd_safe_read_leb128
>  extern bfd_byte * _bfd_write_unsigned_leb128
>    (bfd_byte *, bfd_byte *, bfd_vma) ATTRIBUTE_HIDDEN;
>  
> +extern struct bfd_link_info *_bfd_get_link_info (bfd *);
> +
>  #if GCC_VERSION >= 7000
>  #define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res)
>  #else
> diff --git a/bfd/libbfd.h b/bfd/libbfd.h
> index 7271a2ad5a1..3a481ea468f 100644
> --- a/bfd/libbfd.h
> +++ b/bfd/libbfd.h
> @@ -904,6 +904,8 @@ extern bfd_vma _bfd_safe_read_leb128
>  extern bfd_byte * _bfd_write_unsigned_leb128
>    (bfd_byte *, bfd_byte *, bfd_vma) ATTRIBUTE_HIDDEN;
>  
> +extern struct bfd_link_info *_bfd_get_link_info (bfd *);
> +
>  #if GCC_VERSION >= 7000
>  #define _bfd_mul_overflow(a, b, res) __builtin_mul_overflow (a, b, res)
>  #else
> diff --git a/ld/ldelf.c b/ld/ldelf.c
> index 049992544a2..c7de958dbdc 100644
> --- a/ld/ldelf.c
> +++ b/ld/ldelf.c
> @@ -81,6 +81,30 @@ ldelf_load_symbols (lang_input_statement_type *entry)
>  {
>    int link_class = 0;
>  
> +  if (bfd_link_pde (&link_info)
> +      && entry->the_bfd->xvec->flavour == bfd_target_coff_flavour
> +      && strcmp (entry->the_bfd->xvec->name, "pe-x86-64") == 0
> +      && strcmp (link_info.output_bfd->xvec->name, "elf64-x86-64") == 0)
> +    {
> +      /* NB: When linking Windows x86-64 relocatable object files to
> +	 generate ELF executable, create an indirect reference to
> +	 __executable_start for __ImageBase to support R_AMD64_IMAGEBASE
> +	 relocation which is relative to __ImageBase.  */
> +      struct elf_link_hash_table *htab = elf_hash_table (&link_info);
> +      struct elf_link_hash_entry *h, *hi;
> +      hi = elf_link_hash_lookup (htab, "__ImageBase", TRUE, FALSE,
> +				 FALSE);
> +      if (hi->root.type == bfd_link_hash_new
> +	  || hi->type == bfd_link_hash_undefined
> +	  || hi->type == bfd_link_hash_undefweak)
> +	{
> +	  h = elf_link_hash_lookup (htab, "__executable_start",
> +				    TRUE, FALSE, TRUE);
> +	  hi->root.type = bfd_link_hash_indirect;
> +	  hi->root.u.i.link = (struct bfd_link_hash_entry *) h;
> +	}
> +    }
> +
>    /* Tell the ELF linker that we don't want the output file to have a
>       DT_NEEDED entry for this file, unless it is used to resolve
>       references in a regular object.  */

This doesn't look like the right place to set up a reference to
__executable_start.  Doing so in a new check_relocs in pei-x86_64.c
that tests for an ELF output would be better.  I'm not suggesting that
you run over relocs looking for R_AMD64_IMAGEBASE, that could be done
but seems unnecessary.

-- 
Alan Modra
Australia Development Lab, IBM


More information about the Binutils mailing list