Linux下编译的一些基本命令
来源:互联网 发布:纸箱软件 编辑:程序博客网 时间:2024/06/05 02:39
在windows下图形界面编程习惯了,准备学习使用gcc,但是一些命令还没有记清楚,总是搞混,好在学校里的论坛有个比较详细的介绍,原文在论坛里,我转载见下:
这个只针对c语言,另外不涉及系统编程
1.编程环境建立,这个就不多说了
sudo apt-get install build-essential 以后就可以开工了
2.来个经典的hello world
可能有同学写好了程序,但是却找不到bulid按钮哪儿去了,然后就觉得linux的世界真迷惑了……
复制代码
- #include <stdio.h>
- int main(void)
- {
- printf(“hello world \n");
- return 0;
- }
这段程序应该家喻户晓了
用vi/vim/emacs或者gedit等编辑好源代码后保存,假设为hello.c
比方说文件保存路径是/home/xx/test/hello.c
在虚拟终端中cd到/home/xx/test
执行
复制代码
- gcc hello.c -o hello
完了后
复制代码
- ./hello
你就能见到那亲切的hello world了
编译的过程中发生了什么呢?
用惯了IDE的同学可能会觉得源文件到可执行文件时一步完成的,因为点击build and run就坐等效果了
一个可执行文件的产生经历了 复制代码
- 源文件(.c)——汇编文件(.S)——目标文件(.o)——可执行文件
源文件到汇编中间会经过预处理
通过复制代码
- gcc -E hello.c
通过复制代码
- gcc -S hello.c
通过复制代码
- gcc -c hello.c
上面的命令看似只涉及gcc,其实gcc在预处理过程中是调用cpp完成预处理工作的
gcc完成由预处理文件到汇编文件的转换
汇编是gcc调用as同时向as传递参数完成的
as生成的是目标文件,最后目标文件通过ld链接标准库后得到可执行文件hello
理论上来说你可以单步调用各个阶段的工具一步一步建立可执行文件,但是过程中传递的参数需要你自己去考量了,
你可以这么干,但是我暂时不建议你这么做。
现在来扩充一下
复制代码
- .file "hello.c"
- .section .rodata
- .LC0:
- .string "hello world"
- .text
- .globl main
- .type main, @function
- main:
- pushl %ebp
- movl %esp, %ebp
- andl $-16, %esp
- subl $16, %esp
- movl $.LC0, (%esp)
- call puts
- movl $0, %eax
- leave
- ret
- .size main, .-main
- .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
- .section .note.GNU-stack,"",@progbits
我对linux下的x86汇编也不是很了解,这里只能给大家简单的说下
在汇编中一个标号(main,LC0都是标号)代表了一个地址,看上面的代码可以发现main函数入口其实是一个地址,
程序在将字符串的地址压栈后,调用puts显示字符串(puts从栈中提取参数)
这个puts是由于调用printf产生的,但是最后到哪儿去找到这个puts呢
来看看目标文件
复制代码
- nm -n hello.o
得到
复制代码
- U puts
- 00000000 T main
这里汇编器假设了main的地址为0,U代表puts是个未定的符号
上面的问题,这个puts怎么找到它?
ld连接程序在链接的过程中会检测这些符号并且由于各种原因会重定位这些符号,它发现puts未定义,因此它会到预订的地方找puts,如果找到,假设是静态链接,那么它就把那个puts复制过来,如果是动态链接,它就把相关的路径等信息放入即将生成的可执行文件当中,在程序执行的时候再调入puts。
这是连接后的情况
复制代码
- w _Jv_RegisterClasses
- w __gmon_start__
- U __libc_start_main@@GLIBC_2.0
- U puts@@GLIBC_2.0
- 080482b8 T _init
- 08048330 T _start
- 08048360 t __do_global_dtors_aux
- 080483c0 t frame_dummy
- 080483e4 T main
- 08048400 T __libc_csu_fini
- 08048410 T __libc_csu_init
- 0804846a T __i686.get_pc_thunk.bx
- 08048470 t __do_global_ctors_aux
- 0804849c T _fini
- 080484b8 R _fp_hw
- 080484bc R _IO_stdin_used
- 080484cc r __FRAME_END__
- 08049f0c d __CTOR_LIST__
- 08049f0c d __init_array_end
- 08049f0c d __init_array_start
- 08049f10 d __CTOR_END__
- 08049f14 d __DTOR_LIST__
- 08049f18 D __DTOR_END__
- 08049f1c d __JCR_END__
- 08049f1c d __JCR_LIST__
- 08049f20 d _DYNAMIC
- 08049ff4 d _GLOBAL_OFFSET_TABLE_
- 0804a00c D __data_start
- 0804a00c W data_start
- 0804a010 D __dso_handle
- 0804a014 A __bss_start
- 0804a014 A _edata
- 0804a014 b completed.7021
- 0804a018 b dtor_idx.7023
- 0804a01c A _end
可以看到多了很多符号,这都是ld干的事,main被重新定位到了080483e4 这个地址,为什么是这个地址……我也不知道……,需要记住,当你调用一个函数时,本质上你是提供了函数符号的地址。
在hello.c中添加点内容
复制代码
- #include <stdio.h>
- void add1(void)
- {
- printf("add1\n");
- }
- int main(void)
- {
- printf("hello world\n");
- add1();
- return 0;
- }
注意函数最好先声明后使用,这里没有声明,但是被调用函数放在调用函数的前面,如果没有声明且调用函数在被调用函数的前面,将会出现以下警告
- hello.c:10: warning: conflicting types for ‘add1’
- hello.c:7: note: previous implicit declaration of ‘add1’ was here
待续……大家反馈下,我看下情况……是否还需要写下去,如果写的话,接下来是多文件编译和Makefile的简单知识
- Linux下编译的一些基本命令
- Linux下的一些常用的基本命令
- Linux下的一些常用的基本命令
- linux 下vim的安装方法 及一些基本命令
- linux 下vim的安装方法 及一些基本命令
- linux 下vim的安装方法 及一些基本命令
- linux下提示一些基本的命令找不到
- linux下提示一些基本的命令找不到
- LINUX VIM 下一些基本的操作命令
- Linux的一些基本命令
- 一些基本的Linux命令
- linux一些基本的命令
- Linux基本的一些命令
- linux的一些基本命令
- 在linux下一些基本命令
- linux下的基本命令
- linux下的基本命令
- Linux下的基本命令
- 关于一个简单的矩阵运算程序,将A矩阵加上A矩阵的逆放到B矩阵中。
- source insight 的快捷键
- strstr函数实现
- 浅谈Date和Calendar的用法
- 《基于Web访问信息的用户兴趣迁移模式的研究》笔记
- Linux下编译的一些基本命令
- 行业操作系统示例-汽车操作系统
- Hadoop命令大全
- sublime text 快捷键
- JavaScript实用示例之根据其他字段对字段进行检查
- Linux0.11中系统内存的使用
- .NET页面间传值
- perl 开始 头写法
- 数据库大数据量导出多线程版本