从0创建一个OS(十三) C源程序的编译和链接

本节讲学习如何编译和链接C源程序,以及查看最终文件与汇编代码的异同.

关键字:C语言; 目标代码;链接器;反汇编

目标:学习使用C语言编写与汇编语言功能相同的代码

上一节中我们创建了平台无关的编译器,本节我们将使用该编译器的一些功能来编译、链接C源程序.

本节没有什么理论知识,主要是熟悉编译器的一些功能.

编译

首先我们写一个C语言函数,function.c,指示返回int型的0xBABA.

int function() { return 0xBABA; }

 

然后使用我们在上一节创建的编译器进行编译.
在实施具体操作之前,笔者这里先给出本节要使用到的shell指令的相关说明,以表格形式呈现.

指令指令功能选项选项功能
i386-elf-gcc 编译源文件 -ffreestanding 断言编译针对的是独立的环境。独立式环境是其中标准库可能不存在的环境,并且程序启动不一定在“主”环境中。 最明显的例子是OS内核。 这等效于-fno-hosted。
i386-elf-objdump 解析二进制目标文件 -d 显示可执行段的汇编内容
i386-elf-ld 链接二进制目标文件 -o [file format] 设置输出文件格式
    -Ttext [ADDRESS] 设置.text段的地址
    –oformat [target] 设置输出文件的目标
ndisasm 反汇编二进制文件 -b [16/32/64] 设置处理器模式为16/32//64bit

来继续看编译.

# 编译生成与平台无关的目标文件 i386-elf-gcc -ffreestanding -c function.c -o function.o
# 或
gcc -fno-pie -m32 -ffreestanding -c function.c -p function.o

 

执行完成后你就会看到执行目录下多了一个function.o文件,然后我们使用以下语句查看其内容.

# 显示可执行段的汇编内容 i386-elf-objdump -d function.o
# 或
objdump -m i386 -d function.o

 

在这里插入图片描述
但是我们一直使用的汇编都是nasm,这个汇编中的那些百分号看不懂呀,没关系,你只需要知道C源程序最终可以转化成汇编形式就好.

经过以上步骤,编译阶段就完成了,我们实现了将C源程序编译成目标文件xxx.o文件,但是在比较大的工程中,会有许多C源程序,也就会编译生成很多目标文件,但最终的可执行程序只有一个,怎么实现的呢?自然,我们需要链接!

链接

我们可以使用以下指令将多个(单个也行)目标文件链接成最终的目标文件.

i386-elf-ld -o function.bin -Ttext 0x0 --oformat binary function.o
# 或
ld -pie -m elf_i386 -o function.bin -Ttext 0x0 --oformat binary function.o

 

此指令可能会出现一个warning,不要理会.

现在你的执行目录里又多了一个文件,function.bin.
那么function.ofunction.bin有什么区别呢?我们可以使用xxd查看.

xxd function.o

 

在这里插入图片描述

xxd function.bin

在这里插入图片描述

可以看到function.ofunction.bin多很多debug信息和标签,function.bin为机器码.

反汇编

处于好奇,我们想看一下function.bin中的机器码都有什么含义,因此对其进行反汇编.

ndisasm -b 32 function.bin

 

在这里插入图片描述
图中标红部分可不就是我们熟悉的代码吗?该函数在进入时创建了自己的函数栈,用来存放局部变量,EAX寄存器存放返回值,最终释放函数栈并返回.


现在我们了解如何编译并链接C源代码了,接下来给大家留一个任务,对本文后面留的3个c文件进行编译和反汇编,并尝试回答这个问题:

为什么pointers.c经过反汇编得到的汇编代码跟我们预想的情况不一样呢?“Hello”的ASCII码0x48656c6c6f在哪里呢?

functioncalls.c

void caller() { my_func(0xdede); } int my_func(int arg) { return arg; }

 

localvars.c

int my_function() { int my_var = 0xbaba; return my_var; }

 

pointers.c

void func() { char* string = "Hello"; }

 

提示:ndisasm工具无法识别代码段和数据段,它将这二者都当做代码段对待.


__EOF__

本文作者EwanHai
本文链接https://www.cnblogs.com/haiyonghao/p/14623195.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   EwanHai  阅读(299)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示