首先,我们有如下输出hello,world!
程序:
- hello.c
#include <stdio.h>
#if 1
//argument count
//argument value
int main(int argc, char **argv)
{
printf("hello, world!\n");
return 5;
}
#endif
一、基本操作
程序代码与Windows平台的程序并无不同,只是在不同的环境中具有不同的约定。
在Linux环境下,不以文件拓展名作为区分文件的标准。
- 编译:
root@aemonair:~/tmp# gcc hello.c -o hello
- 运行:
root@aemonair:~/tmp# ./hello
hello, world!
编译的具体步骤:
Ⅰ.预处理
- 生成.i的文件:
root@aemonair:~/tmp# gcc -E hello.c -o hello.i
root@aemonair:~/tmp# vim hello.i
使用
vim
或者任意编辑器打开hello.i
可以看到是将头文件展开去掉注释等操作的预处理之后的文件。
Ⅱ.编译
- 将预处理后的文件转换成汇编语言,生成文件.s:
root@aemonair:~/tmp# gcc -S hello.i -o hello.s
打开hello.s可以看到,是输出
hello,worl!
的汇编代码。
Ⅲ.汇编
- 将汇编代码变为目标代码(机器代码)生成.o的文件
root@aemonair:~/tmp# gcc -c hello.s -o hello.o
这时用file命令查看hello.o则发现:
root@aemonair:~/tmp# file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
此时hello.o
是一个二进制64位的可重定位的目标文件。
Ⅳ .链接
root@aemonair:~/tmp# gcc hello.o -o hello
生成可执行文件。
二、查看基本信息
- 查看当前的文件:
root@aemonair:~/tmp# ls -l hello*
-rwxr-xr-x 1 root root 6728 May 27 14:40 hello
-rw-r--r-- 1 root root 148 May 27 13:25 hello.c
-rw-r--r-- 1 root root 17187 May 27 13:26 hello.i
-rw-r--r-- 1 root root 1504 May 27 13:30 hello.o
-rw-r--r-- 1 root root 497 May 27 13:28 hello.s
- file命令,
得以辨识该文件的类型。
root@aemonair:~/tmp# file hello.c
hello.c: C source, ASCII text # C语言ASCII码源文件
root@aemonair:~/tmp# file hello.i
hello.i: C source, ASCII text
root@aemonair:~/tmp# file hello.s
hello.s: assembler source, ASCII text # ASCII码汇编文件
root@aemonair:~/tmp# file hello.o
hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped # 二进制64位的可重定位的目标文件
root@aemonair:~/tmp# file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=1420b36f9837a8d7ba2f4a9ec6b968d16c403d23, not stripped #可执行的二进制文件
- objdump
objdump命令是Linux下的反汇编目标文件或者可执行文件的命令
root@aemonair:~/tmp# objdump -s -d hello.o
hello.o: file format elf64-x86-64
Contents of section .text:
0000 554889e5 4883ec10 897dfc48 8975f0bf UH..H....}.H.u..
0010 00000000 e8000000 00b80500 0000c9c3 ................
Contents of section .rodata:
0000 68656c6c 6f2c2077 6f726c64 2100 hello, world!.
Contents of section .comment:
0000 00474343 3a202844 65626961 6e20352e .GCC: (Debian 5.
0010 332e312d 38292035 2e332e31 20323031 3.1-8) 5.3.1 201
0020 36303230 3500 60205.
Contents of section .eh_frame:
0000 14000000 00000000 017a5200 01781001 .........zR..x..
0010 1b0c0708 90010000 1c000000 1c000000 ................
0020 00000000 20000000 00410e10 8602430d .... ....A....C.
0030 065b0c07 08000000 .[......
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 89 7d fc mov %edi,-0x4(%rbp)
b: 48 89 75 f0 mov %rsi,-0x10(%rbp)
f: bf 00 00 00 00 mov $0x0,%edi
14: e8 00 00 00 00 callq 19 <main+0x19>
19: b8 05 00 00 00 mov $0x5,%eax
1e: c9 leaveq
1f: c3 retq
-s 除了显示test的全部Header信息,还显示他们对应的十六进制文件代码
-d 反汇编test中的需要执行指令的那些section
- readelf命令
readelf用来显示ELF格式目标文件的信息.可通过参数选项来控制显示哪些特定信息。
root@aemonair:~/tmp# readelf -s -d hello.o
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 8
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 32 FUNC GLOBAL DEFAULT 1 main
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND puts
三、基本基础
- 返回值
-若要在Bash
中获得函数运行的返回值,则在终端中输入以下代码并回车。
echo $?
root@aemonair:~/tmp# echo $?
5
一般的正确返回则返回0;这里的5是我们为了查看效果。
若我们再执行一次echo $?
则会是多少呢?
root@aemonair:~/tmp# echo $?
0
因为我们这时的返回值是成功执行了echo
的成功返回值,故为0。
- main函数参数:
int argc
:参数的个数
char **argv
: 参数的值 //也可写成char *argv[]
;
/*argtest.c*/
#include <stdio.h>
//argument count
//argument value
int main(int argc, char **argv)
{
int i = 0 ;
printf("argc: %d\n",argc);
for(i = 0; i < argc ;++i)
{
printf("argv[%d]:%s\n",i,argv[i]);
}
return 0;
}
编译源文件为可执行文件:
root@aemonair:~/tmp# gcc argtest.c -o argtest
./argtest.c 1 2 c def $
运行得到:
root@aemonair:~/tmp# ./argtest 1 2 c def $
argc: 6
argv[0]:./argtest
argv[1]:1
argv[2]:2
argv[3]:c
argv[4]:def
argv[5]:$
可见,argc是命令行传入的参数个数,argv是一个二位指针,可以通过[ ]将其值%s
打印出来。
而且,argv[0]即是程序名本身。
- 条件编译:
-此时,codes2的内容将不会运行。
#if 1
//codes
#else
//cods 2
#endif
可用来作为注释或程序运行时的条件决策。