Linux下编译的一些基本命令

来源:互联网 发布:纸箱软件 编辑:程序博客网 时间:2024/06/05 02:39

在windows下图形界面编程习惯了,准备学习使用gcc,但是一些命令还没有记清楚,总是搞混,好在学校里的论坛有个比较详细的介绍,原文在论坛里,我转载见下:


注意哈,这个内容会很详细,分类选了嵌入式……

这个只针对c语言,另外不涉及系统编程

1.编程环境建立,这个就不多说了
sudo apt-get install build-essential 以后就可以开工了

2.来个经典的hello world
可能有同学写好了程序,但是却找不到bulid按钮哪儿去了,然后就觉得linux的世界真迷惑了……
复制代码
  1. #include <stdio.h>
  2. int main(void)
  3. {
  4.      printf(“hello world \n");
  5.      return 0;
  6. }

这段程序应该家喻户晓了
用vi/vim/emacs或者gedit等编辑好源代码后保存,假设为hello.c
比方说文件保存路径是/home/xx/test/hello.c
在虚拟终端中cd到/home/xx/test
执行
复制代码
  1. gcc hello.c -o hello

完了后
复制代码
  1. ./hello

你就能见到那亲切的hello world了

编译的过程中发生了什么呢?
用惯了IDE的同学可能会觉得源文件到可执行文件时一步完成的,因为点击build and run就坐等效果了

一个可执行文件的产生经历了  复制代码
  1. 源文件(.c)——汇编文件(.S)——目标文件(.o)——可执行文件


源文件到汇编中间会经过预处理
通过复制代码
  1. gcc -E hello.c
可以得到与处理后的文件,可以看到预处理完成了头文件及宏的替换……
通过复制代码
  1. gcc -S hello.c
可以得到linux汇编文件,名字默认为hello.s
通过复制代码
  1. gcc -c hello.c
可以得到elf格式的目标文件,名字默认为hello.o

上面的命令看似只涉及gcc,其实gcc在预处理过程中是调用cpp完成预处理工作的
gcc完成由预处理文件到汇编文件的转换
汇编是gcc调用as同时向as传递参数完成的
as生成的是目标文件,最后目标文件通过ld链接标准库后得到可执行文件hello

理论上来说你可以单步调用各个阶段的工具一步一步建立可执行文件,但是过程中传递的参数需要你自己去考量了,
你可以这么干,但是我暂时不建议你这么做。

现在来扩充一下
复制代码
  1.     
  2.     .file    "hello.c"
  3.     .section    .rodata
  4. .LC0:
  5.     .string    "hello world"
  6.     .text
  7. .globl main
  8.     .type    main, @function
  9. main:
  10.     pushl    %ebp
  11.     movl    %esp, %ebp
  12.     andl    $-16, %esp
  13.     subl    $16, %esp
  14.     movl    $.LC0, (%esp)
  15.     call    puts
  16.     movl    $0, %eax
  17.     leave
  18.     ret
  19.     .size    main, .-main
  20.     .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
  21.     .section    .note.GNU-stack,"",@progbits



我对linux下的x86汇编也不是很了解,这里只能给大家简单的说下
在汇编中一个标号(main,LC0都是标号)代表了一个地址,看上面的代码可以发现main函数入口其实是一个地址,
程序在将字符串的地址压栈后,调用puts显示字符串(puts从栈中提取参数)

这个puts是由于调用printf产生的,但是最后到哪儿去找到这个puts呢
来看看目标文件
复制代码
  1. nm -n hello.o

得到
复制代码
  1.                U puts
  2. 00000000 T main

这里汇编器假设了main的地址为0,U代表puts是个未定的符号
上面的问题,这个puts怎么找到它?
ld连接程序在链接的过程中会检测这些符号并且由于各种原因会重定位这些符号,它发现puts未定义,因此它会到预订的地方找puts,如果找到,假设是静态链接,那么它就把那个puts复制过来,如果是动态链接,它就把相关的路径等信息放入即将生成的可执行文件当中,在程序执行的时候再调入puts。

这是连接后的情况
复制代码
  1.          w _Jv_RegisterClasses
  2.          w __gmon_start__
  3.          U __libc_start_main@@GLIBC_2.0
  4.          U puts@@GLIBC_2.0
  5. 080482b8 T _init
  6. 08048330 T _start
  7. 08048360 t __do_global_dtors_aux
  8. 080483c0 t frame_dummy
  9. 080483e4 T main
  10. 08048400 T __libc_csu_fini
  11. 08048410 T __libc_csu_init
  12. 0804846a T __i686.get_pc_thunk.bx
  13. 08048470 t __do_global_ctors_aux
  14. 0804849c T _fini
  15. 080484b8 R _fp_hw
  16. 080484bc R _IO_stdin_used
  17. 080484cc r __FRAME_END__
  18. 08049f0c d __CTOR_LIST__
  19. 08049f0c d __init_array_end
  20. 08049f0c d __init_array_start
  21. 08049f10 d __CTOR_END__
  22. 08049f14 d __DTOR_LIST__
  23. 08049f18 D __DTOR_END__
  24. 08049f1c d __JCR_END__
  25. 08049f1c d __JCR_LIST__
  26. 08049f20 d _DYNAMIC
  27. 08049ff4 d _GLOBAL_OFFSET_TABLE_
  28. 0804a00c D __data_start
  29. 0804a00c W data_start
  30. 0804a010 D __dso_handle
  31. 0804a014 A __bss_start
  32. 0804a014 A _edata
  33. 0804a014 b completed.7021
  34. 0804a018 b dtor_idx.7023
  35. 0804a01c A _end

可以看到多了很多符号,这都是ld干的事,main被重新定位到了080483e4 这个地址,为什么是这个地址……我也不知道……,需要记住,当你调用一个函数时,本质上你是提供了函数符号的地址。

在hello.c中添加点内容
复制代码
  1. #include <stdio.h>

  2. void add1(void)
  3. {
  4.     printf("add1\n");
  5. }
  6. int main(void)
  7. {
  8.     printf("hello world\n");
  9.     add1();
  10.     return 0;
  11. }

注意函数最好先声明后使用,这里没有声明,但是被调用函数放在调用函数的前面,如果没有声明且调用函数在被调用函数的前面,将会出现以下警告


复制代码
  1. hello.c:10: warning: conflicting types for ‘add1’
  2. hello.c:7: note: previous implicit declaration of ‘add1’ was here


待续……大家反馈下,我看下情况……是否还需要写下去,如果写的话,接下来是多文件编译和Makefile的简单知识
原创粉丝点击