`
lobin
  • 浏览: 425961 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

C: 第101章 编译链接

 
阅读更多

编译链接

编译链接其实包含一系列过程。

 

还是以上面的例子为例来讲解编译链接,采用gcc编译器:

 

 

#include <stdio.h>

void main()
{
  printf("hello, c!\n");
}

 

可以将编译链接分开进行,通常我们可以将这两个过程一起完成。上面的那个例子:

直接编译链接生成可执行程序

>gcc maintest.c -o maintest

>.\maintest

hello, c!

我们可以查看这一次编译链接过程的细节:

>gcc maintest.c -o maintest -v

Using built-in specs.

 

COLLECT_GCC=gcc

 

COLLECT_LTO_WRAPPER=/usr/lib/gcc/i686-pc-cygwin/4.5.3/lto-wrapper.exe

 

Target: i686-pc-cygwin

 

Configured with: /gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3/configure --srcdir=/gnu/gcc/releases/respins/4.5.3-3/gcc4-4.5.3-3/src/gcc-4.5.3 --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/lib --datadir=/usr/share --localstatedir=/var --sysconfdir=/etc --datarootdir=/usr/share --docdir=/usr/share/doc/gcc4 -C --datadir=/usr/share --infodir=/usr/share/info --mandir=/usr/share/man -v --with-gmp=/usr --with-mpfr=/usr --enable-bootstrap --enable-version-specific-runtime-libs --libexecdir=/usr/lib --enable-static --enable-shared --enable-shared-libgcc --disable-__cxa_atexit --with-gnu-ld --with-gnu-as --with-dwarf2 --disable-sjlj-exceptions --enable-languages=ada,c,c++,fortran,java,lto,objc,obj-c++ --enable-graphite --enable-lto --enable-java-awt=gtk --disable-symvers --enable-libjava --program-suffix=-4 --enable-libgomp --enable-libssp --enable-libada --enable-threads=posix --with-arch=i686 --with-tune=generic --enable-libgcj-sublibs CC=gcc-4 CXX=g++-4 CC_FOR_TARGET=gcc-4 CXX_FOR_TARGET=g++-4 GNATMAKE_FOR_TARGET=gnatmake GNATBIND_FOR_TARGET=gnatbind --with-ecj-jar=/usr/share/java/ecj.jar

 

Thread model: posix

gcc version 4.5.3 (GCC)

 

COLLECT_GCC_OPTIONS='-o' 'maintest.exe' '-v' '-mtune=generic' '-march=i686' 

 

 /usr/lib/gcc/i686-pc-cygwin/4.5.3/cc1.exe -quiet -v -D__CYGWIN32__ -D__CYGWIN__ -Dunix -D__unix__ -D__unix -idirafter /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../include/w32api -idirafter /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/lib/../../include/w32api maintest.c -quiet -dumpbase maintest.c -mtune=generic -march=i686 -auxbase maintest -version -o /cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cceNNRyv.s

 

GNU C (GCC) version 4.5.3 (i686-pc-cygwin)

        compiled by GNU C version 4.5.3, GMP version 4.3.2, MPFR version 3.0.1-p4, MPC version 0.8

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072

ignoring nonexistent directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/include"

ignoring duplicate directory "/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/lib/../../include/w32api"

#include "..." search starts here:

#include <...> search starts here:

 /usr/local/include

 /usr/lib/gcc/i686-pc-cygwin/4.5.3/include

 /usr/lib/gcc/i686-pc-cygwin/4.5.3/include-fixed

 /usr/include

 /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../include/w32api

End of search list.

 

GNU C (GCC) version 4.5.3 (i686-pc-cygwin)

        compiled by GNU C version 4.5.3, GMP version 4.3.2, MPFR version 3.0.1-p4, MPC version 0.8

GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072

Compiler executable checksum: 89d6774c1d510265da7d48b735ce61fb

 

COLLECT_GCC_OPTIONS='-o' 'maintest.exe' '-v' '-mtune=generic' '-march=i686'

 

 /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/bin/as.exe -v -o /cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ccrMX9kq.o /cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cceNNRyv.s

 

GNU assembler version 2.23.52 (i686-cygwin) using BFD version (GNU Binutils) 2.23.52.20130309

 

COMPILER_PATH=/usr/lib/gcc/i686-pc-cygwin/4.5.3/:/usr/lib/gcc/i686-pc-cygwin/4.5.3/:/usr/lib/gcc/i686-pc-cygwin/:/usr/lib/gcc/i686-pc-cygwin/4.5.3/:/usr/lib/gcc/i686-pc-cygwin/:/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/bin/

 

LIBRARY_PATH=/usr/lib/gcc/i686-pc-cygwin/4.5.3/:/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../:/lib/:/usr/lib/

 

COLLECT_GCC_OPTIONS='-o' 'maintest.exe' '-v' '-mtune=generic' '-march=i686'

 

 /usr/lib/gcc/i686-pc-cygwin/4.5.3/collect2.exe --wrap _Znwj --wrap _Znaj --wrap _ZdlPv --wrap _ZdaPv --wrap _ZnwjRKSt9nothrow_t --wrap _ZnajRKSt9nothrow_t --wrap _ZdlPvRKSt9nothrow_t --wrap _ZdaPvRKSt9nothrow_t -Bdynamic --dll-search-prefix=cyg --large-address-aware --tsaware -o maintest.exe /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../crt0.o /usr/lib/gcc/i686-pc-cygwin/4.5.3/crtbegin.o -L/usr/lib/gcc/i686-pc-cygwin/4.5.3 -L/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../.. /cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ccrMX9kq.o -lgcc -lgcc_eh -lcygwin -luser32 -lkernel32 -ladvapi32 -lshell32 -lgcc -lgcc_eh /usr/lib/gcc/i686-pc-cygwin/4.5.3/crtend.o

 

可以看到这个编译链接过程还挺复杂,分成3个步骤,其中:

/usr/lib/gcc/i686-pc-cygwin/4.5.3/cc1.exe -quiet -v -D__CYGWIN32__ -D__CYGWIN__ -Dunix -D__unix__ -D__unix -idirafter /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../include/w32api -idirafter /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/lib/../../include/w32api maintest.c -quiet -dumpbase maintest.c -mtune=generic -march=i686 -auxbase maintest -version -o /cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cceNNRyv.s

这段是对maintest.c进行汇编编译成汇编程序cceNNRyv.s。这里用到了一个cc1.exe工具,这个工具我们一般用不到,它是一个将C程序汇编编译成汇编程序的工具。

 

它的效果其实就跟下面的一样:

>gcc -S maintest.c -o maintest.s

生成的汇编程序如下:

 

	.file	"maintest.c"
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC0:
	.ascii "hello, c!\0"
	.text
.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
	pushl	%ebp
	movl	%esp, %ebp
	andl	$-16, %esp
	subl	$16, %esp
	call	___main
	movl	$LC0, (%esp)
	call	_puts
	leave
	ret
	.def	_puts;	.scl	2;	.type	32;	.endef

 

 

/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../../i686-pc-cygwin/bin/as.exe -v -o /cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ccrMX9kq.o /cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cceNNRyv.s

这段是将上面的汇编程序cceNNRyv.s编译成对象文件ccrMX9kq.o。通过as工具,as这个工具如果我们用过as汇编的话就对它很熟,它是一个汇编编译器。

 

我们上面的编译过程实际上还包括这两个过程:

1、将c程序汇编编译成汇编程序

2、将汇编程序编译成对象文件

也就是说:

gcc -c maintest.c -o maintest.o

实际上包括上面这两个步骤。

 

/usr/lib/gcc/i686-pc-cygwin/4.5.3/collect2.exe --wrap _Znwj --wrap _Znaj --wrap _ZdlPv --wrap _ZdaPv --wrap _ZnwjRKSt9nothrow_t --wrap _ZnajRKSt9nothrow_t --wrap _ZdlPvRKSt9nothrow_t --wrap _ZdaPvRKSt9nothrow_t -Bdynamic --dll-search-prefix=cyg --large-address-aware --tsaware -o maintest.exe /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../crt0.o /usr/lib/gcc/i686-pc-cygwin/4.5.3/crtbegin.o -L/usr/lib/gcc/i686-pc-cygwin/4.5.3 -L/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../.. /cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/ccrMX9kq.o -lgcc -lgcc_eh -lcygwin -luser32 -lkernel32 -ladvapi32 -lshell32 -lgcc -lgcc_eh /usr/lib/gcc/i686-pc-cygwin/4.5.3/crtend.o

这段是将上面的ccrMX9kq.o链接成可执行程序maintest.exe。这里用到了一个collect2.exe工具,这个是一个链接工具。

 

这个工具我们一般也用不到,实际上我们单独链接的话,也不会用到这个工具,我们会通过ld工具或者直接通过gcc进行单独链接。但这个工具其实就是ld工具的一个封装,它用的气势就是ld工具来进行链接。当然,通过gcc进行单独链接最后也是通过ld工具来进行链接。

 

所以,如果已经编译了的话, 我们可以通过这个工具单独进行链接:

 

>gcc -c maintest.c -o maintest.o

 

>D:\sbin\lib\gcc\i686-pc-cygwin\4.5.3\collect2.exe --wrap _Znwj --wrap _Znaj --wrap _ZdlPv --wrap _ZdaPv --wrap _ZnwjRKSt9nothrow_t --wrap _ZnajRKSt9nothrow_t --wrap _ZdlPvRKSt9nothrow_t --wrap _ZdaPvRKSt9nothrow_t -Bdynamic --dll-search-prefix=cyg --large-address-aware --tsaware -o maintest.exe /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../crt0.o /usr/lib/gcc/i686-pc-cygwin/4.5.3/crtbegin.o -L/usr/lib/gcc/i686-pc-cygwin/4.5.3 -L/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../.. maintest.o -lgcc -lgcc_eh -lcygwin -luser32 -lkernel32 -ladvapi32 -lshell32 -lgcc -lgcc_eh /usr/lib/gcc/i686-pc-cygwin/4.5.3/crtend.o

 

>.\maintest

 

hello, c!

 

上面通过collect2.exe工具链接的时候指定了很多多余的选项以及链接库,很多都没有用到,去掉没有用到的,其实很简单:

>gcc -c maintest.c -o maintest.o

 

>D:\sbin\lib\gcc\i686-pc-cygwin\4.5.3\collect2.exe -o maintest.exe /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../crt0.o maintest.o -lcygwin -lkernel32

 

可以通过以下命令dump查看cygwin和crt0.o的内部实现:

$ objdump -d libcygwin.a

$ objdump -d /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../crt0.o

 

>.\maintest

hello, c!

 

 

通常如果工程简单的话,我们可以一次完成整个过程,如果复杂的话,我们会分两次或多次完成,简单来讲,它分为编译和链接两个过程。

 

如果将编译和链接分开的话,

编译

>gcc -c maintest.c -o maintest.o

 

链接

>gcc maintest.o -o maintest

>.\maintest

hello, c!

 

通过ld工具进行链接

 

>ld maintest.o -o maintest

这样会报错:

maintest.o:maintest.c:(.text+0xa): undefined reference to `__main'

maintest.o:maintest.c:(.text+0x16): undefined reference to `puts'

 

缺失的__main和puts实际上在cygwin链接库中,需要加上-lcygwin。同时mainCRTStartup入口函数在/usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../crt0.o中,链接的时候需要将这个对象文件一起链入进去。另外还需要-lkernel32,链接的时候依赖这个库。

 

puts函数的实现:

Disassembly of section .text:

 

00000000 <_puts>:

   0:   ff 25 00 00 00 00       jmp    *0x0

   6:   90                      nop

   7:   90                      nop

 

t-d001504.o:     文件格式 pe-i386

 

所以,应该这样:

>gcc -c maintest.c -o maintest.o

 

>ld -o maintest.exe /usr/lib/gcc/i686-pc-cygwin/4.5.3/../../../crt0.o maintest.o -lcygwin -lkernel32

这里其实就是直接将上面通过collect2.exe工具链接的时候,直接将collect2.exe替换为ld工具进行链接。

 

>.\maintest

hello, c!

 

 

分享到:
评论

相关推荐

    C语言设计案例教程第一章c语言概述.ppt

    本章主要介绍C语言的基础知识,包括程序设计的概念、C语言的特点、程序的构成、变量和常量的使用以及标识符的规则。 1. 程序设计与程序设计语言 程序是解决问题的逻辑描述,由计算机能够执行的指令序列组成。程序...

    c语言程序设计教程答案

    #### 第一章 C语言程序设计概述 ##### 习题答案解析 **1. 算法的描述基本方法** - **自然语言**:使用日常的语言来描述算法的步骤。 - **专用工具**:例如流程图、N-S图等图形化的表示方法。 **2. C语言程序的...

    Reversing:逆向工程揭密

    其中第5章、第9章和附录A、B、C由韩琪翻译,第3章、第11章和第13章由杨艳翻译,第7章、第8章和第10章由王玉英翻译,第4章和第6章由李娜翻译,第1章由褚华翻译,第2章由陈贵敏翻译,第12章由辛健斌翻译;全部译稿的...

    寒江独钓-Windows内核安全编程(高清完整版).part4

    1.1.2 编写第一个C文件 3 1.1.3 编译一个工程 5 1.2 安装与运行 6 1.2.1 下载一个安装工具 6 1.2.2 运行与查看输出信息 7 1.2.3 在虚拟机中运行 9 1.3 调试内核模块 9 1.3.1 下载和安装WinDbg 9 1.3.2 设置Windows ...

    Python灰帽子-黑客与逆向工程师的Python编程之道[简体中文版]

    《Python灰帽子:黑客与逆向工程师的Python编程之道》是由知名安全机构Immunity Inc的资深黑帽Justin Seitz先生主笔撰写的一本关于编程语言Python如何被广泛应用于黑客与逆向工程领域的书籍。老牌黑客,同时也是...

    Linux多线程服务端编程:使用muduo C++网络库

    《Linux多线程服务端编程:使用muduo C++网络库》主要讲述采用现代C++在x86-64 Linux上编写多线程TCP网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。...

    Samba工具使用指南:UNIX与Windows_NT网络互连

    第一部分 概述和系统规划 第1章 UNIX和Windows网络互连 1 1.1 系统规划 4 1.1.1 桌面客户议题 5 1.1.2 企业计算问题 5 1.1.3 域和Realms 5 1.1.4 口令 6 1.2 使用Samba共享资源 6 第2章 UNIX概述 7 2.1 服务和守护...

    SAMBA工具使用指南:UNIX与WINDOWS NT网络互连

    第一部分 概述和系统规划 第1章 UNIX和Windows网络互连 1 1.1 系统规划 4 1.1.1 桌面客户议题 5 1.1.2 企业计算问题 5 1.1.3 域和Realms 5 1.1.4 口令 6 1.2 使用Samba共享资源 6 第2章 UNIX概述 7 2.1 服务和守护...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

    第3章 判断和循环 101 3.1 比较数据值 101 3.1.1 if语句 102 3.1.2 嵌套的if语句 104 3.1.3 嵌套的if-else语句 107 3.1.4 逻辑运算符和表达式 109 3.1.5 条件运算符 112 3.1.6 switch语句 113 3.1.7 无条件...

    samba工具使用指南:unix与windows网络互连

    第一部分 概述和系统规划 第1章 UNIX和Windows网络互连 1 1.1 系统规划 4 1.1.1 桌面客户议题 5 1.1.2 企业计算问题 5 1.1.3 域和Realms 5 1.1.4 口令 6 1.2 使用Samba共享资源 6 第2章 UNIX概述 7 2.1 ...

    C++第一章习题答案.pdf

    返回值是较大的数作为第一个参数,较小的数作为第二个参数。 1-121-12 函数`s`用于交换两个结构变量,通过引用参数实现。在`main`函数中,创建结构体实例并调用`s`验证其功能。 1-131-13 定义一个宏`MAX_OF_THREE...

    ARM详解 ARM入门必备

    似乎搞ARM开发入门都是用这本书。 上网搜了很久,勉强下载下来了 1分,算是搜索小费啦 ...8.2.2 编译和链接工程 225 8.2.3 使用命令行工具编译应用程序 229 8.3 用AXD 进行代码调试 230 8.4 本章小结 233

    PHP3程序设计

    第5章 中场一:数据库连接 67 5.1 开端 67 5.2 创建连接 67 5.3 获取HTML表单信息 69 5.4 使用HTML表单信息 70 5.5 common.inc文件 72 5.6 总结 73 第6章 数据库和SQL 74 6.1 信息和数据有何不同 74 6.2 从信息向...

    C# Winform数据库应用设计(附开发案例

    初步的认识,了解C#的开发环境,编写第一个C#控制台应用程序。C#语言的基本语法和我 们之前的学习的C语言、Java语言一样同属于C语言语法系列,有很多相似的地方,同时也 有一些不同的地方,因此我们在学习的时候需要注意...

Global site tag (gtag.js) - Google Analytics