Skip to content

Commit bd94549

Browse files
committed
loader: Verify alignment of the LOAD segment for the x86-64 kernel
For the x86-64 kernel, the loaded address must be aligned to 2MB; otherwise, the check in __startup_64() in the Linux kernel would fail and cause the kernel to hang. Therefore, verify the alignment of the LOAD segment during the loading process. Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
1 parent 66b8753 commit bd94549

File tree

5 files changed

+42
-17
lines changed

5 files changed

+42
-17
lines changed

docs/elfio_files/bad_align_writer.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ int main( void )
5656
// Create a loadable segment
5757
segment* text_seg = writer.segments.add();
5858
text_seg->set_type( PT_LOAD );
59-
text_seg->set_virtual_address( 0x400 );
60-
text_seg->set_physical_address( 0x400 );
59+
text_seg->set_virtual_address( 0x200000 );
60+
text_seg->set_physical_address( 0x200000 );
6161
text_seg->set_flags( PF_X | PF_R );
62-
text_seg->set_align( 0x100 );
62+
text_seg->set_align( 0x200000 );
6363

6464
// Add code section into program segment
6565
text_seg->add_section_index( text_sec->get_index(),
@@ -80,10 +80,10 @@ int main( void )
8080
// Create a read/write segment
8181
segment* data_seg = writer.segments.add();
8282
data_seg->set_type( PT_NOTE );
83-
data_seg->set_virtual_address( 0x8888048020 );
84-
data_seg->set_physical_address( 0x8888048020 );
83+
data_seg->set_virtual_address( 0x8888400000 );
84+
data_seg->set_physical_address( 0x888400000 );
8585
data_seg->set_flags( PF_W | PF_R );
86-
data_seg->set_align( 13 );
86+
data_seg->set_align( 0x200000 );
8787

8888
// Add code section into program segment
8989
data_seg->add_section_index( data_sec->get_index(),
@@ -103,7 +103,7 @@ int main( void )
103103
// In this example, the code starts at the first address of the
104104
// 'text_seg' segment. Therefore, the start address is set
105105
// to be equal to the segment location
106-
writer.set_entry( 0x400 );
106+
writer.set_entry( 0x200000 );
107107

108108
// Create ELF file
109109
writer.save( "test_bad_align.bin" );

docs/elfio_files/basic_elf.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ int main( void )
5656
// Create a loadable segment
5757
segment* text_seg = writer.segments.add();
5858
text_seg->set_type( PT_LOAD );
59-
text_seg->set_virtual_address( 0x400 );
60-
text_seg->set_physical_address( 0x0 );
59+
text_seg->set_virtual_address( 0x200000 );
60+
text_seg->set_physical_address( 0x200000 );
6161
text_seg->set_flags( PF_X | PF_R );
62-
text_seg->set_align( 0x1 );
62+
text_seg->set_align( 0x200000 );
6363

6464
// Add code section into program segment
6565
text_seg->add_section_index( text_sec->get_index(),
@@ -80,10 +80,10 @@ int main( void )
8080
// Create a read/write segment
8181
segment* data_seg = writer.segments.add();
8282
data_seg->set_type( PT_LOAD );
83-
data_seg->set_virtual_address( 0x0420 );
84-
data_seg->set_physical_address( 0x0420 );
83+
data_seg->set_virtual_address( 0x400000 );
84+
data_seg->set_physical_address( 0x400000 );
8585
data_seg->set_flags( PF_W | PF_R );
86-
data_seg->set_align( 0x10 );
86+
data_seg->set_align( 0x200000 );
8787

8888
// Add code section into program segment
8989
data_seg->add_section_index( data_sec->get_index(),
@@ -103,7 +103,7 @@ int main( void )
103103
// In this example, the code starts at the first address of the
104104
// 'text_seg' segment. Therefore, the start address is set
105105
// to be equal to the segment location
106-
writer.set_entry( 0x400 );
106+
writer.set_entry( 0x200000 );
107107

108108
// Create ELF file
109109
writer.save( "test_elf.bin" );

src/loader/elf/mod.rs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ impl Elf {
159159
}
160160
}
161161

162+
// x86-64 kernel must be aligned to 2MB.
163+
const KERNEL_SEGMENT_ALIGN: u64 = 0x200000; // 2 MiB
164+
162165
impl KernelLoader for Elf {
163166
/// Loads a kernel from a vmlinux elf image into guest memory.
164167
///
@@ -280,6 +283,12 @@ impl KernelLoader for Elf {
280283
None => GuestAddress(phdr.p_paddr),
281284
};
282285

286+
// Ensure that mem_offset is aligned to 2M, otherwise, the kernel may hang during
287+
// booting. Refer to the check in __startup_64() in the Linux kernel source code.
288+
if mem_offset.raw_value() & (KERNEL_SEGMENT_ALIGN - 1) != 0 {
289+
return Err(Error::Align.into());
290+
}
291+
283292
guest_mem
284293
.read_exact_volatile_from(mem_offset, kernel_image, phdr.p_filesz as usize)
285294
.map_err(|_| Error::ReadKernelImage)?;
@@ -479,10 +488,10 @@ mod tests {
479488
Some(highmem_start_address),
480489
)
481490
.unwrap();
482-
assert_eq!(loader_result.kernel_load.raw_value(), 0x200400);
491+
assert_eq!(loader_result.kernel_load.raw_value(), 0x400000);
483492

484493
loader_result = Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap();
485-
assert_eq!(loader_result.kernel_load.raw_value(), 0x200400);
494+
assert_eq!(loader_result.kernel_load.raw_value(), 0x400000);
486495

487496
loader_result = Elf::load(
488497
&gm,
@@ -491,7 +500,7 @@ mod tests {
491500
Some(highmem_start_address),
492501
)
493502
.unwrap();
494-
assert_eq!(loader_result.kernel_load.raw_value(), 0x400);
503+
assert_eq!(loader_result.kernel_load.raw_value(), 0x200000);
495504

496505
highmem_start_address = GuestAddress(0xa00000);
497506
assert_eq!(
@@ -632,4 +641,20 @@ mod tests {
632641
.err()
633642
);
634643
}
644+
645+
#[test]
646+
fn test_unaligned_loadaddr() {
647+
let gm = create_guest_mem();
648+
let image = make_elf_bin();
649+
assert_eq!(
650+
Some(KernelLoaderError::Elf(Error::Align)),
651+
Elf::load(
652+
&gm,
653+
Some(GuestAddress(0x1000)),
654+
&mut Cursor::new(&image),
655+
None
656+
)
657+
.err()
658+
);
659+
}
635660
}

src/loader/elf/test_bad_align.bin

4 MB
Binary file not shown.

src/loader/elf/test_elf.bin

4 MB
Binary file not shown.

0 commit comments

Comments
 (0)