ET_DYN also known as .so files are actually meant to be loaded dynamically.
Most of the work of linking is done by normal linker and we need to do
only simple relocation by offset. This significantly simplifies our
dynamic loading. Moreover the tools are meant to produce .so files and so
it's easier to get them to produce correct output. Also it reduce the
core size on i386-pc by 44 bytes on config biosdisk+part_msdos+ext2

This required to use -fPIC on mips as otherwise linker chokes on relocations
incompatible with -shared

This was originally meant for Rust as rustc produces .so output
as a possibility but it simplifies the code, so it's useful independently of
Rust

Signed-off-by: Vladimir Serbinenko <phco...@gmail.com>
---
 conf/Makefile.common                          |  10 +-
 conf/i386-modules.sc                          |  35 +++
 configure.ac                                  |  39 +--
 .../commands/tpm2_key_protector/tpm2key.c     |   7 +
 grub-core/genmod.sh.in                        |  20 +-
 grub-core/genmoddep.awk                       |   2 +-
 grub-core/kern/arm/dl.c                       |  20 +-
 grub-core/kern/arm64/dl.c                     |  22 +-
 grub-core/kern/dl.c                           | 274 ++++++++----------
 grub-core/kern/emu/full.c                     |   4 +-
 grub-core/kern/i386/dl.c                      |  19 +-
 grub-core/kern/ia64/dl.c                      |  49 +++-
 grub-core/kern/loongarch64/dl.c               |  20 +-
 grub-core/kern/mips/dl.c                      |  78 ++++-
 grub-core/kern/powerpc/dl.c                   |  15 +-
 grub-core/kern/riscv/dl.c                     |  23 +-
 grub-core/kern/sparc64/dl.c                   |  15 +-
 grub-core/kern/x86_64/dl.c                    |  21 +-
 grub-core/lib/backtrace.c                     |   9 +-
 include/grub/dl.h                             |  33 ++-
 include/grub/elf.h                            |   4 +
 util/grub-mkimagexx.c                         |   2 +
 util/grub-module-verifier.c                   |  30 +-
 util/grub-module-verifierXX.c                 |  56 +++-
 24 files changed, 519 insertions(+), 288 deletions(-)
 create mode 100644 conf/i386-modules.sc

diff --git a/conf/Makefile.common b/conf/Makefile.common
index c60f55386..3e2dff090 100644
--- a/conf/Makefile.common
+++ b/conf/Makefile.common
@@ -51,7 +51,15 @@ endif
 endif
 
 CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding
-LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) 
-Wl,-r
+if COND_emu
+LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib -Wl,-r
+else
+if COND_mips
+LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) 
-shared
+else
+LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) 
-shared -Wl,-Ttext-segment=0 -Wl,-Bstatic -Wl,-T${srcdir}/../conf/modules.sc
+endif
+endif
 CPPFLAGS_MODULE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM)
 CCASFLAGS_MODULE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM)
 
diff --git a/conf/i386-modules.sc b/conf/i386-modules.sc
new file mode 100644
index 000000000..aab26e4a1
--- /dev/null
+++ b/conf/i386-modules.sc
@@ -0,0 +1,35 @@
+SECTIONS
+{
+  .text :
+  {
+    *(.text)
+  }
+  .data :
+  {
+    *(.data)
+    *(.rdata)
+    *(.pdata)
+  }
+  .bss :
+  {
+    *(.bss)
+    *(COMMON)
+  }
+  .edata :
+  {
+    *(.edata)
+  }
+  .stab :
+  {
+    *(.stab)
+  }
+  .stabstr :
+  {
+    *(.stabstr)
+  }
+
+  /DISCARD/ :
+  {
+     *(.dynamic)
+  }
+}
diff --git a/configure.ac b/configure.ac
index ad1e7bea5..cdb6a5c41 100644
--- a/configure.ac
+++ b/configure.ac
@@ -876,16 +876,8 @@ if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = 
xx86_64 ); then
 fi
 
 if test "x$target_cpu" = xloongarch64; then
-  AC_CACHE_CHECK([whether _mno_explicit_relocs works], 
[grub_cv_cc_mno_explicit_relocs], [
-    CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs -Werror"
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
-       [grub_cv_cc_mno_explicit_relocs=yes],
-       [grub_cv_cc_mno_explicit_relocs=no])
-  ])
-  if test "x$grub_cv_cc_mno_explicit_relocs" = xyes; then
-    TARGET_CFLAGS="$TARGET_CFLAGS -mno-explicit-relocs -fno-plt"
-    TARGET_CCASFLAGS="$TARGET_CCASFLAGS -mno-explicit-relocs -fno-plt"
-  fi
+  TARGET_CFLAGS="$TARGET_CFLAGS -fno-plt"
+  TARGET_CCASFLAGS="$TARGET_CCASFLAGS -fno-plt"
 
   AC_CACHE_CHECK([for no-relax options], grub_cv_target_cc_mno_relax, [
     grub_cv_target_cc_mno_relax=no
@@ -1215,6 +1207,10 @@ else
   TARGET_IMG_CFLAGS=
 fi
 
+if test x${platform} = xefi || test x${platform} = xemu; then
+  TARGET_LDFLAGS_OLDMAGIC=
+fi
+
 CFLAGS="$TARGET_CFLAGS"
 
 AC_ARG_ENABLE([efiemu],
@@ -1395,18 +1391,27 @@ grub_CHECK_PIC
 [# On most platforms we don't want PIC as it only makes relocations harder
 # and code less efficient. On mips we want to have one got table per module
 # and reload $gp in every function.
-# GCC implements it using symbol __gnu_local_gp in non-PIC as well.
-# However with clang we need PIC for this reloading to happen.
 # With arm64 we need relocations that are in some way representable in
 # PE as we need to support arm64-efi. Without -fPIC clang generates
 # movk's which aren't representable.
 # Since default varies across dictributions use either -fPIC or -fno-PIC
 # explicitly.
-if ( test x$target_cpu = xmips || test x$target_cpu = xmipsel || test 
x$target_cpu = xarm64 ) && test "x$grub_cv_cc_target_clang" = xyes ; then
-   TARGET_CFLAGS="$TARGET_CFLAGS -fPIC"
-elif [ x"$pic_possible" = xyes ]; then
-   TARGET_CFLAGS="$TARGET_CFLAGS -fno-PIC"
-fi]
+case $target_cpu-$platform in
+     mips-* | mipsel-*)
+        TARGET_CFLAGS="$TARGET_CFLAGS -fPIC -mabicalls"
+       ;;
+     arm64-*)
+       if test "x$grub_cv_cc_target_clang" = xyes ; then
+          TARGET_CFLAGS="$TARGET_CFLAGS -fPIC"
+       fi
+       ;;
+     riscv32-* | ia64-*)
+        TARGET_CFLAGS="$TARGET_CFLAGS -fPIC"
+       ;;
+     *)
+        TARGET_CFLAGS="$TARGET_CFLAGS -fno-PIC"
+       ;;
+esac]
 
 CFLAGS="$TARGET_CFLAGS"
 
diff --git a/grub-core/commands/tpm2_key_protector/tpm2key.c 
b/grub-core/commands/tpm2_key_protector/tpm2key.c
index 3b6001d84..b8d5227dd 100644
--- a/grub-core/commands/tpm2_key_protector/tpm2key.c
+++ b/grub-core/commands/tpm2_key_protector/tpm2key.c
@@ -39,6 +39,13 @@ asn1_allocate_and_read (asn1_node node, const char *name, 
void **content, grub_s
     return ASN1_MEM_ERROR;
 
   ret = asn1_read_value (node, name, NULL, &tmpstr_size);
+  if (ret == ASN1_SUCCESS)
+    {
+      *content = NULL;
+      *content_size = 0;
+
+      return ASN1_SUCCESS;
+    }
   if (ret != ASN1_MEM_ERROR)
     return ret;
 
diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in
index 337753c57..fb15b6608 100644
--- a/grub-core/genmod.sh.in
+++ b/grub-core/genmod.sh.in
@@ -23,6 +23,7 @@ infile=$2
 outfile=$4
 
 tmpfile=${outfile}.tmp
+tmpfile2=${outfile}.tmp2
 modname=`echo $infile | sed -e 's@\.module.*$@@'`
 
 if ! grep ^$modname: $moddep >/dev/null; then
@@ -33,7 +34,7 @@ fi
 deps=`grep ^$modname: $moddep | sed s@^.*:@@`
 
 # remove old files if any
-rm -f $tmpfile $outfile
+rm -f $tmpfile $tmpfile2 $outfile
 
 if test x@TARGET_APPLE_LINKER@ != x1; then
     # stripout .modname and .moddeps sections from input module
@@ -53,10 +54,8 @@ if test x@TARGET_APPLE_LINKER@ != x1; then
     fi
     rm -f $t1 $t2
 
-       if test x@platform@ != xemu; then
-           @TARGET_STRIP@ --strip-unneeded \
-               -K grub_mod_init -K grub_mod_fini \
-               -K _grub_mod_init -K _grub_mod_fini \
+    if test x@platform@ != xemu; then
+       STRIP_FLAGS="--strip-unneeded \
                -R .note.GNU-stack \
                -R .note.gnu.gold-version \
                -R .note.gnu.property \
@@ -65,13 +64,20 @@ if test x@TARGET_APPLE_LINKER@ != x1; then
                -R .rel.gnu.build.attributes \
                -R .rela.gnu.build.attributes \
                -R .eh_frame -R .rela.eh_frame -R .rel.eh_frame \
-               -R .note -R .comment -R .ARM.exidx $tmpfile || exit 1
+               -R .note -R .comment -R .gnu.hash -R .ARM.exidx \
+               -R .symtab -R .strtab -R .shstrtab"
+       if test x@target_cpu@ != xmips && test x@target_cpu@ != xmipsel && test 
x@target_cpu@ != xia64; then
+           STRIP_FLAGS="$STRIP_FLAGS -R .dynamic"
        fi
+       @TARGET_STRIP@ $STRIP_FLAGS -s $tmpfile || exit 1
+    else
+       @TARGET_CC@ -Wl,--export-dynamic -o $tmpfile2 -shared -nostdlib $tmpfile
+       cp $tmpfile2 $tmpfile
+    fi
        if ! test -z "${TARGET_OBJ2ELF}"; then
            "${TARGET_OBJ2ELF}" $tmpfile || exit 1
        fi
 else
-    tmpfile2=${outfile}.tmp2
     t1=${outfile}.t1.c
     t2=${outfile}.t2.c
 
diff --git a/grub-core/genmoddep.awk b/grub-core/genmoddep.awk
index ab457cb2b..09832ab91 100644
--- a/grub-core/genmoddep.awk
+++ b/grub-core/genmoddep.awk
@@ -18,7 +18,7 @@ BEGIN {
 
 {
   if ($1 == "defined") {
-    if ($3 !~ /^\.refptr\./ && $3 in symtab) {
+    if ($3 !~ /^\.refptr\./ && $3 != "grub_mod_init" && $3 != "grub_mod_fini" 
&& $3 != "__bss_start" && $3 != "_fdata" && $3 != "_ftext" && $3 != "_fbss" && 
$3 != "_edata" && $3 != "_end" && $3 != "__aeabi_uidivmod" && $3 != 
"__aeabi_idivmod" && $3 in symtab) {
       printf "%s in %s is duplicated in %s\n", $3, $2, symtab[$3] 
>"/dev/stderr";
       error++;
     }
diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c
index eab9d17ff..3d8bd9f9a 100644
--- a/grub-core/kern/arm/dl.c
+++ b/grub-core/kern/arm/dl.c
@@ -108,8 +108,7 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, 
grub_size_t *tramp,
  * Runtime dynamic linker with helper functions. *
  *************************************************/
 grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
-                              Elf_Shdr *s, grub_dl_segment_t seg)
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s)
 {
   Elf_Rel *rel, *max;
 
@@ -122,10 +121,11 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       grub_err_t retval;
       Elf_Sym *sym;
 
-      if (seg->size < rel->r_offset)
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
-      target = (void *) ((char *) seg->addr + rel->r_offset);
+                          "reloc offset is out of the segment: %x not in 
[%x..%x]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
+      target = (void *) ((char *) mod->base + rel->r_offset - mod->min_addr);
       sym = (Elf_Sym *) ((char *) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
 
@@ -134,6 +134,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       switch (ELF_R_TYPE (rel->r_info))
        {
        case R_ARM_ABS32:
+       case R_ARM_GLOB_DAT:
          {
            /* Data will be naturally aligned */
            retval = grub_arm_reloc_abs32 (target, sym_addr);
@@ -141,6 +142,15 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
              return retval;
          }
          break;
+
+       case R_ARM_JUMP_SLOT:
+         *target = sym_addr;
+         break;
+
+       case R_ARM_RELATIVE:
+         *target += (grub_addr_t) mod->base - mod->min_addr;
+         break;
+
        case R_ARM_CALL:
        case R_ARM_JUMP24:
          {
diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c
index a2b5789a9..7aceff077 100644
--- a/grub-core/kern/arm64/dl.c
+++ b/grub-core/kern/arm64/dl.c
@@ -52,8 +52,7 @@ grub_arch_dl_check_header (void *ehdr)
  * Unified function for both REL and RELA
  */
 grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
-                              Elf_Shdr *s, grub_dl_segment_t seg)
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s)
 {
   Elf_Rel *rel, *max;
   unsigned unmatched_adr_got_page = 0;
@@ -67,9 +66,10 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       void *place;
       grub_uint64_t sym_addr;
 
-      if (rel->r_offset >= seg->size)
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
+                          "reloc offset is out of the segment: %lx not in 
[%lx..%lx]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
 
       sym = (Elf_Sym *) ((char *) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
@@ -78,11 +78,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       if (s->sh_type == SHT_RELA)
        sym_addr += ((Elf_Rela *) rel)->r_addend;
 
-      place = (void *) ((grub_addr_t) seg->addr + rel->r_offset);
+      place = (void *) ((char *) mod->base + rel->r_offset - mod->min_addr);
 
       switch (ELF_R_TYPE (rel->r_info))
        {
        case R_AARCH64_ABS64:
+       case R_AARCH64_JUMP_SLOT:
+       case R_AARCH64_GLOB_DAT:
          {
            grub_uint64_t *abs_place = place;
 
@@ -125,7 +127,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
            grub_int64_t value;
            Elf64_Word *addr32 = place;
            value = ((grub_int32_t) *addr32) + sym_addr -
-             (Elf64_Xword) (grub_addr_t) seg->addr - rel->r_offset;
+             (Elf64_Xword) (grub_addr_t) place;
            if (value != (grub_int32_t) value)
              return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of 
range");
            grub_dprintf("dl", "  reloc_prel32 %p => 0x%016llx\n",
@@ -155,7 +157,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
                  && ((Elf_Rela *) rel)->r_addend == rel2->r_addend
                  && ELF_R_TYPE (rel2->r_info) == R_AARCH64_LD64_GOT_LO12_NC)
                {
-                 grub_arm64_set_abs_lo12_ldst64 ((void *) ((grub_addr_t) 
seg->addr + rel2->r_offset),
+                 grub_arm64_set_abs_lo12_ldst64 ((void *) ((char *) mod->base 
+ rel2->r_offset - mod->min_addr),
                                                  (grub_uint64_t)gp);
                  break;
                }
@@ -182,6 +184,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
          }
          break;
 
+       case R_AARCH64_RELATIVE:
+         *(grub_uint64_t *)place = (grub_addr_t) mod->base - mod->min_addr;
+         if (s->sh_type == SHT_RELA)
+           *(grub_uint64_t *)place += ((Elf_Rela *) rel)->r_addend;
+         break;
+
        default:
          {
            char rel_info[17]; /* log16(2^64) = 16, plus NUL. */
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index de8c3aa8d..e5fd16bd5 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -187,19 +187,6 @@ grub_dl_unregister_symbols (grub_dl_t mod)
     }
 }
 
-/* Return the address of a section whose index is N.  */
-static void *
-grub_dl_get_section_addr (grub_dl_t mod, unsigned n)
-{
-  grub_dl_segment_t seg;
-
-  for (seg = mod->segment; seg; seg = seg->next)
-    if (seg->section == n)
-      return seg->addr;
-
-  return 0;
-}
-
 /* Check if EHDR is a valid ELF header.  */
 static grub_err_t
 grub_dl_check_header (void *ehdr, grub_size_t size)
@@ -232,35 +219,31 @@ static grub_err_t
 grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
 {
   unsigned i;
-  const Elf_Shdr *s;
-  grub_size_t tsize = 0, talign = 1, arch_addralign = 1;
+  const Elf_Phdr *p;
+  grub_size_t talign = DL_ALIGN;
 #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
   !defined (__loongarch__)
   grub_size_t tramp;
   grub_size_t tramp_align;
   grub_size_t got;
   grub_size_t got_align;
+  grub_addr_t tramp_addr = 0;
+  grub_addr_t got_addr = 0;
   grub_err_t err;
 #endif
-  char *ptr;
+  grub_addr_t min_addr = ~(grub_addr_t)0;
+  grub_addr_t max_addr = 0;
 
-  arch_addralign = DL_ALIGN;
-
-  for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize))
+  for (i = 0, p = (const Elf_Phdr *)((const char *) e + e->e_phoff);
+       i < e->e_phnum;
+       i++, p = (const Elf_Phdr *)((const char *) p + e->e_phentsize))
     {
-      grub_size_t sh_addralign;
-      grub_size_t sh_size;
-
-      if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC))
+      if (p->p_type != PT_LOAD)
        continue;
 
-      sh_addralign = ALIGN_UP (s->sh_addralign, arch_addralign);
-      sh_size = ALIGN_UP (s->sh_size, sh_addralign);
-
-      tsize = ALIGN_UP (tsize, sh_addralign) + sh_size;
-      talign = grub_max (talign, sh_addralign);
+      min_addr = grub_min(min_addr, p->p_vaddr);
+      max_addr = grub_max(max_addr, p->p_vaddr + p->p_memsz);
+      talign = grub_max (talign, p->p_align);
     }
 
 #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
@@ -268,79 +251,48 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
   err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got);
   if (err)
     return err;
-  tramp_align = grub_max (GRUB_ARCH_DL_TRAMP_ALIGN, arch_addralign);
-  tsize += ALIGN_UP (tramp, tramp_align);
+  tramp_align = grub_max (GRUB_ARCH_DL_TRAMP_ALIGN, DL_ALIGN);
+  tramp_addr = ALIGN_UP (max_addr, tramp_align);
+  max_addr = ALIGN_UP (tramp_addr+tramp, tramp_align);
   talign = grub_max (talign, tramp_align);
-  got_align = grub_max (GRUB_ARCH_DL_GOT_ALIGN, arch_addralign);
-  tsize += ALIGN_UP (got, got_align);
+  got_align = grub_max (GRUB_ARCH_DL_GOT_ALIGN, DL_ALIGN);
+  got_addr = ALIGN_UP(max_addr, got_align);
+  max_addr = ALIGN_UP(got_addr + got, got_align);
   talign = grub_max (talign, got_align);
 #endif
 
+  min_addr = ALIGN_DOWN(min_addr, talign);
+
 #ifdef GRUB_MACHINE_EMU
-  mod->base = grub_osdep_dl_memalign (talign, tsize);
+  mod->base = grub_osdep_dl_memalign (talign, max_addr - min_addr);
 #else
-  mod->base = grub_memalign (talign, tsize);
+  mod->base = grub_memalign (talign, max_addr - min_addr);
 #endif
   if (!mod->base)
     return grub_errno;
-  mod->sz = tsize;
-  ptr = mod->base;
+  mod->sz = max_addr - min_addr;
+  mod->min_addr = min_addr;
 
-  for (i = 0, s = (Elf_Shdr *)((char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
+  for (i = 0, p = (const Elf_Phdr *)((const char *) e + e->e_phoff);
+       i < e->e_phnum;
+       i++, p = (const Elf_Phdr *)((const char *) p + e->e_phentsize))
     {
-      grub_size_t sh_addralign = ALIGN_UP (s->sh_addralign, arch_addralign);
-      grub_size_t sh_size = ALIGN_UP (s->sh_size, sh_addralign);
-
-      if (s->sh_flags & SHF_ALLOC)
-       {
-         grub_dl_segment_t seg;
-
-         seg = (grub_dl_segment_t) grub_malloc (sizeof (*seg));
-         if (! seg)
-           return grub_errno;
-
-         if (s->sh_size)
-           {
-             void *addr;
-
-             ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign);
-             addr = ptr;
-             ptr += sh_size;
-
-             switch (s->sh_type)
-               {
-               case SHT_PROGBITS:
-                 grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
-                 grub_memset ((char *) addr + s->sh_size, 0, sh_size - 
s->sh_size);
-                 break;
-               case SHT_NOBITS:
-                 grub_memset (addr, 0, sh_size);
-                 break;
-               }
+#if defined(__mips__) || defined(__ia64__)
+      if (p->p_type == PT_DYNAMIC)
+       grub_arch_dl_parse_dynamic (mod, (Elf_Dyn *) ((char *) e + 
p->p_offset), p->p_filesz);
+#endif
 
-             seg->addr = addr;
-           }
-         else
-           seg->addr = 0;
+      if (p->p_type != PT_LOAD)
+       continue;
 
-         seg->size = sh_size;
-         seg->section = i;
-         seg->next = mod->segment;
-         mod->segment = seg;
-       }
+      void *addr = (char *)mod->base + (p->p_vaddr - mod->min_addr);
+      grub_memcpy (addr, (char *) e + p->p_offset, p->p_filesz);
+      grub_memset ((char *) addr + p->p_filesz, 0, p->p_memsz - p->p_filesz);
     }
 #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
   !defined (__loongarch__)
-  ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, tramp_align);
-  mod->tramp = ptr;
-  mod->trampptr = ptr;
-  ptr += tramp;
-  ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, got_align);
-  mod->got = ptr;
-  mod->gotptr = ptr;
-  ptr += got;
+  mod->trampptr = mod->tramp = (char *) mod->base + (tramp_addr - 
mod->min_addr);
+  mod->gotptr = mod->got = (char *) mod->base + (got_addr - mod->min_addr);
 #endif
 
   return GRUB_ERR_NONE;
@@ -355,10 +307,42 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
   const char *str;
   Elf_Word size, entsize;
 
+  /* On emu mod_init/mod_fini are not exported.  */
+#ifdef GRUB_MACHINE_EMU
+    for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
+       i < e->e_shnum;
+       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
+      if (s->sh_type == SHT_SYMTAB)
+       {
+         Elf_Shdr *s2;
+         sym = (Elf_Sym *) ((char *) e + s->sh_offset);
+         size = s->sh_size;
+         entsize = s->sh_entsize;
+
+         s2 = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shentsize * 
s->sh_link);
+         str = (char *) e + s2->sh_offset;
+
+         for (i = 0;
+              i < size / entsize;
+              i++, sym = (Elf_Sym *) ((char *) sym + entsize))
+           {
+             const char *name = str + sym->st_name;
+
+             if (ELF_ST_TYPE (sym->st_info) == STT_FUNC)
+               {
+                 if (grub_strcmp (name, "grub_mod_init") == 0)
+                   mod->init = (void (*) (grub_dl_t)) (sym->st_value + 
(Elf_Addr) mod->base - mod->min_addr);
+                 else if (grub_strcmp (name, "grub_mod_fini") == 0)
+                   mod->fini = (void (*) (void)) (sym->st_value + (Elf_Addr) 
mod->base - mod->min_addr);
+               }
+           }
+       }
+#endif
+
   for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
        i < e->e_shnum;
        i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
-    if (s->sh_type == SHT_SYMTAB)
+    if (s->sh_type == SHT_DYNSYM)
       break;
 
   /* Module without symbol table may still be used to pull in dependencies.
@@ -390,15 +374,19 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
       unsigned char type = ELF_ST_TYPE (sym->st_info);
       unsigned char bind = ELF_ST_BIND (sym->st_info);
       const char *name = str + sym->st_name;
+      int isfunc = type == STT_FUNC;
 
       switch (type)
        {
        case STT_NOTYPE:
        case STT_OBJECT:
+       case STT_FUNC:
          /* Resolve a global symbol.  */
          if (sym->st_name != 0 && sym->st_shndx == 0)
            {
              grub_symbol_t nsym = grub_dl_resolve_symbol (name);
+             if (! nsym && bind == STB_WEAK)
+               break;
              if (! nsym)
                return grub_error (GRUB_ERR_BAD_MODULE,
                                   N_("symbol `%s' not found"), name);
@@ -408,41 +396,32 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e)
            }
          else
            {
-             sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
-                                                                   
sym->st_shndx);
+             sym->st_value += (Elf_Addr) mod->base - mod->min_addr;
+#ifdef __ia64__
+             if (isfunc)
+               {
+                 /* FIXME: free descriptor once it's not used anymore. */
+                 char **desc;
+                 desc = grub_malloc (2 * sizeof (char *));
+                 if (!desc)
+                   return grub_errno;
+                 desc[0] = (void *) sym->st_value;
+                 desc[1] = (char *) mod->base + mod->pltgot;
+                 sym->st_value = (grub_addr_t) desc;
+               }
+#endif
              if (bind != STB_LOCAL)
-               if (grub_dl_register_symbol (name, (void *) sym->st_value, 0, 
mod))
+               if (grub_dl_register_symbol (name, (void *) sym->st_value, 
isfunc, mod))
                  return grub_errno;
+             if (isfunc && grub_strcmp (name, "grub_mod_init") == 0)
+               mod->init = (void (*) (grub_dl_t)) sym->st_value;
+             else if (isfunc && grub_strcmp (name, "grub_mod_fini") == 0)
+               mod->fini = (void (*) (void)) sym->st_value;
            }
          break;
 
-       case STT_FUNC:
-         sym->st_value += (Elf_Addr) grub_dl_get_section_addr (mod,
-                                                               sym->st_shndx);
-#ifdef __ia64__
-         {
-             /* FIXME: free descriptor once it's not used anymore. */
-             char **desc;
-             desc = grub_malloc (2 * sizeof (char *));
-             if (!desc)
-               return grub_errno;
-             desc[0] = (void *) sym->st_value;
-             desc[1] = mod->base;
-             sym->st_value = (grub_addr_t) desc;
-         }
-#endif
-         if (bind != STB_LOCAL)
-           if (grub_dl_register_symbol (name, (void *) sym->st_value, 1, mod))
-             return grub_errno;
-         if (grub_strcmp (name, "grub_mod_init") == 0)
-           mod->init = (void (*) (grub_dl_t)) sym->st_value;
-         else if (grub_strcmp (name, "grub_mod_fini") == 0)
-           mod->fini = (void (*) (void)) sym->st_value;
-         break;
-
        case STT_SECTION:
-         sym->st_value = (Elf_Addr) grub_dl_get_section_addr (mod,
-                                                              sym->st_shndx);
+         sym->st_value = (Elf_Addr) mod->base - mod->min_addr;
          break;
 
        case STT_FILE:
@@ -620,26 +599,14 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
        i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
     if (s->sh_type == SHT_REL || s->sh_type == SHT_RELA)
       {
-       grub_dl_segment_t seg;
        grub_err_t err;
 
-       if (!(s->sh_flags & SHF_INFO_LINK))
-         continue;
-
-       /* Find the target segment.  */
-       for (seg = mod->segment; seg; seg = seg->next)
-         if (seg->section == s->sh_info)
-           break;
-
-       if (seg)
-         {
-           if (!mod->symtab)
-             return grub_error (GRUB_ERR_BAD_MODULE, "relocation without 
symbol table");
+       if (!mod->symtab)
+         return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol 
table");
 
-           err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg);
-           if (err)
-             return err;
-         }
+       err = grub_arch_dl_relocate_symbols (mod, ehdr, s);
+       if (err)
+         return err;
       }
 
   return GRUB_ERR_NONE;
@@ -651,7 +618,7 @@ static grub_err_t
 grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr)
 {
   unsigned i;
-  const Elf_Shdr *s;
+  const Elf_Phdr *p;
   const Elf_Ehdr *e = ehdr;
   grub_err_t err;
 #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
@@ -661,39 +628,33 @@ grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr)
   grub_size_t tgsz;
 #endif
 
-  for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
-       i < e->e_shnum;
-       i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize))
+  for (i = 0, p = (const Elf_Phdr *) ((const char *) e + e->e_phoff);
+       i < e->e_phnum;
+       i++, p = (const Elf_Phdr *) ((const char *) p + e->e_phentsize))
     {
-      grub_dl_segment_t seg;
       grub_uint64_t set_attrs = GRUB_MEM_ATTR_R;
       grub_uint64_t clear_attrs = GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X;
 
-      for (seg = mod->segment; seg; seg = seg->next)
-       /* Does this ELF section's index match GRUB DL segment? */
-       if (seg->section == i)
-         break;
-
-      /* No GRUB DL segment found for this ELF section, skip it. */
-      if (!seg)
+      if (p->p_memsz == 0)
        continue;
 
-      if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC))
-       continue;
-
-      if (s->sh_flags & SHF_WRITE)
+      if (p->p_flags & PF_W)
        {
          set_attrs |= GRUB_MEM_ATTR_W;
          clear_attrs &= ~GRUB_MEM_ATTR_W;
        }
 
-      if (s->sh_flags & SHF_EXECINSTR)
+      if (p->p_flags & PF_X)
        {
          set_attrs |= GRUB_MEM_ATTR_X;
          clear_attrs &= ~GRUB_MEM_ATTR_X;
        }
 
-      err = grub_update_mem_attrs ((grub_addr_t) seg->addr, seg->size,
+      grub_addr_t from = (grub_addr_t) ((char *)mod->base + (p->p_vaddr - 
mod->min_addr));
+      grub_addr_t to = from + p->p_memsz;
+
+      err = grub_update_mem_attrs (ALIGN_DOWN(from, DL_ALIGN),
+                                  ALIGN_UP(to, DL_ALIGN) - ALIGN_DOWN(from, 
DL_ALIGN),
                                   set_attrs, clear_attrs);
       if (err != GRUB_ERR_NONE)
        return err;
@@ -746,16 +707,16 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
   if (grub_dl_check_header (e, size))
     return 0;
 
-  if (e->e_type != ET_REL)
+  if (e->e_type != ET_REL && 0)
     {
       grub_error (GRUB_ERR_BAD_MODULE, N_("this ELF file is not of the right 
type"));
       return 0;
     }
 
   /* Make sure that every section is within the core.  */
-  if (size < e->e_shoff + (grub_uint32_t) e->e_shentsize * e->e_shnum)
+  if (size < e->e_phoff + (grub_uint32_t) e->e_phentsize * e->e_phnum)
     {
-      grub_error (GRUB_ERR_BAD_OS, "ELF sections outside core");
+      grub_error (GRUB_ERR_BAD_OS, "ELF program headers outside core");
       return 0;
     }
 
@@ -780,6 +741,9 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
       || grub_dl_load_segments (mod, e)
       || grub_dl_resolve_symbols (mod, e)
       || grub_dl_relocate_symbols (mod, e)
+#ifdef __mips__
+      || grub_arch_dl_relocate_pltgot (mod)
+#endif
       || grub_dl_set_mem_attrs (mod, e))
     {
       mod->fini = 0;
diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c
index e8d63b1f5..18e1ab346 100644
--- a/grub-core/kern/emu/full.c
+++ b/grub-core/kern/emu/full.c
@@ -39,13 +39,11 @@ grub_arch_dl_check_header (void *ehdr)
 }
 
 grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
-                              Elf_Shdr *s, grub_dl_segment_t seg)
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s)
 {
   (void) mod;
   (void) ehdr;
   (void) s;
-  (void) seg;
   return GRUB_ERR_BAD_MODULE;
 }
 
diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c
index 1346da5cc..7c4353bab 100644
--- a/grub-core/kern/i386/dl.c
+++ b/grub-core/kern/i386/dl.c
@@ -41,7 +41,7 @@ grub_arch_dl_check_header (void *ehdr)
 /* 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_Shdr *s)
 {
   Elf_Rel *rel, *max;
 
@@ -53,16 +53,22 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       Elf_Word *addr;
       Elf_Sym *sym;
 
-      if (seg->size < rel->r_offset)
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
+                          "reloc offset is out of the segment: %x not in 
[%x..%x]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
 
-      addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+      addr = (Elf_Word *) ((char *) mod->base + rel->r_offset - mod->min_addr);
       sym = (Elf_Sym *) ((char *) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
 
       switch (ELF_R_TYPE (rel->r_info))
        {
+       case R_386_JMP_SLOT:
+         *addr = sym->st_value;
+         break;
+
+       case R_386_GLOB_DAT:
        case R_386_32:
          *addr += sym->st_value;
          break;
@@ -70,6 +76,11 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
        case R_386_PC32:
          *addr += (sym->st_value - (grub_addr_t) addr);
          break;
+
+       case R_386_RELATIVE:
+         *addr += (grub_addr_t) mod->base - mod->min_addr;
+         break;
+
        default:
          return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
                             N_("relocation 0x%x is not implemented yet"),
diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c
index db59300fe..75b63a79f 100644
--- a/grub-core/kern/ia64/dl.c
+++ b/grub-core/kern/ia64/dl.c
@@ -43,14 +43,29 @@ grub_arch_dl_check_header (void *ehdr)
   return GRUB_ERR_NONE;
 }
 
+void
+grub_arch_dl_parse_dynamic (grub_dl_t mod, Elf_Dyn *dyn, grub_size_t sz)
+{
+  unsigned i;
+  for (i = 0; i < sz / sizeof(dyn[0]); i++)
+    switch (dyn[i].d_tag)
+      {
+      case DT_PLTGOT:
+       mod->pltgot = dyn[i].d_un.d_ptr;
+       break;
+      case DT_NULL:
+       return;
+      }
+}
+
 #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)
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s)
 {
   Elf_Rela *rel, *max;
+  grub_addr_t gp = (grub_addr_t) mod->base + (grub_addr_t) mod->pltgot;
 
   for (rel = (Elf_Rela *) ((char *) ehdr + s->sh_offset),
         max = (Elf_Rela *) ((char *) rel + s->sh_size);
@@ -61,11 +76,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       Elf_Sym *sym;
       grub_uint64_t value;
 
-      if (seg->size < (rel->r_offset & ~3))
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
+                          "reloc offset is out of the segment: %lx not in 
[%lx..%lx]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
 
-      addr = (grub_addr_t) seg->addr + rel->r_offset;
+      addr = (grub_addr_t) ((char *) mod->base + rel->r_offset - 
mod->min_addr);
       sym = (Elf_Sym *) ((char *) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
 
@@ -94,25 +110,28 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
            grub_ia64_add_value_to_slot_20b (addr, noff);
          }
          break;
-       case R_IA64_SEGREL64LSB:
-         *(grub_uint64_t *) addr += value - (grub_addr_t) seg->addr;
+       case R_IA64_IPLTLSB:
+         grub_memcpy((void *) addr, (void *) value, 16);
          break;
        case R_IA64_FPTR64LSB:
        case R_IA64_DIR64LSB:
-         *(grub_uint64_t *) addr += value;
+         *(grub_uint64_t *) addr = value;
+         break;
+       case R_IA64_REL64LSB:
+         *(grub_uint64_t *) addr = (grub_addr_t) mod->base - mod->min_addr + 
rel->r_addend;
          break;
        case R_IA64_PCREL64LSB:
          *(grub_uint64_t *) addr += value - addr;
          break;
        case R_IA64_GPREL64I:
-         grub_ia64_set_immu64 (addr, value - (grub_addr_t) mod->base);
+         grub_ia64_set_immu64 (addr, value - gp);
          break;
        case R_IA64_GPREL22:
-         if ((value - (grub_addr_t) mod->base) & ~MASK20)
+         if ((value - gp) & ~MASK20)
            return grub_error (GRUB_ERR_BAD_MODULE,
                               "gprel offset too big (%lx)",
-                              value - (grub_addr_t) mod->base);
-         grub_ia64_add_value_to_slot_21 (addr, value - (grub_addr_t) 
mod->base);
+                              value - gp);
+         grub_ia64_add_value_to_slot_21 (addr, value - gp);
          break;
 
        case R_IA64_LTOFF22X:
@@ -124,11 +143,11 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
          {
            grub_uint64_t *gpptr = mod->gotptr;
            *gpptr = value;
-           if (((grub_addr_t) gpptr - (grub_addr_t) mod->base) & ~MASK20)
+           if (((grub_addr_t) gpptr - gp) & ~MASK20)
              return grub_error (GRUB_ERR_BAD_MODULE,
                                 "gprel offset too big (%lx)",
-                                (grub_addr_t) gpptr - (grub_addr_t) mod->base);
-           grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - 
(grub_addr_t) mod->base);
+                                (grub_addr_t) gpptr - gp);
+           grub_ia64_add_value_to_slot_21 (addr, (grub_addr_t) gpptr - gp);
            mod->gotptr = gpptr + 1;
            break;
          }
diff --git a/grub-core/kern/loongarch64/dl.c b/grub-core/kern/loongarch64/dl.c
index 7f923b415..670ca887e 100644
--- a/grub-core/kern/loongarch64/dl.c
+++ b/grub-core/kern/loongarch64/dl.c
@@ -46,7 +46,7 @@ grub_arch_dl_check_header (void *ehdr)
  */
 grub_err_t
 grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
-                              Elf_Shdr *s, grub_dl_segment_t seg)
+                              Elf_Shdr *s)
 {
   Elf_Rel *rel, *max;
   struct grub_loongarch64_stack stack;
@@ -61,9 +61,10 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       void *place;
       grub_uint64_t sym_addr;
 
-      if (rel->r_offset >= seg->size)
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is outside the segment");
+                          "reloc offset is out of the segment: %lx not in 
[%lx..%lx]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
 
       sym = (Elf_Sym *) ((char*) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
@@ -72,10 +73,14 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       if (s->sh_type == SHT_RELA)
        sym_addr += ((Elf_Rela *) rel)->r_addend;
 
-      place = (void *) ((grub_addr_t) seg->addr + rel->r_offset);
+      place = (void *) ((char *) mod->base + rel->r_offset - mod->min_addr);
 
       switch (ELF_R_TYPE (rel->r_info))
        {
+       case R_LARCH_JUMP_SLOT:
+         *(grub_uint64_t *)place = sym_addr;
+         break;
+
        case R_LARCH_64:
          {
            grub_uint64_t *abs_place = place;
@@ -83,9 +88,14 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
            grub_dprintf ("dl", "reloc_abs64 %p => 0x%016llx, %p\n",
                          place, (unsigned long long) sym_addr, abs_place);
 
-           *abs_place += (grub_uint64_t) sym_addr;
+           *abs_place = (grub_uint64_t) sym_addr;
          }
          break;
+       case R_LARCH_RELATIVE:
+         *(grub_uint64_t *)place = (grub_addr_t) mod->base - mod->min_addr;
+         if (s->sh_type == SHT_RELA)
+           *(grub_uint64_t *)place += ((Elf_Rela *) rel)->r_addend;
+         break;
        case R_LARCH_MARK_LA:
          break;
        case R_LARCH_SOP_PUSH_PCREL:
diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c
index 5b02f97fc..cda40fd07 100644
--- a/grub-core/kern/mips/dl.c
+++ b/grub-core/kern/mips/dl.c
@@ -29,6 +29,54 @@
 static char __gnu_local_gp_dummy;
 static char _gp_disp_dummy;
 
+
+void
+grub_arch_dl_parse_dynamic (grub_dl_t mod, Elf_Dyn *dyn, grub_size_t sz)
+{
+  unsigned i;
+  for (i = 0; i < sz / sizeof(dyn[0]); i++)
+    switch (dyn[i].d_tag)
+      {
+      case DT_PLTGOT:
+       mod->pltgot = dyn[i].d_un.d_ptr;
+       break;
+      case DT_MIPS_GOTSYM:
+       mod->gotsym = dyn[i].d_un.d_val;
+       break;
+      case DT_MIPS_SYMTABNO:
+       mod->symtabno = dyn[i].d_un.d_val;
+       break;
+      case DT_MIPS_LOCAL_GOTNO:
+       mod->local_gotno = dyn[i].d_un.d_val;
+       break;
+      case DT_NULL:
+       return;
+      }
+}
+
+grub_err_t
+grub_arch_dl_relocate_pltgot (grub_dl_t mod)
+{
+  grub_uint32_t i = 0;
+  grub_uint32_t *pltgot = (grub_uint32_t *) (void *) ((char *) mod->base + 
(mod->pltgot - mod->min_addr));
+
+  for (i = 0; i < mod->local_gotno; i++)
+    {
+      pltgot[i] += (grub_addr_t)mod->base - mod->min_addr;
+      grub_dprintf("dl", "Locating local %p[%x] to %x\n", &pltgot[i], 
mod->pltgot + (i) * 4, pltgot[i]);
+    }
+  for (i = 0; i < mod->symtabno - mod->gotsym; i++)
+    {
+      Elf_Sym *sym;
+      sym = (Elf_Sym *) (void *) ((char *) mod->symtab
+                                 + mod->symsize * (i + mod->gotsym));
+      pltgot[i+mod->local_gotno] = sym->st_value;
+      grub_dprintf("dl", "Locating %p[%x] to %x\n", 
&pltgot[i+mod->local_gotno], mod->pltgot + (i+mod->local_gotno) * 4, 
sym->st_value);
+    }
+
+  return GRUB_ERR_NONE;
+}
+
 /* Check if EHDR is a valid ELF header.  */
 grub_err_t
 grub_arch_dl_check_header (void *ehdr)
@@ -96,8 +144,7 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, 
grub_size_t *tramp,
 
 /* Relocate symbols.  */
 grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
-                              Elf_Shdr *s, grub_dl_segment_t seg)
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s)
 {
   grub_uint32_t gp0;
   Elf_Ehdr *e = ehdr;
@@ -130,11 +177,14 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       Elf_Sym *sym;
       grub_uint32_t sym_value;
 
-      if (seg->size < rel->r_offset)
-       return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
+      if (ELF_R_TYPE (rel->r_info) == R_MIPS_NONE)
+       continue;
 
-      addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset);
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
+       return grub_error (GRUB_ERR_BAD_MODULE,
+                          "reloc offset is out of the segment: %x not in 
[%x..%x]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
+      addr = (void *) ((char *) mod->base + rel->r_offset - mod->min_addr);
       sym = (Elf_Sym *) ((char *) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
       sym_value = sym->st_value;
@@ -174,7 +224,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
                  && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
                {
                  value += *(grub_int16_t *)
-                   ((char *) seg->addr + rel2->r_offset
+                   (((char *) mod->base + rel2->r_offset - mod->min_addr)
 #ifdef GRUB_CPU_WORDS_BIGENDIAN
                     + 2
 #endif
@@ -190,6 +240,18 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
 #endif
          *(grub_uint16_t *) addr += sym_value & 0xffff;
          break;
+       case R_MIPS_REL32:
+         if (ELF_R_SYM (rel->r_info) == 0 || ELF_ST_TYPE (sym->st_info) == 
STT_SECTION)
+           {
+             *(grub_uint32_t *) addr += (grub_addr_t) mod->base - 
mod->min_addr;
+             if (s->sh_type == SHT_RELA)
+               *(grub_uint32_t *) addr += ((Elf_Rela *) rel)->r_addend;
+           }
+         else
+           {
+             *(grub_uint32_t *) addr = (grub_addr_t) sym_value;
+           }
+         break;
        case R_MIPS_32:
          *(grub_uint32_t *) addr += sym_value;
          break;
@@ -226,7 +288,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
                    && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
                  {
                    sym_value += *(grub_int16_t *)
-                     ((char *) seg->addr + rel2->r_offset
+                     (((char *) mod->base + rel2->r_offset - mod->min_addr)
 #ifdef GRUB_CPU_WORDS_BIGENDIAN
                       + 2
 #endif
diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c
index 7b6418eab..03cca93f0 100644
--- a/grub-core/kern/powerpc/dl.c
+++ b/grub-core/kern/powerpc/dl.c
@@ -92,8 +92,7 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, 
grub_size_t *tramp,
 
 /* Relocate symbols.  */
 grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
-                              Elf_Shdr *s, grub_dl_segment_t seg)
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s)
 {
   Elf_Rela *rel, *max;
 
@@ -106,11 +105,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       Elf_Sym *sym;
       grub_uint32_t value;
 
-      if (seg->size < rel->r_offset)
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
+                          "reloc offset is out of the segment: %x not in 
[%x..%x]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
 
-      addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+      addr = (Elf_Word *) ((char *) mod->base + rel->r_offset - mod->min_addr);
       sym = (Elf_Sym *) ((char *) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
 
@@ -150,10 +150,15 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
          *(Elf_Half *) addr = (value + 0x8000) >> 16;
          break;
 
+       case GRUB_ELF_R_PPC_JMP_SLOT:
        case GRUB_ELF_R_PPC_ADDR32:
          *addr = value;
          break;
 
+       case GRUB_ELF_R_PPC_RELATIVE:
+         *addr += (grub_addr_t) mod->base - mod->min_addr + rel->r_addend;
+         break;
+
        case GRUB_ELF_R_PPC_REL32:
          *addr = value - (Elf_Word) addr;
          break;
diff --git a/grub-core/kern/riscv/dl.c b/grub-core/kern/riscv/dl.c
index 896653bb4..5b343f342 100644
--- a/grub-core/kern/riscv/dl.c
+++ b/grub-core/kern/riscv/dl.c
@@ -54,7 +54,7 @@ grub_arch_dl_check_header (void *ehdr)
 /* 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_Shdr *s)
 {
   Elf_Rel *rel, *max;
 
@@ -67,9 +67,11 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       void *place;
       grub_size_t sym_addr;
 
-      if (rel->r_offset >= seg->size)
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
+                          "reloc offset is out of the segment: %lx not in 
[%lx..%lx]",
+                          (unsigned long) rel->r_offset, (unsigned long) 
mod->min_addr,
+                          (unsigned long) mod->min_addr + mod->sz);
 
       sym = (Elf_Sym *) ((char *) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
@@ -78,7 +80,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       if (s->sh_type == SHT_RELA)
        sym_addr += ((Elf_Rela *) rel)->r_addend;
 
-      place = (void *) ((grub_addr_t) seg->addr + rel->r_offset);
+      place = (void *) ((char *) mod->base + rel->r_offset - mod->min_addr);
 
       switch (ELF_R_TYPE (rel->r_info))
        {
@@ -270,7 +272,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
 
                rel2_offset = rel2->r_offset;
                rel2_info = rel2->r_info;
-               rel2_loc = (grub_addr_t) seg->addr + rel2_offset;
+               rel2_loc = (Elf_Addr) ((char *) mod->base + rel2_offset - 
mod->min_addr);
 
                if (ELF_R_TYPE (rel2_info) == R_RISCV_PCREL_HI20
                    && rel2_loc == sym_addr)
@@ -330,6 +332,17 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
 
        case R_RISCV_RELAX:
          break;
+
+       case R_RISCV_JUMP_SLOT:
+         *(grub_size_t *)place = sym_addr;
+         break;
+
+       case R_RISCV_RELATIVE:
+         *(grub_size_t *)place += (grub_addr_t) mod->base - mod->min_addr;
+         if (s->sh_type == SHT_RELA)
+           *(grub_size_t *)place += ((Elf_Rela *) rel)->r_addend;
+         break;
+
        default:
          {
            char rel_info[17]; /* log16(2^64) = 16, plus NUL. */
diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c
index f3d960186..6571edaea 100644
--- a/grub-core/kern/sparc64/dl.c
+++ b/grub-core/kern/sparc64/dl.c
@@ -97,8 +97,7 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, 
grub_size_t *tramp,
 
 /* Relocate symbols.  */
 grub_err_t
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
-                              Elf_Shdr *s, grub_dl_segment_t seg)
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s)
 {
   Elf_Rela *rel, *max;
 
@@ -111,17 +110,21 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       Elf_Sym *sym;
       Elf_Addr value;
 
-      if (seg->size < rel->r_offset)
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
+                          "reloc offset is out of the segment: %lx not in 
[%lx..%lx]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
 
-      addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
+      addr = (Elf_Word *) ((char *) mod->base + rel->r_offset - mod->min_addr);
       sym = (Elf_Sym *) ((char *) mod->symtab
                         + mod->symsize * ELF_R_SYM (rel->r_info));
 
       value = sym->st_value + rel->r_addend;
       switch (ELF_R_TYPE (rel->r_info) & 0xff)
        {
+       case R_SPARC_RELATIVE:
+         *(Elf_Xword *) addr += (grub_addr_t) mod->base - mod->min_addr + 
rel->r_addend;
+         break;
        case R_SPARC_32: /* 3 V-word32 */
          if (value & 0xFFFFFFFF00000000)
            return grub_error (GRUB_ERR_BAD_MODULE,
@@ -166,6 +169,8 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
        case R_SPARC_LO10: /* 12 T-simm13 */
          *addr = (*addr & 0xFFFFFC00) | (value & 0x3FF);
          break;
+       case R_SPARC_JMP_SLOT:
+       case R_SPARC_GLOB_DAT:
        case R_SPARC_64: /* 32 V-xwords64 */
          *(Elf_Xword *) addr = value;
          break;
diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c
index e5a8bdcf4..5cd05039b 100644
--- a/grub-core/kern/x86_64/dl.c
+++ b/grub-core/kern/x86_64/dl.c
@@ -41,7 +41,7 @@ grub_arch_dl_check_header (void *ehdr)
 /* 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_Shdr *s)
 {
   Elf64_Rela *rel, *max;
 
@@ -54,11 +54,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       Elf64_Xword *addr64;
       Elf64_Sym *sym;
 
-      if (seg->size < rel->r_offset)
+      if (mod->min_addr + mod->sz <= rel->r_offset || mod->min_addr > 
rel->r_offset)
        return grub_error (GRUB_ERR_BAD_MODULE,
-                          "reloc offset is out of the segment");
+                          "reloc offset is out of the segment: %lx not in 
[%lx..%lx]",
+                          rel->r_offset, mod->min_addr, mod->min_addr + 
mod->sz);
 
-      addr32 = (Elf64_Word *) ((char *) seg->addr + rel->r_offset);
+      addr32 = (Elf64_Word *) ((char *) mod->base + rel->r_offset - 
mod->min_addr);
       addr64 = (Elf64_Xword *) addr32;
       sym = (Elf64_Sym *) ((char *) mod->symtab
                           + mod->symsize * ELF_R_SYM (rel->r_info));
@@ -66,7 +67,13 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
       switch (ELF_R_TYPE (rel->r_info))
        {
        case R_X86_64_64:
-         *addr64 += rel->r_addend + sym->st_value;
+       case R_X86_64_GLOB_DAT:
+       case R_X86_64_JUMP_SLOT:
+         *addr64 = rel->r_addend + sym->st_value;
+         break;
+
+       case R_X86_64_RELATIVE:
+         *addr64 = (grub_addr_t) mod->base - mod->min_addr + rel->r_addend;
          break;
 
        case R_X86_64_PC32:
@@ -74,7 +81,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
          {
            grub_int64_t value;
            value = ((grub_int32_t) *addr32) + rel->r_addend + sym->st_value -
-             (Elf64_Xword) (grub_addr_t) seg->addr - rel->r_offset;
+             (Elf64_Xword) (grub_addr_t) addr32;
            if (value != (grub_int32_t) value)
              return grub_error (GRUB_ERR_BAD_MODULE, "relocation out of 
range");
            *addr32 = value;
@@ -84,7 +91,7 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
        case R_X86_64_PC64:
          {
            *addr64 += rel->r_addend + sym->st_value -
-             (Elf64_Xword) (grub_addr_t) seg->addr - rel->r_offset;
+             (Elf64_Xword) (grub_addr_t) addr64;
          }
          break;
 
diff --git a/grub-core/lib/backtrace.c b/grub-core/lib/backtrace.c
index 825a8800e..23f4d18af 100644
--- a/grub-core/lib/backtrace.c
+++ b/grub-core/lib/backtrace.c
@@ -33,13 +33,10 @@ grub_backtrace_print_address (void *addr)
 
   FOR_DL_MODULES (mod)
   {
-    grub_dl_segment_t segment;
-    for (segment = mod->segment; segment; segment = segment->next)
-      if (segment->addr <= addr && (grub_uint8_t *) segment->addr
-         + segment->size > (grub_uint8_t *) addr)
+    if (mod->base <= addr && (grub_uint8_t *) mod->base + mod->sz > 
(grub_uint8_t *) addr)
        {
-         grub_printf ("%s.%x+%" PRIxGRUB_SIZE, mod->name, segment->section,
-                      (grub_size_t) ((grub_uint8_t *) addr - (grub_uint8_t *) 
segment->addr));
+         grub_printf ("%s[0x%" PRIxGRUB_SIZE "]", mod->name,
+                      (grub_size_t) (mod->min_addr + ((grub_uint8_t *) addr - 
(grub_uint8_t *) mod->base)));
          return;
        }
   }
diff --git a/include/grub/dl.h b/include/grub/dl.h
index 84509c5c1..b30e9506c 100644
--- a/include/grub/dl.h
+++ b/include/grub/dl.h
@@ -41,13 +41,13 @@
 #if !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined 
(GRUB_KERNEL)
 
 #define GRUB_MOD_INIT(name)    \
-static void grub_mod_init (grub_dl_t mod __attribute__ ((unused))) 
__attribute__ ((used)); \
-static void \
+void grub_mod_init (grub_dl_t mod __attribute__ ((unused))) __attribute__ 
((used)); \
+void \
 grub_mod_init (grub_dl_t mod __attribute__ ((unused)))
 
 #define GRUB_MOD_FINI(name)    \
-static void grub_mod_fini (void) __attribute__ ((used)); \
-static void \
+void grub_mod_fini (void) __attribute__ ((used)); \
+void \
 grub_mod_fini (void)
 
 #elif defined (GRUB_KERNEL)
@@ -152,15 +152,6 @@ static const char grub_module_name_##name[] \
 
 #ifndef ASM_FILE
 
-struct grub_dl_segment
-{
-  struct grub_dl_segment *next;
-  void *addr;
-  grub_size_t size;
-  unsigned section;
-};
-typedef struct grub_dl_segment *grub_dl_segment_t;
-
 struct grub_dl;
 
 struct grub_dl_dep
@@ -177,7 +168,6 @@ struct grub_dl
   grub_uint64_t ref_count;
   int persistent;
   grub_dl_dep_t dep;
-  grub_dl_segment_t segment;
   Elf_Sym *symtab;
   grub_size_t symsize;
   void (*init) (struct grub_dl *mod);
@@ -190,9 +180,16 @@ struct grub_dl
 #endif
 #ifdef __mips__
   grub_uint32_t *reginfo;
+  grub_uint32_t gotsym;
+  grub_uint32_t local_gotno;
+  grub_uint32_t symtabno;
+#endif
+#if defined (__mips__) || defined(__ia64__)
+  grub_size_t pltgot;
 #endif
   void *base;
   grub_size_t sz;
+  grub_addr_t min_addr;
   struct grub_dl *next;
 };
 #endif
@@ -263,12 +260,14 @@ grub_err_t grub_arch_dl_check_header (void *ehdr);
 #ifndef GRUB_UTIL
 grub_err_t
 grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
-                              Elf_Shdr *s, grub_dl_segment_t seg);
+                              Elf_Shdr *s);
 #endif
 
 #if defined (_mips)
 #define GRUB_LINKER_HAVE_INIT 1
 void grub_arch_dl_init_linker (void);
+
+grub_err_t grub_arch_dl_relocate_pltgot (grub_dl_t mod);
 #endif
 
 #define GRUB_IA64_DL_TRAMP_ALIGN 16
@@ -281,6 +280,10 @@ grub_err_t
 grub_arm64_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp,
                                  grub_size_t *got);
 
+#if defined (__ia64__) || defined (__mips__)
+void grub_arch_dl_parse_dynamic (grub_dl_t mod, Elf_Dyn *dyn, grub_size_t sz);
+#endif
+
 #if defined (__ia64__)
 #define GRUB_ARCH_DL_TRAMP_ALIGN GRUB_IA64_DL_TRAMP_ALIGN
 #define GRUB_ARCH_DL_GOT_ALIGN GRUB_IA64_DL_GOT_ALIGN
diff --git a/include/grub/elf.h b/include/grub/elf.h
index bd313a70b..fc9148458 100644
--- a/include/grub/elf.h
+++ b/include/grub/elf.h
@@ -2540,6 +2540,8 @@ typedef Elf32_Addr Elf32_Conflict;
 /* LoongArch relocations */
 #define R_LARCH_NONE                         0
 #define R_LARCH_64                           2
+#define R_LARCH_RELATIVE                     3
+#define R_LARCH_JUMP_SLOT                    5
 #define R_LARCH_MARK_LA                              20
 #define R_LARCH_SOP_PUSH_PCREL               22
 #define R_LARCH_SOP_PUSH_ABSOLUTE            23
@@ -2581,6 +2583,7 @@ typedef Elf32_Addr Elf_Addr;
 typedef Elf32_Nhdr Elf_Nhdr;
 typedef Elf32_Ehdr Elf_Ehdr;
 typedef Elf32_Phdr Elf_Phdr;
+typedef Elf32_Dyn Elf_Dyn;
 typedef Elf32_Half Elf_Half;
 typedef Elf32_Off Elf_Off;
 typedef Elf32_Rel Elf_Rel;
@@ -2611,6 +2614,7 @@ typedef Elf64_Addr Elf_Addr;
 typedef Elf64_Nhdr Elf_Nhdr;
 typedef Elf64_Ehdr Elf_Ehdr;
 typedef Elf64_Phdr Elf_Phdr;
+typedef Elf64_Dyn Elf_Dyn;
 typedef Elf64_Half Elf_Half;
 typedef Elf64_Off Elf_Off;
 typedef Elf64_Rel Elf_Rel;
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index 448862b2e..97e83671a 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -1410,8 +1410,10 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct 
section_metadata *smd,
                     {
                       grub_uint32_t hi20, lo12;
 
+#if defined(MKIMAGE_ELF64)
                       if (off != (grub_int32_t)off)
                         grub_util_error ("target %lx not reachable from 
pc=%lx", (long)sym_addr, (long)((char *)target - (char *)e));
+#endif
 
                       hi20 = (off + 0x800) & 0xfffff000;
                       lo12 = (off - hi20) & 0xfff;
diff --git a/util/grub-module-verifier.c b/util/grub-module-verifier.c
index 91d9e8f88..802aa8899 100644
--- a/util/grub-module-verifier.c
+++ b/util/grub-module-verifier.c
@@ -10,11 +10,17 @@ struct grub_module_verifier_arch archs[] = {
   { "i386", 4, 0, EM_386, GRUB_MODULE_VERIFY_SUPPORTS_REL, (int[]){
       R_386_32,
       R_386_PC32,
+      R_386_RELATIVE,
+      R_386_JMP_SLOT,
+      R_386_GLOB_DAT,
       -1
     } },
   { "x86_64", 8, 0, EM_X86_64, GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
       R_X86_64_64,
       R_X86_64_PC64,
+      R_X86_64_RELATIVE,
+      R_X86_64_GLOB_DAT,
+      R_X86_64_JUMP_SLOT,
       /* R_X86_64_32, R_X86_64_32S are supported but shouldn't be used because 
of their limited range.  */
       -1
     }, (int[]){
@@ -30,6 +36,8 @@ struct grub_module_verifier_arch archs[] = {
       GRUB_ELF_R_PPC_ADDR32,
       GRUB_ELF_R_PPC_REL32,
       GRUB_ELF_R_PPC_PLTREL24,
+      GRUB_ELF_R_PPC_RELATIVE,
+      GRUB_ELF_R_PPC_JMP_SLOT,
       -1
     } },
   { "sparc64", 8, 1, EM_SPARCV9, GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
@@ -46,6 +54,9 @@ struct grub_module_verifier_arch archs[] = {
         usually. */
       R_SPARC_HI22,
       R_SPARC_32,
+      R_SPARC_RELATIVE,
+      R_SPARC_GLOB_DAT,
+      R_SPARC_JMP_SLOT,
       -1
     } },
   { "ia64", 8, 0, EM_IA_64, GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
@@ -56,7 +67,6 @@ struct grub_module_verifier_arch archs[] = {
                          for anything else, so assume that it always points to 
a
                          function.
                       */
-      R_IA64_SEGREL64LSB,
       R_IA64_FPTR64LSB,
       R_IA64_DIR64LSB,
       R_IA64_PCREL64LSB,
@@ -65,12 +75,16 @@ struct grub_module_verifier_arch archs[] = {
       R_IA64_GPREL64I,
       R_IA64_LTOFF_FPTR22,
       R_IA64_LDXMOV,
+      R_IA64_IPLTLSB,
+      R_IA64_REL64LSB,
       -1
     }, (int[]){
       R_IA64_GPREL22,
       -1
     } },
   { "mipsel", 4, 0, EM_MIPS, GRUB_MODULE_VERIFY_SUPPORTS_REL | 
GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
+      R_MIPS_NONE,
+      R_MIPS_REL32,
       R_MIPS_HI16,
       R_MIPS_LO16,
       R_MIPS_32,
@@ -82,6 +96,8 @@ struct grub_module_verifier_arch archs[] = {
       -1
     } },
   { "mips", 4, 1, EM_MIPS, GRUB_MODULE_VERIFY_SUPPORTS_REL | 
GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
+      R_MIPS_NONE,
+      R_MIPS_REL32,
       R_MIPS_HI16,
       R_MIPS_LO16,
       R_MIPS_32,
@@ -103,6 +119,9 @@ struct grub_module_verifier_arch archs[] = {
       R_ARM_THM_MOVW_ABS_NC,
       R_ARM_THM_MOVT_ABS,
       R_ARM_THM_JUMP19,
+      R_ARM_RELATIVE,
+      R_ARM_JUMP_SLOT,
+      R_ARM_GLOB_DAT,
       -1
     } },
   { "arm64", 8, 0, EM_AARCH64, GRUB_MODULE_VERIFY_SUPPORTS_REL | 
GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
@@ -111,6 +130,9 @@ struct grub_module_verifier_arch archs[] = {
       R_AARCH64_JUMP26,
       R_AARCH64_ADR_GOT_PAGE,
       R_AARCH64_LD64_GOT_LO12_NC,
+      R_AARCH64_RELATIVE,
+      R_AARCH64_JUMP_SLOT,
+      R_AARCH64_GLOB_DAT,
       -1
     }, (int[]){
       R_AARCH64_ADR_PREL_PG_HI21,
@@ -122,6 +144,8 @@ struct grub_module_verifier_arch archs[] = {
   { "loongarch64", 8, 0, EM_LOONGARCH, GRUB_MODULE_VERIFY_SUPPORTS_REL | 
GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
       R_LARCH_NONE,
       R_LARCH_64,
+      R_LARCH_RELATIVE,
+      R_LARCH_JUMP_SLOT,
       R_LARCH_MARK_LA,
       R_LARCH_SOP_PUSH_PCREL,
       R_LARCH_SOP_PUSH_ABSOLUTE,
@@ -178,6 +202,8 @@ struct grub_module_verifier_arch archs[] = {
       R_RISCV_RELAX,
       R_RISCV_RVC_BRANCH,
       R_RISCV_RVC_JUMP,
+      R_RISCV_RELATIVE,
+      R_RISCV_JUMP_SLOT,
       -1
     } },
   { "riscv64", 8, 0, EM_RISCV, GRUB_MODULE_VERIFY_SUPPORTS_REL | 
GRUB_MODULE_VERIFY_SUPPORTS_RELA, (int[]){
@@ -206,6 +232,8 @@ struct grub_module_verifier_arch archs[] = {
       R_RISCV_RELAX,
       R_RISCV_RVC_BRANCH,
       R_RISCV_RVC_JUMP,
+      R_RISCV_RELATIVE,
+      R_RISCV_JUMP_SLOT,
       -1
     }
   },
diff --git a/util/grub-module-verifierXX.c b/util/grub-module-verifierXX.c
index a42c20bd1..3724f696e 100644
--- a/util/grub-module-verifierXX.c
+++ b/util/grub-module-verifierXX.c
@@ -163,6 +163,16 @@ get_shdr (const struct grub_module_verifier_arch *arch, 
Elf_Ehdr *e, Elf_Word in
   return s;
 }
 
+static Elf_Phdr *
+get_phdr (const struct grub_module_verifier_arch *arch, Elf_Ehdr *e, Elf_Word 
index)
+{
+  if (grub_target_to_host (e->e_phoff) == 0)
+    grub_util_error ("Invalid program header offset");
+
+  return (Elf_Phdr *) ((char *) e + grub_target_to_host (e->e_phoff) +
+                   index * grub_target_to_host16 (e->e_phentsize));
+}
+
 static Elf_Shnum
 get_shnum (const struct grub_module_verifier_arch *arch, Elf_Ehdr *e)
 {
@@ -252,7 +262,7 @@ get_symtab (const struct grub_module_verifier_arch *arch, 
Elf_Ehdr *e, Elf_Word
     {
       s = get_shdr (arch, e, i, module_size);
 
-      if (grub_target_to_host32 (s->sh_type) == SHT_SYMTAB)
+      if (grub_target_to_host32 (s->sh_type) == SHT_DYNSYM)
        break;
     }
 
@@ -357,7 +367,8 @@ is_symbol_local(Elf_Sym *sym)
 static void
 section_check_relocations (const char * const modname,
                           const struct grub_module_verifier_arch *arch, void 
*ehdr,
-                          Elf_Shdr *s, size_t target_seg_size, size_t 
module_size)
+                          Elf_Shdr *s, size_t module_size,
+                          grub_uint64_t min_addr, grub_uint64_t max_addr)
 {
   Elf_Rel *rel, *max;
   Elf_Sym *symtab;
@@ -374,11 +385,16 @@ section_check_relocations (const char * const modname,
     {
       Elf_Sym *sym;
       unsigned i;
+      grub_uint32_t type = ELF_R_TYPE (grub_target_to_host (rel->r_info));
 
-      if (target_seg_size < grub_target_to_host (rel->r_offset))
-       grub_util_error ("%s: reloc offset is out of the segment", modname);
+      if (type == 0)
+       continue;
 
-      grub_uint32_t type = ELF_R_TYPE (grub_target_to_host (rel->r_info));
+      if (grub_target_to_host (rel->r_offset) < min_addr || 
grub_target_to_host (rel->r_offset) >= max_addr)
+       grub_util_error ("%s: reloc offset is out of the segment: %llx not in 
%llx-%llx",
+                        modname,
+                        (long long) grub_target_to_host (rel->r_offset),
+                        (long long) min_addr, (long long) max_addr);
 
       if (arch->machine == EM_SPARCV9)
        type &= 0xff;
@@ -439,7 +455,7 @@ section_check_relocations (const char * const modname,
 
 static void
 check_relocations (const char * const modname,
-                  const struct grub_module_verifier_arch *arch, Elf_Ehdr *e, 
size_t module_size)
+                  const struct grub_module_verifier_arch *arch, Elf_Ehdr *e, 
size_t module_size, grub_uint64_t min_addr, grub_uint64_t max_addr)
 {
   Elf_Shdr *s;
   unsigned i;
@@ -457,12 +473,7 @@ check_relocations (const char * const modname,
          if (grub_target_to_host32 (s->sh_type) == SHT_RELA && !(arch->flags & 
GRUB_MODULE_VERIFY_SUPPORTS_RELA))
            grub_util_error ("%s: unsupported SHT_RELA", modname);
 
-         /* Find the target segment. */
-         if (grub_target_to_host32 (s->sh_info) >= get_shnum (arch, e))
-           grub_util_error ("%s: orphaned reloc section", modname);
-         ts = get_shdr (arch, e, grub_target_to_host32 (s->sh_info), 
module_size);
-
-         section_check_relocations (modname, arch, e, s, grub_target_to_host 
(ts->sh_size), module_size);
+         section_check_relocations (modname, arch, e, s, module_size, 
min_addr, max_addr);
        }
     }
 }
@@ -493,14 +504,14 @@ SUFFIX(grub_module_verify) (const char * const filename,
       || grub_target_to_host16 (e->e_machine) != arch->machine)
     grub_util_error ("%s: invalid arch-dependent ELF magic", filename);
 
-  if (grub_target_to_host16 (e->e_type) != ET_REL)
+  if (grub_target_to_host16 (e->e_type) != ET_DYN)
     {
       grub_util_error ("%s: this ELF file is not of the right type", filename);
     }
 
   /* Make sure that every section is within the core.  */
-  if (size < grub_target_to_host (e->e_shoff)
-      + (grub_uint32_t) grub_target_to_host16 (e->e_shentsize) * get_shnum 
(arch, e))
+  if (size < grub_target_to_host (e->e_phoff)
+      + (grub_uint32_t) grub_target_to_host16 (e->e_phentsize) * 
grub_target_to_host16 (e->e_phnum))
     {
       grub_util_error ("%s: ELF sections outside core", filename);
     }
@@ -516,6 +527,19 @@ SUFFIX(grub_module_verify) (const char * const filename,
 
   modname = (const char *) e + grub_target_to_host (s->sh_offset);
 
+  unsigned i;
+  grub_uint64_t min_addr = ~(grub_uint64_t)0;
+  grub_uint64_t max_addr = 0;
+
+  for (i = 0; i < grub_target_to_host16 (e->e_phnum); i++)
+    {
+      Elf_Phdr *p = get_phdr (arch, e, i);
+
+      min_addr = grub_min(min_addr, grub_target_to_host(p->p_vaddr));
+      max_addr = grub_max(max_addr, grub_target_to_host(p->p_vaddr) + 
grub_target_to_host(p->p_memsz));
+    }
+
+
   check_symbols(arch, e, modname, whitelist_empty, size);
-  check_relocations(modname, arch, e, size);
+  check_relocations(modname, arch, e, size, min_addr, max_addr);
 }
-- 
2.49.0


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to