Linux – Repack the filesystem image from vmlinux.bin (embedded initramfs) without rebuilding

elfinitramfsinitrdlinuxlinux-kernel

In How do I extract the filesystem image from vmlinux.bin? and https://wiki.gentoo.org/wiki/Custom_Initramfs#Salvaging methods are presented for getting and unpacking an embedded initramfs/initrd included in the kernel image.

Now I would like to insert the modified file system (cpio + possibly packed using e.g. lzma) into the kernel executable without having to recompile it. Would it be possible to modify the ELF image of the kernel in this way? If so then how? Would I need to keep something in respect if I were to simply replace the bytes in-place (maybe some hash?)?


objdump-h Output:

vmlinux.64.orig:     file format elf64-big

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         004162b8  ffffffff80100000  ffffffff80100000  00010000  2**7
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 __ex_table    000063a0  ffffffff805162c0  ffffffff805162c0  004262c0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .notes        00000024  ffffffff8051c660  ffffffff8051c660  0042c660  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .rodata       0041f700  ffffffff8051d000  ffffffff8051d000  0042d000  2**8
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .pci_fixup    00000d40  ffffffff8093c700  ffffffff8093c700  0084c700  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 __ksymtab     0000a430  ffffffff8093d440  ffffffff8093d440  0084d440  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 __ksymtab_gpl 00004ff0  ffffffff80947870  ffffffff80947870  00857870  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 __ksymtab_strings 00010f14  ffffffff8094c860  ffffffff8094c860  0085c860  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 __init_rodata 00000500  ffffffff8095d778  ffffffff8095d778  0086d778  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 __param       00001388  ffffffff8095dc78  ffffffff8095dc78  0086dc78  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .data         000508c0  ffffffff80960000  ffffffff80960000  00870000  2**14
                  CONTENTS, ALLOC, LOAD, DATA
 11 .init.text    0002b084  ffffffff809b1000  ffffffff809b1000  008c1000  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .init.data    00bc6d78  ffffffff809dc088  ffffffff809dc088  008ec088  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 13 .exit.text    000019e0  ffffffff815a2e00  ffffffff815a2e00  014b2e00  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .data.percpu  00003680  ffffffff815a5000  ffffffff815a5000  014b5000  2**7
                  CONTENTS, ALLOC, LOAD, DATA
 15 .bss          00068fb0  ffffffff815b0000  ffffffff815b0000  014b8680  2**16
                  ALLOC
 16 .mdebug.abi64 00000000  ffffffff81618fb0  ffffffff81618fb0  014b8680  2**0
                  CONTENTS, READONLY
 17 .comment      0000cd74  0000000000000000  0000000000000000  014b8680  2**0
                  CONTENTS, READONLY
 18 .gnu.attributes 00000010  0000000000000000  0000000000000000  014c53f4  2**0

Best Answer

Yes, it is possible but changing the .init.ramfs section size and addresses is not enough because the Kernel's ELF executable is statically linked with the virtual address of the start and end of the initramfs' section.

In the Linux sources the relevant code is located in the iniramfs.c source file:

void __init populate_rootfs(void)
{
  char *err = unpack_to_rootfs(__initramfs_start, __initramfs_end - __initramfs_start, 0); 
...
}

So you also need to change these two offsets in the machine code of the invocation of the unpack_to_rootfs() function, which is located in the .init.text section. ( watch out for any relevant entries in the relocation table! ...if one exists )

Also, in reference to Icarus's reply, the manipulation of the initramfs' section' size, file offset and starting virtual address, as well as these two aforementioned offsets (arguments to the unpack_to_rootfs() function), enables you to add you own custom LARGER initramfs section that is loaded ABOVE the maximum virtual address of the ELF file. The Program Header's (PHeader) "Memory Size" field needs to be modified too, in order to reflect the larger initramfs section appended after the end of the old virtual address space.

P.S. The "hole" in the Kernel's virtual address space remaining after moving the original init.ramfs section to a new high starting virtual address, does not hurt anything because the associated memory is later freed by the free_initmem(void) function defined in the init.c source file.

Related Question