Correct ELF alignment settings

This commit is contained in:
Ruihan Li 2025-12-25 23:33:03 +08:00 committed by Jianfeng Jiang
parent 545efaa155
commit e92fbe3e69
2 changed files with 92 additions and 16 deletions

View File

@ -21,7 +21,7 @@ use crate::{
},
vm::{
perms::VmPerms,
vmar::{VMAR_LOWEST_ADDR, Vmar},
vmar::{VMAR_CAP_ADDR, VMAR_LOWEST_ADDR, Vmar},
vmo::Vmo,
},
};
@ -205,24 +205,28 @@ fn map_segment_vmos(
let map_range = if elf.is_shared_object() {
// Relocatable object.
let align = elf.max_load_align();
// Given that `elf_va_range` is guaranteed to be below `VMAR_CAP_ADDR`, as long as
// `VMAR_CAP_ADDR * 2` does not overflow, the following `align_up(align)` cannot overflow
// either.
const { assert!(VMAR_CAP_ADDR.checked_mul(2).is_some()) };
// Allocate a continuous range of virtual memory for all segments in advance.
//
// All segments in the ELF program must be mapped to a continuous VM range to
// ensure the relative offset of each segment not changed.
let elf_va_range_aligned =
elf_va_range.start.align_down(PAGE_SIZE)..elf_va_range.end.align_up(PAGE_SIZE);
elf_va_range.start.align_down(align)..elf_va_range.end.align_up(align);
let map_size = elf_va_range_aligned.len();
let vmar_map_options = vmar
.new_map(map_size, VmPerms::empty())?
.align(elf.max_load_align())
.handle_page_faults_around();
let vmar_map_options = vmar.new_map(map_size, VmPerms::empty())?.align(align);
let aligned_range = vmar_map_options.build().map(|addr| addr..addr + map_size)?;
let start_in_page_offset = elf_va_range.start - elf_va_range_aligned.start;
let end_in_page_offset = elf_va_range_aligned.end - elf_va_range.end;
let start_offset = elf_va_range.start - elf_va_range_aligned.start;
let end_offset = elf_va_range_aligned.end - elf_va_range.end;
aligned_range.start + start_in_page_offset..aligned_range.end - end_in_page_offset
aligned_range.start + start_offset..aligned_range.end - end_offset
} else {
// Not relocatable object. Map as-is.

View File

@ -24,7 +24,7 @@ static struct custom_elf elf;
#define UD2_INSTR \
"\x0f\x0b" // "ud2" in x86-64. TODO: Support other architectures.
FN_SETUP(init)
FN_SETUP(init_exec)
{
elf.ehdr.e_ident[EI_MAG0] = ELFMAG0;
elf.ehdr.e_ident[EI_MAG1] = ELFMAG1;
@ -71,23 +71,30 @@ static int do_execve(void)
#pragma GCC diagnostic pop
}
FN_TEST(good)
static int do_execve_good(void)
{
pid_t pid;
int status;
// First of all, verify that `elf` is a good ELF.
memcpy(elf.buf, UD2_INSTR, sizeof(UD2_INSTR));
pid = TEST_SUCC(fork());
pid = CHECK(fork());
if (pid == 0) {
CHECK(do_execve());
exit(EXIT_FAILURE);
}
TEST_RES(wait(&status), _ret == pid && WIFSIGNALED(status) &&
WTERMSIG(status) == SIGILL);
CHECK_WITH(wait(&status), _ret == pid);
if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGILL)
return -1;
return 0;
}
FN_TEST(good_exec)
{
// First of all, verify that `elf` is a good ELF.
TEST_RES(do_execve_good(), _ret == 0);
}
END_TEST()
@ -418,6 +425,71 @@ FN_TEST(filesz_larger_than_memsz)
}
END_TEST()
// ==========================
// Below are tests for ET_DYN
// ==========================
FN_SETUP(init_dyn)
{
elf.ehdr.e_type = ET_DYN;
}
END_SETUP()
FN_TEST(good_dyn)
{
// First of all, verify that `elf` is a good ELF.
TEST_RES(do_execve_good(), _ret == 0);
}
END_TEST()
FN_TEST(bad_align)
{
long old;
old = elf.phdr[0].p_align;
// 2048 is smaller than PAGE_SIZE.
elf.phdr[0].p_align = 2048;
TEST_RES(do_execve_good(), _ret == 0);
// 2047 is not a power of two.
elf.phdr[0].p_align = 2047;
TEST_RES(do_execve_good(), _ret == 0);
elf.phdr[0].p_align = old;
}
END_TEST()
FN_TEST(large_align)
{
long old;
old = elf.phdr[0].p_align;
elf.phdr[0].p_align = 1ul << 21;
TEST_RES(do_execve_good(), _ret == 0);
elf.phdr[0].p_align = 1ul << 42;
TEST_RES(do_execve_good(), _ret == 0);
elf.phdr[0].p_align = 1ul << 63;
// FIXME: p_align exceeds the size of the user address space.
// What does this mean? As far as Linux is concerned, it tries
// to map to a zero address due to [1], [2], and [3]. This
// does not seem to make much sense.
// [1]: https://elixir.bootlin.com/linux/v6.19-rc2/source/fs/binfmt_elf.c#L1172
// [2]: https://elixir.bootlin.com/linux/v6.19-rc2/source/fs/binfmt_elf.c#L1185
// [3]: https://elixir.bootlin.com/linux/v6.19-rc2/source/fs/binfmt_elf.c#L1188
#ifdef __asterinas__
TEST_ERRNO(do_execve_fatal(), ENOMEM);
#else
TEST_ERRNO(do_execve_fatal(), EPERM);
#endif
elf.phdr[0].p_align = old;
}
END_TEST()
FN_SETUP(cleanup)
{
CHECK(unlink(EXE_PATH));