前言
#include <stdio.h>
int main()
{
printf("hello, world\n");
}
从这个最简单的例子开始分析
一、预编译
gcc -E hello.c -o hello.i
# 0 "helllo.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "helllo.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/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
# 402 "/usr/include/features.h" 3 4
# 1 "/usr/include/features-time64.h" 1 3 4
# 20 "/usr/include/features-time64.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 21 "/usr/include/features-time64.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/timesize.h" 1 3 4
# 19 "/usr/include/x86_64-linux-gnu/bits/timesize.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 20 "/usr/include/x86_64-linux-gnu/bits/timesize.h" 2 3 4
# 22 "/usr/include/features-time64.h" 2 3 4
# 403 "/usr/include/features.h" 2 3 4
# 510 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4
# 730 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 731 "/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
# 732 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 511 "/usr/include/features.h" 2 3 4
# 534 "/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
# 535 "/usr/include/features.h" 2 3 4
# 34 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 2 3 4
# 29 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h" 1 3 4
# 214 "/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h" 3 4
# 214 "/usr/lib/gcc/x86_64-linux-gnu/14/include/stddef.h" 3 4
typedef long unsigned int size_t;
# 35 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h" 1 3 4
# 40 "/usr/lib/gcc/x86_64-linux-gnu/14/include/stdarg.h" 3 4
typedef __builtin_va_list __gnuc_va_list;
# 38 "/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
# 1 "/usr/include/x86_64-linux-gnu/bits/timesize.h" 1 3 4
# 19 "/usr/include/x86_64-linux-gnu/bits/timesize.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 20 "/usr/include/x86_64-linux-gnu/bits/timesize.h" 2 3 4
# 29 "/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 __int8_t __int_least8_t;
typedef __uint8_t __uint_least8_t;
typedef __int16_t __int_least16_t;
typedef __uint16_t __uint_least16_t;
typedef __int32_t __int_least32_t;
typedef __uint32_t __uint_least32_t;
typedef __int64_t __int_least64_t;
typedef __uint64_t __uint_least64_t;
typedef long int __quad_t;
typedef unsigned long int __u_quad_t;
typedef long int __intmax_t;
typedef unsigned long int __uintmax_t;
# 141 "/usr/include/x86_64-linux-gnu/bits/types.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/typesizes.h" 1 3 4
# 142 "/usr/include/x86_64-linux-gnu/bits/types.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/time64.h" 1 3 4
# 143 "/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 long int __suseconds64_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;
# 40 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h" 1 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;
# 6 "/usr/include/x86_64-linux-gnu/bits/types/__fpos_t.h" 2 3 4
typedef struct _G_fpos_t
{
__off_t __pos;
__mbstate_t __state;
} __fpos_t;
# 41 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h" 1 3 4
# 10 "/usr/include/x86_64-linux-gnu/bits/types/__fpos64_t.h" 3 4
typedef struct _G_fpos64_t
{
__off64_t __pos;
__mbstate_t __state;
} __fpos64_t;
# 42 "/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;
# 43 "/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;
# 44 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h" 1 3 4
# 35 "/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h" 3 4
struct _IO_FILE;
struct _IO_marker;
struct _IO_codecvt;
struct _IO_wide_data;
typedef void _IO_lock_t;
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;
__off64_t _offset;
struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
struct _IO_FILE **_prevchain;
int _mode;
char _unused2[15 * sizeof (int) - 5 * sizeof (void *)];
};
# 45 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h" 1 3 4
# 27 "/usr/include/x86_64-linux-gnu/bits/types/cookie_io_functions_t.h" 3 4
typedef __ssize_t cookie_read_function_t (void *__cookie, char *__buf,
size_t __nbytes);
typedef __ssize_t cookie_write_function_t (void *__cookie, const char *__buf,
size_t __nbytes);
typedef int cookie_seek_function_t (void *__cookie, __off64_t *__pos, int __w);
typedef int cookie_close_function_t (void *__cookie);
typedef struct _IO_cookie_io_functions_t
{
cookie_read_function_t *read;
cookie_write_function_t *write;
cookie_seek_function_t *seek;
cookie_close_function_t *close;
} cookie_io_functions_t;
# 48 "/usr/include/stdio.h" 2 3 4
typedef __gnuc_va_list va_list;
# 64 "/usr/include/stdio.h" 3 4
typedef __off_t off_t;
# 78 "/usr/include/stdio.h" 3 4
typedef __ssize_t ssize_t;
typedef __fpos_t fpos_t;
# 129 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/stdio_lim.h" 1 3 4
# 130 "/usr/include/stdio.h" 2 3 4
# 149 "/usr/include/stdio.h" 3 4
extern FILE *stdin;
extern FILE *stdout;
extern 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__));
# 184 "/usr/include/stdio.h" 3 4
extern int fclose (FILE *__stream) __attribute__ ((__nonnull__ (1)));
# 194 "/usr/include/stdio.h" 3 4
extern FILE *tmpfile (void)
__attribute__ ((__malloc__)) __attribute__ ((__malloc__ (fclose, 1))) ;
# 211 "/usr/include/stdio.h" 3 4
extern char *tmpnam (char[20]) __attribute__ ((__nothrow__ , __leaf__)) ;
extern char *tmpnam_r (char __s[20]) __attribute__ ((__nothrow__ , __leaf__)) ;
# 228 "/usr/include/stdio.h" 3 4
extern char *tempnam (const char *__dir, const char *__pfx)
__attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) __attribute__ ((__malloc__ (__builtin_free, 1)));
extern int fflush (FILE *__stream);
# 245 "/usr/include/stdio.h" 3 4
extern int fflush_unlocked (FILE *__stream);
# 264 "/usr/include/stdio.h" 3 4
extern FILE *fopen (const char *__restrict __filename,
const char *__restrict __modes)
__attribute__ ((__malloc__)) __attribute__ ((__malloc__ (fclose, 1))) ;
extern FILE *freopen (const char *__restrict __filename,
const char *__restrict __modes,
FILE *__restrict __stream) __attribute__ ((__nonnull__ (3)));
# 299 "/usr/include/stdio.h" 3 4
extern FILE *fdopen (int __fd, const char *__modes) __attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((__malloc__)) __attribute__ ((__malloc__ (fclose, 1))) ;
extern FILE *fopencookie (void *__restrict __magic_cookie,
const char *__restrict __modes,
cookie_io_functions_t __io_funcs) __attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((__malloc__)) __attribute__ ((__malloc__ (fclose, 1))) ;
extern FILE *fmemopen (void *__s, size_t __len, const char *__modes)
__attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) __attribute__ ((__malloc__ (fclose, 1))) ;
extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((__malloc__)) __attribute__ ((__malloc__ (fclose, 1))) ;
# 334 "/usr/include/stdio.h" 3 4
extern void setbuf (FILE *__restrict __stream, char *__restrict __buf) __attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((__nonnull__ (1)));
extern int setvbuf (FILE *__restrict __stream, char *__restrict __buf,
int __modes, size_t __n) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern void setbuffer (FILE *__restrict __stream, char *__restrict __buf,
size_t __size) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern void setlinebuf (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern int fprintf (FILE *__restrict __stream,
const char *__restrict __format, ...) __attribute__ ((__nonnull__ (1)));
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) __attribute__ ((__nonnull__ (1)));
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)));
extern int vasprintf (char **__restrict __ptr, const char *__restrict __f,
__gnuc_va_list __arg)
__attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 2, 0))) ;
extern int __asprintf (char **__restrict __ptr,
const char *__restrict __fmt, ...)
__attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 2, 3))) ;
extern int asprintf (char **__restrict __ptr,
const char *__restrict __fmt, ...)
__attribute__ ((__nothrow__)) __attribute__ ((__format__ (__printf__, 2, 3))) ;
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, ...) __attribute__ ((__nonnull__ (1)));
extern int scanf (const char *__restrict __format, ...) ;
extern int sscanf (const char *__restrict __s,
const char *__restrict __format, ...) __attribute__ ((__nothrow__ , __leaf__));
# 1 "/usr/include/x86_64-linux-gnu/bits/floatn.h" 1 3 4
# 120 "/usr/include/x86_64-linux-gnu/bits/floatn.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/floatn-common.h" 1 3 4
# 24 "/usr/include/x86_64-linux-gnu/bits/floatn-common.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/long-double.h" 1 3 4
# 25 "/usr/include/x86_64-linux-gnu/bits/floatn-common.h" 2 3 4
# 121 "/usr/include/x86_64-linux-gnu/bits/floatn.h" 2 3 4
# 438 "/usr/include/stdio.h" 2 3 4
# 463 "/usr/include/stdio.h" 3 4
extern int fscanf (FILE *__restrict __stream, const char *__restrict __format, ...) __asm__ ("" "__isoc99_fscanf")
__attribute__ ((__nonnull__ (1)));
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__))
;
# 490 "/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))) __attribute__ ((__nonnull__ (1)));
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)));
# 540 "/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))) __attribute__ ((__nonnull__ (1)));
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)));
# 575 "/usr/include/stdio.h" 3 4
extern int fgetc (FILE *__stream) __attribute__ ((__nonnull__ (1)));
extern int getc (FILE *__stream) __attribute__ ((__nonnull__ (1)));
extern int getchar (void);
extern int getc_unlocked (FILE *__stream) __attribute__ ((__nonnull__ (1)));
extern int getchar_unlocked (void);
# 600 "/usr/include/stdio.h" 3 4
extern int fgetc_unlocked (FILE *__stream) __attribute__ ((__nonnull__ (1)));
# 611 "/usr/include/stdio.h" 3 4
extern int fputc (int __c, FILE *__stream) __attribute__ ((__nonnull__ (2)));
extern int putc (int __c, FILE *__stream) __attribute__ ((__nonnull__ (2)));
extern int putchar (int __c);
# 627 "/usr/include/stdio.h" 3 4
extern int fputc_unlocked (int __c, FILE *__stream) __attribute__ ((__nonnull__ (2)));
extern int putc_unlocked (int __c, FILE *__stream) __attribute__ ((__nonnull__ (2)));
extern int putchar_unlocked (int __c);
extern int getw (FILE *__stream) __attribute__ ((__nonnull__ (1)));
extern int putw (int __w, FILE *__stream) __attribute__ ((__nonnull__ (2)));
extern char *fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
__attribute__ ((__access__ (__write_only__, 1, 2))) __attribute__ ((__nonnull__ (3)));
# 689 "/usr/include/stdio.h" 3 4
extern __ssize_t __getdelim (char **__restrict __lineptr,
size_t *__restrict __n, int __delimiter,
FILE *__restrict __stream) __attribute__ ((__nonnull__ (4)));
extern __ssize_t getdelim (char **__restrict __lineptr,
size_t *__restrict __n, int __delimiter,
FILE *__restrict __stream) __attribute__ ((__nonnull__ (4)));
extern __ssize_t getline (char **__restrict __lineptr,
size_t *__restrict __n,
FILE *__restrict __stream) __attribute__ ((__nonnull__ (3)));
extern int fputs (const char *__restrict __s, FILE *__restrict __stream)
__attribute__ ((__nonnull__ (2)));
extern int puts (const char *__s);
extern int ungetc (int __c, FILE *__stream) __attribute__ ((__nonnull__ (2)));
extern size_t fread (void *__restrict __ptr, size_t __size,
size_t __n, FILE *__restrict __stream)
__attribute__ ((__nonnull__ (4)));
extern size_t fwrite (const void *__restrict __ptr, size_t __size,
size_t __n, FILE *__restrict __s) __attribute__ ((__nonnull__ (4)));
# 756 "/usr/include/stdio.h" 3 4
extern size_t fread_unlocked (void *__restrict __ptr, size_t __size,
size_t __n, FILE *__restrict __stream)
__attribute__ ((__nonnull__ (4)));
extern size_t fwrite_unlocked (const void *__restrict __ptr, size_t __size,
size_t __n, FILE *__restrict __stream)
__attribute__ ((__nonnull__ (4)));
extern int fseek (FILE *__stream, long int __off, int __whence)
__attribute__ ((__nonnull__ (1)));
extern long int ftell (FILE *__stream) __attribute__ ((__nonnull__ (1)));
extern void rewind (FILE *__stream) __attribute__ ((__nonnull__ (1)));
# 793 "/usr/include/stdio.h" 3 4
extern int fseeko (FILE *__stream, __off_t __off, int __whence)
__attribute__ ((__nonnull__ (1)));
extern __off_t ftello (FILE *__stream) __attribute__ ((__nonnull__ (1)));
# 819 "/usr/include/stdio.h" 3 4
extern int fgetpos (FILE *__restrict __stream, fpos_t *__restrict __pos)
__attribute__ ((__nonnull__ (1)));
extern int fsetpos (FILE *__stream, const fpos_t *__pos) __attribute__ ((__nonnull__ (1)));
# 850 "/usr/include/stdio.h" 3 4
extern void clearerr (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern int feof (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern int ferror (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern void clearerr_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern int feof_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern int ferror_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern void perror (const char *__s) __attribute__ ((__cold__));
extern int fileno (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern int fileno_unlocked (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
# 887 "/usr/include/stdio.h" 3 4
extern int pclose (FILE *__stream) __attribute__ ((__nonnull__ (1)));
extern FILE *popen (const char *__command, const char *__modes)
__attribute__ ((__malloc__)) __attribute__ ((__malloc__ (pclose, 1))) ;
extern char *ctermid (char *__s) __attribute__ ((__nothrow__ , __leaf__))
__attribute__ ((__access__ (__write_only__, 1)));
# 931 "/usr/include/stdio.h" 3 4
extern void flockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern int ftrylockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__nonnull__ (1)));
# 949 "/usr/include/stdio.h" 3 4
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
# 973 "/usr/include/stdio.h" 3 4
# 2 "helllo.c" 2
# 2 "helllo.c"
int main()
{
printf("hello, world\n");
}
预编译过程是C语言编译过程中的第一个阶段,它处理源代码文件(.c
文件)并生成一个中间文件(.i
文件),这个中间文件包含了预编译的结果。
-
gcc -E hello.c -o hello.i
- 这个命令告诉GCC编译器只执行预编译阶段,并将结果输出到
hello.i
文件中。
- 这个命令告诉GCC编译器只执行预编译阶段,并将结果输出到
-
# 1 “hello.c”
- 这是一个预编译器标记,表示预编译器开始处理
hello.c
文件。
- 这是一个预编译器标记,表示预编译器开始处理
-
# 1 “”
- 这表示预编译器正在处理内置的宏定义和预编译指令。
-
# 1 “”
- 这表示预编译器正在处理从命令行传递给编译器的参数。
-
*extern int printf (const char __restrict __format, …);
- 这是
printf
函数的声明,它可能来自包含的头文件(如<stdio.h>
)。预编译器会处理所有的#include
指令,将相应的头文件内容插入到源代码中。
- 这是
-
main() {
- 这是
main
函数的定义开始。
- 这是
-
printf(“hello, world\n”);
- 这是
main
函数中的一条语句,调用printf
函数打印 “hello, world\n”。
- 这是
-
}
- 这是
main
函数的定义结束。
- 这是
这些行号和文件名标记是由预处理器添加的,以便在编译时跟踪源代码的位置。
-
文件和命令行信息:
# 0 "helllo.c"
:表示当前处理的文件是hello.c
。# 0 "<built-in>"
:表示预处理器正在处理内置的宏定义。# 0 "<command-line>"
:表示预处理器正在处理命令行参数。
-
头文件包含:
# 1 "/usr/include/stdc-predef.h" 1 3 4
:表示预处理器开始处理/usr/include/stdc-predef.h
头文件,1 3 4
表示宏定义__STDC__
、__STDC_VERSION__
和__STDC_UTF_16__
被定义。# 0 "<command-line>" 2
:表示预处理器回到了命令行模式,2
表示这是第二个指令。# 1 "hello.c"
:表示预处理器回到了hello.c
文件。
-
标准库头文件:
# 1 "/usr/include/stdio.h" 1 3 4
:表示预处理器开始处理/usr/include/stdio.h
头文件。# 28 "/usr/include/stdio.h" 3 4
:表示预处理器在stdio.h
文件的第28行。
-
系统头文件:
# 1 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 1 3 4
:表示预处理器开始处理系统特定的头文件libc-header-start.h
。
-
类型定义:
typedef long unsigned int size_t;
:定义了size_t
类型,通常用于表示对象的大小。typedef __builtin_va_list __gnuc_va_list;
:定义了__gnuc_va_list
类型,用于处理可变参数列表。
-
基本数据类型定义:
typedef unsigned char __u_char;
:定义了__u_char
类型,表示无符号字符。typedef signed char __int8_t;
:定义了__int8_t
类型,表示有符号的8位整数。- 其他类似的行定义了不同大小和符号的整数类型。
-
文件结构定义:
struct _IO_FILE
:定义了_IO_FILE
结构,它是FILE
结构的内部表示,用于标准I/O操作。
-
I/O函数类型定义:
typedef struct _IO_cookie_io_functions_t
:定义了I/O函数类型的结构。
-
宏和函数声明:
extern int remove (const char *__filename) __attribute__ ((__nothrow__ , __leaf__));
:声明了remove
函数,用于删除文件。extern int rename (const char *__old, const char *__new) __attribute__ ((__nothrow__ , __leaf__));
:声明了rename
函数,用于重命名文件。
-
主函数:
int main()
:定义了main
函数,程序的入口点。printf("hello, world\n");
:调用printf
函数打印"hello, world"。}
:main
函数的结束。
预编译过程的具体处理内容如下:
-
处理预编译指令:预编译器会查找以
#
开头的指令,并根据这些指令执行相应的操作。 -
宏定义的展开:预编译器会删除所有的
#define
指令,并展开代码中的宏定义。例如,如果在代码中有#define MAX 100
,那么预编译器会将所有的MAX
替换为100
。 -
条件预编译指令:预编译器会处理
#if
、#ifdef
、#elif
、#else
、#endif
等条件预编译指令,根据这些指令决定是否包含某些代码。 -
包含头文件:预编译器会处理
#include
指令,将被包含的文件内容插入到该指令的位置。这个过程是递归的,意味着如果被包含的文件中还有#include
指令,预编译器会继续处理这些指令。 -
删除注释:预编译器会删除源代码中的所有注释(
//
和/* */
)。 -
添加行号和文件名标号:预编译器会在输出的
.i
文件中添加行号和文件名信息,以便于调试和错误报告。 -
保留 #pragma 编译器指令:
#pragma
指令是编译器特定的指令,预编译器会保留这些指令,以便后续的编译阶段处理。
二、编译阶段
gcc -S hello.c -o helo.s
.file "helllo.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
leaq .LC0(%rip), %rax
movq %rax, %rdi
call puts@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Debian 14.2.0-6) 14.2.0"
.section .note.GNU-stack,"",@progbits
这段代码是一个用GCC编译器生成的x86-64位Linux系统下的C语言程序的汇编代码。程序的功能是打印 “hello, world” 到标准输出。下面是对这段汇编代码的详细解释:
-
.file "helllo.c"
:- 这是一个文件标识符,用于调试信息,表明这段代码来自源文件 “helllo.c”。
-
.text
:- 这是一个指令,告诉汇编器接下来的代码应该放在程序的文本段(代码段)。
-
.section .rodata
:- 指定接下来的数据应该放在只读数据段(.rodata),通常用于存放程序中的常量。
-
.LC0:
:- 定义了一个标签(label),用于标识常量字符串 “hello, world”。
-
.string "hello, world"
:- 定义了一个字符串常量 “hello, world”。
-
.text
:- 再次指定接下来的代码应该放在程序的文本段。
-
.globl main
:- 声明
main
函数是全局符号,意味着它可以被其他文件引用。
- 声明
-
.type main, @function
:- 指定
main
是一个函数类型。
- 指定
-
main:
:main
函数的开始。
-
.LFB0:
:- 定义了一个标签,用于函数的开始。
-
.cfi_startproc
、.cfi_def_cfa_offset
、.cfi_offset
、.cfi_def_cfa_register
:- 这些指令是调用帧信息(Call Frame Information)的一部分,用于异常处理和调试。它们定义了栈帧和寄存器的状态。
-
pushq %rbp
:- 将基指针(rbp)压入栈中,保存当前函数的栈帧指针。
-
movq %rsp, %rbp
:- 将栈指针(rsp)的值移动到基指针(rbp),设置新的栈帧。
-
leaq .LC0(%rip), %rax
:- 将字符串 “hello, world” 的地址加载到寄存器
rax
中。
- 将字符串 “hello, world” 的地址加载到寄存器
-
movq %rax, %rdi
:- 将
rax
寄存器的值(字符串地址)移动到参数寄存器rdi
中,准备调用puts
函数。
- 将
-
call puts@PLT
:- 调用标准库函数
puts
,@PLT
表示这是一个位置无关代码(Position Independent Code)的间接跳转。
- 调用标准库函数
-
movl $0, %eax
:- 将立即数0移动到寄存器
eax
中,准备返回值。在x86-64位系统调用约定中,eax
用于存储函数的返回值。
- 将立即数0移动到寄存器
-
popq %rbp
:- 将之前压入栈的基指针值弹出,恢复到调用者的栈帧。
-
ret
:- 返回到调用者。
-
.cfi_endproc
:- 标记函数的结束。
-
.LFE0:
:- 定义了函数的结束标签。
-
.size main, .-main
:- 指定
main
函数的大小,从main
标签到.LFE0
标签。
- 指定
-
.ident "GCC: (Debian 14.2.0-6) 14.2.0"
:- 这是一个版本标识符,表明这段代码是由GCC编译器版本14.2.0-6编译的。
-
.section .note.GNU-stack,"",@progbits
:- 指定接下来的数据应该放在
.note.GNU-stack
段,这是一个GNU特有的段,用于存储栈信息。
- 指定接下来的数据应该放在
这段汇编代码展示了GCC编译器如何将C语言代码转换为机器代码,并包含了用于调试和异常处理的额外信息。
三、汇编
1, objdump的用法
当然,以下是 objdump
工具中每个选项的详细解释:
-
-a, --archive-headers
这个选项用于显示归档文件(如.a
文件)的头部信息,包括归档的成员列表。 -
-f, --file-headers
显示二进制文件的整体文件头信息,这通常包括文件的类型、架构、版本等。 -
-p, --private-headers
显示特定于对象文件格式的文件头内容,这些信息通常不是标准的 ELF 头信息。 -
-P, --private=OPT,OPT…
类似于--private-headers
,但允许你指定特定的选项来显示对象格式特有的内容。 -
-h, --[section-]headers
显示所有节(section)的头部信息,包括节的名字、大小、位置等。 -
-x, --all-headers
显示所有可用的头部信息,包括文件头、节头等。 -
-d, --disassemble
反汇编可执行的节(section),并显示汇编指令。 -
-D, --disassemble-all
反汇编所有节,而不仅仅是可执行的节。 -
–disassemble=
仅反汇编从指定符号<sym>
开始的代码。 -
-S, --source
混合显示源代码和对应的反汇编代码。 -
-s, --full-contents
显示所有请求节的完整内容,包括未初始化的数据。 -
-Z, --decompress
在显示节的内容之前,先对节进行解压缩。 -
-g, --debugging
显示对象文件中的调试信息。 -
-e, --debugging-tags
以 ctags 风格显示调试信息。 -
-G, --stabs
以原始形式显示任何 STABS 调试信息。 -
-W, --dwarf
显示 DWARF 调试节的内容,可以指定多个子选项来过滤显示的内容。 -
-Wk,–dwarf=links
显示链接到独立调试文件的节内容。 -
-WK,–dwarf=follow-links
默认选项,跟随链接到独立的调试信息文件。 -
-WN,–dwarf=no-follow-links
不跟随链接到独立的调试信息文件。 -
-L, --process-links
显示分离的调试信息文件中非调试节的内容。 -
-t, --syms
显示符号表的内容,包括符号名称、地址、大小等。 -
-T, --dynamic-syms
显示动态符号表的内容。 -
-r, --reloc
显示文件中的重定位条目,这些条目用于链接时的地址调整。 -
-R, --dynamic-reloc
显示动态重定位条目。 -
@
从一个文件中读取objdump
选项。 -
-v, --version
显示objdump
程序的版本号。 -
-i, --info
列出objdump
支持的对象格式和架构。 -
-H, --help
显示帮助信息。 -
-b, --target=BFDNAME
指定目标对象格式,BFDNAME 是 Binary File Descriptor 的名称。 -
-m, --architecture=MACHINE
指定目标架构。 -
-j, --section=NAME
仅显示指定节的信息。 -
-M, --disassembler-options=OPT
将文本选项 OPT 传递给反汇编器。 -
-EB, --endian=big
假设反汇编时使用大端格式。 -
-EL, --endian=little
假设反汇编时使用小端格式。 -
–file-start-context
与-S
选项一起使用时,包括从文件开始的上下文。 -
-I, --include=DIR
添加 DIR 到搜索源文件的目录列表。 -
-l, --line-numbers
在输出中包含行号和文件名。 -
-F, --file-offsets
在显示信息时包含文件偏移量。 -
-C, --demangle[=STYLE]
解码 mangled/processed 符号名称,可以指定不同的样式。 -
-w, --wide
格式化输出以适应超过80列的显示。 -
-U, --disassemble-zeroes
在反汇编时不跳过零块。 -
-z, --disassemble-zeroes
同-U
选项。 -
–unicode=[default|locale|invalid|hex|escape|highlight]
控制 UTF-8 unicode 字符的显示方式。 -
–start-address=ADDR
仅处理地址大于等于 ADDR 的数据。 -
–stop-address=ADDR
仅处理地址小于 ADDR 的数据。 -
–no-addresses
在反汇编时不打印地址。 -
–prefix-addresses
在反汇编时打印完整的地址。 -
–[no-]show-raw-insn
显示汇编指令旁边的十六进制代码。 -
–insn-width=WIDTH
在反汇编时,每行显示 WIDTH 字节。 -
–adjust-vma=OFFSET
对所有显示的节地址添加偏移量 OFFSET。 -
–show-all-symbols
在反汇编时显示给定地址的所有符号。 -
–special-syms
在符号转储中包括特殊符号。 -
–inlines
打印源代码行的所有内联。 -
–prefix=PREFIX
为-S
选项添加绝对路径前缀。 -
–prefix-strip=LEVEL
为-S
选项剥离初始目录名称。 -
–dwarf-depth=N
不显示深度为 N 或更大的 DIE。 -
–dwarf-start=N
从偏移量 N 开始显示 DIE。 -
–dwarf-check
进行额外的 DWARF 一致性检查。 -
–ctf-parent=NAME
使用 CTF 归档成员 NAME 作为 CTF 父项。 -
–visualize-jumps
通过绘制 ASCII 线条来可视化跳转。 -
–visualize-jumps=color
在 ASCII 线条中使用颜色。 -
–visualize-jumps=extended-color
使用扩展的 8 位颜色代码。 -
–visualize-jumps=off
禁用跳转可视化。 -
–disassembler-color=off
禁用反汇编器的颜色输出。 -
–disassembler-color=terminal
如果显示在终端上,则启用反汇编器的颜色输出。 -
–disassembler-color=on
启用反汇编器的颜色输出。 -
–disassembler-color=extended
在反汇编器输出中使用 8 位颜色。
这些选项可以组合使用,以获取所需的特定信息。
2,objdump 举例
objdump -f hello.o
hello.o: file format elf64-x86-64
architecture: i386:x86-64, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x0000000000000000
hello.o:
表示文件名。file format elf64-x86-64
表示这个文件是一个 64 位的 ELF(Executable and Linkable Format)格式文件,适用于 x86-64 架构。architecture: i386:x86-64
表示目标文件是为 x86-64 架构编译的,这是 Intel 64 位架构的另一种称呼。flags 0x00000011:
表示文件的一些标志,这里0x11
表示文件包含重定位信息(HAS_RELOC)和符号表(HAS_SYMS)。HAS_RELOC
表示目标文件包含重定位入口,这些入口在链接时用于确定如何修改代码和数据,以便它们在内存中的最终位置。HAS_SYMS
表示目标文件包含符号表,符号表包含了函数、变量等符号的信息,这些信息在链接时用于解析不同目标文件之间的引用。start address 0x0000000000000000
表示这个目标文件的起始地址是 0,这通常是未链接的程序的默认起始地址,实际的起始地址会在程序被链接并加载到内存时确定。
└─$ objdump -h hello.o
hello.o: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001a 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 0000000000000000 0000000000000000 0000005a 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 0000000000000000 0000000000000000 0000005a 2**0
ALLOC
3 .rodata 0000000d 0000000000000000 0000000000000000 0000005a 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 0000001f 0000000000000000 0000000000000000 00000067 2**0
CONTENTS, READONLY
5 .note.GNU-stack 00000000 0000000000000000 0000000000000000 00000086 2**0
CONTENTS, READONLY
6 .eh_frame 00000038 0000000000000000 0000000000000000 00000088 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
这个 objdump -h
命令的输出显示了 hello.o
文件中的各个节(Sections)的详细信息。下面是每个节的解释:
- Idx: 节的索引编号。
- Name: 节的名称。
- Size: 节的大小,单位是字节。
- VMA (Virtual Memory Address): 节在虚拟内存中的地址。
- LMA (Load Memory Address): 节在文件中的加载地址。
- File off: 节在文件中的偏移量。
- Algn: 节的对齐方式,表示为2的幂。
现在,让我们逐一解释这些节:
-
.text: 这是代码段,包含了程序的机器代码。它被标记为
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
,意味着它包含实际的数据内容,会被分配内存,会被加载到内存中,包含重定位信息,是只读的,并且是代码。- Size: 26字节
- VMA: 0x0000000000000000
- LMA: 0x0000000000000000
- File off: 64字节(从文件开头计算)
- Algn: 1(即2^0,不需要特别的对齐)
-
.data: 这是数据段,用于存储初始化的全局变量和静态变量。
- Size: 0字节(空)
- VMA: 0x0000000000000000
- LMA: 0x0000000000000000
- File off: 90字节
- Algn: 1
-
.bss: 这是未初始化的全局变量和静态变量的存储区域。
- Size: 0字节(空)
- VMA: 0x0000000000000000
- LMA: 0x0000000000000000
- File off: 90字节
- Algn: 1
-
.rodata: 这是只读数据段,用于存储程序中的只读数据,如字符串常量。
- Size: 13字节
- VMA: 0x0000000000000000
- LMA: 0x0000000000000000
- File off: 90字节
- Algn: 1
-
.comment: 这是一个特殊的节,用于存储编译器生成的注释信息。
- Size: 31字节
- VMA: 0x0000000000000000
- LMA: 0x0000000000000000
- File off: 103字节
- Algn: 1
-
.note.GNU-stack: 这是一个GNU特有的节,用于指定堆栈的执行权限。如果这个节存在,通常意味着堆栈是可执行的。
- Size: 0字节(空)
- VMA: 0x0000000000000000
- LMA: 0x0000000000000000
- File off: 119字节
- Algn: 1
-
.eh_frame: 这是异常处理帧信息,用于C++异常处理和堆栈展开。
- Size: 56字节
- VMA: 0x0000000000000000
- LMA: 0x0000000000000000
- File off: 120字节
- Algn: 8(即2^3)
这些节是目标文件的基本组成部分,它们在链接过程中会被合并到最终的可执行文件或共享库中。
└─$ objdump -x hello.o
hello.o: file format elf64-x86-64
hello.o
architecture: i386:x86-64, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x0000000000000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001a 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 0000000000000000 0000000000000000 0000005a 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 0000000000000000 0000000000000000 0000005a 2**0
ALLOC
3 .rodata 0000000d 0000000000000000 0000000000000000 0000005a 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 0000001f 0000000000000000 0000000000000000 00000067 2**0
CONTENTS, READONLY
5 .note.GNU-stack 00000000 0000000000000000 0000000000000000 00000086 2**0
CONTENTS, READONLY
6 .eh_frame 00000038 0000000000000000 0000000000000000 00000088 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 helllo.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 g F .text 000000000000001a main
0000000000000000 *UND* 0000000000000000 puts
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
0000000000000007 R_X86_64_PC32 .rodata-0x0000000000000004
000000000000000f R_X86_64_PLT32 puts-0x0000000000000004
RELOCATION RECORDS FOR [.eh_frame]:
OFFSET TYPE VALUE
0000000000000020 R_X86_64_PC32 .text
-
文件格式和架构:
elf64-x86-64
:文件是64位的ELF格式,适用于x86-64架构。HAS_RELOC, HAS_SYMS
:文件包含重定位信息和符号表。
-
节信息:
.text
:代码段,大小为26字节,包含指令代码。.data
:数据段,大小为0字节,用于存储初始化的全局变量和静态变量。.bss
:未初始化数据段,大小为0字节,用于存储未初始化的全局变量和静态变量。.rodata
:只读数据段,大小为13字节,通常包含常量字符串。.comment
:注释段,大小为31字节,包含编译器生成的注释信息。.note.GNU-stack
:GNU栈注解,大小为0字节,通常用于堆栈不可执行标记。.eh_frame
:异常处理帧,大小为56字节,用于C++异常处理。
-
符号表:
*ABS*
:绝对位置,表示符号不依赖于任何节。hello.c
:源文件名。.text
和.rodata
:节名称。main
:全局符号,函数main
的入口点,位于.text
节。*UND*
:未定义,表示puts
函数在本文件中未定义,需要在链接时解析。
-
重定位记录:
.text
节的重定位记录:0000000000000007 R_X86_64_PC32 .rodata-0x0000000000000004
:32位PC相对地址重定位,指向.rodata
节的某个位置。000000000000000f R_X86_64_PLT32 puts-0x0000000000000004
:32位过程链接表(PLT)重定位,指向puts
函数。
.eh_frame
节的重定位记录:0000000000000020 R_X86_64_PC32 .text
:32位PC相对地址重定位,指向.text
节。
objdump -d hello.o
hello.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # b <main+0xb>
b: 48 89 c7 mov %rax,%rdi
e: e8 00 00 00 00 call 13 <main+0x13>
13: b8 00 00 00 00 mov $0x0,%eax
18: 5d pop %rbp
19: c3 ret
(.venv) ┌──(.venv)─(zqy㉿kali)-[~/tonlearn/c_learn]
└─$ objdump -s hello.o
hello.o: file format elf64-x86-64
Contents of section .text:
0000 554889e5 488d0500 00000048 89c7e800 UH..H......H....
0010 000000b8 00000000 5dc3 ........].
Contents of section .rodata:
0000 68656c6c 6f2c2077 6f726c64 00 hello, world.
Contents of section .comment:
0000 00474343 3a202844 65626961 6e203134 .GCC: (Debian 14
0010 2e322e30 2d362920 31342e32 2e3000 .2.0-6) 14.2.0.
Contents of section .eh_frame:
0000 14000000 00000000 017a5200 01781001 .........zR..x..
0010 1b0c0708 90010000 1c000000 1c000000 ................
0020 00000000 1a000000 00410e10 8602430d .........A....C.
0030 06550c07 08000000 .U......
你提供的 objdump -d hello.o
命令输出显示了 hello.o
对象文件中 .text
节的反汇编代码。以下是对这段代码的逐行解释:
-
0000000000000000 <main>:
:- 这是函数
main
的开始,地址为0x0000000000000000
。
- 这是函数
-
0: 55 push %rbp
:- 将基指针寄存器
rbp
的值压入栈中,这是函数调用的常规操作,用于保存旧的基指针。
- 将基指针寄存器
-
1: 48 89 e5 mov %rsp,%rbp
:- 将栈指针寄存器
rsp
的值移动到基指针寄存器rbp
中,设置新的基指针。
- 将栈指针寄存器
-
4: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax
:- 将指令指针寄存器
rip
相对的地址(这里是当前指令的地址加上0,即当前地址)加载到rax
寄存器中。这里可能是一个错误,因为lea
指令通常用于加载有效的内存地址。
- 将指令指针寄存器
-
b: 48 89 c7 mov %rax,%rdi
:- 将
rax
寄存器的值移动到第一个参数寄存器rdi
中,准备调用函数。
- 将
-
e: e8 00 00 00 00 call 13 <main+0x13>
:- 调用地址
0x13
处的函数。由于这是一个相对跳转,实际的调用地址是0x13
加上当前rip
的值。
- 调用地址
-
13: b8 00 00 00 00 mov $0x0,%eax
:- 将立即数
0
移动到eax
寄存器中,这通常是函数返回值的寄存器。
- 将立即数
-
18: 5d pop %rbp
:- 从栈中弹出值到
rbp
寄存器,恢复旧的基指针。
- 从栈中弹出值到
-
19: c3 ret
:- 返回到调用
main
函数的地方。
- 返回到调用
从反汇编代码来看,这个 main
函数似乎调用了一个函数(可能是 puts
,因为之前在重定位记录中看到了对 puts
的引用),然后返回 0
。但是,lea
指令中的地址 0x0
可能是一个占位符,实际的地址将在链接时解析。这个函数没有做任何实质性的工作,只是调用了一个函数并返回 0
。这可能是一个简单的示例程序,用于演示函数调用和返回。
objdump -s hello.o 输出(节内容)
这部分输出显示了 hello.o
文件中不同节的内容,以十六进制形式表示。
-
.text 节内容:
0000 554889e5 488d0500 00000048 89c7e800 UH..H......H.... 0010 000000b8 00000000 5dc3 ........].
- 这些是
.text
节的机器代码指令,与-d
选项的反汇编输出相对应。
- 这些是
-
.rodata 节内容:
0000 68656c6c 6f2c2077 6f726c64 00 hello, world.
- 这是只读数据节,通常包含程序中的字符串常量。这里包含了字符串 “hello, world.”。
-
.comment 节内容:
0000 00474343 3a202844 65626961 6e203134 .GCC: (Debian 14 0010 2e322e30 2d362920 31342e32 2e3000 .2.0-6) 14.2.0.
- 这是注释节,包含了编译器的版本信息。这里显示的是 GCC 编译器的版本信息,表明这个文件是用 Debian 14.2.0-6 版本的 GCC 编译的。
-
.eh_frame 节内容:
0000 14000000 00000000 017a5200 01781001 .........zR..x.. 0010 1b0c0708 90010000 1c000000 1c000000 ................ 0020 00000000 1a000000 00410e10 8602430d .........A....C. 0030 06550c07 08000000 .U......
- 这是异常处理帧节,包含了用于异常处理和栈展开的信息。这些数据对于运行时库中的异常处理机制是必要的。
这些输出提供了对象文件的详细视图,包括代码、数据和元数据。如果你有任何具体问题或需要进一步的解释,请随时提问。
四,代码段
Contents of section .comment:
Contents of section .data:
Contents of section .got:
Contents of section .dynamic:
Contents of section .fini_array:
Contents of section .eh_frame:
Contents of section .eh_frame_hdr:
Contents of section .fini:
Contents of section .rodata:
Contents of section .text:
Contents of section .plt.got:
Contents of section .plt.sec:
Contents of section .init:
Contents of section .plt:
Contents of section .dynstr:
Contents of section .dynsym:
你列出了一系列的ELF文件节(sections),这些节在可执行文件、可重定位文件和共享库中扮演着不同的角色。下面是每个节的简要说明:
-
.comment:
- 包含文件的版本信息和编译器信息,通常用于调试和版本跟踪。
-
.data:
- 包含已初始化的全局变量和静态变量。
-
.got(Global Offset Table):
- 用于动态链接的程序,存储全局变量和函数地址的引用。
-
.dynamic:
- 包含动态链接信息,用于动态加载和链接共享库。
-
.fini_array:
- 包含终止时(程序退出时)需要调用的函数指针数组。
-
.eh_frame:
- 包含异常处理信息,用于C++异常和非局部跳转。
-
.eh_frame_hdr:
- 包含异常处理帧头部信息,用于解析
.eh_frame
节。
- 包含异常处理帧头部信息,用于解析
-
.fini:
- 包含程序终止时需要执行的代码,通常用于关闭资源和清理。
-
.rodata(Read-Only Data):
- 包含只读数据,如字符串常量。
-
.text:
- 包含程序的执行代码。
-
.plt.got:
- 特定于某些架构(如ARM),用于处理PLT(过程链接表)中的全局偏移表。
-
.plt.sec:
- 特定于某些架构,与
.plt
节相关。
- 特定于某些架构,与
-
.init:
- 包含程序启动时需要执行的代码,用于初始化。
-
.plt(Procedure Linkage Table):
- 包含用于动态链接的代码,用于解析外部函数调用。
-
.dynstr:
- 包含动态链接中使用的字符串表,如符号名称。
-
.dynsym(Dynamic Symbol Table):
- 包含动态链接中使用的符号表,用于符号解析。
这些节是ELF文件格式的一部分,它们在程序的生命周期中(如加载、执行、终止)起着关键作用。每个节都有特定的目的和用途,对于理解程序的结构和行为至关重要。