不用链接C库的裸程序,向标准输出打印hello并调用exit退出:
.section .rodata hellostr: .ascii "hello\n" .section .text .global _start
_start: mov x0, #1 ldr x1, =hellostr mov x2, #6 mov x8, #64 //syscall write svc
#0 mov x0, #0 mov x8, #93 //syscall exit svc #0 all: as main.s -o main.o ld
main.o -o a.out rm main.o
链接C库,调用printf打印hello并退出:
.section .rodata hellostr: .ascii "hello\n" .section .text .global _start
_start: ldr x0, =hellostr bl printf mov x0, #0 bl exit all: as main.s -o main.o
ld -dynamic-linker /lib/ld-linux-aarch64.so.1 -lc main.o -o a.out rm main.o $
ldd a.out linux-vdso.so.1 (0x0000007fa9beb000) libc.so.6 =>
/lib/aarch64-linux-gnu/libc.so.6 (0x0000007fa9a33000)
/lib/ld-linux-aarch64.so.1 (0x0000007fa9bbb000)
ldd 可以看到依赖了C库, vdso及ld解释器(loader)。
关于vdso,
为了提高时间相关的系统调用效率,内核会把时间及时刷新到一块内存上,把这段内存再映射到用户空间,获取这这些数据相关的代码就是在vdso中实现的,映射到用户空间的内存数据就是vvar。找一个运行中的程序,内存映射如下,vvar即是存储了时间的只读数据段,vdso即是访问它的代码段:
55a483a000-55a485b000 rw-p 00000000 00:00 0 [heap] 7fa8154000-7fa873d000 r--p
00000000 b3:02 660 /usr/lib/locale/locale-archive 7fa873d000-7fa889a000 r-xp
00000000 b3:02 7015 /usr/lib/aarch64-linux-gnu/libc-2.31.so
7fa889a000-7fa88aa000 ---p 0015d000 b3:02 7015
/usr/lib/aarch64-linux-gnu/libc-2.31.so 7fa88aa000-7fa88ad000 r--p 0015d000
b3:02 7015 /usr/lib/aarch64-linux-gnu/libc-2.31.so 7fa88ad000-7fa88b0000 rw-p
00160000 b3:02 7015 /usr/lib/aarch64-linux-gnu/libc-2.31.so
7fa88b0000-7fa88b3000 rw-p 00000000 00:00 0 7fa88c5000-7fa88e7000 r-xp 00000000
b3:02 6839 /usr/lib/aarch64-linux-gnu/ld-2.31.so 7fa88f1000-7fa88f3000 rw-p
00000000 00:00 0 7fa88f3000-7fa88f5000 r--p 00000000 00:00 0 [vvar]
7fa88f5000-7fa88f6000 r-xp 00000000 00:00 0 [vdso] 7fa88f6000-7fa88f7000 r--p
00021000 b3:02 6839 /usr/lib/aarch64-linux-gnu/ld-2.31.so 7fa88f7000-7fa88f9000
rw-p 00022000 b3:02 6839 /usr/lib/aarch64-linux-gnu/ld-2.31.so
7fe3763000-7fe3784000 rw-p 00000000 00:00 0 [stack]
ld-linux-aarch64.so 是一个加载器,在命令行上运行程序时,bash终端执行fork,
exec打开a.out,a.out需要解释器,内核就将ld-linux-aarch64.so映射进来,接下来ld-linux-aarch64.so
将a.out依赖的动态库加载到进程地址空间(代码段,数据段等映射进来),准备好相关工作后,调用a.out入口点_start 。
用strace 查看a.out执行相关的系统调用,没有ld-linux-aarch64.so, 是因为execve执行时,内核把它加载了。
$ strace ./a.out execve("./a.out", ["./a.out"], 0x7fd3127fb0 /* 24 vars */) =
0 brk(NULL) = 0x33b33000 faccessat(AT_FDCWD, "/etc/ld.so.preload", R_OK) = -1
ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache",
O_RDONLY|O_CLOEXEC) = 3 fstat(3, {st_mode=S_IFREG|0644, st_size=70321, ...}) =
0 mmap(NULL, 70321, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa50e4000 close(3) = 0
openat(AT_FDCWD, "/lib/aarch64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0\267\0\1\0\0\0`C\2\0\0\0\0\0"...,
832) = 832 fstat(3, {st_mode=S_IFREG|0755, st_size=1458480, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) =
0x7fa5122000 mmap(NULL, 1531032, PROT_READ|PROT_EXEC,
MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa4f6e000 mprotect(0x7fa50cb000, 65536,
PROT_NONE) = 0 mmap(0x7fa50db000, 24576, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15d000) = 0x7fa50db000
mmap(0x7fa50e1000, 11416, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa50e1000 close(3) = 0
mprotect(0x7fa50db000, 12288, PROT_READ) = 0 mprotect(0x410000, 4096,
PROT_READ) = 0 mprotect(0x7fa5127000, 4096, PROT_READ) = 0 munmap(0x7fa50e4000,
70321) = 0 write(1, "hello\n", 6hello ) = 6 fstat(1, {st_mode=S_IFCHR|0620,
st_rdev=makedev(0x88, 0), ...}) = 0 brk(NULL) = 0x33b33000 brk(0x33b54000) =
0x33b54000 write(1, "hello\n", 6hello ) = 6 exit_group(0) = ? +++ exited with 0
+++