实验要求:
实验步骤:
一、实验环境准备
1.下载内核源代码
sudo apt install axel axel -n 20 https://mirrors.edge.kernel.org/pub/linux/kernel/v5.x/linux-5.4.34.tar.xz xz -d linux-5.4.34.tar.xz tar -xvf linux-5.4.34.tar cd linux-5.4.34
2.配置内核选项
make defconfifig # Default confifiguration is based on ‘x86_64_defconfifig‘ make menuconfifig # 打开debug相关选项 Kernel hacking ---> Compile-time checks and compiler options ---> [*] Compile the kernel with debug info [*] Provide GDB scripts for kernel debugging [*] Kernel debugging # 关闭KASLR,否则会导致打断点失败 Processor type and features ----> [] Randomize the address of the kernel image (KASLR)
3.编译和运行文件内核
make -j$(nproc) # nproc gives the number of CPU cores/threads available
# 测试?下内核能不能正常加载运?,因为没有?件系统最终会kernel panic
qemu-system-x86_64 -kernel arch/x86/boot/bzImage
编译过程中报错:
Makefile:1076: recipe for target ‘vmlinux‘ failed
make: *** [vmlinux] Error 137
解决:查明原因是swap交换空间不足,于是按以下方法增加swap分区空间。
free#查看swap可用空间大小,发现为0,增加可用空间 cd / sudo mkdir swap cd swap/ sudo dd if=/dev/zero of=swapfile bs=1M count=2048#增加2g空间 sudo mkswap swapfile sudo swapon swapfile free #查看扩展后的空间
此时不能运行属于正常现象。
3.制作根文件系统
回到根文件目录,执行
axel -n 20 https://busybox.net/downloads/busybox-1.31.1.tar.bz2 tar -jxvf busybox-1.31.1.tar.bz2 cd busybox-1.31.1 make menucon?g
make -j$(nproc) && make install
然后制作根系统文件镜像。
mkdir rootfs cd rootfs cp ~/busybox-1.31.1/_install/* ./ -rf mkdir dev proc sys home sudo cp -a /dev/{null,console,tty,tty1,tty2,tty3,tty4} dev/
准备init脚本?件放在根?件系统跟?录下(rootfs/init),添加如下内容到init?件。
#!/bin/sh mount -t proc none /proc mount -t sysfs none /sys echo "Welcome My OS!" echo "-------------------" cd home /bin/sh
给init脚本增加可执行权限
chmod +x init
输入下面代码查看qemu运行情况:
#打包成内存根?件系统镜像 ?nd . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz #回根目录测试挂载根?件系统,看内核启动完成后是否执?init脚本 qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd busybox-1.31.1/rootfs.cpio.gz
执行成功,可以进行接下来的系统调用实验。
二、触发系统调用
1.打开/linux-5.4.34/arch/x86/entry/syscalls/syscall_64.tb文件,查询得知
找到对应函数原型,
shmat的作用是把共享内存区对象映射到调用进程的地址空间,通常要和shmget函数一起出现,达到创建和使用共享内存的目的。共享内存是进程间通信中最简单的方式之一。共享内存允许两个或更多进程访问同一块内存,就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。
所需头文件:
#include <sys/types.h>
#include <sys/shm.h>
函数传入值:
msqid(共享内存标识符)
shmaddr(指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置)
shmflg(SHM_RDONLY:为只读模式,其他为读写模式)
函数返回值
成功:附加好的共享内存地址
出错:-1,错误原因存于error中
fork后子进程继承已连接的共享内存地址。exec后该子进程与已连接的共享内存地址自动脱离(detach)。进程结束后,已连接的共享内存地址会自动脱离(detach)
错误代码:
EACCES:无权限以指定方式连接共享内存
EINVAL:无效的参数shmid或shmaddr
ENOMEM:核心内存不足
2.编写测试函数 testsh.c
#include <sys/shm.h> #include <sys/types.h> #include <unistd.h> #include <string.h> #include <stdio.h> typedef struct{ char name[4]; int age; } people; int main(int argc, char** argv) { int shm_id,i; key_t key; char temp; people *p_map; char* name = "/dev/shm/myshm2"; key = ftok(name,0); if(key==-1) printf("ftok error"); shm_id=shmget(key,4096,IPC_CREAT); if(shm_id==-1) { printf("shmget error"); return 0; } p_map=(people*)shmat(shm_id,NULL,0); temp=‘a‘; for(i = 0;i<10;i++) { temp+=1; memcpy((*(p_map+i)).name,&temp,1); (*(p_map+i)).age=20+i; } if(shmdt(p_map)==-1) printf(" detach error "); return 0; }
将测试文件放在rootfs/home 目录下,进行静态编译,再在rootfs目录下重新打包。
gcc -o testsh testsh.c -static find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../rootfs.cpio.gz
3.gdb系统跟踪调用
先启动qemu
qemu-system-x86_64 -kernel linux-5.4.34/arch/x86/boot/bzImage -initrd rootfs.cpio.gz -S -s -nographic -append "console=ttyS0"
再重新打开一个终端,运行以下命令:
cd linux-5.4.34/ gdb vmlinux target remote:1234 b __x64_sys_shmat
c
在运行qemu的终端中,输入./testssh即可开始单步调试,调试过程如下:
Breakpoint 1, __x64_sys_shmat (regs=0xffffc900001b7f58) at ipc/shm.c:1591 1591 SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) (gdb) n do_syscall_64 (nr=18446744071600450016, regs=0xffffc900001b7f58) at arch/x86/entry/common.c:300 300 syscall_return_slowpath(regs); (gdb) n 301 } (gdb) n entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:184 184 movq RCX(%rsp), %rcx (gdb) n 185 movq RIP(%rsp), %r11 (gdb) n 187 cmpq %rcx, %r11 /* SYSRET requires RCX == RIP */ (gdb) n 188 jne swapgs_restore_regs_and_return_to_usermode (gdb) n 205 shl $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx (gdb) n 206 sar $(64 - (__VIRTUAL_MASK_SHIFT+1)), %rcx (gdb) n 210 cmpq %rcx, %r11 (gdb) n 211 jne swapgs_restore_regs_and_return_to_usermode (gdb) n 213 cmpq $__USER_CS, CS(%rsp) /* CS must match SYSRET */ (gdb) n 214 jne swapgs_restore_regs_and_return_to_usermode (gdb) n 216 movq R11(%rsp), %r11 (gdb) n 217 cmpq %r11, EFLAGS(%rsp) /* R11 == RFLAGS */ (gdb) n 218 jne swapgs_restore_regs_and_return_to_usermode (gdb) n 238 testq $(X86_EFLAGS_RF|X86_EFLAGS_TF), %r11 (gdb) n 239 jnz swapgs_restore_regs_and_return_to_usermode (gdb) n 243 cmpq $__USER_DS, SS(%rsp) /* SS must match SYSRET */ (gdb) n 244 jne swapgs_restore_regs_and_return_to_usermode (gdb) n 253 POP_REGS pop_rdi=0 skip_r11rcx=1 (gdb) n entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:259 //现场恢复 259 movq %rsp, %rdi (gdb) n 260 movq PER_CPU_VAR(cpu_tss_rw + TSS_sp0), %rsp (gdb) n entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:262 262 pushq RSP-RDI(%rdi) /* RSP */ (gdb) n entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:263 263 pushq (%rdi) /* RDI */ (gdb) n entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:271 271 SWITCH_TO_USER_CR3_STACK scratch_reg=%rdi (gdb) n 273 popq %rdi (gdb) n entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:274 274 popq %rsp (gdb) n entry_SYSCALL_64 () at arch/x86/entry/entry_64.S:275 275 USERGS_SYSRET64 (gdb) n 0x000000000044b9f7 in ?? () (gdb) n Cannot find bounds of current function
4.总结分析
进程间需要共享的数据被放在一个叫做IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel结构注同时,还将在特殊文件系统shm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用shmget完成的。
在创建了一个共享内存区域后,还要将它映射到进程地址空间,系统调用shmat()完成此项功能,通过do_syscall_64执行系统调用内容。触发相应函数,完成对象映射后恢复现场。
原文:https://www.cnblogs.com/su-perqaq/p/12956807.html