ARM架构GNU/Linux下汇编程序执行流程及最小实现问询
Great question—let’s break this down step by step to demystify how your assembly program runs, what the C standard library (and those crt*.o files) actually do, and how to build the smallest possible program that returns exit code 5.
First: What’s the C Standard Library & crt*.o Files Doing Here?
When you linked with crt1.o, crti.o, crtn.o, and libc.so, you’re pulling in the C runtime (CRT) infrastructure—here’s the breakdown of each component:
crt1.o: This is the real entry point for your program (notmain!). The Linux kernel doesn’t know or care aboutmain; it jumps to the_startsymbol defined incrt1.o. This file sets up the initial stack, initializes global variables (like.dataand.bsssegments), and calls__libc_start_mainfromlibc.so.__libc_start_main(fromlibc.so): This function handles the heavy lifting: it runs any pre-main initialization (like global constructors), calls yourmainfunction, captures its return value (stored inw0), and then callsexit()to pass that return code to the kernel.crti.o&crtn.o: These files wrap the.initand.finisections of your program. Code in.initruns beforemain(e.g., setting up library state), and.finiruns aftermainexits (e.g., cleaning up resources). They’re mostly for supporting C++ style constructors/destructors or library initialization.libc.so: Beyond__libc_start_mainandexit, it provides all the standard C functions, but in your simple program, you’re only using it to bridge yourmainfunction to the kernel’s exit system call.
Minimal Assembly Program to Return Exit Code 5 (No C Library Needed)
You don’t actually need the C runtime to return an exit code—you can directly invoke the Linux system call yourself. Here’s the minimal AArch64 assembly code:
.text .global _start // Tell the linker this is the entry point _start: mov x0, #5 // Store exit code 5 in x0 (first system call argument) mov x8, #93 // Linux exit system call number is 93 for AArch64 svc #0 // Trigger the system call to the kernel
To build and run this:
as prog.s -o prog.o ld prog.o -o prog ./prog; echo $? // Outputs 5
This skips all the C runtime overhead—your _start is the direct entry point the kernel jumps to, and you’re calling the exit system call directly.
Full Execution Steps When You Run ./prog
Let’s walk through exactly what happens from typing the command to seeing the exit code:
- Shell Initiates Execution: Your shell (e.g., bash) parses the
./progcommand and calls theexecve()system call, passing the path to your program and any arguments. - Kernel Loads the ELF Binary:
- The kernel reads the ELF header of
progto find the program segments (.text,.data, etc.). - It maps these segments into the new process’s virtual address space.
- It sets up the process’s stack (initializes the stack pointer
spto a valid memory location). - It sets the program counter (
pc) to the entry point defined in the ELF header (your_startsymbol).
- The kernel reads the ELF header of
- Kernel Switches to User Mode: The kernel hands control to the user-space process, and the CPU starts executing code at
_start.- For your original libc-linked program:
_startfromcrt1.oinitializes the runtime, calls__libc_start_main, which runs yourmainfunction. Whenmainreturns,__libc_start_maincallsexit(w0), which eventually triggers the exit system call. - For the minimal program: The CPU executes the three instructions immediately, triggering the exit system call.
- For your original libc-linked program:
- Kernel Handles Exit: The exit system call tells the kernel to clean up the process: close open file descriptors, free allocated memory, and update the process’s exit status to the value in
x0(5). - Shell Reports Exit Code: The kernel returns the exit status to the shell (the parent process). When you run
echo $?, the shell prints the stored exit status of the last executed command—your program’s 5.
内容的提问来源于stack exchange,提问作者jregalad

