You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
361 lines
12 KiB
361 lines
12 KiB
2 years ago
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Peter Jones <pjones@redhat.com>
|
||
|
Date: Mon, 21 Mar 2022 17:45:40 -0400
|
||
|
Subject: [PATCH] modules: load module sections at page-aligned addresses
|
||
|
|
||
|
Currently we load module sections at whatever alignment gcc+ld happened
|
||
|
to dump into the ELF section header, which is often pretty useless. For
|
||
|
example, by default time.mod has these sections on a current x86_64
|
||
|
build:
|
||
|
|
||
|
$ eu-readelf -a grub-core/time.mod |& grep ^Section -A13
|
||
|
Section Headers:
|
||
|
[Nr] Name Type Addr Off Size ES Flags Lk Inf Al
|
||
|
[ 0] NULL 0 00000000 00000000 0 0 0 0
|
||
|
[ 1] .text PROGBITS 0 00000040 0000015e 0 AX 0 0 1
|
||
|
[ 2] .rela.text RELA 0 00000458 000001e0 24 I 8 1 8
|
||
|
[ 3] .rodata.str1.1 PROGBITS 0 0000019e 000000a1 1 AMS 0 0 1
|
||
|
[ 4] .module_license PROGBITS 0 00000240 0000000f 0 A 0 0 8
|
||
|
[ 5] .data PROGBITS 0 0000024f 00000000 0 WA 0 0 1
|
||
|
[ 6] .bss NOBITS 0 00000250 00000008 0 WA 0 0 8
|
||
|
[ 7] .modname PROGBITS 0 00000250 00000005 0 0 0 1
|
||
|
[ 8] .symtab SYMTAB 0 00000258 00000150 24 9 6 8
|
||
|
[ 9] .strtab STRTAB 0 000003a8 000000ab 0 0 0 1
|
||
|
[10] .shstrtab STRTAB 0 00000638 00000059 0 0 0 1
|
||
|
|
||
|
With NX protections being page based, loading sections with either a 1
|
||
|
or 8 *byte* alignment does absolutely nothing to help us out.
|
||
|
|
||
|
This patch switches most EFI platforms to load module sections at 4kB
|
||
|
page-aligned addresses. To do so, it adds an new per-arch function,
|
||
|
grub_arch_dl_min_alignment(), which returns the alignment needed for
|
||
|
dynamically loaded sections (in bytes). Currently it sets it to 4096
|
||
|
when GRUB_MACHINE_EFI is true on x86_64, i386, arm, arm64, and emu, and
|
||
|
1-byte alignment on everything else.
|
||
|
|
||
|
It then changes the allocation size computation and the loader code in
|
||
|
grub_dl_load_segments() to align the locations and sizes up to these
|
||
|
boundaries, and fills any added padding with zeros.
|
||
|
|
||
|
All of this happens before relocations are applied, so the relocations
|
||
|
factor that in with no change.
|
||
|
|
||
|
As an aside, initially Daniel Kiper and I thought that it might be a
|
||
|
better idea to split the modules up into top-level sections as
|
||
|
.text.modules, .rodata.modules, .data.modules, etc., so that their page
|
||
|
permissions would get set by the loader that's loading grub itself.
|
||
|
This turns out to have two significant downsides: 1) either in mkimage
|
||
|
or in grub_dl_relocate_symbols(), you wind up having to dynamically
|
||
|
process the relocations to accommodate the moved module sections, and 2)
|
||
|
you then need to change the permissions on the modules and change them
|
||
|
back while relocating them in grub_dl_relocate_symbols(), which means
|
||
|
that any loader that /does/ honor the section flags but does /not/
|
||
|
generally support NX with the memory attributes API will cause grub to
|
||
|
fail.
|
||
|
|
||
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
||
|
(cherry picked from commit 31d52500b281619d92b03b2c2d30fe15aedaf326)
|
||
|
(cherry picked from commit 04f1df6b665493e38de66018aebe377fdac4ceec)
|
||
|
[rharwood: not risc-v yet]
|
||
|
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
|
||
|
---
|
||
|
grub-core/kern/arm/dl.c | 13 +++++++++++++
|
||
|
grub-core/kern/arm64/dl.c | 13 +++++++++++++
|
||
|
grub-core/kern/dl.c | 29 +++++++++++++++++++++--------
|
||
|
grub-core/kern/emu/full.c | 13 +++++++++++++
|
||
|
grub-core/kern/i386/dl.c | 13 +++++++++++++
|
||
|
grub-core/kern/ia64/dl.c | 9 +++++++++
|
||
|
grub-core/kern/mips/dl.c | 8 ++++++++
|
||
|
grub-core/kern/powerpc/dl.c | 9 +++++++++
|
||
|
grub-core/kern/sparc64/dl.c | 9 +++++++++
|
||
|
grub-core/kern/x86_64/dl.c | 13 +++++++++++++
|
||
|
include/grub/dl.h | 2 ++
|
||
|
docs/grub-dev.texi | 6 +++---
|
||
|
12 files changed, 126 insertions(+), 11 deletions(-)
|
||
|
|
||
|
diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c
|
||
|
index eab9d17ff2..9260737936 100644
|
||
|
--- a/grub-core/kern/arm/dl.c
|
||
|
+++ b/grub-core/kern/arm/dl.c
|
||
|
@@ -278,3 +278,16 @@ grub_arch_dl_check_header (void *ehdr)
|
||
|
|
||
|
return GRUB_ERR_NONE;
|
||
|
}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+#ifdef GRUB_MACHINE_EFI
|
||
|
+ return 4096;
|
||
|
+#else
|
||
|
+ return 1;
|
||
|
+#endif
|
||
|
+}
|
||
|
diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c
|
||
|
index fb03373190..826f8e721e 100644
|
||
|
--- a/grub-core/kern/arm64/dl.c
|
||
|
+++ b/grub-core/kern/arm64/dl.c
|
||
|
@@ -191,3 +191,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||
|
|
||
|
return GRUB_ERR_NONE;
|
||
|
}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+#ifdef GRUB_MACHINE_EFI
|
||
|
+ return 4096;
|
||
|
+#else
|
||
|
+ return 1;
|
||
|
+#endif
|
||
|
+}
|
||
|
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
|
||
|
index ba1b33c20b..5c2153acf9 100644
|
||
|
--- a/grub-core/kern/dl.c
|
||
|
+++ b/grub-core/kern/dl.c
|
||
|
@@ -278,7 +278,7 @@ 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;
|
||
|
+ grub_size_t tsize = 0, talign = 1, arch_addralign = 1;
|
||
|
#if !defined (__i386__) && !defined (__x86_64__)
|
||
|
grub_size_t tramp;
|
||
|
grub_size_t got;
|
||
|
@@ -286,16 +286,24 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
||
|
#endif
|
||
|
char *ptr;
|
||
|
|
||
|
+ arch_addralign = grub_arch_dl_min_alignment ();
|
||
|
+
|
||
|
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))
|
||
|
{
|
||
|
+ grub_size_t sh_addralign;
|
||
|
+ grub_size_t sh_size;
|
||
|
+
|
||
|
if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC))
|
||
|
continue;
|
||
|
|
||
|
- tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size;
|
||
|
- if (talign < s->sh_addralign)
|
||
|
- talign = s->sh_addralign;
|
||
|
+ 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;
|
||
|
+ if (talign < sh_addralign)
|
||
|
+ talign = sh_addralign;
|
||
|
}
|
||
|
|
||
|
#if !defined (__i386__) && !defined (__x86_64__)
|
||
|
@@ -324,6 +332,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
||
|
i < e->e_shnum;
|
||
|
i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
|
||
|
{
|
||
|
+ 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;
|
||
|
@@ -336,17 +347,19 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
||
|
{
|
||
|
void *addr;
|
||
|
|
||
|
- ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign);
|
||
|
+ ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign);
|
||
|
addr = ptr;
|
||
|
- ptr += s->sh_size;
|
||
|
+ 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, s->sh_size);
|
||
|
+ grub_memset (addr, 0, sh_size);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
@@ -355,7 +368,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
|
||
|
else
|
||
|
seg->addr = 0;
|
||
|
|
||
|
- seg->size = s->sh_size;
|
||
|
+ seg->size = sh_size;
|
||
|
seg->section = i;
|
||
|
seg->next = mod->segment;
|
||
|
mod->segment = seg;
|
||
|
diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c
|
||
|
index e8d63b1f5f..1de1c28eb0 100644
|
||
|
--- a/grub-core/kern/emu/full.c
|
||
|
+++ b/grub-core/kern/emu/full.c
|
||
|
@@ -67,3 +67,16 @@ grub_arch_dl_init_linker (void)
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
+
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+#ifdef GRUB_MACHINE_EFI
|
||
|
+ return 4096;
|
||
|
+#else
|
||
|
+ return 1;
|
||
|
+#endif
|
||
|
+}
|
||
|
diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c
|
||
|
index 1346da5cc9..d6b4681fc9 100644
|
||
|
--- a/grub-core/kern/i386/dl.c
|
||
|
+++ b/grub-core/kern/i386/dl.c
|
||
|
@@ -79,3 +79,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||
|
|
||
|
return GRUB_ERR_NONE;
|
||
|
}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+#ifdef GRUB_MACHINE_EFI
|
||
|
+ return 4096;
|
||
|
+#else
|
||
|
+ return 1;
|
||
|
+#endif
|
||
|
+}
|
||
|
diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c
|
||
|
index ebcf316298..3bb753a89b 100644
|
||
|
--- a/grub-core/kern/ia64/dl.c
|
||
|
+++ b/grub-core/kern/ia64/dl.c
|
||
|
@@ -143,3 +143,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||
|
}
|
||
|
return GRUB_ERR_NONE;
|
||
|
}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+ return 1;
|
||
|
+}
|
||
|
diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c
|
||
|
index 5d7d299c74..6d83bd71e9 100644
|
||
|
--- a/grub-core/kern/mips/dl.c
|
||
|
+++ b/grub-core/kern/mips/dl.c
|
||
|
@@ -272,3 +272,11 @@ grub_arch_dl_init_linker (void)
|
||
|
grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0);
|
||
|
}
|
||
|
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+ return 1;
|
||
|
+}
|
||
|
diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c
|
||
|
index 3a7fa3ed3d..577e27d871 100644
|
||
|
--- a/grub-core/kern/powerpc/dl.c
|
||
|
+++ b/grub-core/kern/powerpc/dl.c
|
||
|
@@ -165,3 +165,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||
|
|
||
|
return GRUB_ERR_NONE;
|
||
|
}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+ return 1;
|
||
|
+}
|
||
|
diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c
|
||
|
index 739be47174..c741c1782e 100644
|
||
|
--- a/grub-core/kern/sparc64/dl.c
|
||
|
+++ b/grub-core/kern/sparc64/dl.c
|
||
|
@@ -184,3 +184,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||
|
|
||
|
return GRUB_ERR_NONE;
|
||
|
}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+ return 1;
|
||
|
+}
|
||
|
diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c
|
||
|
index 3a73e6e6ce..6c20b7c367 100644
|
||
|
--- a/grub-core/kern/x86_64/dl.c
|
||
|
+++ b/grub-core/kern/x86_64/dl.c
|
||
|
@@ -114,3 +114,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
|
||
|
|
||
|
return GRUB_ERR_NONE;
|
||
|
}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Tell the loader what our minimum section alignment is.
|
||
|
+ */
|
||
|
+grub_size_t
|
||
|
+grub_arch_dl_min_alignment (void)
|
||
|
+{
|
||
|
+#ifdef GRUB_MACHINE_EFI
|
||
|
+ return 4096;
|
||
|
+#else
|
||
|
+ return 1;
|
||
|
+#endif
|
||
|
+}
|
||
|
diff --git a/include/grub/dl.h b/include/grub/dl.h
|
||
|
index 9ec6caf3f9..dd4c3e7ff4 100644
|
||
|
--- a/include/grub/dl.h
|
||
|
+++ b/include/grub/dl.h
|
||
|
@@ -280,6 +280,8 @@ grub_err_t 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_size_t
|
||
|
+grub_arch_dl_min_alignment (void);
|
||
|
#endif
|
||
|
|
||
|
#if defined (_mips)
|
||
|
diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
|
||
|
index 03d53498c5..dbfb94bc06 100644
|
||
|
--- a/docs/grub-dev.texi
|
||
|
+++ b/docs/grub-dev.texi
|
||
|
@@ -638,9 +638,9 @@ declare startup asm file ($cpu_$platform_startup) as well as any other files
|
||
|
(e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c).
|
||
|
At this stage you will also need to add dummy dl.c and cache.S with functions
|
||
|
grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t
|
||
|
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and
|
||
|
-void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They
|
||
|
-won't be used for now.
|
||
|
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c), grub_uint32_t
|
||
|
+grub_arch_dl_min_alignment (void), and void grub_arch_sync_caches (void
|
||
|
+*address, grub_size_t len) (cache.S). They won't be used for now.
|
||
|
|
||
|
You will need to create directory include/$cpu/$platform and a file
|
||
|
include/$cpu/types.h. The later folowing this template:
|