操作系统实战45讲笔记-01 程序的运行过程:从代码到机器运行
计算机硬件是无法直接运行C 语言文本程序代码的,需要 C 语言编译器,把这个代码编译成具体硬件平台的二进制代码。再由具体操作系统建立进程,把这个二进制文件装进其进程的内存空间中,才能运行。
程序编译过程
示例代码:
经典Hello World C代码
#include "stdio.h" int main(int argc, char const *argv[]) { printf("hello world!\n"); return 0; }
GCC 只是完成编译工作的驱动程序,它会根据编译流程分别调用预处理程序、编译程序、汇编程序、链接程序来完成具体工作

• gcc HelloWorld.c -E -o HelloWorld.i 预处理:加入头文件,替换宏。输出如下,
点击查看HelloWorld.i内容
xyjk1002@sheldon:~/cosmos/lesson01/HelloWorld$ cat HelloWorld.i # 1 "HelloWorld.c" # 1 "<built-in>" # 1 "<command-line>" # 31 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 32 "<command-line>" 2 # 1 "HelloWorld.c" # 1 "/usr/include/stdio.h" 1 3 4 # 27 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 1 3 4 # 33 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 3 4 # 1 "/usr/include/features.h" 1 3 4 # 424 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4 # 427 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4 # 428 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/long-double.h" 1 3 4 # 429 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4 # 425 "/usr/include/features.h" 2 3 4 # 448 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4 # 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4 # 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4 # 449 "/usr/include/features.h" 2 3 4 # 34 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 2 3 4 # 28 "/usr/include/stdio.h" 2 3 4 # 1 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h" 1 3 4 # 216 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h" 3 4 # 216 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h" 3 4 typedef long unsigned int size_t; # 34 "/usr/include/stdio.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/types.h" 1 3 4 # 27 "/usr/include/x86_64-linux-gnu/bits/types.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4 # 28 "/usr/include/x86_64-linux-gnu/bits/types.h" 2 3 4 typedef unsigned char __u_char; typedef unsigned short int __u_short; typedef unsigned int __u_int; typedef unsigned long int __u_long; typedef signed char __int8_t; typedef unsigned char __uint8_t; typedef signed short int __int16_t; typedef unsigned short int __uint16_t; typedef signed int __int32_t; typedef unsigned int __uint32_t; typedef signed long int __int64_t; typedef unsigned long int __uint64_t; typedef long int __quad_t; typedef unsigned long int __u_quad_t; typedef long int __intmax_t; typedef unsigned long int __uintmax_t; # 130 "/usr/include/x86_64-linux-gnu/bits/types.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/typesizes.h" 1 3 4 # 131 "/usr/include/x86_64-linux-gnu/bits/types.h" 2 3 4 typedef unsigned long int __dev_t; typedef unsigned int __uid_t; typedef unsigned int __gid_t; typedef unsigned long int __ino_t; typedef unsigned long int __ino64_t; typedef unsigned int __mode_t; typedef unsigned long int __nlink_t; typedef long int __off_t; typedef long int __off64_t; typedef int __pid_t; typedef struct { int __val[2]; } __fsid_t; typedef long int __clock_t; typedef unsigned long int __rlim_t; typedef unsigned long int __rlim64_t; typedef unsigned int __id_t; typedef long int __time_t; typedef unsigned int __useconds_t; typedef long int __suseconds_t; typedef int __daddr_t; typedef int __key_t; typedef int __clockid_t; typedef void * __timer_t; typedef long int __blksize_t; typedef long int __blkcnt_t; typedef long int __blkcnt64_t; typedef unsigned long int __fsblkcnt_t; typedef unsigned long int __fsblkcnt64_t; typedef unsigned long int __fsfilcnt_t; typedef unsigned long int __fsfilcnt64_t; typedef long int __fsword_t; typedef long int __ssize_t; typedef long int __syscall_slong_t; typedef unsigned long int __syscall_ulong_t; typedef __off64_t __loff_t; typedef char *__caddr_t; typedef long int __intptr_t; typedef unsigned int __socklen_t; typedef int __sig_atomic_t; # 36 "/usr/include/stdio.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/types/__FILE.h" 1 3 4 struct _IO_FILE; typedef struct _IO_FILE __FILE; # 37 "/usr/include/stdio.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/types/FILE.h" 1 3 4 struct _IO_FILE; typedef struct _IO_FILE FILE; # 38 "/usr/include/stdio.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/libio.h" 1 3 4 # 35 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/_G_config.h" 1 3 4 # 19 "/usr/include/x86_64-linux-gnu/bits/_G_config.h" 3 4 # 1 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h" 1 3 4 # 20 "/usr/include/x86_64-linux-gnu/bits/_G_config.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h" 1 3 4 # 13 "/usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h" 3 4 typedef struct { int __count; union { unsigned int __wch; char __wchb[4]; } __value; } __mbstate_t; # 22 "/usr/include/x86_64-linux-gnu/bits/_G_config.h" 2 3 4 typedef struct { __off_t __pos; __mbstate_t __state; } _G_fpos_t; typedef struct { __off64_t __pos; __mbstate_t __state; } _G_fpos64_t; # 36 "/usr/include/x86_64-linux-gnu/bits/libio.h" 2 3 4 # 53 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 # 1 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stdarg.h" 1 3 4 # 40 "/usr/lib/gcc/x86_64-linux-gnu/7/include/stdarg.h" 3 4 typedef __builtin_va_list __gnuc_va_list; # 54 "/usr/include/x86_64-linux-gnu/bits/libio.h" 2 3 4 # 149 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 struct _IO_jump_t; struct _IO_FILE; typedef void _IO_lock_t; struct _IO_marker { struct _IO_marker *_next; struct _IO_FILE *_sbuf; int _pos; # 177 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 }; enum __codecvt_result { __codecvt_ok, __codecvt_partial, __codecvt_error, __codecvt_noconv }; # 245 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 struct _IO_FILE { int _flags; char* _IO_read_ptr; char* _IO_read_end; char* _IO_read_base; char* _IO_write_base; char* _IO_write_ptr; char* _IO_write_end; char* _IO_buf_base; char* _IO_buf_end; char *_IO_save_base; char *_IO_backup_base; char *_IO_save_end; struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; int _flags2; __off_t _old_offset; unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; _IO_lock_t *_lock; # 293 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 __off64_t _offset; void *__pad1; void *__pad2; void *__pad3; void *__pad4; size_t __pad5; int _mode; char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)]; }; typedef struct _IO_FILE _IO_FILE; struct _IO_FILE_plus; extern struct _IO_FILE_plus _IO_2_1_stdin_; extern struct _IO_FILE_plus _IO_2_1_stdout_; extern struct _IO_FILE_plus _IO_2_1_stderr_; # 337 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 typedef __ssize_t __io_read_fn (void *__cookie, char *__buf, size_t __nbytes); typedef __ssize_t __io_write_fn (void *__cookie, const char *__buf, size_t __n); typedef int __io_seek_fn (void *__cookie, __off64_t *__pos, int __w); typedef int __io_close_fn (void *__cookie); # 389 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 extern int __underflow (_IO_FILE *); extern int __uflow (_IO_FILE *); extern int __overflow (_IO_FILE *, int); # 433 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 extern int _IO_getc (_IO_FILE *__fp); extern int _IO_putc (int __c, _IO_FILE *__fp); extern int _IO_feof (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__)); extern int _IO_ferror (_IO_FILE *__fp) __attribute__ ((__nothrow__ , __leaf__)); extern int _IO_peekc_locked (_IO_FILE *__fp); extern void _IO_flockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); extern void _IO_funlockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); extern int _IO_ftrylockfile (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); # 462 "/usr/include/x86_64-linux-gnu/bits/libio.h" 3 4 extern int _IO_vfscanf (_IO_FILE * __restrict, const char * __restrict, __gnuc_va_list, int *__restrict); extern int _IO_vfprintf (_IO_FILE *__restrict, const char *__restrict, __gnuc_va_list); extern __ssize_t _IO_padn (_IO_FILE *, int, __ssize_t); extern size_t _IO_sgetn (_IO_FILE *, void *, size_t); extern __off64_t _IO_seekoff (_IO_FILE *, __off64_t, int, int); extern __off64_t _IO_seekpos (_IO_FILE *, __off64_t, int); extern void _IO_free_backup_area (_IO_FILE *) __attribute__ ((__nothrow__ , __leaf__)); # 42 "/usr/include/stdio.h" 2 3 4 typedef __gnuc_va_list va_list; # 57 "/usr/include/stdio.h" 3 4 typedef __off_t off_t; # 71 "/usr/include/stdio.h" 3 4 typedef __ssize_t ssize_t; typedef _G_fpos_t fpos_t; # 131 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/stdio_lim.h" 1 3 4 # 132 "/usr/include/stdio.h" 2 3 4 extern struct _IO_FILE *stdin; extern struct _IO_FILE *stdout; extern struct _IO_FILE *stderr; extern int remove (const char *__filename) __attribute__ ((__nothrow__ , __leaf__)); extern int rename (const char *__old, const char *__new) __attribute__ ((__nothrow__ , __leaf__)); extern int renameat (int __oldfd, const char *__old, int __newfd, const char *__new) __attribute__ ((__nothrow__ , __leaf__)); extern FILE *tmpfile (void) ; # 173 "/usr/include/stdio.h" 3 4 extern char *tmpnam (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ; extern char *tmpnam_r (char *__s) __attribute__ ((__nothrow__ , __leaf__)) ; # 190 "/usr/include/stdio.h" 3 4 extern char *tempnam (const char *__dir, const char *__pfx) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) ; extern int fclose (FILE *__stream); extern int fflush (FILE *__stream); # 213 "/usr/include/stdio.h" 3 4 extern int fflush_unlocked (FILE *__stream); # 232 "/usr/include/stdio.h" 3 4 extern FILE *fopen (const char *__restrict __filename, const char *__restrict __modes) ; extern FILE *freopen (const char *__restrict __filename, const char *__restrict __modes, FILE *__restrict __stream) ; # 265 "/usr/include/stdio.h" 3 4 extern FILE *fdopen (int __fd, const char *__modes) __attribute__ ((__nothrow__ , __leaf__)) ; # 278 "/usr/include/stdio.h" 3 4 extern FILE *fmemopen (void *__s, size_t __len, const char *__modes) __attribute__ ((__nothrow__ , __leaf__)) ; extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __attribute__ ((__nothrow__ , __leaf__)) ; extern void setbuf (FILE *__restrict __stream, char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__)); extern int setvbuf (FILE *__restrict __stream, char *__restrict __buf, int __modes, size_t __n) __attribute__ ((__nothrow__ , __leaf__)); extern void setbuffer (FILE *__restrict __stream, char *__restrict __buf, size_t __size) __attribute__ ((__nothrow__ , __leaf__)); extern void setlinebuf (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); extern int fprintf (FILE *__restrict __stream, const char *__restrict __format, ...); extern int printf (const char *__restrict __format, ...); extern int sprintf (char *__restrict __s, const char *__restrict __format, ...) __attribute__ ((__nothrow__)); extern int vfprintf (FILE *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg); extern int vprintf (const char *__restrict __format, __gnuc_va_list __arg); extern int vsprintf (char *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__nothrow__)); extern int snprintf (char *__restrict __s, size_t __maxlen, const char *__restrict __format, ...) __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 4))); extern int vsnprintf (char *__restrict __s, size_t __maxlen, const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 3, 0))); # 365 "/usr/include/stdio.h" 3 4 extern int vdprintf (int __fd, const char *__restrict __fmt, __gnuc_va_list __arg) __attribute__ ((__format__ (__printf__, 2, 0))); extern int dprintf (int __fd, const char *__restrict __fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); extern int fscanf (FILE *__restrict __stream, const char *__restrict __format, ...) ; extern int scanf (const char *__restrict __format, ...) ; extern int sscanf (const char *__restrict __s, const char *__restrict __format, ...) __attribute__ ((__nothrow__ , __leaf__)); # 395 "/usr/include/stdio.h" 3 4 extern int fscanf (FILE *__restrict __stream, const char *__restrict __format, ...) __asm__ ("" "__isoc99_fscanf") ; extern int scanf (const char *__restrict __format, ...) __asm__ ("" "__isoc99_scanf") ; extern int sscanf (const char *__restrict __s, const char *__restrict __format, ...) __asm__ ("" "__isoc99_sscanf") __attribute__ ((__nothrow__ , __leaf__)) ; # 420 "/usr/include/stdio.h" 3 4 extern int vfscanf (FILE *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__format__ (__scanf__, 2, 0))) ; extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__format__ (__scanf__, 1, 0))) ; extern int vsscanf (const char *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__format__ (__scanf__, 2, 0))); # 443 "/usr/include/stdio.h" 3 4 extern int vfscanf (FILE *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vfscanf") __attribute__ ((__format__ (__scanf__, 2, 0))) ; extern int vscanf (const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vscanf") __attribute__ ((__format__ (__scanf__, 1, 0))) ; extern int vsscanf (const char *__restrict __s, const char *__restrict __format, __gnuc_va_list __arg) __asm__ ("" "__isoc99_vsscanf") __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__format__ (__scanf__, 2, 0))); # 477 "/usr/include/stdio.h" 3 4 extern int fgetc (FILE *__stream); extern int getc (FILE *__stream); extern int getchar (void); # 495 "/usr/include/stdio.h" 3 4 extern int getc_unlocked (FILE *__stream); extern int getchar_unlocked (void); # 506 "/usr/include/stdio.h" 3 4 extern int fgetc_unlocked (FILE *__stream); # 517 "/usr/include/stdio.h" 3 4 extern int fputc (int __c, FILE *__stream); extern int putc (int __c, FILE *__stream); extern int putchar (int __c); # 537 "/usr/include/stdio.h" 3 4 extern int fputc_unlocked (int __c, FILE *__stream); extern int putc_unlocked (int __c, FILE *__stream); extern int putchar_unlocked (int __c); extern int getw (FILE *__stream); extern int putw (int __w, FILE *__stream); extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream) ; # 603 "/usr/include/stdio.h" 3 4 extern __ssize_t __getdelim (char **__restrict __lineptr, size_t *__restrict __n, int __delimiter, FILE *__restrict __stream) ; extern __ssize_t getdelim (char **__restrict __lineptr, size_t *__restrict __n, int __delimiter, FILE *__restrict __stream) ; extern __ssize_t getline (char **__restrict __lineptr, size_t *__restrict __n, FILE *__restrict __stream) ; extern int fputs (const char *__restrict __s, FILE *__restrict __stream); extern int puts (const char *__s); extern int ungetc (int __c, FILE *__stream); extern size_t fread (void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __stream) ; extern size_t fwrite (const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s); # 673 "/usr/include/stdio.h" 3 4 extern size_t fread_unlocked (void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __stream) ; extern size_t fwrite_unlocked (const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __stream); extern int fseek (FILE *__stream, long int __off, int __whence); extern long int ftell (FILE *__stream) ; extern void rewind (FILE *__stream); # 707 "/usr/include/stdio.h" 3 4 extern int fseeko (FILE *__stream, __off_t __off, int __whence); extern __off_t ftello (FILE *__stream) ; # 731 "/usr/include/stdio.h" 3 4 extern int fgetpos (FILE *__restrict __stream, fpos_t *__restrict __pos); extern int fsetpos (FILE *__stream, const fpos_t *__pos); # 757 "/usr/include/stdio.h" 3 4 extern void clearerr (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); extern int feof (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern int ferror (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern void clearerr_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); extern int feof_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern int ferror_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern void perror (const char *__s); # 1 "/usr/include/x86_64-linux-gnu/bits/sys_errlist.h" 1 3 4 # 26 "/usr/include/x86_64-linux-gnu/bits/sys_errlist.h" 3 4 extern int sys_nerr; extern const char *const sys_errlist[]; # 782 "/usr/include/stdio.h" 2 3 4 extern int fileno (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern int fileno_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; # 800 "/usr/include/stdio.h" 3 4 extern FILE *popen (const char *__command, const char *__modes) ; extern int pclose (FILE *__stream); extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__)); # 840 "/usr/include/stdio.h" 3 4 extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) ; extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); # 868 "/usr/include/stdio.h" 3 4 # 2 "HelloWorld.c" 2 # 2 "HelloWorld.c" int main(int argc, char const *argv[]) { printf("hello world!\n"); return 0; }
• gcc HelloWorld.c -S -c -o HelloWorld.s 编译:包含预处理,将 C 程序转换成汇编程序。
点击查看HelloWorld.s
xyjk1002@sheldon:~/cosmos/lesson01/HelloWorld$ cat HelloWorld.s .file "HelloWorld.c" .text .section .rodata .LC0: .string "hello world!" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movl %edi, -4(%rbp) movq %rsi, -16(%rbp) leaq .LC0(%rip), %rdi call puts@PLT movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Debian 7.3.0-19) 7.3.0" .section .note.GNU-stack,"",@progbits
• gcc HelloWorld.c -c -o HelloWorld.o 汇编:包含预处理和编译,将汇编程序转换成可链接的二进制程序
•gcc HelloWorld.c -o HelloWorld 链接:包含以上所有操作,将可链接的二进制程序和其它别的库链接在一起,形成可执行的程序文件。
程序装载执行
根据冯诺依曼体系结构构成的计算机,必须具有如下功能:
• 把程序和数据装入到计算机中;
• 必须具有长期记住程序、数据的中间结果及最终运算结果;
• 完成各种算术、逻辑运算和数据传送等数据加工处理;
• 根据需要控制程序走向,并能根据指令控制机器的各部件协调操作;
• 能够按照要求将处理的数据结果显示给用户。
为了完成上述的功能,计算机必须具备五大基本组成部件:
• 装载数据和程序的输入设备;
• 记住程序和数据的存储器;
• 完成数据加工处理的运算器;
• 控制程序执行的控制器;
• 显示处理结果的输出设备。
根据冯诺依曼的理论,我们只要把图灵机的几个部件换成电子设备,就可以变成一个最小核心的电子计算机,如下图:

更形象地将 HelloWorld 程序装入原型计算机
通过 gcc -c -S HelloWorld 只能得到汇编代码。使用 objdump -d HelloWorld 程序即可获取到二进制代码,其中有很多库代码(只需关注 main 函数相关的代码)
Hello World的二进制代码
HelloWorld: 文件格式 elf64-x86-64 Disassembly of section .init: 00000000000004e8 <_init>: 4e8: 48 83 ec 08 sub $0x8,%rsp 4ec: 48 8b 05 f5 0a 20 00 mov 0x200af5(%rip),%rax # 200fe8 <__gmon_start__> 4f3: 48 85 c0 test %rax,%rax 4f6: 74 02 je 4fa <_init+0x12> 4f8: ff d0 callq *%rax 4fa: 48 83 c4 08 add $0x8,%rsp 4fe: c3 retq Disassembly of section .plt: 0000000000000500 <.plt>: 500: ff 35 02 0b 20 00 pushq 0x200b02(%rip) # 201008 <_GLOBAL_OFFSET_TABLE_+0x8> 506: ff 25 04 0b 20 00 jmpq *0x200b04(%rip) # 201010 <_GLOBAL_OFFSET_TABLE_+0x10> 50c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000000510 <puts@plt>: 510: ff 25 02 0b 20 00 jmpq *0x200b02(%rip) # 201018 <puts@GLIBC_2.2.5> 516: 68 00 00 00 00 pushq $0x0 51b: e9 e0 ff ff ff jmpq 500 <.plt> Disassembly of section .plt.got: 0000000000000520 <__cxa_finalize@plt>: 520: ff 25 d2 0a 20 00 jmpq *0x200ad2(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5> 526: 66 90 xchg %ax,%ax Disassembly of section .text: 0000000000000530 <_start>: 530: 31 ed xor %ebp,%ebp 532: 49 89 d1 mov %rdx,%r9 535: 5e pop %rsi 536: 48 89 e2 mov %rsp,%rdx 539: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp 53d: 50 push %rax 53e: 54 push %rsp 53f: 4c 8d 05 8a 01 00 00 lea 0x18a(%rip),%r8 # 6d0 <__libc_csu_fini> 546: 48 8d 0d 13 01 00 00 lea 0x113(%rip),%rcx # 660 <__libc_csu_init> 54d: 48 8d 3d e6 00 00 00 lea 0xe6(%rip),%rdi # 63a <main> 554: ff 15 86 0a 20 00 callq *0x200a86(%rip) # 200fe0 <__libc_start_main@GLIBC_2.2.5> 55a: f4 hlt 55b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 0000000000000560 <deregister_tm_clones>: 560: 48 8d 3d c9 0a 20 00 lea 0x200ac9(%rip),%rdi # 201030 <__TMC_END__> 567: 55 push %rbp 568: 48 8d 05 c1 0a 20 00 lea 0x200ac1(%rip),%rax # 201030 <__TMC_END__> 56f: 48 39 f8 cmp %rdi,%rax 572: 48 89 e5 mov %rsp,%rbp 575: 74 19 je 590 <deregister_tm_clones+0x30> 577: 48 8b 05 5a 0a 20 00 mov 0x200a5a(%rip),%rax # 200fd8 <_ITM_deregisterTMCloneTable> 57e: 48 85 c0 test %rax,%rax 581: 74 0d je 590 <deregister_tm_clones+0x30> 583: 5d pop %rbp 584: ff e0 jmpq *%rax 586: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 58d: 00 00 00 590: 5d pop %rbp 591: c3 retq 592: 0f 1f 40 00 nopl 0x0(%rax) 596: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 59d: 00 00 00 00000000000005a0 <register_tm_clones>: 5a0: 48 8d 3d 89 0a 20 00 lea 0x200a89(%rip),%rdi # 201030 <__TMC_END__> 5a7: 48 8d 35 82 0a 20 00 lea 0x200a82(%rip),%rsi # 201030 <__TMC_END__> 5ae: 55 push %rbp 5af: 48 29 fe sub %rdi,%rsi 5b2: 48 89 e5 mov %rsp,%rbp 5b5: 48 c1 fe 03 sar $0x3,%rsi 5b9: 48 89 f0 mov %rsi,%rax 5bc: 48 c1 e8 3f shr $0x3f,%rax 5c0: 48 01 c6 add %rax,%rsi 5c3: 48 d1 fe sar %rsi 5c6: 74 18 je 5e0 <register_tm_clones+0x40> 5c8: 48 8b 05 21 0a 20 00 mov 0x200a21(%rip),%rax # 200ff0 <_ITM_registerTMCloneTable> 5cf: 48 85 c0 test %rax,%rax 5d2: 74 0c je 5e0 <register_tm_clones+0x40> 5d4: 5d pop %rbp 5d5: ff e0 jmpq *%rax 5d7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 5de: 00 00 5e0: 5d pop %rbp 5e1: c3 retq 5e2: 0f 1f 40 00 nopl 0x0(%rax) 5e6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 5ed: 00 00 00 00000000000005f0 <__do_global_dtors_aux>: 5f0: 80 3d 39 0a 20 00 00 cmpb $0x0,0x200a39(%rip) # 201030 <__TMC_END__> 5f7: 75 2f jne 628 <__do_global_dtors_aux+0x38> 5f9: 48 83 3d f7 09 20 00 cmpq $0x0,0x2009f7(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5> 600: 00 601: 55 push %rbp 602: 48 89 e5 mov %rsp,%rbp 605: 74 0c je 613 <__do_global_dtors_aux+0x23> 607: 48 8b 3d 1a 0a 20 00 mov 0x200a1a(%rip),%rdi # 201028 <__dso_handle> 60e: e8 0d ff ff ff callq 520 <__cxa_finalize@plt> 613: e8 48 ff ff ff callq 560 <deregister_tm_clones> 618: c6 05 11 0a 20 00 01 movb $0x1,0x200a11(%rip) # 201030 <__TMC_END__> 61f: 5d pop %rbp 620: c3 retq 621: 0f 1f 80 00 00 00 00 nopl 0x0(%rax) 628: f3 c3 repz retq 62a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 0000000000000630 <frame_dummy>: 630: 55 push %rbp 631: 48 89 e5 mov %rsp,%rbp 634: 5d pop %rbp 635: e9 66 ff ff ff jmpq 5a0 <register_tm_clones> 000000000000063a <main>: 63a: 55 push %rbp 63b: 48 89 e5 mov %rsp,%rbp 63e: 48 83 ec 10 sub $0x10,%rsp 642: 89 7d fc mov %edi,-0x4(%rbp) 645: 48 89 75 f0 mov %rsi,-0x10(%rbp) 649: 48 8d 3d 94 00 00 00 lea 0x94(%rip),%rdi # 6e4 <_IO_stdin_used+0x4> 650: e8 bb fe ff ff callq 510 <puts@plt> 655: b8 00 00 00 00 mov $0x0,%eax 65a: c9 leaveq 65b: c3 retq 65c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000000660 <__libc_csu_init>: 660: 41 57 push %r15 662: 41 56 push %r14 664: 49 89 d7 mov %rdx,%r15 667: 41 55 push %r13 669: 41 54 push %r12 66b: 4c 8d 25 76 07 20 00 lea 0x200776(%rip),%r12 # 200de8 <__frame_dummy_init_array_entry> 672: 55 push %rbp 673: 48 8d 2d 76 07 20 00 lea 0x200776(%rip),%rbp # 200df0 <__init_array_end> 67a: 53 push %rbx 67b: 41 89 fd mov %edi,%r13d 67e: 49 89 f6 mov %rsi,%r14 681: 4c 29 e5 sub %r12,%rbp 684: 48 83 ec 08 sub $0x8,%rsp 688: 48 c1 fd 03 sar $0x3,%rbp 68c: e8 57 fe ff ff callq 4e8 <_init> 691: 48 85 ed test %rbp,%rbp 694: 74 20 je 6b6 <__libc_csu_init+0x56> 696: 31 db xor %ebx,%ebx 698: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1) 69f: 00 6a0: 4c 89 fa mov %r15,%rdx 6a3: 4c 89 f6 mov %r14,%rsi 6a6: 44 89 ef mov %r13d,%edi 6a9: 41 ff 14 dc callq *(%r12,%rbx,8) 6ad: 48 83 c3 01 add $0x1,%rbx 6b1: 48 39 dd cmp %rbx,%rbp 6b4: 75 ea jne 6a0 <__libc_csu_init+0x40> 6b6: 48 83 c4 08 add $0x8,%rsp 6ba: 5b pop %rbx 6bb: 5d pop %rbp 6bc: 41 5c pop %r12 6be: 41 5d pop %r13 6c0: 41 5e pop %r14 6c2: 41 5f pop %r15 6c4: c3 retq 6c5: 90 nop 6c6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 6cd: 00 00 00 00000000000006d0 <__libc_csu_fini>: 6d0: f3 c3 repz retq Disassembly of section .fini: 00000000000006d4 <_fini>: 6d4: 48 83 ec 08 sub $0x8,%rsp 6d8: 48 83 c4 08 add $0x8,%rsp 6dc: c3 retq

以上图中,分成四列:第一列为地址;第二列为十六进制,表示真正装入机器中的代码数据;第三列是对应的汇编代码;第四列是相关代码的注释。这是 x86_64 体系的代码,由此可以看出 x86 CPU 是变长指令集。
我们把这段代码数据装入最小电子计算机,状态如下图:

总结
现代电子计算机正是通过内存中的信息(指令和数据)做出相应的操作,并通过内存地址的变化,达到程序读取数据,控制程序流程(顺序、跳转对应该图灵机的读头来回移动)的功能。
思考题
为了实现 C 语言中函数的调用和返回功能,CPU 实现了函数调用和返回指令,即上图汇编代码中的“call”,“ret”指令,请你思考一下:call 和 ret 指令在逻辑上执行的操作是怎样的呢?
参考答案一:
1.call和ret指令都是转移指令,它们都修改IP的值,或同时修改CS和IP的值。它们经常共同用语实现子程序的设计。
2.CPU执行ret指令时,
(1)(IP)=((ss)*16+(sp))
得到IP地址,并将IP出栈
(2)(sp)=(sp)+2
将IP更新到 PC 寄存器中
3.CPU执行call指令时,进行两步操作:
(1)将当前的IP或CS和IP压入栈中;
(2)转移(jmp)到对应的ip或cs执行代码
注释:ss sp就是栈堆的指针,和cs ip 是代码段指针,知道ss,sp求物理地址的公式是 物理地址=ss* 16+ sp,或者物理地址=ss* 10H+ sp 也就是对于2进制来说,ss里的数要加上4个0 再加sp得到物理地址。比如ss存放 1234H sp 存放 2000H ,那么物理地址就是 1234H*10H+2000H=14340H
参考答案二:
首先假设CPU执行指令是顺序执行的,那么程序的调用需要考虑几个问题:
1,call指令要执行的代码在哪?也就是被调用函数的第一条指令所在的内存地址
2,被调用函数执行完之后,返回哪个位置继续执行?
只要解决上面这两个问题,那么函数调用时指令的间的跳转就迎刃而解了。
针对第一个问题,在gcc编译完成之后,函数对应的指令序列所在的位置就已经确定了,因此这是编译阶段需要考虑的问题
至于第二个问题,在执行完call指令的同时,需要将call指令下面一条指令的地址保存到栈内存中,同时更新%rsp寄存器指向的位置,然后就可以开始执行被调函数的指令序列,执行完毕后,由ret指令从rsp中获取栈顶的returnadress地址,然后跳转到call的下一条指令继续执行。
参考
本文来自博客园,作者:miyan,转载请注明原文链接:https://www.cnblogs.com/xyjk1002-rejuvenation/p/16626613.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库