Create a tiny Linux by reusing android build images -- The AndLinux

WE LIKE THE SIMPLE OF LINUX

Posted by half cup coffee on June 15, 2025

Introduction

Please refer to this Re-use the android build images to create a minimal linux system to get the concept. This page here will list the steps how to setup such tiny Linux by reusing android build images.

Fetch the android source code

Yes, we still need android source code. The reason is we still need some tools which come from the android build folder.

Export PATH for some tools from android source

export ANDROID_ROOT=...
export PATH=$PATH:$ANDROID_ROOT/system/tools/mkbootimg/
export PATH=$PATH:$ANDROID_ROOT/kernel_platform/prebuilts/kernel-build-tools/linux-x86/bin
export PATH=$PATH:$ANDROID_ROOT//out/host/linux-x86/bin

Get the android build images

Cope the android build images to a folder which you will work with. Those android build images are the image which can make the device work after reflash with them.

boot.img  super.img  vendor_boot.img

hanlde the super.img

unsparse a sparse super.img:

simg2img super.img super.img.unsparse
mv super.img.unsparse super_out

use lpunpack to unpack the super.img.unsparse

lpunpack is available after build android in the out folder. In this future this folder can be copied to avoid build android.

$ lpunpack ./super.img.unsparse
Attempting to extract partition 'product_b'...
Attempting to extract partition 'system_ext_b'...
Attempting to extract partition 'product_a'...
  Dealing with extent 0 from target source 0...
Attempting to extract partition 'system_b'...
Attempting to extract partition 'system_a'...
  Dealing with extent 0 from target source 0...
Attempting to extract partition 'vendor_b'...
Attempting to extract partition 'system_ext_a'...
  Dealing with extent 0 from target source 0...
Attempting to extract partition 'system_dlkm_a'...
  Dealing with extent 0 from target source 0...
Attempting to extract partition 'system_dlkm_b'...
Attempting to extract partition 'vendor_a'...
  Dealing with extent 0 from target source 0...
Attempting to extract partition 'vendor_dlkm_b'...
Attempting to extract partition 'vendor_dlkm_a'...
  Dealing with extent 0 from target source 0...

$  ls -l
total 14554372
-rw-r--r-- 1 xx xx  2979463168 Apr 29 13:04 product_a.img
-rw-r--r-- 1 xx xx           0 Apr 29 13:04 product_b.img
-rw-r--r-- 1 xx xx  3783004160 Apr 29 13:04 system_a.img
-rw-r--r-- 1 xx xx           0 Apr 29 13:04 system_b.img
-rw-r--r-- 1 xx xx    12328960 Apr 29 13:04 system_dlkm_a.img
-rw-r--r-- 1 xx xx           0 Apr 29 13:04 system_dlkm_b.img
-rw-r--r-- 1 xx xx   444387328 Apr 29 13:04 system_ext_a.img
-rw-r--r-- 1 xx xx           0 Apr 29 13:04 system_ext_b.img
-rw-r--r-- 1 xx xx   514940928 Apr 29 13:04 vendor_a.img
-rw-r--r-- 1 xx xx           0 Apr 29 13:04 vendor_b.img
-rw-r--r-- 1 xx xx    38473728 Apr 29 13:04 vendor_dlkm_a.img
-rw-r--r-- 1 xx xx           0 Apr 29 13:04 vendor_dlkm_b.img

system_dlkm_a.img and vendor_dlkm_a.img are the image files what we need.

Extract files

kernel image

$ unpack_bootimg.py --boot_img boot.img --out out_boot
boot magic: ANDROID!
kernel_size: 35289600
ramdisk size: 14241666
os version: 14.0.0
os patch level: 2025-03
boot image header version: 3
command line args:
$ ls -l out_boot/
total 48372
-rw-rw-r-- 1 xx xxh 35289600 Apr 29 13:13 kernel
-rw-rw-r-- 1 xx xxh 14241666 Apr 29 13:13 ramdisk

out_boot/kernel is the kernel image.

device tree image

$ unpack_bootimg.py --boot_img vendor_boot.img --out out_vendor_boot
boot magic: VNDRBOOT
vendor boot image header version: 3
page size: 0x00001000
kernel load address: 0x00008000
ramdisk load address: 0x01000000
vendor ramdisk size: 5037716
vendor command line args: debug user_debug=31 loglevel=0 print-fatal-signals=1 init=/init swiotlb=4096 kpti=0 pcie_ports=compat firmware_class.path=/vendor/firmware_mnt/image loop.max_part=7 androidboot.hardware=qcom androidboot.memcg=1 androidboot.recover_usb=1 androidboot.load_modules_parallel=true androidboot.fstab_suffix=gen3.qcom msm_show_resume_irq.debug_mask=1 log_buf_len=1M console=hvc0,115200 androidboot.console=hvc0 androidboot.selinux=enforcing
kernel tags load address: 0x00000100
product name: 86277384.LU
vendor boot image header size: 2112
dtb size: 1972599
dtb address: 0x0000000001f00000


$ ls -l out_vendor_boot
total 6848
-rw-rw-r-- 1 xx xx 1972599 Apr 29 13:14 dtb
-rw-rw-r-- 1 xx xx 5037716 Apr 29 13:14 vendor_ramdisk

The dtb file is the device tree binary. The vendor_ramdisk is the ramdisk image for android early bootup. we need to get some kernel modules(android first stage modules) from there

extract first stage kernel modules

The first stage kernel modules are needed to be extract from vendor_ramdisk.

out_vendor_boot$ mkdir out
out_vendor_boot$ cd out
out_vendor_boot/out$ lz4 -dc < ../vendor_ramdisk | cpio -id
31462 blocks

out_vendor_boot/out$ ls -l

first_stage_ramdisk
lib

out_vendor_boot/out$ ls -l lib/modules/

aquantia.ko                     mhi.ko                          pinctrl-monaco_auto.ko          smcinvoke_dlkm.ko
arm_smmu.ko                     minidump.ko                     pinctrl-msm.ko                  smem.ko
bootmarker_proxy.ko             modules.alias                   pinctrl-sdmshrike.ko            socinfo_dt.ko
boot_stats.ko                   modules.blocklist               pinctrl-slpi.ko                 spidev.ko
cfg80211.ko                     modules.dep                     pinctrl-sm6150.ko               spi-msm-geni.ko
clk-dummy.ko                    modules.load                    pinctrl-sm8150.ko               stmmac.ko
........

This kernel module folder need to be copied to the busybox rootfs image. Busybox rootfs image is the image be loaded by kernel. So the first stage modules need to be loaded from there because these are basic drivers suck as virtio-blk. We will talk about more later about image creations.

extract kernel modules from vendor_dlkm

Back to the super_out folder:

super_out$ sudo mount -o loop,ro vendor_dlkm_a.img for_mount/
super_out$ ls for_mount/

etc  lib  lost+found

cd for_mount/lib/modules

super_out/for_mount/lib/modules$ ls
adsp_loader_dlkm_legacy.ko  heap_mem_ext_v01.ko             msm-vm-poweroff.ko              q6_notifier_dlkm_legacy.ko  snd_event_dlkm_legacy.ko
apr_dlkm.ko                 hsi2s.ko                        native_dlkm.ko                  qca_cld3_qca6390.ko         socinfo_dt.ko
...

There is a file modules.load in this folder. This file will be used as a configuration file for a script to load the modules. Modules’ sequence must follow the definition from this file. This kernel module folder(lib/modules) need to be copied to the data.img (vendor_dlkm). This data.img is a block device be pased to the tinylinux then we can load these modules by script. We will talk about more later about image creations.

Extract kernel modules from system_dlkm

Back to the super_out folder:

super_out$ sudo mount -o loop,ro system_dlkm_a.img for_mount/

The modules folder structure is different from the vendor_dlkm.

super_out$ ls for_mount/lib/modules/6.1.99-android14-11-maybe-dirty/ -l

total 428
drwxr-xr-x. 8 root root   4096 Apr 15 15:54 kernel
-rw-r--r--. 1 root root  67078 Apr 15 15:54 modules.alias
-rw-r--r--. 1 root root  69016 Apr 15 15:54 modules.alias.bin
-rw-r--r--. 1 root root  22284 Apr 15 15:54 modules.builtin
-rw-r--r--. 1 root root  25913 Apr 15 15:54 modules.builtin.alias.bin
-rw-r--r--. 1 root root  26267 Apr 15 15:54 modules.builtin.bin
-rw-r--r--. 1 root root 141192 Apr 15 15:54 modules.builtin.modinfo
-rw-r--r--. 1 root root   4229 Apr 15 15:54 modules.dep
-rw-r--r--. 1 root root   6846 Apr 15 15:54 modules.dep.bin
-rw-r--r--. 1 root root     97 Apr 15 15:54 modules.devname
-rw-r--r--. 1 root root   1955 Apr 15 15:54 modules.load
-rw-r--r--. 1 root root   1955 Apr 15 15:54 modules.order
-rw-r--r--. 1 root root     55 Apr 15 15:54 modules.softdep
-rw-r--r--. 1 root root  14590 Apr 15 15:54 modules.symbols
-rw-r--r--. 1 root root  18117 Apr 15 15:54 modules.symbols.bin

There is a file modules.load in this folder. This file will be used as a configuration file for a script to load the modules. Modules’ sequence must followe the definition from this file.

This kernel module folder(lib/modules) need to be copied to the data.img (system_dlkm). This data.img is a block device be passed to the linux guest then we can load these modules by script. We will talk about more later about image creations.

Create images

Busybox root filesystem image

How to compile the busybox and create a rootfs from scratch will not include in this section. Because it is not necessary to create everytime from scratch. Here we only talk about to work with a available busybox image. To add configuration, scripts and kernel modules and generate a updated image.

Introduction to the busybox image:

sudo mount -o loop,rw bxrfs.img for_mount/

The file for_mount/etc/init.d/rcS is the init script when this image it loaded:

for_mount/etc/init.d$ cat rcS
mkdir -p /sys
mkdir -p /tmp
mkdir -p /proc
mkdir -p /mnt
mkdir -p /mnt/data
mkdir -p /mnt/vda
mkdir -p /vendor/
mkdir -p /system
mkdir -p /vendor/firmware_mnt   ## this is the partition for FW. we will mount the corresponding partition here.

ln -s /mnt/data/vendor_dlkm/lib/ /vendor/  ## /mnt/data/ is the folder which mounted with data.img. Here create a folder linked to the vendor_dlkm modules
ln -s /mnt/data/system_dlkm/lib/ /system   ## /mnt/data/ is the folder which mounted with data.img. Here create a folder linked to the system_dlkm modules

/bin/mount -a
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s
mdev -d

echo "===============load modules==============="
/bin/sh /etc/load_boot_m.sh     ## this is the script to load the first stage modules


/bin/mount -a   ## mount all partitions defined in the for_mount/etc/fstab

The content of for_mount/etc/fstab:

#device  mount-point    type     options   dump   fsck order
proc /proc proc defaults 0 0
tmpfs /tmp tmpfs defaults 0 0
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
debugfs /sys/kernel/debug debugfs defaults 0 0
/dev/vda /mnt/vda  ext4  defaults 0 0
/dev/vdb /mnt/data  ext2  defaults 0 0
/dev/vde /vendor/firmware_mnt vfat defaults 0 0

The folder for_mount/lib/modules is the place to store the first stage modules. Delete for_mount/lib/modules and replace with the folder from the new first stage kernel modules you extracted.

Then you get a updated busybox image.

6.2 data image for the rest of kernel modules

data.img is a 512MB image only stores the kernel modules extracted from vendor_dlkm and system_dlkm image. we don’t need create data.img from scrach. Just update current available one is enough.

sudo mount -o loop,ro data.img for_mount/

ls for_mount/
lost+found  system_dlkm  vendor_dlkm

system_dlkm and vendor_dlkm folder are contains the lib/modules from system_dlkm.img and vendor_dlkm.img.

7. Introduction for the QVM config

Because we already have the qvm configuration for android bootup. We can recreate a QVM config based on that. Below provide the delta difference:


load /var/test_folder/kernel  # Load the kernel
initrd load /var/test_folder/bxrfs.img  # Load the fs image as ramdisk
fdt load /var/test_folder/sa8155-vm-xxx.dtb # Load the device tree blob

# The kernel command line options
cmdline "console=ttyAMA0 earlycon=pl011,0x1c090000 rw rootfstype=ext2 root=/dev/ram0 init=/linuxrc ramdisk_size=65536 swiotlb=4096 kpti=0 pcie_ports=com    pat firmware_class.path=/vendor/firmware_mnt/image loop.max_part=7"

# UART console
vdev vdev-pl011.so loc 0x1c090000 intr gic:37 sched 20 hostdev /dev/ptyqa

# VDA for debian rootfs,  it is not used.
vdev vdev-virtio-blk.so sched 10 loc 0x1c0b0000  intr gic:40 threads 4 hostdev  /var/test_folder/debian.img
# VDB for store vendor_dlkm and system_dlkm kernel modules 
vdev vdev-virtio-blk.so sched 10 loc 0x1c0e0000 intr gic:39 threads 4 hostdev /var/test_folder/data.img

# VDE the android original partition which includes the FW images
vdev vdev-virtio-blk.so sched 10 loc 0x1c110000  intr gic:44 threads 4 hostdev /dev/disk/modem_a dio enable


8. Example log to update the images from android images

sudo mount -o loop,ro system_dlkm_a.img for_mount_system_dlkm
sudo mount -o loop,ro vendor_dlkm_a.img for_mount_vendor_dlkm

sudo mount -o loop,rw data.img for_mount
cp ../../../super_out/for_mount_system_dlkm/lib/ ./ -a

for_mount$ tree -L 4 system_dlkm/
system_dlkm/
└── lib
    └── modules
        └── 6.1.99-android14-11-maybe-dirty
            ├── kernel
            ├── modules.alias
            ├── modules.alias.bin
            ├── modules.builtin
            ├── modules.builtin.alias.bin
            ├── modules.builtin.bin
            ├── modules.builtin.modinfo
            ├── modules.dep
            ├── modules.dep.bin
            ├── modules.devname
            ├── modules.load
            ├── modules.order
            ├── modules.softdep
            ├── modules.symbols
            └── modules.symbols.bin

we meed to move the folder lib/modules/6.1.99-android14-11-maybe-dirty/* to the upper layer folder with command like :

mv system_dlkm/lib/modules/6.1.99-android14-11-maybe-dirty/* system_dlkm/lib/modules/


for_mount/vendor_dlkm$ tree -L 2 lib/
lib/
└── modules
    ├── adsp_loader_dlkm_legacy.ko
    ├── apr_dlkm.ko
    ├── aquantia.ko
    ├── boot_marker.ko
    ├── btpower.ko
    ├── cfg80211.ko
    ├── clk-dummy.ko
    ├── clk-qcom.ko
    ├── cnss2.ko
    ├── cnss_nl.ko
......

cd sudo mount -o loop,rw bxrfs.img for_mount/
sudo umount for_mount/


sudo mount -o loop,rw bxrfs.img for_mount/
cp -ar ../../../out_vendor_boot/out/lib/modules/ ./

cd sudo mount -o loop,rw bxrfs.img for_mount/
sudo umount for_mount/

Copy files to target and launch it: Below are the files you need to copy to


# ls -l /var/00000000/
total 2376927
-rwxrwxrwx   1 root      root       67108864 May 06 12:13 bxrfs.img
-rwxrwxrwx   1 root      root      536870912 May 04 23:24 data.img
-rwxrwxrwx   1 root      root      536870912 May 04 23:23 debian.img
-rwxrwxrwx   1 root      root          12518 May 04 23:19 high_qvm_config.config
-rw-rw-r--   1 root      root       35289600 May 05 09:46 kernel
-rwxrwxrwx   1 root      root           3494 May 05 09:53 vdisk.config

Stop current qvm:

dtach -a /tmp/vmm
su
reboot -p

wait 10s to launch the tiny Linux

export GUESTDUMP_ENABLE=0
qvm @/var/00000000/high_qvm_config.config  @/var/00000000/vdisk.config &
dtach -a /tmp/vmm

After boot up, we need execute 3 scripts to load the modules:

sh system_dlkm_modprobe.sh

sh set_wlan_fw.sh
-rwxr--r--    1 70675579 70675579     12786 Jun  6  2025 /mnt/data/vendor/firmware/wlan/qca_cld/qca6390/WCNSS_qcom_cfg.ini
total 16
-rwxr--r--    1 0        0            12786 Jan  1 00:04 WCNSS_qcom_cfg.ini

sh vendor_modprobe.sh

lspci

    01:00.0 Class 0280: 17cb:1101
    00:00.0 Class 0604: 17cb:0109


ifconfig wlan0
    wlan0     Link encap:Ethernet  HWaddr E0:2D:F0:98:75:54
              BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:3000
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

ifconfig wlan1
    wlan1     Link encap:Ethernet  HWaddr E0:2D:F0:19:75:54
              BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:3000
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


Till now, the Linux and kernel driver modules are all loadded.

Prepare a busybox - the rootfs creation

We will build and generate rootfs from busybox(here with busybox-1.35.0.tar.bz2). To build the busybox we need first download a toolchian. Here is a link for a Linaro toolchain : gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu.tar.xz It can be downloaded from https://releases.linaro.org/components/toolchain/binaries/latest-5/aarch64-linux-gnu/

For example, we put the toolchain under folder /media/data-nvm/temp/rootfs/gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu, then run below commands:

 export PATH=/...../gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/bin:$PATH
 export ARCH=arm64
 export CROSS_COMPILE=aarch64-linux-gnu-

Config the busybix to make it build to static bianry.

make menuconfig
 setting
 [*] Build static binary (no shared libs)

Compile busybox:

make
make install

## below command is important to copy the libc dependencies: 
cp -ar gcc-linaro-5.5.0-2017.10-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/libc/* _install/lib

Create rootfs

cd _install
mkdir dev etc lib sys proc tmp var home root mnt  
vi etc/profile
 #!/bin/sh
 export HOSTNAME=weller
 export USER=root
 export HOME=/home
 export PS1="[$USER@$HOSTNAME \W]\# "
 PATH=/bin:/sbin:/usr/bin:/usr/sbin
 LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
 export PATH LD_LIBRARY_PATH
vi etc/inittab
 ::sysinit:/etc/init.d/rcS
 ::respawn:-/bin/sh
 ::askfirst:-/bin/sh
 ::ctrlaltdel:/bin/umount -a -r
vi etc/fstab
 #device  mount-point    type     options   dump   fsck order
 proc /proc proc defaults 0 0
 tmpfs /tmp tmpfs defaults 0 0
 sysfs /sys sysfs defaults 0 0
 tmpfs /dev tmpfs defaults 0 0
 debugfs /sys/kernel/debug debugfs defaults 0 0
 kmod_mount /mnt 9p trans=virtio 0 0
vi etc/fstab
 #device  mount-point    type     options   dump   fsck order
 proc /proc proc defaults 0 0
 tmpfs /tmp tmpfs defaults 0 0
 sysfs /sys sysfs defaults 0 0
 tmpfs /dev tmpfs defaults 0 0
 debugfs /sys/kernel/debug debugfs defaults 0 0
 kmod_mount /mnt 9p trans=virtio 0 0
cd dev/
 sudo mknod console c 5 1