Radix cross Linux

The main Radix cross Linux repository contains the build scripts of packages, which have the most complete and common functionality for desktop machines

452 Commits   2 Branches   1 Tag
Index: create.patch.sh
===================================================================
--- create.patch.sh	(nonexistent)
+++ create.patch.sh	(revision 216)
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+VERSION=2.06
+
+tar --files-from=file.list -xJvf ../grub-$VERSION.tar.xz
+mv grub-$VERSION grub-$VERSION-orig
+
+cp -rf ./grub-$VERSION-new ./grub-$VERSION
+
+diff --unified -Nr  grub-$VERSION-orig  grub-$VERSION > grub-$VERSION-riscv-relocation.patch
+
+mv grub-$VERSION-riscv-relocation.patch ../patches
+
+rm -rf ./grub-$VERSION
+rm -rf ./grub-$VERSION-orig

Property changes on: create.patch.sh
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: file.list
===================================================================
--- file.list	(nonexistent)
+++ file.list	(revision 216)
@@ -0,0 +1,2 @@
+grub-2.06/grub-core/kern/riscv/dl.c
+grub-2.06/util/grub-mkimagexx.c
Index: grub-2.06-new/grub-core/kern/riscv/dl.c
===================================================================
--- grub-2.06-new/grub-core/kern/riscv/dl.c	(nonexistent)
+++ grub-2.06-new/grub-core/kern/riscv/dl.c	(revision 216)
@@ -0,0 +1,346 @@
+/* dl.c - arch-dependent part of loadable module support */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2018  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/elf.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/i18n.h>
+
+/*
+ * Instructions and instruction encoding are documented in the RISC-V
+ * specification. This file is based on version 2.2:
+ *
+ * https://github.com/riscv/riscv-isa-manual/blob/master/release/riscv-spec-v2.2.pdf
+ */
+#define LDR 0x58000050
+#define BR 0xd61f0200
+
+/*
+ * Check if EHDR is a valid ELF header.
+ */
+grub_err_t
+grub_arch_dl_check_header (void *ehdr)
+{
+  Elf_Ehdr *e = ehdr;
+
+  /* Check the magic numbers.  */
+  if (e->e_ident[EI_DATA] != ELFDATA2LSB || e->e_machine != EM_RISCV)
+    return grub_error (GRUB_ERR_BAD_OS,
+		       N_("invalid arch-dependent ELF magic"));
+
+  return GRUB_ERR_NONE;
+}
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+/* Relocate symbols. */
+grub_err_t
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
+			       Elf_Shdr *s, grub_dl_segment_t seg)
+{
+  Elf_Rel *rel, *max;
+
+  for (rel = (Elf_Rel *) ((char *) ehdr + s->sh_offset),
+	 max = (Elf_Rel *) ((char *) rel + s->sh_size);
+       rel < max;
+       rel = (Elf_Rel *) ((char *) rel + s->sh_entsize))
+    {
+      Elf_Sym *sym;
+      void *place;
+      grub_size_t sym_addr;
+
+      if (rel->r_offset >= seg->size)
+	return grub_error (GRUB_ERR_BAD_MODULE,
+			   "reloc offset is out of the segment");
+
+      sym = (Elf_Sym *) ((char *) mod->symtab
+			 + mod->symsize * ELF_R_SYM (rel->r_info));
+
+      sym_addr = sym->st_value;
+      if (s->sh_type == SHT_RELA)
+	sym_addr += ((Elf_Rela *) rel)->r_addend;
+
+      place = (void *) ((grub_addr_t) seg->addr + rel->r_offset);
+
+      switch (ELF_R_TYPE (rel->r_info))
+	{
+	case R_RISCV_32:
+	  {
+	    grub_uint32_t *abs_place = place;
+
+	    grub_dprintf ("dl", "  reloc_abs32 %p => 0x%016llx\n",
+			  place, (unsigned long long) sym_addr);
+
+	    *abs_place = (grub_uint32_t) sym_addr;
+	  }
+	  break;
+	case R_RISCV_64:
+	  {
+	    grub_size_t *abs_place = place;
+
+	    grub_dprintf ("dl", "  reloc_abs64 %p => 0x%016llx\n",
+			  place, (unsigned long long) sym_addr);
+
+	    *abs_place = (grub_size_t) sym_addr;
+	  }
+	  break;
+
+	case R_RISCV_ADD8:
+	  {
+	    grub_uint8_t *abs_place = place;
+
+	    *abs_place += (grub_uint8_t) sym_addr;
+	  }
+	  break;
+	case R_RISCV_ADD16:
+	  {
+	    grub_uint16_t *abs_place = place;
+
+	    *abs_place += (grub_uint16_t) sym_addr;
+	  }
+	  break;
+	case R_RISCV_ADD32:
+	  {
+	    grub_uint32_t *abs_place = place;
+
+	    *abs_place += (grub_uint32_t) sym_addr;
+	  }
+	  break;
+	case R_RISCV_ADD64:
+	  {
+	    grub_size_t *abs_place = place;
+
+	    *abs_place += (grub_size_t) sym_addr;
+	  }
+	  break;
+
+	case R_RISCV_SUB8:
+	  {
+	    grub_uint8_t *abs_place = place;
+
+	    *abs_place -= (grub_uint8_t) sym_addr;
+	  }
+	  break;
+	case R_RISCV_SUB16:
+	  {
+	    grub_uint16_t *abs_place = place;
+
+	    *abs_place -= (grub_uint16_t) sym_addr;
+	  }
+	  break;
+	case R_RISCV_SUB32:
+	  {
+	    grub_uint32_t *abs_place = place;
+
+	    *abs_place -= (grub_uint32_t) sym_addr;
+	  }
+	  break;
+	case R_RISCV_SUB64:
+	  {
+	    grub_size_t *abs_place = place;
+
+	    *abs_place -= (grub_size_t) sym_addr;
+	  }
+	  break;
+
+	case R_RISCV_BRANCH:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_ssize_t off = sym_addr - (grub_addr_t) place;
+	    grub_uint32_t imm12 = (off & 0x1000) << (31 - 12);
+	    grub_uint32_t imm11 = (off & 0x800) >> (11 - 7);
+	    grub_uint32_t imm10_5 = (off & 0x7e0) << (30 - 10);
+	    grub_uint32_t imm4_1 = (off & 0x1e) << (11 - 4);
+	    *abs_place = (*abs_place & 0x1fff07f)
+			 | imm12 | imm11 | imm10_5 | imm4_1;
+	  }
+	  break;
+
+	case R_RISCV_JAL:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_ssize_t off = sym_addr - (grub_addr_t) place;
+	    grub_uint32_t imm20 = (off & 0x100000) << (31 - 20);
+	    grub_uint32_t imm19_12 = (off & 0xff000);
+	    grub_uint32_t imm11 = (off & 0x800) << (20 - 11);
+	    grub_uint32_t imm10_1 = (off & 0x7fe) << (30 - 10);
+	    *abs_place = (*abs_place & 0xfff)
+			 | imm20 | imm19_12 | imm11 | imm10_1;
+	  }
+	  break;
+
+	case R_RISCV_CALL:
+	case R_RISCV_CALL_PLT:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_ssize_t off = sym_addr - (grub_addr_t) place;
+	    grub_uint32_t hi20, lo12;
+
+	    if (off != (grub_int32_t) off)
+	      return grub_error (GRUB_ERR_BAD_MODULE, "relocation overflow");
+
+	    hi20 = (off + 0x800) & 0xfffff000;
+	    lo12 = (off - hi20) & 0xfff;
+	    abs_place[0] = (abs_place[0] & 0xfff) | hi20;
+	    abs_place[1] = (abs_place[1] & 0xfffff) | (lo12 << 20);
+	  }
+	  break;
+
+	case R_RISCV_RVC_BRANCH:
+	  {
+	    grub_uint16_t *abs_place = place;
+	    grub_ssize_t off = sym_addr - (grub_addr_t) place;
+	    grub_uint16_t imm8 = (off & 0x100) << (12 - 8);
+	    grub_uint16_t imm7_6 = (off & 0xc0) >> (6 - 5);
+	    grub_uint16_t imm5 = (off & 0x20) >> (5 - 2);
+	    grub_uint16_t imm4_3 = (off & 0x18) << (12 - 5);
+	    grub_uint16_t imm2_1 = (off & 0x6) << (12 - 10);
+	    *abs_place = (*abs_place & 0xe383)
+			 | imm8 | imm7_6 | imm5 | imm4_3 | imm2_1;
+	  }
+	  break;
+
+	case R_RISCV_RVC_JUMP:
+	  {
+	    grub_uint16_t *abs_place = place;
+	    grub_ssize_t off = sym_addr - (grub_addr_t) place;
+	    grub_uint16_t imm11 = (off & 0x800) << (12 - 11);
+	    grub_uint16_t imm10 = (off & 0x400) >> (10 - 8);
+	    grub_uint16_t imm9_8 = (off & 0x300) << (12 - 11);
+	    grub_uint16_t imm7 = (off & 0x80) >> (7 - 6);
+	    grub_uint16_t imm6 = (off & 0x40) << (12 - 11);
+	    grub_uint16_t imm5 = (off & 0x20) >> (5 - 2);
+	    grub_uint16_t imm4 = (off & 0x10) << (12 - 5);
+	    grub_uint16_t imm3_1 = (off & 0xe) << (12 - 10);
+	    *abs_place = ((*abs_place & 0xe003)
+			  | imm11 | imm10 | imm9_8 | imm7 | imm6
+			  | imm5 | imm4 | imm3_1);
+	  }
+	  break;
+
+	case R_RISCV_PCREL_HI20:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_ssize_t off = sym_addr - (grub_addr_t) place;
+	    grub_int32_t hi20;
+
+	    if (off != (grub_int32_t)off)
+	      return grub_error (GRUB_ERR_BAD_MODULE, "relocation overflow");
+
+	    hi20 = (off + 0x800) & 0xfffff000;
+	    *abs_place = (*abs_place & 0xfff) | hi20;
+	  }
+	break;
+
+	case R_RISCV_PCREL_LO12_I:
+	case R_RISCV_PCREL_LO12_S:
+	  {
+	    grub_uint32_t *t32 = place;
+	    Elf_Rela *rel2;
+	    /* Search backwards for matching HI20 reloc.  */
+	    for (rel2 = (Elf_Rela *) ((char *) rel - s->sh_entsize);
+		    (unsigned long)rel2 >= ((unsigned long)ehdr + s->sh_offset);
+		    rel2 = (Elf_Rela *) ((char *) rel2 - s->sh_entsize))
+	      {
+		Elf_Addr rel2_info;
+		Elf_Addr rel2_offset;
+		Elf_Addr rel2_sym_addr;
+		Elf_Addr rel2_loc;
+		grub_ssize_t rel2_off;
+		grub_ssize_t off;
+		Elf_Sym *sym2;
+
+		rel2_offset = rel2->r_offset;
+		rel2_info = rel2->r_info;
+		rel2_loc = (grub_addr_t) seg->addr + rel2_offset;
+
+		if (ELF_R_TYPE (rel2_info) == R_RISCV_PCREL_HI20
+		    && rel2_loc == sym_addr)
+		  {
+		    sym2 = (Elf_Sym *) ((char *) mod->symtab
+				+ mod->symsize * ELF_R_SYM (rel2->r_info));
+		    rel2_sym_addr = sym2->st_value;
+		    if (s->sh_type == SHT_RELA)
+		      rel2_sym_addr += ((Elf_Rela *) rel2)->r_addend;
+
+		    rel2_off = rel2_sym_addr - rel2_loc;
+		    off = rel2_off - ((rel2_off + 0x800) & 0xfffff000);
+
+		    if (ELF_R_TYPE (rel->r_info) == R_RISCV_PCREL_LO12_I)
+		      *t32 = (*t32 & 0xfffff) | (off & 0xfff) << 20;
+		    else
+		      {
+			grub_uint32_t imm11_5 = (off & 0xfe0) << (31 - 11);
+			grub_uint32_t imm4_0 = (off & 0x1f) << (11 - 4);
+			*t32 = (*t32 & 0x1fff07f) | imm11_5 | imm4_0;
+		      }
+		    break;
+		  }
+	      }
+	    if ((unsigned long)rel2 < ((unsigned long)ehdr + s->sh_offset))
+	      return grub_error (GRUB_ERR_BAD_MODULE, "cannot find matching HI20 relocation");
+	  }
+	  break;
+
+	case R_RISCV_HI20:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    *abs_place = (*abs_place & 0xfff) |
+			 (((grub_int32_t) sym_addr + 0x800) & 0xfffff000);
+	  }
+	  break;
+
+	case R_RISCV_LO12_I:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_int32_t lo12 = (grub_int32_t) sym_addr -
+				(((grub_int32_t) sym_addr + 0x800) & 0xfffff000);
+	    *abs_place = (*abs_place & 0xfffff) | ((lo12 & 0xfff) << 20);
+	  }
+	  break;
+
+	case R_RISCV_LO12_S:
+	  {
+	    grub_uint32_t *abs_place = place;
+	    grub_int32_t lo12 = (grub_int32_t) sym_addr -
+				(((grub_int32_t) sym_addr + 0x800) & 0xfffff000);
+	    grub_uint32_t imm11_5 = (lo12 & 0xfe0) << (31 - 11);
+	    grub_uint32_t imm4_0 = (lo12 & 0x1f) << (11 - 4);
+	    *abs_place = (*abs_place & 0x1fff07f) | imm11_5 | imm4_0;
+	  }
+	  break;
+
+	case R_RISCV_RELAX:
+	  break;
+	default:
+	  {
+	    char rel_info[17]; /* log16(2^64) = 16, plus NUL. */
+
+	    grub_snprintf (rel_info, sizeof (rel_info) - 1, "%" PRIxGRUB_UINT64_T,
+			   (grub_uint64_t) ELF_R_TYPE (rel->r_info));
+	    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+			       N_("relocation 0x%s is not implemented yet"), rel_info);
+	  }
+	}
+    }
+
+  return GRUB_ERR_NONE;
+}
Index: grub-2.06-new/util/grub-mkimagexx.c
===================================================================
--- grub-2.06-new/util/grub-mkimagexx.c	(nonexistent)
+++ grub-2.06-new/util/grub-mkimagexx.c	(revision 216)
@@ -0,0 +1,2454 @@
+/* grub-mkimage.c - make a bootable image */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <grub/types.h>
+#include <grub/elf.h>
+#include <grub/aout.h>
+#include <grub/i18n.h>
+#include <grub/kernel.h>
+#include <grub/disk.h>
+#include <grub/emu/misc.h>
+#include <grub/util/misc.h>
+#include <grub/util/resolve.h>
+#include <grub/misc.h>
+#include <grub/offsets.h>
+#include <grub/crypto.h>
+#include <grub/dl.h>
+#include <time.h>
+#include <multiboot.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <grub/efi/pe32.h>
+#include <grub/uboot/image.h>
+#include <grub/arm/reloc.h>
+#include <grub/arm64/reloc.h>
+#include <grub/ia64/reloc.h>
+#include <grub/osdep/hostfile.h>
+#include <grub/util/install.h>
+#include <grub/util/mkimage.h>
+
+#include <xen/elfnote.h>
+
+#pragma GCC diagnostic ignored "-Wcast-align"
+
+#define GRUB_MKIMAGEXX
+#if !defined(MKIMAGE_ELF32) && !defined(MKIMAGE_ELF64)
+#if __SIZEOF_POINTER__ == 8
+#include "grub-mkimage64.c"
+#else
+#include "grub-mkimage32.c"
+#endif
+#endif
+
+/* These structures are defined according to the CHRP binding to IEEE1275,
+   "Client Program Format" section.  */
+
+struct grub_ieee1275_note_desc
+{
+  grub_uint32_t real_mode;
+  grub_uint32_t real_base;
+  grub_uint32_t real_size;
+  grub_uint32_t virt_base;
+  grub_uint32_t virt_size;
+  grub_uint32_t load_base;
+};
+
+#define GRUB_IEEE1275_NOTE_NAME "PowerPC"
+#define GRUB_IEEE1275_NOTE_TYPE 0x1275
+
+struct grub_ieee1275_note
+{
+  Elf32_Nhdr header;
+  char name[ALIGN_UP(sizeof (GRUB_IEEE1275_NOTE_NAME), 4)];
+  struct grub_ieee1275_note_desc descriptor;
+};
+
+#define GRUB_XEN_NOTE_NAME "Xen"
+
+struct fixup_block_list
+{
+  struct fixup_block_list *next;
+  int state;
+  struct grub_pe32_fixup_block b;
+};
+
+#define ALIGN_ADDR(x) (ALIGN_UP((x), image_target->voidp_sizeof))
+
+struct section_metadata
+{
+  Elf_Half num_sections;
+  Elf_Shdr *sections;
+  Elf_Addr *addrs;
+  Elf_Addr *vaddrs;
+  Elf_Half section_entsize;
+  Elf_Shdr *symtab;
+  const char *strtab;
+};
+
+static int
+is_relocatable (const struct grub_install_image_target_desc *image_target)
+{
+  return image_target->id == IMAGE_EFI || image_target->id == IMAGE_UBOOT
+    || (image_target->id == IMAGE_COREBOOT && image_target->elf_target == EM_ARM);
+}
+
+#ifdef MKIMAGE_ELF32
+
+/*
+ * R_ARM_THM_CALL/THM_JUMP24
+ *
+ * Relocate Thumb (T32) instruction set relative branches:
+ *   B.W, BL and BLX
+ */
+static grub_err_t
+grub_arm_reloc_thm_call (grub_uint16_t *target, Elf32_Addr sym_addr)
+{
+  grub_int32_t offset;
+
+  offset = grub_arm_thm_call_get_offset (target);
+
+  grub_dprintf ("dl", "    sym_addr = 0x%08x", sym_addr);
+
+  offset += sym_addr;
+
+  grub_dprintf("dl", " BL*: target=%p, sym_addr=0x%08x, offset=%d\n",
+	       target, sym_addr, offset);
+
+  /* Keep traditional (pre-Thumb2) limits on blx. In any case if the kernel
+     is bigger than 2M  (currently under 150K) then we probably have a problem
+     somewhere else.  */
+  if (offset < -0x200000 || offset >= 0x200000)
+    return grub_error (GRUB_ERR_BAD_MODULE,
+		       "THM_CALL Relocation out of range.");
+
+  grub_dprintf ("dl", "    relative destination = %p",
+		(char *) target + offset);
+
+  return grub_arm_thm_call_set_offset (target, offset);
+}
+
+/*
+ * R_ARM_THM_JUMP19
+ *
+ * Relocate conditional Thumb (T32) B<c>.W
+ */
+static grub_err_t
+grub_arm_reloc_thm_jump19 (grub_uint16_t *target, Elf32_Addr sym_addr)
+{
+  grub_int32_t offset;
+
+  if (!(sym_addr & 1))
+    return grub_error (GRUB_ERR_BAD_MODULE,
+		       "Relocation targeting wrong execution state");
+
+  offset = grub_arm_thm_jump19_get_offset (target);
+
+  /* Adjust and re-truncate offset */
+  offset += sym_addr;
+
+  if (!grub_arm_thm_jump19_check_offset (offset))
+    return grub_error (GRUB_ERR_BAD_MODULE,
+		       "THM_JUMP19 Relocation out of range.");
+
+  grub_arm_thm_jump19_set_offset (target, offset);
+
+  return GRUB_ERR_NONE;
+}
+
+/*
+ * R_ARM_JUMP24
+ *
+ * Relocate ARM (A32) B
+ */
+static grub_err_t
+grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
+{
+  grub_int32_t offset;
+
+  if (sym_addr & 1)
+    return grub_error (GRUB_ERR_BAD_MODULE,
+		       "Relocation targeting wrong execution state");
+
+  offset = grub_arm_jump24_get_offset (target);
+  offset += sym_addr;
+
+  if (!grub_arm_jump24_check_offset (offset))
+    return grub_error (GRUB_ERR_BAD_MODULE,
+		       "JUMP24 Relocation out of range.");
+
+
+  grub_arm_jump24_set_offset (target, offset);
+
+  return GRUB_ERR_NONE;
+}
+
+#endif
+
+void
+SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target,
+				    int note, char **core_img, size_t *core_size,
+				    Elf_Addr target_addr,
+				    struct grub_mkimage_layout *layout)
+{
+  char *elf_img;
+  size_t program_size;
+  Elf_Ehdr *ehdr;
+  Elf_Phdr *phdr;
+  Elf_Shdr *shdr;
+  int header_size, footer_size = 0;
+  int phnum = 1;
+  int shnum = 4;
+  int string_size = sizeof (".text") + sizeof ("mods") + 1;
+
+  if (image_target->id != IMAGE_LOONGSON_ELF)
+    phnum += 2;
+
+  if (note)
+    {
+      phnum++;
+      footer_size += sizeof (struct grub_ieee1275_note);
+    }
+  if (image_target->id == IMAGE_XEN || image_target->id == IMAGE_XEN_PVH)
+    {
+      phnum++;
+      shnum++;
+      string_size += sizeof (".xen");
+      footer_size += (image_target->id == IMAGE_XEN) ? XEN_NOTE_SIZE : XEN_PVH_NOTE_SIZE;
+    }
+  header_size = ALIGN_UP (sizeof (*ehdr) + phnum * sizeof (*phdr)
+			  + shnum * sizeof (*shdr) + string_size, layout->align);
+
+  program_size = ALIGN_ADDR (*core_size);
+
+  elf_img = xmalloc (program_size + header_size + footer_size);
+  memset (elf_img, 0, program_size + header_size + footer_size);
+  memcpy (elf_img  + header_size, *core_img, *core_size);
+  ehdr = (void *) elf_img;
+  phdr = (void *) (elf_img + sizeof (*ehdr));
+  shdr = (void *) (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr));
+  memcpy (ehdr->e_ident, ELFMAG, SELFMAG);
+  ehdr->e_ident[EI_CLASS] = ELFCLASSXX;
+  if (!image_target->bigendian)
+    ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+  else
+    ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
+  ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+  ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
+  ehdr->e_type = grub_host_to_target16 (ET_EXEC);
+  ehdr->e_machine = grub_host_to_target16 (image_target->elf_target);
+  ehdr->e_version = grub_host_to_target32 (EV_CURRENT);
+
+  ehdr->e_phoff = grub_host_to_target32 ((char *) phdr - (char *) ehdr);
+  ehdr->e_phentsize = grub_host_to_target16 (sizeof (*phdr));
+  ehdr->e_phnum = grub_host_to_target16 (phnum);
+
+  ehdr->e_shoff = grub_host_to_target32 ((grub_uint8_t *) shdr
+					 - (grub_uint8_t *) ehdr);
+  if (image_target->id == IMAGE_LOONGSON_ELF)
+    ehdr->e_shentsize = grub_host_to_target16 (0);
+  else
+    ehdr->e_shentsize = grub_host_to_target16 (sizeof (Elf_Shdr));
+  ehdr->e_shnum = grub_host_to_target16 (shnum);
+  ehdr->e_shstrndx = grub_host_to_target16 (1);
+
+  ehdr->e_ehsize = grub_host_to_target16 (sizeof (*ehdr));
+
+  phdr->p_type = grub_host_to_target32 (PT_LOAD);
+  phdr->p_offset = grub_host_to_target32 (header_size);
+  phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X);
+
+  ehdr->e_entry = grub_host_to_target32 (target_addr);
+  phdr->p_vaddr = grub_host_to_target32 (target_addr);
+  phdr->p_paddr = grub_host_to_target32 (target_addr);
+  phdr->p_align = grub_host_to_target32 (layout->align > image_target->link_align ?
+					 layout->align : image_target->link_align);
+  if (image_target->id == IMAGE_LOONGSON_ELF)
+    ehdr->e_flags = grub_host_to_target32 (0x1000 | EF_MIPS_NOREORDER 
+					   | EF_MIPS_PIC | EF_MIPS_CPIC);
+  else
+    ehdr->e_flags = 0;
+  if (image_target->id == IMAGE_LOONGSON_ELF)
+    {
+      phdr->p_filesz = grub_host_to_target32 (*core_size);
+      phdr->p_memsz = grub_host_to_target32 (*core_size);
+    }
+  else
+    {
+      grub_uint32_t target_addr_mods;
+      phdr->p_filesz = grub_host_to_target32 (layout->kernel_size);
+      if (image_target->id == IMAGE_COREBOOT && image_target->elf_target == EM_ARM)
+	phdr->p_memsz = grub_host_to_target32 (layout->kernel_size);
+      else
+	phdr->p_memsz = grub_host_to_target32 (layout->kernel_size + layout->bss_size);
+
+      phdr++;
+      phdr->p_type = grub_host_to_target32 (PT_GNU_STACK);
+      phdr->p_offset = grub_host_to_target32 (header_size + layout->kernel_size);
+      phdr->p_paddr = phdr->p_vaddr = phdr->p_filesz = phdr->p_memsz = 0;
+      phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X);
+      phdr->p_align = grub_host_to_target32 (image_target->link_align);
+
+      phdr++;
+      phdr->p_type = grub_host_to_target32 (PT_LOAD);
+      phdr->p_offset = grub_host_to_target32 (header_size + layout->kernel_size);
+      phdr->p_flags = grub_host_to_target32 (PF_R | PF_W | PF_X);
+      phdr->p_filesz = phdr->p_memsz
+	= grub_host_to_target32 (*core_size - layout->kernel_size);
+
+      if (image_target->id == IMAGE_COREBOOT && image_target->elf_target == EM_386)
+	target_addr_mods = GRUB_KERNEL_I386_COREBOOT_MODULES_ADDR;
+      else if (image_target->id == IMAGE_COREBOOT && image_target->elf_target == EM_ARM)
+	target_addr_mods = ALIGN_UP (target_addr + layout->end
+				     + image_target->mod_gap,
+				     image_target->mod_align);
+      else
+	target_addr_mods = ALIGN_UP (target_addr + layout->kernel_size + layout->bss_size
+				     + image_target->mod_gap,
+				     image_target->mod_align);
+      phdr->p_vaddr = grub_host_to_target_addr (target_addr_mods);
+      phdr->p_paddr = grub_host_to_target_addr (target_addr_mods);
+      phdr->p_align = grub_host_to_target32 (image_target->link_align);
+    }
+
+  if (image_target->id == IMAGE_XEN)
+    {
+      char *note_start = (elf_img + program_size + header_size);
+      Elf_Nhdr *note_ptr;
+      char *ptr = (char *) note_start;
+
+      grub_util_info ("adding XEN NOTE segment");
+
+      /* Guest OS.  */
+      note_ptr = (Elf_Nhdr *) ptr;
+      note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME));
+      note_ptr->n_descsz = grub_host_to_target32 (sizeof (PACKAGE_NAME));
+      note_ptr->n_type = grub_host_to_target32 (XEN_ELFNOTE_GUEST_OS);
+      ptr += sizeof (Elf_Nhdr);
+      memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME));
+      ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4);
+      memcpy (ptr, PACKAGE_NAME, sizeof (PACKAGE_NAME));
+      ptr += ALIGN_UP (sizeof (PACKAGE_NAME), 4);
+
+      /* Loader.  */
+      note_ptr = (Elf_Nhdr *) ptr;
+      note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME));
+      note_ptr->n_descsz = grub_host_to_target32 (sizeof ("generic"));
+      note_ptr->n_type = grub_host_to_target32 (XEN_ELFNOTE_LOADER);
+      ptr += sizeof (Elf_Nhdr);
+      memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME));
+      ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4);
+      memcpy (ptr, "generic", sizeof ("generic"));
+      ptr += ALIGN_UP (sizeof ("generic"), 4);
+
+      /* Version.  */
+      note_ptr = (Elf_Nhdr *) ptr;
+      note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME));
+      note_ptr->n_descsz = grub_host_to_target32 (sizeof ("xen-3.0"));
+      note_ptr->n_type = grub_host_to_target32 (XEN_ELFNOTE_XEN_VERSION);
+      ptr += sizeof (Elf_Nhdr);
+      memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME));
+      ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4);
+      memcpy (ptr, "xen-3.0", sizeof ("xen-3.0"));
+      ptr += ALIGN_UP (sizeof ("xen-3.0"), 4);
+
+      /* Entry.  */
+      note_ptr = (Elf_Nhdr *) ptr;
+      note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME));
+      note_ptr->n_descsz = grub_host_to_target32 (image_target->voidp_sizeof);
+      note_ptr->n_type = grub_host_to_target32 (XEN_ELFNOTE_ENTRY);
+      ptr += sizeof (Elf_Nhdr);
+      memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME));
+      ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4);
+      memset (ptr, 0, image_target->voidp_sizeof);
+      ptr += image_target->voidp_sizeof;
+
+      /* Virt base.  */
+      note_ptr = (Elf_Nhdr *) ptr;
+      note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME));
+      note_ptr->n_descsz = grub_host_to_target32 (image_target->voidp_sizeof);
+      note_ptr->n_type = grub_host_to_target32 (XEN_ELFNOTE_VIRT_BASE);
+      ptr += sizeof (Elf_Nhdr);
+      memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME));
+      ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4);
+      memset (ptr, 0, image_target->voidp_sizeof);
+      ptr += image_target->voidp_sizeof;
+
+      /* PAE.  */
+      if (image_target->elf_target == EM_386)
+	{
+	  note_ptr = (Elf_Nhdr *) ptr;
+	  note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME));
+	  note_ptr->n_descsz = grub_host_to_target32 (sizeof ("yes,bimodal"));
+	  note_ptr->n_type = grub_host_to_target32 (XEN_ELFNOTE_PAE_MODE);
+	  ptr += sizeof (Elf_Nhdr);
+	  memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME));
+	  ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4);
+	  memcpy (ptr, "yes", sizeof ("yes"));
+	  ptr += ALIGN_UP (sizeof ("yes"), 4);
+	}
+
+      assert (XEN_NOTE_SIZE == (ptr - note_start));
+
+      phdr++;
+      phdr->p_type = grub_host_to_target32 (PT_NOTE);
+      phdr->p_flags = grub_host_to_target32 (PF_R);
+      phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof);
+      phdr->p_vaddr = 0;
+      phdr->p_paddr = 0;
+      phdr->p_filesz = grub_host_to_target32 (XEN_NOTE_SIZE);
+      phdr->p_memsz = 0;
+      phdr->p_offset = grub_host_to_target32 (header_size + program_size);
+    }
+
+  if (image_target->id == IMAGE_XEN_PVH)
+    {
+      char *note_start = (elf_img + program_size + header_size);
+      Elf_Nhdr *note_ptr;
+      char *ptr = (char *) note_start;
+
+      grub_util_info ("adding XEN NOTE segment");
+
+      /* Phys32 Entry.  */
+      note_ptr = (Elf_Nhdr *) ptr;
+      note_ptr->n_namesz = grub_host_to_target32 (sizeof (GRUB_XEN_NOTE_NAME));
+      note_ptr->n_descsz = grub_host_to_target32 (image_target->voidp_sizeof);
+      note_ptr->n_type = grub_host_to_target32 (XEN_ELFNOTE_PHYS32_ENTRY);
+      ptr += sizeof (Elf_Nhdr);
+      memcpy (ptr, GRUB_XEN_NOTE_NAME, sizeof (GRUB_XEN_NOTE_NAME));
+      ptr += ALIGN_UP (sizeof (GRUB_XEN_NOTE_NAME), 4);
+      memset (ptr, 0, image_target->voidp_sizeof);
+      *(grub_uint32_t *) ptr = GRUB_KERNEL_I386_XEN_PVH_LINK_ADDR;
+      ptr += image_target->voidp_sizeof;
+
+      assert (XEN_PVH_NOTE_SIZE == (ptr - note_start));
+
+      phdr++;
+      phdr->p_type = grub_host_to_target32 (PT_NOTE);
+      phdr->p_flags = grub_host_to_target32 (PF_R);
+      phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof);
+      phdr->p_vaddr = 0;
+      phdr->p_paddr = 0;
+      phdr->p_filesz = grub_host_to_target32 (XEN_PVH_NOTE_SIZE);
+      phdr->p_memsz = 0;
+      phdr->p_offset = grub_host_to_target32 (header_size + program_size);
+    }
+
+  if (note)
+    {
+      int note_size = sizeof (struct grub_ieee1275_note);
+      struct grub_ieee1275_note *note_ptr = (struct grub_ieee1275_note *) 
+	(elf_img + program_size + header_size);
+
+      grub_util_info ("adding CHRP NOTE segment");
+
+      note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_IEEE1275_NOTE_NAME));
+      note_ptr->header.n_descsz = grub_host_to_target32 (sizeof (struct grub_ieee1275_note_desc));
+      note_ptr->header.n_type = grub_host_to_target32 (GRUB_IEEE1275_NOTE_TYPE);
+      strcpy (note_ptr->name, GRUB_IEEE1275_NOTE_NAME);
+      note_ptr->descriptor.real_mode = grub_host_to_target32 (0xffffffff);
+      note_ptr->descriptor.real_base = grub_host_to_target32 (0x00c00000);
+      note_ptr->descriptor.real_size = grub_host_to_target32 (0xffffffff);
+      note_ptr->descriptor.virt_base = grub_host_to_target32 (0xffffffff);
+      note_ptr->descriptor.virt_size = grub_host_to_target32 (0xffffffff);
+      note_ptr->descriptor.load_base = grub_host_to_target32 (0x00004000);
+
+      phdr++;
+      phdr->p_type = grub_host_to_target32 (PT_NOTE);
+      phdr->p_flags = grub_host_to_target32 (PF_R);
+      phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof);
+      phdr->p_vaddr = 0;
+      phdr->p_paddr = 0;
+      phdr->p_filesz = grub_host_to_target32 (note_size);
+      phdr->p_memsz = 0;
+      phdr->p_offset = grub_host_to_target32 (header_size + program_size);
+    }
+
+  {
+    char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)
+		       + shnum * sizeof (*shdr));
+    char *ptr = str_start + 1;
+
+    shdr++;
+
+    shdr->sh_name = grub_host_to_target32 (0);
+    shdr->sh_type = grub_host_to_target32 (SHT_STRTAB);
+    shdr->sh_addr = grub_host_to_target_addr (0);
+    shdr->sh_offset = grub_host_to_target_addr (str_start - elf_img);
+    shdr->sh_size = grub_host_to_target32 (string_size);
+    shdr->sh_link = grub_host_to_target32 (0);
+    shdr->sh_info = grub_host_to_target32 (0);
+    shdr->sh_addralign = grub_host_to_target32 (layout->align);
+    shdr->sh_entsize = grub_host_to_target32 (0);
+    shdr++;
+
+    memcpy (ptr, ".text", sizeof (".text"));
+
+    shdr->sh_name = grub_host_to_target32 (ptr - str_start);
+    ptr += sizeof (".text");
+    shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS);
+    shdr->sh_addr = grub_host_to_target_addr (target_addr);
+    shdr->sh_offset = grub_host_to_target_addr (header_size);
+    shdr->sh_size = grub_host_to_target32 (layout->kernel_size);
+    shdr->sh_link = grub_host_to_target32 (0);
+    shdr->sh_info = grub_host_to_target32 (0);
+    shdr->sh_addralign = grub_host_to_target32 (layout->align);
+    shdr->sh_entsize = grub_host_to_target32 (0);
+    shdr++;
+
+    memcpy (ptr, "mods", sizeof ("mods"));
+    shdr->sh_name = grub_host_to_target32 (ptr - str_start);
+    ptr += sizeof ("mods");
+    shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS);
+    shdr->sh_addr = grub_host_to_target_addr (target_addr + layout->kernel_size);
+    shdr->sh_offset = grub_host_to_target_addr (header_size + layout->kernel_size);
+    shdr->sh_size = grub_host_to_target32 (*core_size - layout->kernel_size);
+    shdr->sh_link = grub_host_to_target32 (0);
+    shdr->sh_info = grub_host_to_target32 (0);
+    shdr->sh_addralign = grub_host_to_target32 (image_target->voidp_sizeof);
+    shdr->sh_entsize = grub_host_to_target32 (0);
+    shdr++;
+
+    if (image_target->id == IMAGE_XEN || image_target->id == IMAGE_XEN_PVH)
+      {
+	memcpy (ptr, ".xen", sizeof (".xen"));
+	shdr->sh_name = grub_host_to_target32 (ptr - str_start);
+	ptr += sizeof (".xen");
+	shdr->sh_type = grub_host_to_target32 (SHT_PROGBITS);
+	shdr->sh_addr = grub_host_to_target_addr (target_addr + layout->kernel_size);
+	shdr->sh_offset = grub_host_to_target_addr (program_size + header_size);
+	if (image_target->id == IMAGE_XEN)
+	  shdr->sh_size = grub_host_to_target32 (XEN_NOTE_SIZE);
+	else
+	  shdr->sh_size = grub_host_to_target32 (XEN_PVH_NOTE_SIZE);
+	shdr->sh_link = grub_host_to_target32 (0);
+	shdr->sh_info = grub_host_to_target32 (0);
+	shdr->sh_addralign = grub_host_to_target32 (image_target->voidp_sizeof);
+	shdr->sh_entsize = grub_host_to_target32 (0);
+	shdr++;
+      }
+  }
+
+  free (*core_img);
+  *core_img = elf_img;
+  *core_size = program_size + header_size + footer_size;
+}
+
+/* Relocate symbols; note that this function overwrites the symbol table.
+   Return the address of a start symbol.  */
+static Elf_Addr
+SUFFIX (relocate_symbols) (Elf_Ehdr *e, struct section_metadata *smd,
+			   void *jumpers, Elf_Addr jumpers_addr,
+			   Elf_Addr bss_start, Elf_Addr end,
+			   const struct grub_install_image_target_desc *image_target)
+{
+  Elf_Word symtab_size, sym_size, num_syms;
+  Elf_Off symtab_offset;
+  Elf_Addr start_address = (Elf_Addr) -1;
+  Elf_Sym *sym;
+  Elf_Word i;
+  Elf_Shdr *symtab_section;
+  const char *symtab;
+  grub_uint64_t *jptr = jumpers;
+
+  symtab_section = (Elf_Shdr *) ((char *) smd->sections
+				 + grub_target_to_host32 (smd->symtab->sh_link)
+				   * smd->section_entsize);
+  symtab = (char *) e + grub_target_to_host (symtab_section->sh_offset);
+
+  symtab_size = grub_target_to_host (smd->symtab->sh_size);
+  sym_size = grub_target_to_host (smd->symtab->sh_entsize);
+  symtab_offset = grub_target_to_host (smd->symtab->sh_offset);
+  num_syms = symtab_size / sym_size;
+
+  for (i = 0, sym = (Elf_Sym *) ((char *) e + symtab_offset);
+       i < num_syms;
+       i++, sym = (Elf_Sym *) ((char *) sym + sym_size))
+    {
+      Elf_Section cur_index;
+      const char *name;
+
+      name = symtab + grub_target_to_host32 (sym->st_name);
+
+      cur_index = grub_target_to_host16 (sym->st_shndx);
+      if (cur_index == STN_ABS)
+        {
+          continue;
+        }
+      else if (cur_index == STN_UNDEF)
+	{
+	  if (sym->st_name && grub_strcmp (name, "__bss_start") == 0)
+	    sym->st_value = bss_start;
+	  else if (sym->st_name && grub_strcmp (name, "_end") == 0)
+	    sym->st_value = end;
+	  else if (sym->st_name)
+	    grub_util_error ("undefined symbol %s", name);
+	  else
+	    continue;
+	}
+      else if (cur_index >= smd->num_sections)
+	grub_util_error ("section %d does not exist", cur_index);
+      else
+	{
+	  sym->st_value = (grub_target_to_host (sym->st_value)
+			   + smd->vaddrs[cur_index]);
+	}
+
+      if (image_target->elf_target == EM_IA_64 && ELF_ST_TYPE (sym->st_info)
+	  == STT_FUNC)
+	{
+	  *jptr = grub_host_to_target64 (sym->st_value);
+	  sym->st_value = (char *) jptr - (char *) jumpers + jumpers_addr;
+	  jptr++;
+	  *jptr = 0;
+	  jptr++;
+	}
+      grub_util_info ("locating %s at 0x%"  GRUB_HOST_PRIxLONG_LONG
+		      " (0x%"  GRUB_HOST_PRIxLONG_LONG ")", name,
+		      (unsigned long long) sym->st_value,
+		      (unsigned long long) smd->vaddrs[cur_index]);
+
+      if (start_address == (Elf_Addr)-1)
+	if (strcmp (name, "_start") == 0 || strcmp (name, "start") == 0)
+	  start_address = sym->st_value;
+    }
+
+  return start_address;
+}
+
+/* Return the address of a symbol at the index I in the section S.  */
+static Elf_Addr
+SUFFIX (get_symbol_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Word i,
+			     const struct grub_install_image_target_desc *image_target)
+{
+  Elf_Sym *sym;
+
+  sym = (Elf_Sym *) ((char *) e
+		       + grub_target_to_host (s->sh_offset)
+		       + i * grub_target_to_host (s->sh_entsize));
+  return sym->st_value;
+}
+
+/* Return the address of a modified value.  */
+static Elf_Addr *
+SUFFIX (get_target_address) (Elf_Ehdr *e, Elf_Shdr *s, Elf_Addr offset,
+		    const struct grub_install_image_target_desc *image_target)
+{
+  return (Elf_Addr *) ((char *) e + grub_target_to_host (s->sh_offset) + offset);
+}
+
+#ifdef MKIMAGE_ELF64
+static Elf_Addr
+SUFFIX (count_funcs) (Elf_Ehdr *e, Elf_Shdr *symtab_section,
+		      const struct grub_install_image_target_desc *image_target)
+{
+  Elf_Word symtab_size, sym_size, num_syms;
+  Elf_Off symtab_offset;
+  Elf_Sym *sym;
+  Elf_Word i;
+  int ret = 0;
+
+  symtab_size = grub_target_to_host (symtab_section->sh_size);
+  sym_size = grub_target_to_host (symtab_section->sh_entsize);
+  symtab_offset = grub_target_to_host (symtab_section->sh_offset);
+  num_syms = symtab_size / sym_size;
+
+  for (i = 0, sym = (Elf_Sym *) ((char *) e + symtab_offset);
+       i < num_syms;
+       i++, sym = (Elf_Sym *) ((char *) sym + sym_size))
+    if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
+      ret++;
+
+  return ret;
+}
+#endif
+
+#ifdef MKIMAGE_ELF32
+/* Deal with relocation information. This function relocates addresses
+   within the virtual address space starting from 0. So only relative
+   addresses can be fully resolved. Absolute addresses must be relocated
+   again by a PE32 relocator when loaded.  */
+static grub_size_t
+arm_get_trampoline_size (Elf_Ehdr *e,
+			 Elf_Shdr *sections,
+			 Elf_Half section_entsize,
+			 Elf_Half num_sections,
+			 const struct grub_install_image_target_desc *image_target)
+{
+  Elf_Half i;
+  Elf_Shdr *s;
+  grub_size_t ret = 0;
+
+  for (i = 0, s = sections;
+       i < num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + section_entsize))
+    if ((s->sh_type == grub_host_to_target32 (SHT_REL)) ||
+        (s->sh_type == grub_host_to_target32 (SHT_RELA)))
+      {
+	Elf_Rela *r;
+	Elf_Word rtab_size, r_size, num_rs;
+	Elf_Off rtab_offset;
+	Elf_Shdr *symtab_section;
+	Elf_Word j;
+
+	symtab_section = (Elf_Shdr *) ((char *) sections
+					 + (grub_target_to_host32 (s->sh_link)
+					    * section_entsize));
+
+	rtab_size = grub_target_to_host (s->sh_size);
+	r_size = grub_target_to_host (s->sh_entsize);
+	rtab_offset = grub_target_to_host (s->sh_offset);
+	num_rs = rtab_size / r_size;
+
+	for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset);
+	     j < num_rs;
+	     j++, r = (Elf_Rela *) ((char *) r + r_size))
+	  {
+            Elf_Addr info;
+	    Elf_Addr sym_addr;
+
+	    info = grub_target_to_host (r->r_info);
+	    sym_addr = SUFFIX (get_symbol_address) (e, symtab_section,
+						    ELF_R_SYM (info), image_target);
+
+            sym_addr += (s->sh_type == grub_target_to_host32 (SHT_RELA)) ?
+	      grub_target_to_host (r->r_addend) : 0;
+
+	    switch (ELF_R_TYPE (info))
+	      {
+	      case R_ARM_ABS32:
+	      case R_ARM_V4BX:
+		break;
+	      case R_ARM_THM_CALL:
+	      case R_ARM_THM_JUMP24:
+	      case R_ARM_THM_JUMP19:
+		if (!(sym_addr & 1))
+		  ret += 8;
+		break;
+
+	      case R_ARM_CALL:
+	      case R_ARM_JUMP24:
+		if (sym_addr & 1)
+		  ret += 16;
+		break;
+
+	      default:
+		grub_util_error (_("relocation 0x%x is not implemented yet"),
+				 (unsigned int) ELF_R_TYPE (info));
+		break;
+	      }
+	  }
+      }
+  return ret;
+}
+#endif
+
+static int
+SUFFIX (is_kept_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target);
+static int
+SUFFIX (is_kept_reloc_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target,
+				struct section_metadata *smd);
+
+/* Deal with relocation information. This function relocates addresses
+   within the virtual address space starting from 0. So only relative
+   addresses can be fully resolved. Absolute addresses must be relocated
+   again by a PE32 relocator when loaded.  */
+static void
+SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd,
+			     char *pe_target, Elf_Addr tramp_off, Elf_Addr got_off,
+			     const struct grub_install_image_target_desc *image_target)
+{
+  Elf_Half i;
+  Elf_Shdr *s;
+#ifdef MKIMAGE_ELF64
+  struct grub_ia64_trampoline *tr = (void *) (pe_target + tramp_off);
+  grub_uint64_t *gpptr = (void *) (pe_target + got_off);
+  unsigned unmatched_adr_got_page = 0;
+#define MASK19 ((1 << 19) - 1)
+#else
+  grub_uint32_t *tr = (void *) (pe_target + tramp_off);
+#endif
+
+  for (i = 0, s = smd->sections;
+       i < smd->num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + smd->section_entsize))
+    if ((s->sh_type == grub_host_to_target32 (SHT_REL)) ||
+        (s->sh_type == grub_host_to_target32 (SHT_RELA)))
+      {
+	Elf_Rela *r;
+	Elf_Word rtab_size, r_size, num_rs;
+	Elf_Off rtab_offset;
+	Elf_Word target_section_index;
+	Elf_Addr target_section_addr;
+	Elf_Shdr *target_section;
+	Elf_Word j;
+
+	if (!SUFFIX (is_kept_section) (s, image_target) &&
+	    !SUFFIX (is_kept_reloc_section) (s, image_target, smd))
+	  {
+	    grub_util_info ("not translating relocations for omitted section %s",
+			smd->strtab + grub_le_to_cpu32 (s->sh_name));
+	    continue;
+	  }
+
+	target_section_index = grub_target_to_host32 (s->sh_info);
+	target_section_addr = smd->addrs[target_section_index];
+	target_section = (Elf_Shdr *) ((char *) smd->sections
+					 + (target_section_index
+					    * smd->section_entsize));
+
+	grub_util_info ("dealing with the relocation section %s for %s",
+			smd->strtab + grub_target_to_host32 (s->sh_name),
+			smd->strtab + grub_target_to_host32 (target_section->sh_name));
+
+	rtab_size = grub_target_to_host (s->sh_size);
+	r_size = grub_target_to_host (s->sh_entsize);
+	rtab_offset = grub_target_to_host (s->sh_offset);
+	num_rs = rtab_size / r_size;
+
+	for (j = 0, r = (Elf_Rela *) ((char *) e + rtab_offset);
+	     j < num_rs;
+	     j++, r = (Elf_Rela *) ((char *) r + r_size))
+	  {
+            Elf_Addr info;
+	    Elf_Addr offset;
+	    Elf_Addr sym_addr;
+	    Elf_Addr *target;
+	    Elf_Addr addend;
+
+	    offset = grub_target_to_host (r->r_offset);
+	    target = SUFFIX (get_target_address) (e, target_section,
+						  offset, image_target);
+	    info = grub_target_to_host (r->r_info);
+	    sym_addr = SUFFIX (get_symbol_address) (e, smd->symtab,
+						    ELF_R_SYM (info), image_target);
+
+            addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ?
+	      grub_target_to_host (r->r_addend) : 0;
+
+	   switch (image_target->elf_target)
+	     {
+	     case EM_386:
+	      switch (ELF_R_TYPE (info))
+		{
+		case R_386_NONE:
+		  break;
+
+		case R_386_32:
+		  /* This is absolute.  */
+		  *target = grub_host_to_target32 (grub_target_to_host32 (*target)
+						   + addend + sym_addr);
+		  grub_util_info ("relocating an R_386_32 entry to 0x%"
+				  GRUB_HOST_PRIxLONG_LONG " at the offset 0x%"
+				  GRUB_HOST_PRIxLONG_LONG,
+				  (unsigned long long) *target,
+				  (unsigned long long) offset);
+		  break;
+
+		case R_386_PC32:
+		  /* This is relative.  */
+		  *target = grub_host_to_target32 (grub_target_to_host32 (*target)
+						   + addend + sym_addr
+						   - target_section_addr - offset
+						   - image_target->vaddr_offset);
+		  grub_util_info ("relocating an R_386_PC32 entry to 0x%"
+				  GRUB_HOST_PRIxLONG_LONG " at the offset 0x%"
+				  GRUB_HOST_PRIxLONG_LONG,
+				  (unsigned long long) *target,
+				  (unsigned long long) offset);
+		  break;
+		default:
+		  grub_util_error (_("relocation 0x%x is not implemented yet"),
+				   (unsigned int) ELF_R_TYPE (info));
+		  break;
+		}
+	      break;
+#ifdef MKIMAGE_ELF64
+	     case EM_X86_64:
+	      switch (ELF_R_TYPE (info))
+		{
+
+		case R_X86_64_NONE:
+		  break;
+
+		case R_X86_64_64:
+		  *target = grub_host_to_target64 (grub_target_to_host64 (*target)
+						   + addend + sym_addr);
+		  grub_util_info ("relocating an R_X86_64_64 entry to 0x%"
+				  GRUB_HOST_PRIxLONG_LONG " at the offset 0x%"
+				  GRUB_HOST_PRIxLONG_LONG,
+				  (unsigned long long) *target,
+				  (unsigned long long) offset);
+		  break;
+
+		case R_X86_64_PC32:
+		case R_X86_64_PLT32:
+		  {
+		    grub_uint32_t *t32 = (grub_uint32_t *) target;
+		    *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32)
+						  + addend + sym_addr
+						  - target_section_addr - offset
+						  - image_target->vaddr_offset);
+		    grub_util_info ("relocating an R_X86_64_PC32 entry to 0x%x at the offset 0x%"
+				    GRUB_HOST_PRIxLONG_LONG,
+				    *t32, (unsigned long long) offset);
+		    break;
+		  }
+
+		case R_X86_64_PC64:
+		  {
+		    *target = grub_host_to_target64 (grub_target_to_host64 (*target)
+						     + addend + sym_addr
+						     - target_section_addr - offset
+						     - image_target->vaddr_offset);
+		    grub_util_info ("relocating an R_X86_64_PC64 entry to 0x%"
+				    GRUB_HOST_PRIxLONG_LONG " at the offset 0x%"
+				    GRUB_HOST_PRIxLONG_LONG,
+				    (unsigned long long) *target,
+				    (unsigned long long) offset);
+		    break;
+		  }
+
+		case R_X86_64_32:
+		case R_X86_64_32S:
+		  {
+		    grub_uint32_t *t32 = (grub_uint32_t *) target;
+		    *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32)
+						  + addend + sym_addr);
+		    grub_util_info ("relocating an R_X86_64_32(S) entry to 0x%x at the offset 0x%"
+				    GRUB_HOST_PRIxLONG_LONG,
+				    *t32, (unsigned long long) offset);
+		    break;
+		  }
+
+		default:
+		  grub_util_error (_("relocation 0x%x is not implemented yet"),
+				   (unsigned int) ELF_R_TYPE (info));
+		  break;
+		}
+	      break;
+	     case EM_IA_64:
+	      switch (ELF_R_TYPE (info))
+		{
+		case R_IA64_PCREL21B:
+		  {
+		    grub_uint64_t noff;
+		    grub_ia64_make_trampoline (tr, addend + sym_addr);
+		    noff = ((char *) tr - (char *) pe_target
+			    - target_section_addr - (offset & ~3)) >> 4;
+		    tr++;
+		    if (noff & ~MASK19)
+		      grub_util_error ("trampoline offset too big (%"
+				       GRUB_HOST_PRIxLONG_LONG ")",
+				       (unsigned long long) noff);
+		    grub_ia64_add_value_to_slot_20b ((grub_addr_t) target, noff);
+		  }
+		  break;
+
+		case R_IA64_LTOFF22X:
+		case R_IA64_LTOFF22:
+		  {
+		    Elf_Sym *sym;
+
+		    sym = (Elf_Sym *) ((char *) e
+				       + grub_target_to_host (smd->symtab->sh_offset)
+				       + ELF_R_SYM (info) * grub_target_to_host (smd->symtab->sh_entsize));
+		    if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
+		      sym_addr = grub_target_to_host64 (*(grub_uint64_t *) (pe_target
+									    + sym->st_value
+									    - image_target->vaddr_offset));
+		  }
+		/* FALLTHROUGH */
+		case R_IA64_LTOFF_FPTR22:
+		  *gpptr = grub_host_to_target64 (addend + sym_addr);
+		  grub_ia64_add_value_to_slot_21 ((grub_addr_t) target,
+						  (char *) gpptr - (char *) pe_target
+						  + image_target->vaddr_offset);
+		  gpptr++;
+		  break;
+
+		case R_IA64_GPREL22:
+		  grub_ia64_add_value_to_slot_21 ((grub_addr_t) target,
+						  addend + sym_addr);
+		  break;
+		case R_IA64_GPREL64I:
+		  grub_ia64_set_immu64 ((grub_addr_t) target,
+					addend + sym_addr);
+		  break;
+		case R_IA64_PCREL64LSB:
+		  *target = grub_host_to_target64 (grub_target_to_host64 (*target)
+						   + addend + sym_addr
+						   - target_section_addr - offset
+						   - image_target->vaddr_offset);
+		  break;
+
+		case R_IA64_SEGREL64LSB:
+		  *target = grub_host_to_target64 (grub_target_to_host64 (*target)
+						   + addend + sym_addr - target_section_addr);
+		  break;
+		case R_IA64_DIR64LSB:
+		case R_IA64_FPTR64LSB:
+		  *target = grub_host_to_target64 (grub_target_to_host64 (*target)
+						   + addend + sym_addr);
+		  grub_util_info ("relocating a direct entry to 0x%"
+				  GRUB_HOST_PRIxLONG_LONG " at the offset 0x%"
+				  GRUB_HOST_PRIxLONG_LONG,
+				  (unsigned long long)
+				  grub_target_to_host64 (*target),
+				  (unsigned long long) offset);
+		  break;
+
+		  /* We treat LTOFF22X as LTOFF22, so we can ignore LDXMOV.  */
+		case R_IA64_LDXMOV:
+		  break;
+
+		default:
+		  grub_util_error (_("relocation 0x%x is not implemented yet"),
+				   (unsigned int) ELF_R_TYPE (info));
+		  break;
+		}
+	       break;
+	     case EM_AARCH64:
+	       {
+		 sym_addr += addend;
+		 switch (ELF_R_TYPE (info))
+		   {
+		   case R_AARCH64_ABS64:
+		     {
+		       *target = grub_host_to_target64 (grub_target_to_host64 (*target) + sym_addr);
+		     }
+		     break;
+		   case R_AARCH64_PREL32:
+		     {
+		       grub_uint32_t *t32 = (grub_uint32_t *) target;
+		       *t32 = grub_host_to_target64 (grub_target_to_host32 (*t32)
+						     + sym_addr
+						     - target_section_addr - offset
+						     - image_target->vaddr_offset);
+		       grub_util_info ("relocating an R_AARCH64_PREL32 entry to 0x%x at the offset 0x%"
+				       GRUB_HOST_PRIxLONG_LONG,
+				       *t32, (unsigned long long) offset);
+		       break;
+		     }
+		   case R_AARCH64_ADD_ABS_LO12_NC:
+		     grub_arm64_set_abs_lo12 ((grub_uint32_t *) target,
+					      sym_addr);
+		     break;
+		   case R_AARCH64_LDST64_ABS_LO12_NC:
+		     grub_arm64_set_abs_lo12_ldst64 ((grub_uint32_t *) target,
+						     sym_addr);
+		     break;
+		   case R_AARCH64_JUMP26:
+		   case R_AARCH64_CALL26:
+		     {
+		       sym_addr -= offset;
+		       sym_addr -= target_section_addr + image_target->vaddr_offset;
+		       if (!grub_arm_64_check_xxxx26_offset (sym_addr))
+			 grub_util_error ("%s", "CALL26 Relocation out of range");
+
+		       grub_arm64_set_xxxx26_offset((grub_uint32_t *)target,
+						     sym_addr);
+		     }
+		     break;
+		   case R_AARCH64_ADR_GOT_PAGE:
+		     {
+		       Elf64_Rela *rel2;
+		       grub_int64_t gpoffset = (((char *) gpptr - (char *) pe_target + image_target->vaddr_offset) & ~0xfffULL)
+			 - ((offset + target_section_addr + image_target->vaddr_offset) & ~0xfffULL);
+		       unsigned k;
+		       *gpptr = grub_host_to_target64 (sym_addr);
+		       unmatched_adr_got_page++;
+		       if (!grub_arm64_check_hi21_signed (gpoffset))
+			 grub_util_error ("HI21 out of range");
+		       grub_arm64_set_hi21((grub_uint32_t *)target,
+					   gpoffset);
+		       for (k = 0, rel2 = (Elf_Rela *) ((char *) r + r_size);
+			    k < num_rs;
+			    k++, rel2 = (Elf_Rela *) ((char *) rel2 + r_size))
+			 if (ELF_R_SYM (rel2->r_info)
+			     == ELF_R_SYM (r->r_info)
+			     && r->r_addend == rel2->r_addend
+			     && ELF_R_TYPE (rel2->r_info) == R_AARCH64_LD64_GOT_LO12_NC)
+			   {
+			     grub_arm64_set_abs_lo12_ldst64 ((grub_uint32_t *) SUFFIX (get_target_address) (e, target_section,
+													    grub_target_to_host (rel2->r_offset), image_target),
+							     ((char *) gpptr - (char *) pe_target + image_target->vaddr_offset));
+			     break;
+			   }
+		       if (k >= num_rs)
+			 grub_util_error ("ADR_GOT_PAGE without matching LD64_GOT_LO12_NC");
+		       gpptr++;
+	             }
+		     break;
+		   case R_AARCH64_LD64_GOT_LO12_NC:
+		     if (unmatched_adr_got_page == 0)
+		       grub_util_error ("LD64_GOT_LO12_NC without matching ADR_GOT_PAGE");
+		     unmatched_adr_got_page--;
+		     break;
+		   case R_AARCH64_ADR_PREL_PG_HI21:
+		     {
+		       sym_addr &= ~0xfffULL;
+		       sym_addr -= (offset + target_section_addr + image_target->vaddr_offset) & ~0xfffULL;
+		       if (!grub_arm64_check_hi21_signed (sym_addr))
+			 grub_util_error ("%s", "CALL26 Relocation out of range");
+
+		       grub_arm64_set_hi21((grub_uint32_t *)target,
+					   sym_addr);
+		     }
+		     break;
+		   default:
+		     grub_util_error (_("relocation 0x%x is not implemented yet"),
+				      (unsigned int) ELF_R_TYPE (info));
+		     break;
+		   }
+	       break;
+	       }
+#endif
+#if defined(MKIMAGE_ELF32)
+	     case EM_ARM:
+	       {
+		 sym_addr += addend;
+		 sym_addr -= image_target->vaddr_offset;
+		 switch (ELF_R_TYPE (info))
+		   {
+		   case R_ARM_ABS32:
+		     {
+		       grub_util_info ("  ABS32:\toffset=%d\t(0x%08x)",
+				       (int) sym_addr, (int) sym_addr);
+		       /* Data will be naturally aligned */
+		       if (image_target->id == IMAGE_EFI)
+			 sym_addr += GRUB_PE32_SECTION_ALIGNMENT;
+		       *target = grub_host_to_target32 (grub_target_to_host32 (*target) + sym_addr);
+		     }
+		     break;
+		     /* Happens when compiled with -march=armv4.
+			Since currently we need at least armv5, keep bx as-is.
+		     */
+		   case R_ARM_V4BX:
+		     break;
+		   case R_ARM_THM_CALL:
+		   case R_ARM_THM_JUMP24:
+		   case R_ARM_THM_JUMP19:
+		     {
+		       grub_err_t err;
+		       Elf_Sym *sym;
+		       grub_util_info ("  THM_JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)",
+				       (unsigned long) ((char *) target
+							- (char *) e),
+				       sym_addr);
+		       sym = (Elf_Sym *) ((char *) e
+					  + grub_target_to_host (smd->symtab->sh_offset)
+					  + ELF_R_SYM (info) * grub_target_to_host (smd->symtab->sh_entsize));
+		       if (ELF_ST_TYPE (sym->st_info) != STT_FUNC)
+			 sym_addr |= 1;
+		       if (!(sym_addr & 1))
+			 {
+			   grub_uint32_t tr_addr;
+			   grub_int32_t new_offset;
+			   tr_addr = (char *) tr - (char *) pe_target
+			     - target_section_addr;
+			   new_offset = sym_addr - tr_addr - 12;
+
+			   if (!grub_arm_jump24_check_offset (new_offset))
+			     return grub_util_error ("jump24 relocation out of range");
+
+			   tr[0] = grub_host_to_target32 (0x46c04778); /* bx pc; nop  */
+			   tr[1] = grub_host_to_target32 (((new_offset >> 2) & 0xffffff) | 0xea000000); /* b new_offset */
+			   tr += 2;
+			   sym_addr = tr_addr | 1;
+			 }
+		       sym_addr -= offset;
+		       /* Thumb instructions can be 16-bit aligned */
+		       if (ELF_R_TYPE (info) == R_ARM_THM_JUMP19)
+			 err = grub_arm_reloc_thm_jump19 ((grub_uint16_t *) target, sym_addr);
+		       else
+			 err = grub_arm_reloc_thm_call ((grub_uint16_t *) target,
+							sym_addr);
+		       if (err)
+			 grub_util_error ("%s", grub_errmsg);
+		     }
+		     break;
+
+		   case R_ARM_CALL:
+		   case R_ARM_JUMP24:
+		     {
+		       grub_err_t err;
+		       grub_util_info ("  JUMP24:\ttarget=0x%08lx\toffset=(0x%08x)",  (unsigned long) ((char *) target - (char *) e), sym_addr);
+		       if (sym_addr & 1)
+			 {
+			   grub_uint32_t tr_addr;
+			   grub_int32_t new_offset;
+			   tr_addr = (char *) tr - (char *) pe_target
+			     - target_section_addr;
+			   new_offset = sym_addr - tr_addr - 12;
+
+			   /* There is no immediate version of bx, only register one...  */
+			   tr[0] = grub_host_to_target32 (0xe59fc004); /* ldr	ip, [pc, #4] */
+			   tr[1] = grub_host_to_target32 (0xe08cc00f); /* add	ip, ip, pc */
+			   tr[2] = grub_host_to_target32 (0xe12fff1c); /* bx	ip */
+			   tr[3] = grub_host_to_target32 (new_offset | 1);
+			   tr += 4;
+			   sym_addr = tr_addr;
+			 }
+		       sym_addr -= offset;
+		       err = grub_arm_reloc_jump24 (target,
+						    sym_addr);
+		       if (err)
+			 grub_util_error ("%s", grub_errmsg);
+		     }
+		     break;
+
+		   default:
+		     grub_util_error (_("relocation 0x%x is not implemented yet"),
+				      (unsigned int) ELF_R_TYPE (info));
+		     break;
+		   }
+		 break;
+	       }
+#endif /* MKIMAGE_ELF32 */
+	     case EM_RISCV:
+	       {
+		 grub_uint64_t *t64 = (grub_uint64_t *) target;
+		 grub_uint32_t *t32 = (grub_uint32_t *) target;
+		 grub_uint16_t *t16 = (grub_uint16_t *) target;
+		 grub_uint8_t *t8 = (grub_uint8_t *) target;
+		 grub_int64_t off;
+
+		 /*
+		  * Instructions and instruction encoding are documented in the RISC-V
+		  * specification. This file is based on version 2.2:
+		  *
+		  * https://github.com/riscv/riscv-isa-manual/blob/master/release/riscv-spec-v2.2.pdf
+		  */
+
+		 sym_addr += addend;
+		 off = sym_addr - target_section_addr - offset - image_target->vaddr_offset;
+
+		 switch (ELF_R_TYPE (info))
+		   {
+		   case R_RISCV_ADD8:
+		     *t8 = *t8 + sym_addr;
+		     break;
+		   case R_RISCV_ADD16:
+		     *t16 = grub_host_to_target16 (grub_target_to_host16 (*t16) + sym_addr);
+		     break;
+		   case R_RISCV_32:
+		   case R_RISCV_ADD32:
+		     *t32 = grub_host_to_target32 (grub_target_to_host32 (*t32) + sym_addr);
+		     break;
+		   case R_RISCV_64:
+		   case R_RISCV_ADD64:
+		     *t64 = grub_host_to_target64 (grub_target_to_host64 (*t64) + sym_addr);
+		     break;
+
+		   case R_RISCV_SUB8:
+		     *t8 = sym_addr - *t8;
+		     break;
+		   case R_RISCV_SUB16:
+		     *t16 = grub_host_to_target16 (grub_target_to_host16 (*t16) - sym_addr);
+		     break;
+		   case R_RISCV_SUB32:
+		     *t32 = grub_host_to_target32 (grub_target_to_host32 (*t32) - sym_addr);
+		     break;
+		   case R_RISCV_SUB64:
+		     *t64 = grub_host_to_target64 (grub_target_to_host64 (*t64) - sym_addr);
+		     break;
+		   case R_RISCV_BRANCH:
+		     {
+		       grub_uint32_t imm12 = (off & 0x1000) << (31 - 12);
+		       grub_uint32_t imm11 = (off & 0x800) >> (11 - 7);
+		       grub_uint32_t imm10_5 = (off & 0x7e0) << (30 - 10);
+		       grub_uint32_t imm4_1 = (off & 0x1e) << (11 - 4);
+		       *t32 = grub_host_to_target32 ((grub_target_to_host32 (*t32) & 0x1fff07f)
+						     | imm12 | imm11 | imm10_5 | imm4_1);
+		     }
+		     break;
+		   case R_RISCV_JAL:
+		     {
+		       grub_uint32_t imm20 = (off & 0x100000) << (31 - 20);
+		       grub_uint32_t imm19_12 = (off & 0xff000);
+		       grub_uint32_t imm11 = (off & 0x800) << (20 - 11);
+		       grub_uint32_t imm10_1 = (off & 0x7fe) << (30 - 10);
+		       *t32 = grub_host_to_target32 ((grub_target_to_host32 (*t32) & 0xfff)
+						     | imm20 | imm19_12 | imm11 | imm10_1);
+		     }
+		     break;
+		   case R_RISCV_CALL:
+		   case R_RISCV_CALL_PLT:
+		     {
+		       grub_uint32_t hi20, lo12;
+
+		       if (off != (grub_int32_t)off)
+			 grub_util_error ("target %lx not reachable from pc=%lx", (long)sym_addr, (long)((char *)target - (char *)e));
+
+		       hi20 = (off + 0x800) & 0xfffff000;
+		       lo12 = (off - hi20) & 0xfff;
+		       t32[0] = grub_host_to_target32 ((grub_target_to_host32 (t32[0]) & 0xfff) | hi20);
+		       t32[1] = grub_host_to_target32 ((grub_target_to_host32 (t32[1]) & 0xfffff) | (lo12 << 20));
+		     }
+		     break;
+		   case R_RISCV_RVC_BRANCH:
+		     {
+		       grub_uint16_t imm8 = (off & 0x100) << (12 - 8);
+		       grub_uint16_t imm7_6 = (off & 0xc0) >> (6 - 5);
+		       grub_uint16_t imm5 = (off & 0x20) >> (5 - 2);
+		       grub_uint16_t imm4_3 = (off & 0x18) << (12 - 5);
+		       grub_uint16_t imm2_1 = (off & 0x6) << (12 - 10);
+		       *t16 = grub_host_to_target16 ((grub_target_to_host16 (*t16) & 0xe383)
+						     | imm8 | imm7_6 | imm5 | imm4_3 | imm2_1);
+		     }
+		     break;
+		   case R_RISCV_RVC_JUMP:
+		     {
+		       grub_uint16_t imm11 = (off & 0x800) << (12 - 11);
+		       grub_uint16_t imm10 = (off & 0x400) >> (10 - 8);
+		       grub_uint16_t imm9_8 = (off & 0x300) << (12 - 11);
+		       grub_uint16_t imm7 = (off & 0x80) >> (7 - 6);
+		       grub_uint16_t imm6 = (off & 0x40) << (12 - 11);
+		       grub_uint16_t imm5 = (off & 0x20) >> (5 - 2);
+		       grub_uint16_t imm4 = (off & 0x10) << (12 - 5);
+		       grub_uint16_t imm3_1 = (off & 0xe) << (12 - 10);
+		       *t16 = grub_host_to_target16 ((grub_target_to_host16 (*t16) & 0xe003)
+						     | imm11 | imm10 | imm9_8 | imm7 | imm6
+						     | imm5 | imm4 | imm3_1);
+		     }
+		     break;
+		   case R_RISCV_PCREL_HI20:
+		     {
+		       grub_int32_t hi20;
+
+		       if (off != (grub_int32_t)off)
+			 grub_util_error ("target %lx not reachable from pc=%lx", (long)sym_addr, (long)((char *)target - (char *)e));
+
+		       hi20 = (off + 0x800) & 0xfffff000;
+		       *t32 = grub_host_to_target32 ((grub_target_to_host32 (*t32) & 0xfff) | hi20);
+		     }
+		     break;
+		   case R_RISCV_PCREL_LO12_I:
+		   case R_RISCV_PCREL_LO12_S:
+		     {
+		       Elf_Rela *rel2;
+		       Elf_Word k;
+		       /* Search backwards for matching HI20 reloc.  */
+		       for (k = j, rel2 = (Elf_Rela *) ((char *) r - r_size);
+			    k > 0;
+			    k--, rel2 = (Elf_Rela *) ((char *) rel2 - r_size))
+			 {
+			   Elf_Addr rel2_info;
+			   Elf_Addr rel2_offset;
+			   Elf_Addr rel2_sym_addr;
+			   Elf_Addr rel2_addend;
+			   Elf_Addr rel2_loc;
+			   grub_int64_t rel2_off;
+
+			   rel2_offset = grub_target_to_host (rel2->r_offset);
+			   rel2_info = grub_target_to_host (rel2->r_info);
+			   rel2_loc = target_section_addr + rel2_offset + image_target->vaddr_offset;
+
+			   if (ELF_R_TYPE (rel2_info) == R_RISCV_PCREL_HI20
+			       && rel2_loc == sym_addr)
+			     {
+			       rel2_sym_addr = SUFFIX (get_symbol_address)
+				 (e, smd->symtab, ELF_R_SYM (rel2_info),
+				  image_target);
+			       rel2_addend = (s->sh_type == grub_target_to_host32 (SHT_RELA)) ?
+				 grub_target_to_host (rel2->r_addend) : 0;
+			       rel2_off = rel2_sym_addr + rel2_addend - rel2_loc;
+			       off = rel2_off - ((rel2_off + 0x800) & 0xfffff000);
+
+			       if (ELF_R_TYPE (info) == R_RISCV_PCREL_LO12_I)
+				 *t32 = grub_host_to_target32 ((grub_target_to_host32 (*t32) & 0xfffff) | (off & 0xfff) << 20);
+			       else
+				 {
+				   grub_uint32_t imm11_5 = (off & 0xfe0) << (31 - 11);
+				   grub_uint32_t imm4_0 = (off & 0x1f) << (11 - 4);
+				   *t32 = grub_host_to_target32 ((grub_target_to_host32 (*t32) & 0x1fff07f) | imm11_5 | imm4_0);
+				 }
+			       break;
+			     }
+			 }
+		       if (k == 0)
+			 grub_util_error ("cannot find matching HI20 relocation");
+		     }
+		     break;
+		   case R_RISCV_HI20:
+		     *t32 = grub_host_to_target32 ((grub_target_to_host32 (*t32) & 0xfff) | (((grub_int32_t) sym_addr + 0x800) & 0xfffff000));
+		     break;
+		   case R_RISCV_LO12_I:
+		     {
+		       grub_int32_t lo12 = (grub_int32_t) sym_addr - (((grub_int32_t) sym_addr + 0x800) & 0xfffff000);
+		       *t32 = grub_host_to_target32 ((grub_target_to_host32 (*t32) & 0xfffff) | ((lo12 & 0xfff) << 20));
+		     }
+		     break;
+		   case R_RISCV_LO12_S:
+		     {
+		       grub_int32_t lo12 = (grub_int32_t) sym_addr - (((grub_int32_t) sym_addr + 0x800) & 0xfffff000);
+		       grub_uint32_t imm11_5 = (lo12 & 0xfe0) << (31 - 11);
+		       grub_uint32_t imm4_0 = (lo12 & 0x1f) << (11 - 4);
+		       *t32 = grub_host_to_target32 ((grub_target_to_host32 (*t32) & 0x1fff07f) | imm11_5 | imm4_0);
+		     }
+		     break;
+		   case R_RISCV_RELAX:
+		     break;
+		   default:
+		     grub_util_error (_("relocation 0x%x is not implemented yet"),
+				      (unsigned int) ELF_R_TYPE (info));
+		     break;
+		   }
+	       break;
+	       }
+	     default:
+	       grub_util_error ("unknown architecture type %d",
+				image_target->elf_target);
+	     }
+	  }
+      }
+}
+
+/* Add a PE32's fixup entry for a relocation. Return the resulting address
+   after having written to the file OUT.  */
+static Elf_Addr
+add_fixup_entry (struct fixup_block_list **cblock, grub_uint16_t type,
+		 Elf_Addr addr, int flush, Elf_Addr current_address,
+		 const struct grub_install_image_target_desc *image_target)
+{
+  struct grub_pe32_fixup_block *b;
+
+  b = &((*cblock)->b);
+
+  /* First, check if it is necessary to write out the current block.  */
+  if ((*cblock)->state)
+    {
+      if (flush || addr < b->page_rva || b->page_rva + 0x1000 <= addr)
+	{
+	  grub_uint32_t size;
+
+	  if (flush)
+	    {
+	      /* Add as much padding as necessary to align the address
+		 with a section boundary.  */
+	      Elf_Addr next_address;
+	      unsigned padding_size;
+              size_t cur_index;
+
+	      next_address = current_address + b->block_size;
+	      padding_size = ((ALIGN_UP (next_address, image_target->section_align)
+			       - next_address)
+			      >> 1);
+              cur_index = ((b->block_size - sizeof (*b)) >> 1);
+              grub_util_info ("adding %d padding fixup entries", padding_size);
+	      while (padding_size--)
+		{
+		  b->entries[cur_index++] = 0;
+		  b->block_size += 2;
+		}
+	    }
+          else while (b->block_size & (8 - 1))
+            {
+	      /* If not aligned with a 32-bit boundary, add
+		 a padding entry.  */
+              size_t cur_index;
+
+              grub_util_info ("adding a padding fixup entry");
+              cur_index = ((b->block_size - sizeof (*b)) >> 1);
+              b->entries[cur_index] = 0;
+              b->block_size += 2;
+            }
+
+          /* Flush it.  */
+          grub_util_info ("writing %d bytes of a fixup block starting at 0x%x",
+                          b->block_size, b->page_rva);
+          size = b->block_size;
+	  current_address += size;
+	  b->page_rva = grub_host_to_target32 (b->page_rva);
+	  b->block_size = grub_host_to_target32 (b->block_size);
+	  (*cblock)->next = xmalloc (sizeof (**cblock) + 2 * 0x1000);
+	  memset ((*cblock)->next, 0, sizeof (**cblock) + 2 * 0x1000);
+	  *cblock = (*cblock)->next;
+	}
+    }
+
+  b = &((*cblock)->b);
+
+  if (! flush)
+    {
+      grub_uint16_t entry;
+      size_t cur_index;
+
+      /* If not allocated yet, allocate a block with enough entries.  */
+      if (! (*cblock)->state)
+	{
+	  (*cblock)->state = 1;
+
+	  /* The spec does not mention the requirement of a Page RVA.
+	     Here, align the address with a 4K boundary for safety.  */
+	  b->page_rva = (addr & ~(0x1000 - 1));
+	  b->block_size = sizeof (*b);
+	}
+
+      /* Sanity check.  */
+      if (b->block_size >= sizeof (*b) + 2 * 0x1000)
+	grub_util_error ("too many fixup entries");
+
+      /* Add a new entry.  */
+      cur_index = ((b->block_size - sizeof (*b)) >> 1);
+      entry = GRUB_PE32_FIXUP_ENTRY (type, addr - b->page_rva);
+      b->entries[cur_index] = grub_host_to_target16 (entry);
+      b->block_size += 2;
+    }
+
+  return current_address;
+}
+
+struct raw_reloc
+{
+  struct raw_reloc *next;
+  grub_uint32_t offset;
+  enum raw_reloc_type {
+    RAW_RELOC_NONE = -1,
+    RAW_RELOC_32 = 0,
+    RAW_RELOC_MAX = 1,
+  } type;
+};
+
+struct translate_context
+{
+  /* PE */
+  struct fixup_block_list *lst, *lst0;
+  Elf_Addr current_address;
+
+  /* Raw */
+  struct raw_reloc *raw_relocs;
+};
+
+static void
+translate_reloc_start (struct translate_context *ctx,
+		       const struct grub_install_image_target_desc *image_target)
+{
+  grub_memset (ctx, 0, sizeof (*ctx));
+  if (image_target->id == IMAGE_EFI)
+    {
+      ctx->lst = ctx->lst0 = xmalloc (sizeof (*ctx->lst) + 2 * 0x1000);
+      memset (ctx->lst, 0, sizeof (*ctx->lst) + 2 * 0x1000);
+      ctx->current_address = 0;
+    }
+}
+
+static void
+translate_relocation_pe (struct translate_context *ctx,
+			 Elf_Addr addr,
+			 Elf_Addr info,
+			 const struct grub_install_image_target_desc *image_target)
+{
+  /* Necessary to relocate only absolute addresses.  */
+  switch (image_target->elf_target)
+    {
+    case EM_386:
+      if (ELF_R_TYPE (info) == R_386_32)
+	{
+	  grub_util_info ("adding a relocation entry for 0x%"
+			  GRUB_HOST_PRIxLONG_LONG,
+			  (unsigned long long) addr);
+	  ctx->current_address
+	    = add_fixup_entry (&ctx->lst,
+			       GRUB_PE32_REL_BASED_HIGHLOW,
+			       addr, 0, ctx->current_address,
+			       image_target);
+	}
+      break;
+    case EM_X86_64:
+      if ((ELF_R_TYPE (info) == R_X86_64_32) ||
+	  (ELF_R_TYPE (info) == R_X86_64_32S))
+	{
+	  grub_util_error ("can\'t add fixup entry for R_X86_64_32(S)");
+	}
+      else if (ELF_R_TYPE (info) == R_X86_64_64)
+	{
+	  grub_util_info ("adding a relocation entry for 0x%"
+			  GRUB_HOST_PRIxLONG_LONG,
+			  (unsigned long long) addr);
+	  ctx->current_address
+	    = add_fixup_entry (&ctx->lst,
+			       GRUB_PE32_REL_BASED_DIR64,
+			       addr,
+			       0, ctx->current_address,
+			       image_target);
+	}
+      break;
+    case EM_IA_64:
+      switch (ELF_R_TYPE (info))
+	{
+	case R_IA64_PCREL64LSB:
+	case R_IA64_LDXMOV:
+	case R_IA64_PCREL21B:
+	case R_IA64_LTOFF_FPTR22:
+	case R_IA64_LTOFF22X:
+	case R_IA64_LTOFF22:
+	case R_IA64_GPREL22:
+	case R_IA64_GPREL64I:
+	case R_IA64_SEGREL64LSB:
+	  break;
+
+	case R_IA64_FPTR64LSB:
+	case R_IA64_DIR64LSB:
+#if 1
+	  {
+	    grub_util_info ("adding a relocation entry for 0x%"
+			    GRUB_HOST_PRIxLONG_LONG,
+			    (unsigned long long) addr);
+	    ctx->current_address
+	      = add_fixup_entry (&ctx->lst,
+				 GRUB_PE32_REL_BASED_DIR64,
+				 addr,
+				 0, ctx->current_address,
+				 image_target);
+	  }
+#endif
+	  break;
+	default:
+	  grub_util_error (_("relocation 0x%x is not implemented yet"),
+			   (unsigned int) ELF_R_TYPE (info));
+	  break;
+	}
+      break;
+    case EM_AARCH64:
+      switch (ELF_R_TYPE (info))
+	{
+	case R_AARCH64_ABS64:
+	  {
+	    ctx->current_address
+	      = add_fixup_entry (&ctx->lst,
+				 GRUB_PE32_REL_BASED_DIR64,
+				 addr, 0, ctx->current_address,
+				 image_target);
+	  }
+	  break;
+	  /* Relative relocations do not require fixup entries. */
+	case R_AARCH64_CALL26:
+	case R_AARCH64_JUMP26:
+	case R_AARCH64_PREL32:
+	  break;
+	  /* Page-relative relocations do not require fixup entries. */
+	case R_AARCH64_ADR_PREL_PG_HI21:
+	  /* We page-align the whole kernel, so no need
+	     for fixup entries.
+	  */
+	case R_AARCH64_ADD_ABS_LO12_NC:
+	case R_AARCH64_LDST64_ABS_LO12_NC:
+	  break;
+
+	  /* GOT is relocated separately.  */
+	case R_AARCH64_ADR_GOT_PAGE:
+	case R_AARCH64_LD64_GOT_LO12_NC:
+	  break;
+
+	default:
+	  grub_util_error (_("relocation 0x%x is not implemented yet"),
+			   (unsigned int) ELF_R_TYPE (info));
+	  break;
+	}
+      break;
+      break;
+#if defined(MKIMAGE_ELF32)
+    case EM_ARM:
+      switch (ELF_R_TYPE (info))
+	{
+	case R_ARM_V4BX:
+	  /* Relative relocations do not require fixup entries. */
+	case R_ARM_JUMP24:
+	case R_ARM_THM_CALL:
+	case R_ARM_THM_JUMP19:
+	case R_ARM_THM_JUMP24:
+	case R_ARM_CALL:
+	  {
+	    grub_util_info ("  %s:  not adding fixup: 0x%08x : 0x%08x", __FUNCTION__, (unsigned int) addr, (unsigned int) ctx->current_address);
+	  }
+	  break;
+	  /* Create fixup entry for PE/COFF loader */
+	case R_ARM_ABS32:
+	  {
+	    ctx->current_address
+	      = add_fixup_entry (&ctx->lst,
+				 GRUB_PE32_REL_BASED_HIGHLOW,
+				 addr, 0, ctx->current_address,
+				 image_target);
+	  }
+	  break;
+	default:
+	  grub_util_error (_("relocation 0x%x is not implemented yet"),
+			   (unsigned int) ELF_R_TYPE (info));
+	  break;
+	}
+      break;
+#endif /* defined(MKIMAGE_ELF32) */
+    case EM_RISCV:
+      switch (ELF_R_TYPE (info))
+	{
+	case R_RISCV_32:
+	  {
+	    ctx->current_address
+	      = add_fixup_entry (&ctx->lst,
+				 GRUB_PE32_REL_BASED_HIGHLOW,
+				 addr, 0, ctx->current_address,
+				 image_target);
+	  }
+	  break;
+	case R_RISCV_64:
+	  {
+	    ctx->current_address
+	      = add_fixup_entry (&ctx->lst,
+				 GRUB_PE32_REL_BASED_DIR64,
+				 addr, 0, ctx->current_address,
+				 image_target);
+	  }
+	  break;
+	  /* Relative relocations do not require fixup entries. */
+	case R_RISCV_BRANCH:
+	case R_RISCV_JAL:
+	case R_RISCV_CALL:
+	case R_RISCV_CALL_PLT:
+	case R_RISCV_PCREL_HI20:
+	case R_RISCV_PCREL_LO12_I:
+	case R_RISCV_PCREL_LO12_S:
+	case R_RISCV_RVC_BRANCH:
+	case R_RISCV_RVC_JUMP:
+	case R_RISCV_ADD32:
+	case R_RISCV_SUB32:
+	  grub_util_info ("  %s:  not adding fixup: 0x%08x : 0x%08x", __FUNCTION__, (unsigned int) addr, (unsigned int) ctx->current_address);
+	  break;
+	case R_RISCV_HI20:
+	  {
+	    ctx->current_address
+	      = add_fixup_entry (&ctx->lst,
+				 GRUB_PE32_REL_BASED_RISCV_HI20,
+				 addr, 0, ctx->current_address,
+				 image_target);
+	  }
+	  break;
+	case R_RISCV_LO12_I:
+	  {
+	    ctx->current_address
+	      = add_fixup_entry (&ctx->lst,
+				 GRUB_PE32_REL_BASED_RISCV_LOW12I,
+				 addr, 0, ctx->current_address,
+				 image_target);
+	  }
+	  break;
+	case R_RISCV_LO12_S:
+	  {
+	    ctx->current_address
+	      = add_fixup_entry (&ctx->lst,
+				 GRUB_PE32_REL_BASED_RISCV_LOW12S,
+				 addr, 0, ctx->current_address,
+				 image_target);
+	  }
+	  break;
+	case R_RISCV_RELAX:
+	  break;
+	default:
+	  grub_util_error (_("relocation 0x%x is not implemented yet"),
+			   (unsigned int) ELF_R_TYPE (info));
+	  break;
+	}
+      break;
+    default:
+      grub_util_error ("unknown machine type 0x%x", image_target->elf_target);
+    }
+}
+
+static enum raw_reloc_type
+classify_raw_reloc (Elf_Addr info,
+		    const struct grub_install_image_target_desc *image_target)
+{
+    /* Necessary to relocate only absolute addresses.  */
+  switch (image_target->elf_target)
+    {
+    case EM_ARM:
+      switch (ELF_R_TYPE (info))
+	{
+	case R_ARM_V4BX:
+	case R_ARM_JUMP24:
+	case R_ARM_THM_CALL:
+	case R_ARM_THM_JUMP19:
+	case R_ARM_THM_JUMP24:
+	case R_ARM_CALL:
+	  return RAW_RELOC_NONE;
+	case R_ARM_ABS32:
+	  return RAW_RELOC_32;
+	default:
+	  grub_util_error (_("relocation 0x%x is not implemented yet"),
+			   (unsigned int) ELF_R_TYPE (info));
+	  break;
+	}
+      break;
+    default:
+      grub_util_error ("unknown machine type 0x%x", image_target->elf_target);
+    }
+}
+
+static void
+translate_relocation_raw (struct translate_context *ctx,
+			  Elf_Addr addr,
+			  Elf_Addr info,
+			  const struct grub_install_image_target_desc *image_target)
+{
+  enum raw_reloc_type class = classify_raw_reloc (info, image_target);
+  struct raw_reloc *rel;
+  if (class == RAW_RELOC_NONE)
+    return;
+  rel = xmalloc (sizeof (*rel));
+  rel->next = ctx->raw_relocs;
+  rel->type = class;
+  rel->offset = addr;
+  ctx->raw_relocs = rel;
+}
+
+static void
+translate_relocation (struct translate_context *ctx,
+		      Elf_Addr addr,
+		      Elf_Addr info,
+		      const struct grub_install_image_target_desc *image_target)
+{
+  if (image_target->id == IMAGE_EFI)
+    translate_relocation_pe (ctx, addr, info, image_target);
+  else
+    translate_relocation_raw (ctx, addr, info, image_target);
+}
+
+static void
+finish_reloc_translation_pe (struct translate_context *ctx, struct grub_mkimage_layout *layout,
+			     const struct grub_install_image_target_desc *image_target)
+{
+  ctx->current_address = add_fixup_entry (&ctx->lst, 0, 0, 1, ctx->current_address, image_target);
+
+  {
+    grub_uint8_t *ptr;
+    layout->reloc_section = ptr = xmalloc (ctx->current_address);
+    for (ctx->lst = ctx->lst0; ctx->lst; ctx->lst = ctx->lst->next)
+      if (ctx->lst->state)
+	{
+	  memcpy (ptr, &ctx->lst->b, grub_target_to_host32 (ctx->lst->b.block_size));
+	  ptr += grub_target_to_host32 (ctx->lst->b.block_size);
+	}
+    assert ((ctx->current_address + (grub_uint8_t *) layout->reloc_section) == ptr);
+  }
+
+  for (ctx->lst = ctx->lst0; ctx->lst; )
+    {
+      struct fixup_block_list *next;
+      next = ctx->lst->next;
+      free (ctx->lst);
+      ctx->lst = next;
+    }
+
+  layout->reloc_size = ctx->current_address;
+  if (image_target->elf_target == EM_ARM && layout->reloc_size > GRUB_KERNEL_ARM_STACK_SIZE)
+    grub_util_error ("Reloc section (%d) is bigger than stack size (%d). "
+		     "This breaks assembly assumptions. Please increase stack size",
+		     (int) layout->reloc_size,
+		     (int) GRUB_KERNEL_ARM_STACK_SIZE);
+}
+
+/*
+  Layout:
+  <type 0 relocations>
+  <fffffffe>
+  <type 1 relocations>
+  <fffffffe>
+  ...
+  <type n relocations>
+  <ffffffff>
+  each relocation starts with 32-bit offset. Rest depends on relocation.
+  mkimage stops when it sees first unknown type or end marker.
+  This allows images to be created with mismatched mkimage and
+  kernel as long as no relocations are present in kernel that mkimage
+  isn't aware of (in which case mkimage aborts).
+  This also allows simple assembly to do the relocs.
+*/
+
+#define RAW_SEPARATOR 0xfffffffe
+#define RAW_END_MARKER 0xffffffff
+
+static void
+finish_reloc_translation_raw (struct translate_context *ctx, struct grub_mkimage_layout *layout,
+			      const struct grub_install_image_target_desc *image_target)
+{
+  size_t count = 0, sz;
+  enum raw_reloc_type highest = RAW_RELOC_NONE;
+  enum raw_reloc_type curtype;
+  struct raw_reloc *cur;
+  grub_uint32_t *p;
+  if (!ctx->raw_relocs)
+    {
+      layout->reloc_section = p = xmalloc (sizeof (grub_uint32_t));
+      p[0] = RAW_END_MARKER;
+      layout->reloc_size = sizeof (grub_uint32_t);
+      return;
+    }
+  for (cur = ctx->raw_relocs; cur; cur = cur->next)
+    {
+      count++;
+      if (cur->type > highest)
+	highest = cur->type;
+    }
+  /* highest separators, count relocations and one end marker.  */
+  sz = (highest + count + 1) * sizeof (grub_uint32_t);
+  layout->reloc_section = p = xmalloc (sz);
+  for (curtype = 0; curtype <= highest; curtype++)
+    {
+      /* Support for special cases would go here.  */
+      for (cur = ctx->raw_relocs; cur; cur = cur->next)
+	if (cur->type == curtype)
+	  {
+	    *p++ = cur->offset;
+	  }
+      *p++ = RAW_SEPARATOR;
+    }
+  *--p = RAW_END_MARKER;
+  layout->reloc_size = sz;
+}
+
+static void
+finish_reloc_translation (struct translate_context *ctx, struct grub_mkimage_layout *layout,
+			  const struct grub_install_image_target_desc *image_target)
+{
+  if (image_target->id == IMAGE_EFI)
+    finish_reloc_translation_pe (ctx, layout, image_target);
+  else
+    finish_reloc_translation_raw (ctx, layout, image_target);
+}
+
+
+static void
+create_u64_fixups (struct translate_context *ctx,
+		   Elf_Addr jumpers, grub_size_t njumpers,
+		   const struct grub_install_image_target_desc *image_target)
+{
+  unsigned i;
+  assert (image_target->id == IMAGE_EFI);
+  for (i = 0; i < njumpers; i++)
+    ctx->current_address = add_fixup_entry (&ctx->lst,
+					    GRUB_PE32_REL_BASED_DIR64,
+					    jumpers + 8 * i,
+					    0, ctx->current_address,
+					    image_target);
+}
+
+/* Make a .reloc section.  */
+static void
+make_reloc_section (Elf_Ehdr *e, struct grub_mkimage_layout *layout,
+		    struct section_metadata *smd,
+		    const struct grub_install_image_target_desc *image_target)
+{
+  unsigned i;
+  Elf_Shdr *s;
+  struct translate_context ctx;
+
+  translate_reloc_start (&ctx, image_target);
+
+  for (i = 0, s = smd->sections; i < smd->num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + smd->section_entsize))
+    if ((grub_target_to_host32 (s->sh_type) == SHT_REL) ||
+        (grub_target_to_host32 (s->sh_type) == SHT_RELA))
+      {
+	Elf_Rel *r;
+	Elf_Word rtab_size, r_size, num_rs;
+	Elf_Off rtab_offset;
+	Elf_Addr section_address;
+	Elf_Word j;
+
+	if (!SUFFIX (is_kept_reloc_section) (s, image_target, smd))
+	  {
+	    grub_util_info ("not translating the skipped relocation section %s",
+			    smd->strtab + grub_le_to_cpu32 (s->sh_name));
+	    continue;
+	  }
+
+	grub_util_info ("translating the relocation section %s",
+			smd->strtab + grub_le_to_cpu32 (s->sh_name));
+
+	rtab_size = grub_target_to_host (s->sh_size);
+	r_size = grub_target_to_host (s->sh_entsize);
+	rtab_offset = grub_target_to_host (s->sh_offset);
+	num_rs = rtab_size / r_size;
+
+	section_address = smd->vaddrs[grub_le_to_cpu32 (s->sh_info)];
+
+	for (j = 0, r = (Elf_Rel *) ((char *) e + rtab_offset);
+	     j < num_rs;
+	     j++, r = (Elf_Rel *) ((char *) r + r_size))
+	  {
+	    Elf_Addr info;
+	    Elf_Addr offset;
+	    Elf_Addr addr;
+
+	    offset = grub_target_to_host (r->r_offset);
+	    info = grub_target_to_host (r->r_info);
+
+	    addr = section_address + offset;
+
+	    translate_relocation (&ctx, addr, info, image_target);
+	  }
+      }
+
+  if (image_target->elf_target == EM_IA_64)
+    create_u64_fixups (&ctx,
+		       layout->ia64jmp_off
+		       + image_target->vaddr_offset,
+		       2 * layout->ia64jmpnum,
+		       image_target);
+  if (image_target->elf_target == EM_IA_64 || image_target->elf_target == EM_AARCH64)
+    create_u64_fixups (&ctx,
+		       layout->got_off
+		       + image_target->vaddr_offset,
+		       (layout->got_size / 8),
+		       image_target);
+
+  finish_reloc_translation (&ctx, layout, image_target);
+}
+
+/* Determine if this section is a text section. Return false if this
+   section is not allocated.  */
+static int
+SUFFIX (is_text_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target)
+{
+  if (!is_relocatable (image_target)
+      && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS)
+    return 0;
+  return ((grub_target_to_host (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC))
+	  == (SHF_EXECINSTR | SHF_ALLOC));
+}
+
+/* Determine if this section is a data section.  */
+static int
+SUFFIX (is_data_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target)
+{
+  if (!is_relocatable (image_target) 
+      && grub_target_to_host32 (s->sh_type) != SHT_PROGBITS)
+    return 0;
+  return ((grub_target_to_host (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC))
+	  == SHF_ALLOC) && !(grub_target_to_host32 (s->sh_type) == SHT_NOBITS);
+}
+
+static int
+SUFFIX (is_bss_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target)
+{
+  if (!is_relocatable (image_target))
+    return 0;
+  return ((grub_target_to_host (s->sh_flags) & (SHF_EXECINSTR | SHF_ALLOC))
+	  == SHF_ALLOC) && (grub_target_to_host32 (s->sh_type) == SHT_NOBITS);
+}
+
+/* Determine if a section is going to be in the final output */
+static int
+SUFFIX (is_kept_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target)
+{
+  /* We keep .text and .data */
+  if (SUFFIX (is_text_section) (s, image_target)
+      || SUFFIX (is_data_section) (s, image_target))
+    return 1;
+
+  /*
+   * And we keep .bss if we're producing PE binaries or the target doesn't
+   * have a relocating loader.  Platforms other than EFI and U-boot shouldn't
+   * have .bss in their binaries as we build with -Wl,-Ttext.
+   */
+  if (SUFFIX (is_bss_section) (s, image_target)
+      && (image_target->id == IMAGE_EFI || !is_relocatable (image_target)))
+    return 1;
+
+  /* Otherwise this is not a section we're keeping in the final output. */
+  return 0;
+}
+
+static int
+SUFFIX (is_kept_reloc_section) (Elf_Shdr *s, const struct grub_install_image_target_desc *image_target,
+				struct section_metadata *smd)
+{
+  int i;
+  int r = 0;
+  const char *name = smd->strtab + grub_host_to_target32 (s->sh_name);
+
+  if (!strncmp (name, ".rela.", 6))
+    name += 5;
+  else if (!strncmp (name, ".rel.", 5))
+    name += 4;
+  else
+    return 1;
+
+  for (i = 0, s = smd->sections; i < smd->num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + smd->section_entsize))
+    {
+      const char *sname = smd->strtab + grub_host_to_target32 (s->sh_name);
+      if (strcmp (sname, name))
+	continue;
+
+      return SUFFIX (is_kept_section) (s, image_target);
+    }
+
+  return r;
+}
+
+/* Return if the ELF header is valid.  */
+static int
+SUFFIX (check_elf_header) (Elf_Ehdr *e, size_t size, const struct grub_install_image_target_desc *image_target)
+{
+  if (size < sizeof (*e)
+      || e->e_ident[EI_MAG0] != ELFMAG0
+      || e->e_ident[EI_MAG1] != ELFMAG1
+      || e->e_ident[EI_MAG2] != ELFMAG2
+      || e->e_ident[EI_MAG3] != ELFMAG3
+      || e->e_ident[EI_VERSION] != EV_CURRENT
+      || e->e_ident[EI_CLASS] != ELFCLASSXX
+      || e->e_version != grub_host_to_target32 (EV_CURRENT))
+    return 0;
+
+  return 1;
+}
+
+static Elf_Addr
+SUFFIX (put_section) (Elf_Shdr *s, int i,
+		      Elf_Addr current_address,
+		      struct section_metadata *smd,
+		      const struct grub_install_image_target_desc *image_target)
+{
+	Elf_Word align = grub_host_to_target_addr (s->sh_addralign);
+	const char *name = smd->strtab + grub_host_to_target32 (s->sh_name);
+
+	if (align)
+	  current_address = ALIGN_UP (current_address + image_target->vaddr_offset,
+				      align)
+	    - image_target->vaddr_offset;
+
+	grub_util_info ("locating the section %s at 0x%"
+			GRUB_HOST_PRIxLONG_LONG,
+			name, (unsigned long long) current_address);
+	if (!is_relocatable (image_target))
+	  current_address = grub_host_to_target_addr (s->sh_addr)
+			    - image_target->link_addr;
+	smd->addrs[i] = current_address;
+	current_address += grub_host_to_target_addr (s->sh_size);
+	return current_address;
+}
+
+/*
+ * Locate section addresses by merging code sections and data sections
+ * into .text and .data, respectively.
+ */
+static void
+SUFFIX (locate_sections) (Elf_Ehdr *e, const char *kernel_path,
+			  struct section_metadata *smd,
+			  struct grub_mkimage_layout *layout,
+			  const struct grub_install_image_target_desc *image_target)
+{
+  int i;
+  Elf_Shdr *s;
+
+  layout->align = 1;
+  /* Page-aligning simplifies relocation handling.  */
+  if (image_target->elf_target == EM_AARCH64)
+    layout->align = 4096;
+
+  layout->kernel_size = 0;
+
+  for (i = 0, s = smd->sections;
+       i < smd->num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + smd->section_entsize))
+    if ((grub_target_to_host (s->sh_flags) & SHF_ALLOC)
+	&& grub_host_to_target32 (s->sh_addralign) > layout->align)
+      layout->align = grub_host_to_target32 (s->sh_addralign);
+
+  /* .text */
+  for (i = 0, s = smd->sections;
+       i < smd->num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + smd->section_entsize))
+    if (SUFFIX (is_text_section) (s, image_target))
+      {
+	layout->kernel_size = SUFFIX (put_section) (s, i, layout->kernel_size,
+						smd, image_target);
+	if (!is_relocatable (image_target) &&
+	    grub_host_to_target_addr (s->sh_addr) != image_target->link_addr)
+	  {
+	    char *msg
+	      = grub_xasprintf (_("`%s' is miscompiled: its start address is 0x%llx"
+				  " instead of 0x%llx: ld.gold bug?"),
+				kernel_path,
+				(unsigned long long) grub_host_to_target_addr (s->sh_addr),
+				(unsigned long long) image_target->link_addr);
+	    grub_util_error ("%s", msg);
+	  }
+      }
+
+#ifdef MKIMAGE_ELF32
+  if (image_target->elf_target == EM_ARM)
+    {
+      grub_size_t tramp;
+
+      layout->kernel_size = ALIGN_UP (layout->kernel_size, 16);
+
+      tramp = arm_get_trampoline_size (e, smd->sections, smd->section_entsize,
+				       smd->num_sections, image_target);
+
+      layout->tramp_off = layout->kernel_size;
+      layout->kernel_size += ALIGN_UP (tramp, 16);
+    }
+#endif
+
+  layout->kernel_size = ALIGN_UP (layout->kernel_size + image_target->vaddr_offset,
+			      image_target->section_align)
+    - image_target->vaddr_offset;
+  layout->exec_size = layout->kernel_size;
+
+  /* .data */
+  for (i = 0, s = smd->sections;
+       i < smd->num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + smd->section_entsize))
+    if (SUFFIX (is_data_section) (s, image_target))
+      layout->kernel_size = SUFFIX (put_section) (s, i, layout->kernel_size, smd,
+						  image_target);
+
+  layout->bss_start = layout->kernel_size;
+  layout->end = layout->kernel_size;
+  
+  /* .bss */
+  for (i = 0, s = smd->sections;
+       i < smd->num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + smd->section_entsize))
+    {
+      if (SUFFIX (is_bss_section) (s, image_target))
+	layout->end = SUFFIX (put_section) (s, i, layout->end, smd, image_target);
+
+      /*
+       * This must to be in the last time this function passes through the loop.
+       */
+      smd->vaddrs[i] = smd->addrs[i] + image_target->vaddr_offset;
+    }
+
+  layout->end = ALIGN_UP (layout->end + image_target->vaddr_offset,
+			      image_target->section_align) - image_target->vaddr_offset;
+  /* Explicitly initialize BSS
+     when producing PE32 to avoid a bug in EFI implementations.
+     Platforms other than EFI and U-boot shouldn't have .bss in
+     their binaries as we build with -Wl,-Ttext.
+  */
+  if (image_target->id == IMAGE_EFI || !is_relocatable (image_target))
+    layout->kernel_size = layout->end;
+}
+
+char *
+SUFFIX (grub_mkimage_load_image) (const char *kernel_path,
+				  size_t total_module_size,
+				  struct grub_mkimage_layout *layout,
+				  const struct grub_install_image_target_desc *image_target)
+{
+  char *kernel_img, *out_img;
+  struct section_metadata smd = { 0, 0, 0, 0, 0, 0, 0 };
+  Elf_Ehdr *e;
+  int i;
+  Elf_Shdr *s;
+  Elf_Off section_offset;
+  grub_size_t kernel_size;
+
+  grub_memset (layout, 0, sizeof (*layout));
+
+  layout->start_address = 0;
+
+  kernel_size = grub_util_get_image_size (kernel_path);
+  kernel_img = xmalloc (kernel_size);
+  grub_util_load_image (kernel_path, kernel_img);
+
+  e = (Elf_Ehdr *) kernel_img;
+  if (! SUFFIX (check_elf_header) (e, kernel_size, image_target))
+    grub_util_error ("invalid ELF header");
+
+  section_offset = grub_target_to_host (e->e_shoff);
+  smd.section_entsize = grub_target_to_host16 (e->e_shentsize);
+  smd.num_sections = grub_target_to_host16 (e->e_shnum);
+
+  if (kernel_size < section_offset
+		    + (grub_uint32_t) smd.section_entsize * smd.num_sections)
+    grub_util_error (_("premature end of file %s"), kernel_path);
+
+  smd.sections = (Elf_Shdr *) (kernel_img + section_offset);
+
+  /* Relocate sections then symbols in the virtual address space.  */
+  s = (Elf_Shdr *) ((char *) smd.sections
+		      + grub_host_to_target16 (e->e_shstrndx) * smd.section_entsize);
+  smd.strtab = (char *) e + grub_host_to_target_addr (s->sh_offset);
+
+  smd.addrs = xcalloc (smd.num_sections, sizeof (*smd.addrs));
+  smd.vaddrs = xcalloc (smd.num_sections, sizeof (*smd.vaddrs));
+
+  SUFFIX (locate_sections) (e, kernel_path, &smd, layout, image_target);
+
+  if (!is_relocatable (image_target))
+    {
+      Elf_Addr current_address = layout->kernel_size;
+
+      for (i = 0, s = smd.sections;
+	   i < smd.num_sections;
+	   i++, s = (Elf_Shdr *) ((char *) s + smd.section_entsize))
+	if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS)
+	  {
+	    Elf_Word sec_align = grub_host_to_target_addr (s->sh_addralign);
+	    const char *name = smd.strtab + grub_host_to_target32 (s->sh_name);
+
+	    if (sec_align)
+	      current_address = ALIGN_UP (current_address
+					  + image_target->vaddr_offset,
+					  sec_align)
+		- image_target->vaddr_offset;
+
+	    grub_util_info ("locating the section %s at 0x%"
+			    GRUB_HOST_PRIxLONG_LONG,
+			    name, (unsigned long long) current_address);
+	    if (!is_relocatable (image_target))
+	      current_address = grub_host_to_target_addr (s->sh_addr)
+		- image_target->link_addr;
+
+	    smd.vaddrs[i] = current_address
+	      + image_target->vaddr_offset;
+	    current_address += grub_host_to_target_addr (s->sh_size);
+	  }
+      current_address = ALIGN_UP (current_address + image_target->vaddr_offset,
+				  image_target->section_align)
+	- image_target->vaddr_offset;
+      layout->bss_size = current_address - layout->kernel_size;
+    }
+  else
+    layout->bss_size = 0;
+
+  if (image_target->id == IMAGE_SPARC64_AOUT
+      || image_target->id == IMAGE_SPARC64_RAW
+      || image_target->id == IMAGE_UBOOT
+      || image_target->id == IMAGE_COREBOOT
+      || image_target->id == IMAGE_SPARC64_CDCORE)
+    layout->kernel_size = ALIGN_UP (layout->kernel_size, image_target->mod_align);
+
+  if (is_relocatable (image_target))
+    {
+      smd.symtab = NULL;
+      for (i = 0, s = smd.sections;
+	   i < smd.num_sections;
+	   i++, s = (Elf_Shdr *) ((char *) s + smd.section_entsize))
+	if (s->sh_type == grub_host_to_target32 (SHT_SYMTAB))
+	  {
+	    smd.symtab = s;
+	    break;
+	  }
+      if (! smd.symtab)
+	grub_util_error ("%s", _("no symbol table"));
+#ifdef MKIMAGE_ELF64
+      if (image_target->elf_target == EM_IA_64)
+	{
+	  grub_size_t tramp;
+
+	  layout->kernel_size = ALIGN_UP (layout->kernel_size, 16);
+
+	  grub_ia64_dl_get_tramp_got_size (e, &tramp, &layout->got_size);
+
+	  layout->tramp_off = layout->kernel_size;
+	  layout->kernel_size += ALIGN_UP (tramp, 16);
+
+	  layout->ia64jmp_off = layout->kernel_size;
+	  layout->ia64jmpnum = SUFFIX (count_funcs) (e, smd.symtab,
+						     image_target);
+	  layout->kernel_size += 16 * layout->ia64jmpnum;
+
+	  layout->got_off = layout->kernel_size;
+	  layout->kernel_size += ALIGN_UP (layout->got_size, 16);
+	}
+      if (image_target->elf_target == EM_AARCH64)
+	{
+	  grub_size_t tramp;
+
+	  layout->kernel_size = ALIGN_UP (layout->kernel_size, 16);
+
+	  grub_arm64_dl_get_tramp_got_size (e, &tramp, &layout->got_size);
+
+	  layout->got_off = layout->kernel_size;
+	  layout->kernel_size += ALIGN_UP (layout->got_size, 16);
+	}
+#endif
+
+      if (image_target->id == IMAGE_EFI)
+        layout->kernel_size = ALIGN_UP (layout->kernel_size,
+                                        GRUB_PE32_FILE_ALIGNMENT);
+    }
+  else
+    {
+      layout->reloc_size = 0;
+      layout->reloc_section = NULL;
+    }
+
+  out_img = xmalloc (layout->kernel_size + total_module_size);
+  memset (out_img, 0, layout->kernel_size + total_module_size);
+
+  if (is_relocatable (image_target))
+    {
+      layout->start_address = SUFFIX (relocate_symbols) (e, &smd,
+				  (char *) out_img + layout->ia64jmp_off,
+				  layout->ia64jmp_off + image_target->vaddr_offset,
+				  layout->bss_start, layout->end, image_target);
+
+      if (layout->start_address == (Elf_Addr) -1)
+	grub_util_error ("start symbol is not defined");
+
+      /* Resolve addrs in the virtual address space.  */
+      SUFFIX (relocate_addrs) (e, &smd, out_img, layout->tramp_off,
+				   layout->got_off, image_target);
+
+      make_reloc_section (e, layout, &smd, image_target);
+      if (image_target->id != IMAGE_EFI)
+	{
+	  out_img = xrealloc (out_img, layout->kernel_size + total_module_size
+			      + ALIGN_UP (layout->reloc_size, image_target->mod_align));
+	  memcpy (out_img + layout->kernel_size, layout->reloc_section, layout->reloc_size);
+	  memset (out_img + layout->kernel_size + layout->reloc_size, 0,
+		  total_module_size + ALIGN_UP (layout->reloc_size, image_target->mod_align) - layout->reloc_size);
+	  layout->kernel_size += ALIGN_UP (layout->reloc_size, image_target->mod_align);
+	}
+    }
+
+  for (i = 0, s = smd.sections;
+       i < smd.num_sections;
+       i++, s = (Elf_Shdr *) ((char *) s + smd.section_entsize))
+    if (SUFFIX (is_kept_section) (s, image_target))
+      {
+	if (grub_target_to_host32 (s->sh_type) == SHT_NOBITS)
+	  memset (out_img + smd.addrs[i], 0,
+		  grub_host_to_target_addr (s->sh_size));
+	else
+	  memcpy (out_img + smd.addrs[i],
+		  kernel_img + grub_host_to_target_addr (s->sh_offset),
+		  grub_host_to_target_addr (s->sh_size));
+      }
+  free (kernel_img);
+
+  free (smd.vaddrs);
+  smd.vaddrs = NULL;
+  free (smd.addrs);
+  smd.addrs = NULL;
+
+  return out_img;
+}