On Tue,  8 Apr 2025 15:16:23 +0000
Vladimir Serbinenko <phco...@gmail.com> wrote:

> 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;
> +    }

Am I wrong in thinking that this change was mistakenly added?

Glenn

>    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);
>  }

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

Reply via email to