Hi, recently I have been looking into how user processes are prepared. Today's post is about the how user process and thread are being prepared in terms of allocated memory.
When a new process or thread being created, it calls clone system call. If you want to create a thread, you can pass CLONE_VM flag to the system call. One of the difference between a process and thread is that wheather it shares its memory space or not. The latter does but the former does not, and today's post is about looking into this difference.
In order to see the difference, I prepared a program which ① the parents process assignes a variable with 5, and ② the cloned child process rewrites the variable with 10. The difference mentioned earlier can be seen at the ② point.
Let's first see how the process looks like. Below shows that the PID 1628 is the parent and the 1629 is the child.
crash> ps | grep clone
1628 1601 0 ffff94fd4a271c80 IN 0.1 2628 1336 clone
1629 1628 0 ffff94fd4123b900 IN 0.1 2628 884 clone
crash>
Now let's take a look at the physical address correspoding to the variable's virtual address which is 0x404068. You can see the corresponding physical address is 17bfe068. As expected, the value at the address is still 5 as the child process does not share the address space.
crash> vtop -c ffff94fd4a271c80 404068
VIRTUAL PHYSICAL
404068 17bfe068
...
crash> rd -p 17bfe068 10
17bfe068: 0000000000000005 00000000014b72b0 .........rK.....
...
crash>
However, if you take a look at the threading, you will see the varible is now changed as the child shares the same address space and rewrites the variable.
crash> ps | grep clone
1714 1601 0 ffff94fd44e2d580 IN 0.1 2628 1396 clone
1715 1714 0 ffff94fd41291c80 IN 0.1 2628 964 clone
crash> vtop -c ffff94fd44e2d580 404068
VIRTUAL PHYSICAL
404068 1161e068
...
crash> rd -p 1161e068 10
1161e068: 000000000000000a 00000000004c32b0 .........2L.....
...
crash>
That's it for the today's post. Note that I used x86 for this experiment as vtop on arm64(below) did not work. I wonder why🤔. Might be fun looking into it.
crash> vtop -c 11884 aaaae706c84c
VIRTUAL PHYSICAL
vtop: invalid structure member offset: mm_struct_mmap
FILE: memory.c LINE: 4007 FUNCTION: vm_area_dump()
[/usr/bin/crash] error trace: aaaae706c84c => aaaae706456c => aaaae7101180 => aaaae71010ec
vtop: invalid structure member offset: mm_struct_mmap
FILE: memory.c LINE: 4007 FUNCTION: vm_area_dump()
crash>
Hi, I wanted make a post on how the first process gets initiated as I have been playing around with qemu recently. Specifically, I will describe how the parameters below lead to the /sbin/init, which has the pid of 1.
admin@ip-10-0-16-6:~/busybox/busybox$ qemu-system-aarch64 -M virt -cpu cortex-a72 -m 1024 -kernel /boot/vmlinuz-6.1.0-13-cloud-arm64 -initrd /home/admin/busybox/busybox/rootfs -append "root=/dev/ram0 rdinit=/sbin/init nokaslr" -nographic
Let's start with the source code. After the kernel finishes the initilization of this and that, it gets ready for the first process and calls rest_init. The function inits the rest and calls kernel_init, and then run_init_process gets called.
Now back to the arguments and see what it's doing. If you take a look at the man page of initrd, it says that -initrd gets uncompressed onto the /dev/ram0, which then mounted as the inital root file system. Here I found little confusing is that if you specify root as /dev/ram0, the initial root file system, which is /dev/ram0, will remain as the normal root file system.
Let's make sure that the first run_init_process is the one being called for initiating the first process in the case of using /dev/ram0 as the normal root file system. Here you can see that the execute_command has /sbin/init, the program specifed by rdinit from the normal root file system which now is the /dev/ram0.
(gdb) print ramdisk_execute_command
$1 = 0xffff00003fdf0196 "/sbin/init"
(gdb) print execute_command
$2 = 0x0
(gdb)
By the way, don't forget to specify "nokaslr" for the boot parameter. Otherwise, break points won't work😑. That is it for this post!