arch 1

    技术2025-10-11  7

        通过对makefile的分析,对整一个依赖关系有一个大致的了解。接下来准备对每一个模块进行分析。首先看第一个子目录arch。

        arch目录是针对不同目标开发板设计的,存放着处理器架构相关的代码,比如可能针对的是s3c2440,s3c2410等,不同的处理器架构有着不一样的硬件模块。而整个vivi启动包括两个部分,第一部分就是完成含依赖于CPU的体系结构硬件初始化的代码,包括禁止中断、初始化串口、复制自身到RAM等。这部分代码需要根据不同的处理器来编写,该目录下就保存了针对不同处理器的第一阶段的代码。(后面的讨论针对2440来进行)

         打开该文件夹可以看到:def-configs,s3c2440,config.in,Makefile,vivi.lds.in.

         同样我们也从Makefile开始看起。

         1、Makefile:

         通过Makefile中开头的注释我们就可以大概知道Makefile中的内容了:# Select CPU dependent flags.  Note that order of declaration is important;# the options further down the list override previous items.依赖于CPU的相关选项的设置,比如编译的时候需要指定的跟CPU相关的选项,根据CPU相关的SDRAM的地址来指定程序运行地址等。其中在写法上保持了OPTION-y这样的写法(在vivi Makefile中由配置决定的优化选项好象基本用了这种写法,我个人觉得这个写法是很好的),

        按照CPU体系结构的配置信息追加了一些编译选项:

        CFLAGS        +=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float     AFLAGS        +=$(apcs-y) $(arch-y) -mno-fpu -msoft-float

             。。。。。。

     

        从名称就可以看出,与连接脚本是密切相关的(我们在看总Makefile的时候看到LINKFLAGS = -Tarch/vivi.lds -Bstatic ),但是是.lds后缀的,所以估计下面要在LDSCRIPT    = arch/vivi.lds.in这个上做文章来产生这个.lds脚本。

        LDSCRIPT    = arch/vivi.lds.in(等下会分析这个小文件的内容)

        又比如我的开发板是2440:

        ifeq ($(CONFIG_ARCH_S3C2440),y)     MACHINE        = s3c2440       ifeq ($(CONFIG_S3C2440_NAND_BOOT),y)         TEXTADDR    = 0x33f00000       else         TEXTADDR    = 0x00000000       endif     endif

     

        ifeq ($(CONFIG_VIVI_ADDR),y)     TEXTADDR    = 0x$(CONFIG_VIVI_TEXTADDR)     endif

     

        export    MACHINE PROCESSOR TEXTADDR

     

        其中CONFIG_ARCH_S3C2440 CONFIG_VIVI_ADDR等是在配置过程中产生的(这可以从同目录下的config.in的选项看到,后面将对config.in进行讨论,配置结果最后保存在.config中,.config被总Makefile包含)。最后的地址将由我们的设定值和配置结果决定。对于具体的地址我们可以按照自己的实际情况来设定。比如我最后只配置了CONFIG_ARCH_S3C2440,那么最后我指定的连接的运行地址是0x33f00000,这个值将传递给连接脚本,可以看到在该Makefile中(后面一点的部分):

        arch/vivi.lds: $(LDSCRIPT) dummy         @sed s/TEXTADDR/$(TEXTADDR)/ $(LDSCRIPT) >$@

     

        定义了HEAD := arch/$(MACHINE)/head.o,这就是我们总Makefile(include了这个Makefile)中用到的HEAD的定义。不过我认为这句是不是应该放在下面那段的后面,或者是在下面那个ifeq里面,因为这个目录还在进行判断是否存在,这里却在用了?

     

        然后我们看到:

        # If we have a machine-specific directory, then include it in the build.     MACHDIR        := arch/$(MACHINE)     ifeq ($(MACHDIR),$(wildcard $(MACHDIR)))     SUBDIRS        += $(MACHDIR)     CORE_FILES    := $(MACHDIR)/$(MACHINE).o $(CORE_FILES)     endif

        如果我们在看总Makefile时还纠结于为什么vivi依赖文件里面vivi: include/version.h $(CONFIGURATION) init/main.o init/version.o linuxsubdirs没有直接看到head.o后者说head.o为什么没有被作为CORE_FILES的话,那么这里就清晰了,在主Makefile中因为                      linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS))                                            $(patsubst %, _dir_%, $(SUBDIRS)) : include/version.h                                                  $(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@)

    此时的SUBDIRS已经+= $(MACHDIR),在按照SUBDIRS目录执行子makefile的时候同样也执行了arch/s3c2440目录下的Makefile,生成head.o文件。可以猜想head.o和其他SUBDIRS包含的文件对应的.o文件可能都是用这种办法形成的(如果在主Makefile中没有出现在任何以来中的话)。(那么那些文件夹里面应该有一个Makefile,并且需要有实现规则,这个规则可能是借用调用Rules.make来实现。暂时的猜想。)

     

        CLEAN_FILES += arch/vivi.lds

        也许应该继续往下看来分析为什么可以删除这个连接脚本(当然它肯定是要删除的,因为连接的内容是根据每一次配置而不同的,如运行地址),不过其实我们先面已经分析过了这个规则(红色部分),当然在这个Makefile中这部分其实是放在下面的。由此我们可以知道删除它自然有生成它的办法。这个也是黄色部分的解答。

        # Configuration targets.  Use these to select a     # configuration for your architecture     %_config:         @( /         CFG=$(@:_config=); /         if [ -f arch/def-configs/$$CFG ]; then /           [ -f .config ] && mv -f .config .config.old; /           cp arch/def-configs/$$CFG .config; /           echo "*** Default configuration for $$CFG installed"; /           echo "*** Next, you may run 'make oldconfig'"; /         else /           echo "$$CFG does not exist"; /         fi; /         )   ----------------------------------------------------------------------这里有些符号我还需要弄清楚。     这部分提供CREAT一个自己的定义的配置文件并成为.config文件,因为在总Makefile中我们已经知道配置方式有config,oldconfig,menuconfig等,其中config将逐个询问配置选项默认值,这样在没有.config时相当于询问了全部选择,而存在.config时就根据它来设置默认值;而oldconfig则是询问.config里面还没有配置的选项,当然它需要一个已经存在的.config文件。menuconfig 是图形化了的询问方式,如果.config存在那么用该文件配置默认值。这些都是跟LINUX内核配置方式一致的。

        所以如果我们需要用基于自己定义的文件来进一步配置,则需要将其替换存在的.config文件,并执行make oldconfig进一步配置。上一次在分析总Makefile的时候,最后也是有一个:

        %: ./arch/def-configs/%        $(MAKE) distclean         cp arch/def-configs/$* ./.config -f         $(MAKE) oldconfig         $(MAKE)

        我觉得原理差不多,这里相当于包括了CREAT了.config并且自动运行了整个Makefile。另外这些配置文件都需要放在arch/def-configs这个地方。

        这样这个Makefile就分析完了,回头想想,主要做了3件事情:完成一些CPU体系相关的选项和定义的追加,定义了运行域并以次CREAT了一个连接脚本,提供了一个用自己的配置文件来生成.config文件的途径。

        可能有一个疑问,到目前只出现了head.o,但是并没有生成它的规则。我想这个我们应该去看arch/s3c2440文件夹的内容(也就是所谓的MACHDIR 或者arch/$(MACHINE)),看看要生成head.o到底需要那些依赖以及操作再下定论。而且因为现在已经把这个目录追加到了SUBDIRS中,这就保证了运行其中的Makefile,并且CORE_FILES 也追加了$(MACHDIR)/$(MACHINE).o(也就是head.o),不过这在vivi的连接过程中好象有点重复,因为里面已经有对HEAD的连接了。 不过这之前先分析一下:config.in和 vivi.lds.in,因为前面的过程依赖于它们。

     

        2、config.in   (怎么自己写这个文件其中的语法还需要学习)

        在分析总Makefile的时候,已经接触了这个配置选项的文件,大概知道里面是对一些配置选项的罗列,提示以及操作。

        打开看一下,提示了Documentation/kbuild/config-language.txt,这应该是LINUX下的参考文件,以后可以按照这个学学习一下config的语法。

        如果按照2440的思路来,一种选择将包括:

                                             ARM system type ------>CONFIG_ARCH_S3C2440

                                             Platform------------------>CONFIG_S3C2440_SMDK

                                             General setup----------->CONFIG_VIVI_ADDR(未选),因为根据前面的Makefile中可知道,这将影响TEXTADDR的最后取值,这里没有选中。

                                             CACHE Enable  没有打开任何一个cache

                                                接下来的都没有选择。。。

        其他还调入了其他一些文件夹下的config.in文件,因为这里只是根据CPU和开发平台选择的一些配置。

        根据交互时选择的配置,生成的信息将存入.config文件。

     

     

        3、vivi.lds.in    (连接脚本的书写格式)

                    SECTIONS {                         . = TEXTADDR;                         .text          : { *(.text) }                         .data ALIGN(4) : { *(.data) }                         .bss ALIGN(4)  : { *(.bss)  *(COMMON) }                                    }

     

              三个段和起始地址,第二行用了通配符*表示所有字符,这里的意思就是说指定的每个目标文件的text section的内容都放到同一个.text中。第三行表示指定的每个目标文件的data section的内容都放到同一个.data中,而且要四字节边界对齐。第四行表示指定的每个目标文件的bss section的内容都放到同一个.bss中,所有的普通符号都放到COMMON中,而且要四字节边界对齐。涉及到的这些段在以前分析linker&loader时已经接触过,可以参考。根据前面的配置信息,在Makefile中产生的TEXTADDR的值将用于此。生成vivi的连接操作就按照这个文件生成的.lds脚本进行。

             因为linux是需要MMU的,所以这将被作为VMA,关于虚拟地址,转换后的虚拟地址,物理地址这些需要去研究MMU和上下文切换

     

     

        4、s3c2440

         现在终于要看这个文件夹了,我们很早已经知道它里面存放着一个关键文件head.S。一上来这里就有讲究:.S。这个知识点是根据编译器而来,因为打开这个文件来看,可以发现它并不只有汇编的语法,还有预处理信息。这样我们就需要用到预处理工具:cpp.查看GNU的as手册:You can use the gnu C compiler driver to get other “CPP” style preprocessing, by giving the input file a ‘.S’ suffix.

     或者查看gcc manue:file.S file.sx Assembler code which must be preprocessed. 等等。这样就很明白了。

        题外话:熟悉GNU工具是非常有挑战但是非常非常重要的,比如在as手册中也简单介绍了ld,详细介绍了Sections and Relocation,这些就是就是后面理解linker&loader的基础。再去看ld手册那么就很清楚了。我想这些对于嵌入式linux开发都是致关重要的。

        不过同样首先打开Makefile看了一下:

                O_TARGET    := s3c2440.o             obj-y :=             ifeq ($(CONFIG_S3C2440_NAND_BOOT),y)             obj-y += nand_read.o             endif             obj-y += mmu.o proc.o             obj-$(CONFIG_S3C2440_SMDK) += smdk.o             ifeq ($(CONFIG_S3C2440_SMDK),y)             obj-$(CONFIG_TEST) += smdk2440_test.o             endif            include $(TOPDIR)/Rules.make

        初看只是定义了两个变量,而第二个变量的追加内容对应的就是该文件夹下的除head.S以外的其他文件的目标文件。但是定义这两个变量到底用在哪,最后一句include给出了解释,看了一下Rules.make,确实在里面有O_TARGET等的规则定义:$(O_TARGET): $(obj-y)。这样看来这些文件一起生成了一个s3c2440.o文件。而这些(obj-y中的.o文件在成为依赖的同时,也将由Rules.make中的相应规则所产生。

        那么s3c2440.o这个文件到底有什么用,继续看Rules.make内容:all_targets: $(O_TARGET) $(L_TARGET),是与另外一个L_TARGET一起成为all_targets的依赖,并且没有命令,定位all_targets:                            first_rule: sub_dirs                                                                                                                                     $(MAKE) all_targets

    是first_rule的执行命令,可见这条命令的唯一作用就是生成$(O_TARGET) $(L_TARGET)这两个文件。具体first_rule是用来什么的,sub_dirs指什么,以后将专门来分析Rules.make时具体分析。因为我认为Rules.make在整个vivi中的角色非常重要。

     

        可见,Rules.make定义了很多通用的规则,用以各种源文件生成目标文件。由此想到我们的总Makefile中也是include了这个文件,并且head.o在总Makefile中已经成为依赖(在上一层的Makefile中实现),加上head.o并未在其他地方定义生成规则,所以可能也是用这种方法生成的,那么来看一下head.S的内容(这个文件将在专门一篇文章中分析):首先include了3个头文件,然后就是一系列汇编指令,因此需要一个预编译和一个编译来实现。要由.S文件编译生成.o文件,可以先由cpp变成.s,再由as变成.o实现;或者直接由gcc来实现。而按照Rules.make中提供的编译选项,两种方式都能实现。这里的一个问题是到底由哪种方式生成,我不是很清楚,因为两种生成方式会相差一个中间结果head.s。这里还可以分析的是include的头文件并不与head.s在同一个目录下,因此这肯定需要在编译预处理的时候指示这些头文件的搜索目录,看到无论哪种编译方式,编译选项里面都有$(AFLAGS),定位到总Makefile中这个便宜选项的定义AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS),再定位到预编译选项CPPFLAGS,顺利发现包含两个搜索目录:CPPFLAGS := -I$(VIVIPATH) -I$(LINUX_INCLUDE_DIR),其中VIVIPATH = $(TOPDIR)/include,这些头文件确实在这个目录下,因此对上了,至于另一个LINUX_INCLUDE_DIR    = /usr/local/arm/2.95.3/include我想应该是预编译类似arm linux类似文件时候用到的搜索路径,这里只编译vivi的文件的话,应该是用不到的,但是我想有一种情况如果是将内核跟vivi一起来编译,可能有用,这个以后再研究。顺便看一下另外一个编译选项-D__ASSEMBLY__:

                        -D Ignored. This option is accepted for script compatibility with calls to other assemblers.                     --defsym sym=value                     Define the symbol sym to be value before assembling the input file. value must be an integer constant. As in C, a leading ‘0x’ indicates a hexadecimal value, and a leading ‘0’ indicates an octal value.我的理解是相当于定义了__ASSEMBLY__,但并不影响编译。而且在调用的头文件linkage.h中可以看到:

                                                                                          #ifdef __ASSEMBLY__                                                                                        #define ALIGN __ALIGN                                                                                        #define ALIGN_STR __ALIGN_STR                                                                                         #define ENTRY(name) /                                                                                         .globl SYMBOL_NAME(name); /                                                                                          ALIGN; /                                                                                          SYMBOL_NAME_LABEL(name)                                                                                           #endif                    

    我想这应该就是这个定义的作用,这些在专门分析head.S的时候来具体分析。分析到此:head.o由Rules.make的规则编译生成。这样所有arch文件夹里的文件对应生成的.o文件,都由Rules.make实现。

     

        5、def-configs

        这个文件夹也不陌生了。

        a、总Makefile分析的时候,知道了它存放着默认的配置文件,打开可以看到一个smdk2440文件,如果这个文件的配置信息可以满足需要,那么通过make smdk2440自动完成vivi的生成。回顾一下:

                           %: ./arch/def-configs/%                              $(MAKE) distclean                              cp arch/def-configs/$* ./.config -f                              $(MAKE) oldconfig                              $(MAKE)

        b、在前面分析arch文件夹中的Makefile中也有一段使用自己定义的配置信息文件做母板,通过make %_config加上make oldconfig来CREAT新的.config文件的定义,这个母板也需要存放在这里,这个前面已经分析过。

        又一次提到.config文件,想到另外一个内容相同的文件include/autoconfig.h文件。但是我认为这两个的作用又有点区别。因为正好在head.S文件中通过逐层的包含关系涉及到了这个文件,所以干脆在那个时候去分析了。

     

        到这里为止,已经把arch文件夹的结构理了一遍,接下来就详细分析里面几个重要文件的内容了。

     

     

    最新回复(0)