PMON启动流程分析
B站有讯为电子的详细介绍视频,连接如下,十分推荐第一次学的看一下https://www.bilibili.com/video/BV13M4y1P7H8?p=7&vd_source=7758aca909f593c8652fba5b3a94211dhttps://www.bilibili.com/video/BV13M4y1P7H8?p=7&vd_source=7758aca909f593c8652fba5b3a94211d
1.找到链接脚本,链接脚本决定了入口在哪
2.分析编译命令:
make cfg all tgt=rom ARCH=mips CROSS_COMPILE=mipsel-linux- DEBUG=-g
第一部分:ARCH=mips CROSS_COMPILE=mipsel-linux- DEBUG=-g
ARCH=mips 设置了平台是mips平台
CROSS_COMPILE=mipsel-linux- 设置交叉编译器前缀,在编译时会替换成mipsel-linux-,之所以设置环境变量,一个是方便在这儿不用输入绝对路径,当找不到交叉编译器的时候,需要输入交叉编译器的绝对路径
DEBUG=-g 设置调试信息
第二部分:make cfg all tgt=rom
make cfg 是配置
make all
make tgt=rom 在Makefile.inc里可以看到,是生成的二进制文件
3.在pmon-loongson3/zloader.ls2k文件夹下找到Makefile文件
Makefile.ls2k:
TARGET=LS2K
TARGETEL=ls2k
export START=start.o
MEMSIZE=128
ZLOADER_OPTIONS=-mips3
include Makefile.inc
这些变量决定了在哪个路径下去编译
4.在pmon-loongson3/zloader.ls2k文件夹下找到Makefile.inc文件
Makefile.inc
ejtag_rom ejtag_rom1 ejtag_ram rom: clean ${START} zloader.o
gcc -DSTARTADDR=${GZROMSTARTADDR} -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S > ld.scri
${LD} -T ld.script -e start -o gzrom ${START} zloader.o
${CROSS_COMPILE}objcopy -O binary gzrom gzrom.bin
ejtag_rom ejtag_rom1 ejtag_ram rom: clean ${START} zloader.o
gcc -DSTARTADDR=${GZROMSTARTADDR} -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S > ld.scri ${LD} -T ld.script -e start -o gzrom ${START} zloader.o
${CROSS_COMPILE}objcopy -O binary gzrom gzrom.bin
以上带颜色的部分都是定义了的,红色的在Makefile.ls2k里定义了,黄色的在输入指令的时候定义了,绿色的也是编译器的前缀
下面将带颜色的部分,替换成定义的部分,更好理解这部分代码的意思
ejtag_rom ejtag_rom1 ejtag_ram rom: clean start.ozloader.o
gcc -DSTARTADDR=${GZROMSTARTADDR} -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S > ld.scri
mipsel-linux-ld -T ld.script -e start -o gzrom start.o zloader.o
mipsel-linux-objcopy -O binary gzrom gzrom.bin
ejtag_rom ejtag_rom1 ejtag_ram rom: clean start.ozloader.o
gcc -DSTARTADDR=${GZROMSTARTADDR} -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S > ld.scri //这一段代码就是为了将ld.script.s编译生成ld.script,而这个ld.script就是链接脚本
mipsel-linux-ld -T ld.script -e start -o gzrom start.o zloader.o
mipsel-linux-objcopy -O binary gzrom gzrom.bin
${START}:
rm -f ../Targets/${TARGET}/compile/${TARGETEL}/${START}
gcc -DSTARTADDR=${ROMSTARTADDR} -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S > ../Targets/${TARGET}/conf/ld.script
make -C ../Targets/${TARGET}/compile/${TARGETEL}/
cp ../Targets/${TARGET}/compile/${TARGETEL}/${START} .
${START}:
rm -f ../Targets/${TARGET}/compile/${TARGETEL}/${START}
gcc -DSTARTADDR=${ROMSTARTADDR} -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S > ../Targets/${TARGET}/conf/ld.script
make -C ../Targets/${TARGET}/compile/${TARGETEL}/
cp ../Targets/${TARGET}/compile/${TARGETEL}/${START} .
带颜色的部分,都可以在Makefile.ls2k中找到对应的定义
将定义带入:
${START}:
rm -f ../Targets/LS2K/compile/LS2K/start.o
gcc -DSTARTADDR=${ROMSTARTADDR} -DOUT_FORMAT=\"${OUT_FORMAT}\" -DOUT_ARCH=mips -Umips -E -P ld.script.S > ../Targets/LS2K/conf/ld.script
make -C ../Targets/LS2K/compile/LS2K/ //使用 -C 标识会进入这个目录去执行相应的make
cp ../Targets/LS2K/compile/LS2K/start.o. //执行完后,就将这个目录下的start.o复制过去了
链接脚本:
实际上是一个文本文件,由若干个命令组成,以分号隔开,这些命令就是命令链接器(mipsel-linux-ld)来控制链接国产
链接脚本的作用:
我们需要分析一下这行代码:
mipsel-linux-ld -T ld.script -e start -o gzrom start.o zloader.o
第一部分:输入文件 start.o zloader.o
第二部分:输出文件 gzrom
第三部分:链接脚本 ld.script
可以加将这行代码比喻成吃东西消化的国产,输入文件就是吃进去的东西,链接脚本就是消化过程,输出文件就是拉出去的东西
我们在看看链接脚本的代码ld.script:
在此,将在ENTRY函数找到PMON入口_start
OUTPUT_FORMAT("elf32-tradlittlemips", "elf32-tradbigmips",
"elf32-tradlittlemips")//说明输出二进制的格式
OUTPUT_ARCH(mips)//说明输出二进制文件的平台
ENTRY(_start)//ENTRY函数的作用就是将括号中的符号值设置成入口地址,所以我们找到了PMON入口
SECTIONS
{
. = 0xffffffff8f900000;//.可以理解成指针,指向一个地址,
//这个值是在Makefile.inc里决定的,GZROMSTARTADDR
.text : //是将所有的下面*.文件合并成.text文件
{
_ftext = . ;
*(.text)
*(.rodata)
*(.rodata1)
*(.reginfo)
*(.init)
*(.stub)
*(.gnu.warning)
} =0
_etext = .;
PROVIDE (etext = .);
.fini : { *(.fini) } =0
.data :
{
_fdata = . ;
*(.data)
. = ALIGN(32);
*(.data.align32)
. = ALIGN(64);
*(.data.align64)
. = ALIGN(128);
*(.data.align128)
. = ALIGN(4096);
*(.data.align4096)
CONSTRUCTORS
}
.data1 : { *(.data1) }
.ctors :
{
__CTOR_LIST__ = .;
LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2)
*(.ctors)
LONG(0)
__CTOR_END__ = .;
}
.dtors :
{
__DTOR_LIST__ = .;
LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2)
*(.dtors)
LONG(0)
__DTOR_END__ = .;
}
_gp = ALIGN(16) + 0x7ff0;
.got :
{
*(.got.plt) *(.got)
}
.sdata : { *(.sdata) }
.lit8 : { *(.lit8) }
.lit4 : { *(.lit4) }
_edata = .;
PROVIDE (edata = .);
__bss_start = .;
_fbss = .;
.sbss : { *(.sbss) *(.scommon) }
.bss :
{
*(.dynbss)
*(.bss)
. = ALIGN(32);
*(.bss.align32)
. = ALIGN(64);
*(.bss.align64)
. = ALIGN(128);
*(.bss.align128)
. = ALIGN(4096);
*(.bss.align4096)
*(COMMON)
}
_end = . ;
PROVIDE (end = .);
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.debug 0 : { *(.debug) }
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
.debug_sfnames 0 : { *(.debug_sfnames) }
.line 0 : { *(.line) }
.gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
.gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
}
5._start的定义
最后在编译pmon的时候,会将start.o编译成start.s文件,可以将PMON编译信息存到一个文件中,查看编译信息,在这里就不展示了
最后在pmon-loongson3/Target/LS2K/ls2k文件夹下,可以看到生成的start.s文件内对_start的定义