构建和管理多文件以及编译流程

项目工程

Posted by Bruce Lee on 2024-09-20

关于我

欢迎来到我的博客!这里汇集了我对编程和技术的洞见和总结。本站内容分为几个主要类别,涵盖从具体技术实现到编程理念的广泛话题。

主要内容分类

  • 项目工程:深入探讨技术的实现细节和理解。
  • C/C++:围绕C/C++语言的技术点和编程技巧进行详细总结。
  • 程序员哲学:分享程序员在职业生涯中应该具备的哲学理念和思考方式。

想要了解更多具体内容,您可以访问文章分类页面。

联系我

如果您有任何问题或想要交流,欢迎通过关于页面与我联系。

感谢您的阅读和支持,希望我的博客能为您的技术旅程带来帮助!


概括

文档详细描述了在navy-apps项目中如何进行多文件布局和编译,创建一个集成的ramdisk.img。说明了navy-apps与nanos-lite操作系统之间文件和编译依赖关系的配置,特别是如何通过Makefile来管理和编译资源。强调了NAVY框架的角色,它作为库支持应用程序使用操作系统的功能,同时也提供了必要的封装来简化应用程序对操作系统底层接口的访问

多文件布局编译

执行了文档提供的自动化make代码,会将navy-apps/fsimg中的代码挨个循环编译组成一个ramdisk.img.

然后在同目录下有一个ramdisk.h文件,这里记录了一个文件记录表.

同时在nanos-lite下的src有一个files.h文件,里面复制了ramdisk.h的内容.

在fs.c中的file_table中,有一个包含预编译指令,将files.h中包含了,那么所有的文件描述都有了.

nanos-lite中的Makefile

makefile中,./src/resources.S依赖与所有的RAMDISK_FILE.

RAMDISK_FILE就是build/ramdisk.img文件.

如果没有这个文件那么就是创建一个空的ramdisk.img文件.

如果打开了HAS_NAVY,那么就会将file变量赋予ramdisk.img,src/files.h,src/syscall.h值

然后使用wildcard对syscall.h文件进行扩展匹配,判断是否为空.

如果是空,那么就是调用error,输出错误信息,然后终止make进程.

NAVY的存在,就是为了运行在这个nanos-lite中的,而nanos-lite,至少是需要有至少功能的文件系统.

update构建目标,更新了ramdisk.img中的文件信息,以及files.h中的文件记录表.

nanos目录下的makefile,启动nanos中的make程序

1
$(MAKE) -s -C $(NAVY_HOME) ISA=$(ISA) ramdisk

使用当前的make实例.因为这是在运行nanos中的make命令

使用静默模式,让navy中的make命令全部不要输出,只要错误信息

工作目录设置在navy下,然后指定navy的编译目标架构是nanos中的make命令下指定的目标架构,

构建目标就是ramdisk,这个就是更新磁盘,因为应用程序或者navy的库文件被更改了,就需要重新构建对应的可执行文件,集成到ramdisk中.

在navy中,重新make更新了ramdisk,生成的ramdisk是在navy目录下的build目录中,但是我们的Resouce.S文件中的磁盘文件是nanos下的build/ramdisk.img.所以这里就创建了一个软连接.

使用nanos中的ramdisk.img时,引用的就是navy中的ramdisk.img文件.

所以,在nanos中看到的ramdisk.img文件,其中的内容和navy中的构建目录中的ramdisk.img内容一致,包括下面的files.h文件.

同样软链接了files.h->ramdisk.h, syscall.h->syscall.h

所以,这三个文件的本源是来自navy,其中如果在添加自定义玩法的功能的时候,要增加一些自己的系统调用,需要在navy中的syscall.h进行操作,而不是在nanos中进行操作.因为我们在操作系统想增加一些自定义的功能的时候,目的就是为了让用户程序去调用,达到某种便利.而如果用户程序编写了含有操作系统自定义的功能函数(某些系统调用),首先就是无法编译成功.

因为编译器不知道,链接库也没有相关的函数信息.

所以,我们需要在navy中编写对应的库函数,去调用这个操作系统自定义接口.

navy是一个库,用来支持应用程序去使用操作系统提供的功能.

同时navy也会对操作系统的接口进行包装,提供更便利,更加抽象的接口,来给应用程序提供更优质的服务.

navy就像我们的c库,c库会对底层的操作系统接口进行封装,提供更加安全,抽象的函数.

当然,navy不会全部封装,让我们的应用程序无法直接调用操作系统提供的接口.所以在应用程序中,同样也可以直接调用_syscall_这类底层的接口函数(相对于应用程序来说).

所以,直接的暴露操作系统接口的库(比如libos库,对操作系统接口的封装非常少),应用程序可以调用.

同样的例子,navy中的libndl,libminiSDL,就是对libos中的接口进行再进一步的封装.提供更高级的抽象.

所以,我们在编写操作系统的自定义功能时,也要在navy中提供相对应的封装.严格的来说,如果navy中不提供对应的封装.

应用程序使用魔术来调用_syscall_,也是可以达到使用这个只有操作系统自己知道的系统调用,但是这样并不是我们的初衷.

同样的,在navy的makefile前面是做了个安全检查.

打开Makefile.html查看,最重要的就是对构建目标的检查.

怎么检查呢,就是对照scripts中的mk文件进行检查.每一个构建目标架构,都有一个独属于自己的mk文件,用于包含专用的链接库.

通用编译规则

在riscv中,就会定义交叉编译的编译器前缀,链接地址.

链接地址根据VME是否定义,来决定是链接到0x83000000还是0x40000000.

CFLAGS中,添加了一些选项.要编译到nanos操作系统上,我们需要生成位置有关代码,这对于这个简单的操作系统来说(准确的来说,是简单的加载器),更好处理.

因为位置有关,我们直接把程序加载到对应的地址上,然后跳转就可以执行了,而不是在加载的过程中,重新计算地址.

所以,我们编译的都是静态链接.

然后定义目标架构是rv64g, 和一些其他参数.

链接器,添加了禁止指令重排优化,保持指令的原始顺序,然后添加.text端的起始地址就是之前做的链接地址工作.

riscv32编译规则

通过include指令,将通用规则包含进来.

riscv32文件,就会对上述的目标架构覆写,改成rv32g的

这里还定义了-mabi参数,规定了目标的二进制程序接口,int,long和指针都是32位长度的.这是符合我们的nemu模拟器的架构的.

链接器上,指定输出文件格式是elf32位的riscv文件.这里确保的是32位elf,小端格式.

编译目标

定义了应用程序的目标,就是在navy目录下的build/$(NAME)-$(ISA)文件

ARCHIVE归档文件也同样如此.

添加库

添加了libc库和libos库,前者是能够在我们的nanos上运行的c库,是对底层接口包装的c库.

后者是最核心的库,libc中大多的函数就是对libos中的接口进行包装.

这是建立在我们不是编译到native中,如果是编译到我们的nanos中,就需要这些库,因为标准库中的有些接口和行为,我们的实现是有一些差异的,所以,我们需要自己的libc库.

32位架构下的64位乘除法软件支持库

如果构建目标中有32位的,那么就在LIBS中追加compiler-rt库

这个库的作用就是为了某些嵌入式环境中,资源有限的环境中,可以将一些指令转换为软件来实现.

在后续的定点处理中,我们的nemu中没有试下FTU,那么我们依然可以使用定点来处理浮点运算.

然后就是将所有的库都添加到包含路径中.

添加的ndl库的链接

查看了LIBS中的追加的库,发现并没有添加libndl,所以这里需要添加这个库,不然在编译的时候就会出现找不到符号的错误.

archive目标构建

如果构建目标是archive,那么就只构建归档库.

每一个生成的归档文件就是在各个库源代码下的build目录下.通过addsuffix和basename,addsuffix指令来达到生成在特定目录下的具有某个名字的文件.

然后是排序去重.便利LIBS中的所有的库,给每一个库生成一个对应的完整路径,这样就是生成对应目录下的archive

编译器选择和通用的编译选项

然后就是通过CC,LD这些来选择编译工具,通过添加前缀来选择交叉编译选项.

文件包含

包含我们在scripts中的mk文件

编译

目标文件中的所有.o,依赖于所有.c,.cpp,.S文件.通过常用的编译make语句来实现

这里所做的编译,都是针对我们的navy中的库,进行独立的编译,生成独立的归档文件.

然后针对我们的应用程序,进行编译和链接操作.

将所有的编译好的elf文件,都放到fsing目录下.

我们的所有应用程序和tests文件,都会被自动编译好,然后我们可以在fsimg中,添加的tests文件,就会被制作成的ramdisk.

然后最重要的来了.

构建ramdisk,依赖于fsimg,fsimg依赖于定义的tests和apps程序.

通过install指令,编译好的应用程序,都会被反倒fsimg目录下面.

获取所有的文件,写到ramdisk文件中,然后调整ramdisk文件的大小,512对齐.然后在向ramdisk文件中中写入一些注释.
然后通过计算每一个文件的大小,生成相应的头文件内容.

对于这些ramdisk文件和头文件的处理,就在上述分析过的nanos目录下的makefile中处理.


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. All the images used in the blog are my original works or AI works, if you want to take it,don't hesitate. Thank you !