
HNU-操作系统实验Lab1-2022级
操作系统实验一环境配置
实验目的
- 配置实验所需的工程环境
- 完成基本程序的搭建
- 创建工程文件,为后续实验做准备
实验内容
安装工具链
-
交叉编译工具链
选用ARM官网的aarch64 11.2版本,并将目录加入到环境变量PATH中。用:
aarch64-none-elf-gcc --version
测试是否安装成功:
交叉编译: 在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,我们就称这种编译器支持交叉编译。这个编译过程就叫交叉编译。简单地说,就是在一个平台上生成另一个平台上的可执行代码。要进行交叉编译,我们需要在主机平台上安装对应的交叉编译工具链,然后用这个交叉编译工具链编译我们的源代码,最终生成可在目标平台上运行的代码。在本实验中,我们利用arm-linux-gcc编译器,可编译出针对Linux ARM平台的可执行代码。
-
QEMU模拟器
使用:
sudo apt-get update sudo apt-get install qemu sudo apt-get install qemu-system
安装qemu模拟器,QEMU(Quick EMUlator)是一款功能强大的开源模拟器和虚拟化软件,它能够模拟各种处理器架构,包括x86、ARM、MIPS等。QEMU不仅可以在各种平台上模拟不同的处理器架构,还支持虚拟化技术,使得用户可以在虚拟机中运行操作系统。
通过:
qemu-system-x86_64 --version
检测是否安装成功:
-
CMake
使用:
sudo apt-get install cmake
安装CMake,CMake是一个跨平台的编译(Build)工具,可以用简单的语句来描述所有平台的编译过程。CMake能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
通过:
cmake --version
检测是否安装成功:
创建裸机(Bare Metal)程序
由于我们的目标是编写一个操作系统,所以我们需要创建一个独立于操作系统的可执行程序,又称 独立式可执行程序(freestanding executable) 或 裸机程序(bare-metal executable) 。这意味着所有依赖于操作系统的库我们都不能使用。比如 std 中的大部分内容(io, thread, file system, etc.)都需要操作系统的支持,所以这部分内容我们不能使用。
但是,不依赖与操作系统的语言特性还是可以继续使用的。
创建项目
参照UniProton设计项目的目录层次,但为了理解方便,将会进行相应简化。其初始目录结构如下图所示:
- src :放置包括main.c在内的源代码
- bsp:包括 CMakeLists.txt 和两个汇编文件 start.S 和 prt_reset_vector.S。
- include:有 prt_typedef.h 头文件,它是 UniProton 所使用的基本数据类型和结构的定义,如 U8、U16、U32、U64等。
下面是各文件的作用:
-
main.c
#include "prt_typedef.h" #define UART_REG_WRITE(value, addr) (*(volatile U32 *)((uintptr_t)addr) = (U32)value) S32 main(void) { char out_str[] = "AArch64 Bare Metal"; int length = sizeof(out_str) / sizeof(out_str[0]); // 逐个输出字符 for (int i = 0; i < length - 1; i++) { UART_REG_WRITE(out_str[i], 0x9000000); } }
main 函数的主要功能(L12-L13)是把 out_str 中的字符通过宏 UART_REG_WRITE 逐个写入地址为 0x9000000 的地方。
-
prt_typedef.h
UniProton 所使用的基本数据类型和结构的定义,如 U8、U16、U32、U64等。
-
start.s
.global OsEnterMain .extern __os_sys_sp_end .type start, function .section .text.bspinit, "ax" .balign 4 .global OsElxState .type OsElxState, @function OsElxState: MRS x6, CurrentEL // 把系统寄存器 CurrentEL 的值读入到通用寄存器 x6 中 MOV x2, #0x4 // CurrentEL EL1: bits [3:2] = 0b01 CMP w6, w2 BEQ Start // 若 CurrentEl 为 EL1 级别,跳转到 Start 处执行,否则死循环。 OsEl2Entry: B OsEl2Entry Start: LDR x1, =__os_sys_sp_end // 符号在ld文件中定义 BIC sp, x1, #0xf // 设置栈指针 B OsEnterMain OsEnterReset: B OsEnterReset
用于操作系统的启动代码,主要功能是初始化一些系统寄存器和设置栈指针,然后跳转到
OsEnterMain
函数执行。 -
prt_reset_vector.S
DAIF_MASK = 0x1C0 // disable SError Abort, IRQ, FIQ .global OsVectorTable .global OsEnterMain .section .text.startup, "ax" OsEnterMain: BL main MOV x2, DAIF_MASK // bits [9:6] disable SError Abort, IRQ, FIQ MSR DAIF, x2 // 把通用寄存器 x2 的值写入系统寄存器 DAIF 中 EXITLOOP: B EXITLOOP
它的作用是启动操作系统,总的来说,这段汇编代码的功能是调用
main
函数,然后设置DAIF
寄存器以屏蔽SError Abort、IRQ和FIQ(因为中断处理尚未设置),最后进入一个无限循环。 -
aarch64-qemu.ld(仅展示部分)
ENTRY(__text_start) _stack_size = 0x10000; _heap_size = 0x10000; MEMORY { IMU_SRAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x800000 /* 内存区域 */ MMU_MEM (rwx) : ORIGIN = 0x40800000, LENGTH = 0x800000 /* 内存区域 */ } SECTIONS { text_start = .; .start_bspinit : { __text_start = .; /* __text_start 指向当前位置, "." 表示当前位置 */ KEEP(*(.text.bspinit)) } > IMU_SRAM ... ... ... .heap (NOLOAD) : { . = ALIGN(8); PROVIDE (__HEAP_INIT = .); . = . + _heap_size; /* 堆空间 */ . = ALIGN(8); PROVIDE (__HEAP_END = .); } > IMU_SRAM .stack (NOLOAD) : { . = ALIGN(8); PROVIDE (__os_sys_sp_start = .); . = . + _stack_size; /* 栈空间 */ . = ALIGN(8); PROVIDE (__os_sys_sp_end = .); } > IMU_SRAM end = .; ... ... ... }
该文件为连接脚本,链接脚本主要的工作就是告诉链接器如何将各个目标文件、库中的段进行组织,生成输出文件,该输出文件可以是动态库、可执行文件,大部分的链接脚本都只做这个事,因此链接并不算是一个很难理解的过程。
通常每个输入文件中都存在多个段,每个段的描述信息被单独保存在段表中,而段的信息单独保存,链接器通过分析段表中的内容,获取段相应的信息,比如段的读写属性、size、文件内偏移。
链接过程之后生成可加载的输出文件,通常是可执行文件,对于输出文件而言,内容是以 segment 的形式进行组织的,组织的依据是根据各个段的读写属性来确定的,对于代码部分通常是 读+执行 的权限,对于数据通常是 读+写 的属性,相同属性的段被组织在一起,最终在执行时被加载到内存中。
在给出的部分中,
-
ENTRY(__text_start)
: 定义程序的入口点为__text_start
, -
MEMORY
: 定义了程序的内存布局,比如IMU_SRAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x800000
: 定义了一个名为IMU_SRAM
的内存区域,起始地址为0x40000000
,长度为0x800000
字节。 -
SECTIONS
: 定义了不同段(sections)在内存中的放置方式,比如start_bspinit
: 开始了一个名为.start_bspinit
的段;heap (NOLOAD)
: 定义了一个heap
段,但不会加载到内存中;stack (NOLOAD)
: 定义了一个stack
段,同样不会加载到内存中。
这个链接脚本的作用是定义了程序在内存中的布局,指导链接器将不同的段放置到不同的内存区域,并定义了程序的入口点和一些符号,以便在程序中引用。
-
工程构建
-
src下的CMakeList.txt
cmake_minimum_required(VERSION 3.12) set(CMAKE_SYSTEM_NAME "Generic") # 目标系统(baremental): cmake/tool_chain/uniproton_tool_chain_gcc_arm64.cmake 写的是Linux set(CMAKE_SYSTEM_PROCESSOR "aarch64") # 目标系统CPU set(TOOLCHAIN_PATH "/home/aarch64-none-elf") # 修改为交叉工具链实际所在目录 build.py config.xml中定义 set(CMAKE_C_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-none-elf-gcc) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-none-elf-g++) set(CMAKE_ASM_COMPILER ${TOOLCHAIN_PATH}/bin/aarch64-none-elf-gcc) set(CMAKE_LINKER ${TOOLCHAIN_PATH}/bin/aarch64-none-elf-ld) # 定义编译和链接等选项 set(CC_OPTION "-Os -Wformat-signedness -Wl,--build-id=none -fno-PIE -fno-PIE --specs=nosys.specs -fno-builtin -fno-dwarf2-cfi-asm -fomit-frame-pointer -fzero-initialized-in-bss -fdollars-in-identifiers -ffunction-sections -fdata-sections -fno-aggressive-loop-optimizations -fno-optimize-strlen -fno-schedule-insns -fno-inline-small-functions -fno-inline-functions-called-once -fno-strict-aliasing -finline-limit=20 -mlittle-endian -nostartfiles -funwind-tables") set(AS_OPTION "-Os -Wformat-signedness -Wl,--build-id=none -fno-PIE -fno-PIE --specs=nosys.specs -fno-builtin -fno-dwarf2-cfi-asm -fomit-frame-pointer -fzero-initialized-in-bss -fdollars-in-identifiers -ffunction-sections -fdata-sections -fno-aggressive-loop-optimizations -fno-optimize-strlen -fno-schedule-insns -fno-inline-small-functions -fno-inline-functions-called-once -fno-strict-aliasing -finline-limit=20 -mlittle-endian -nostartfiles -funwind-tables") set(LD_OPTION " ") set(CMAKE_C_FLAGS "${CC_OPTION} ") set(CMAKE_ASM_FLAGS "${AS_OPTION} ") set(CMAKE_LINK_FLAGS "${LD_OPTION} -T ${CMAKE_CURRENT_SOURCE_DIR}/aarch64-qemu.ld") # 指定链接脚本 set(CMAKE_EXE_LINKER_FLAGS "${LD_OPTION} -T ${CMAKE_CURRENT_SOURCE_DIR}/aarch64-qemu.ld") # 指定链接脚本 set (CMAKE_C_LINK_FLAGS " ") set (CMAKE_CXX_LINK_FLAGS " ") set(HOME_PATH ${CMAKE_CURRENT_SOURCE_DIR}) set(APP "miniEuler") # APP变量,后面会用到 ${APP} project(${APP} LANGUAGES C ASM) # 工程名及所用语言 set(CMAKE_BUILD_TYPE Debug) # 生成 Debug 版本 include_directories( # include 目录 ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/bsp ) add_subdirectory(bsp) # 包含子文件夹的内容 list(APPEND OBJS $<TARGET_OBJECTS:bsp>) add_executable(${APP} main.c ${OBJS}) # 可执行文件
这段 CMakeLists.txt 文件用于配置 CMake 项目的构建过程,它描述了项目的目标系统、工具链路径、编译选项、链接选项以及源代码组织等内容。比如:
-
set(CMAKE_SYSTEM_NAME "Generic")
: 设置了目标系统为 “Generic”,表示目标系统为裸机环境而非特定的操作系统; -
set(TOOLCHAIN_PATH "/home/aarch64-none-elf")
: 设置了交叉编译工具链的安装路径; -
CC_OPTION
定义了 C 编译选项,AS_OPTION
定义了汇编编译选项,这两个定义包含了一系列的编译选项,例如优化级别、警告级别、调试信息等。这些选项会在编译过程中传递给相应的编译器。 -
LD_OPTION
定义了链接器选项,在这个文件中,LD_OPTION
被设置为空,即没有额外的链接选项。然而,实际项目中可能会根据需要添加一些链接选项,例如链接脚本、库路径等。 -
include_directories()
: 这个函数用于指定包含目录,即告诉编译器在哪里搜索头文件。 -
add_subdirectory(bsp)
: 这个函数用于包含子目录。在这个文件中,将bsp
子目录包含进来,该子目录中包含了 BSP相关的代码。 -
add_executable(${APP} main.c ${OBJS})
: 这个函数用于定义可执行文件。在这个文件中,使用了add_executable()
函数定义了一个名为${APP}
的可执行文件,该可执行文件由main.c
和OBJS
(BSP 相关的目标文件)组成。
-
-
src/bsp下的CMakeLists.txt
set(SRCS start.S prt_reset_vector.S ) add_library(bsp OBJECT ${SRCS}) # OBJECT类型只编译生成.o目标文件,但不实际链接成库
这段代码的作用是将
start.S
和prt_reset_vector.S
这两个汇编源文件编译成目标文件(.o
文件),但不将它们链接成一个库文件。
编译运行
- 编译
首先在项目目录 lab1 下创建 build 目录用于编译生成,然后进入 build 目录执行:
cmake ../src
cmake --build .
- 运行
在项目目录 lab1 下执行:
qemu-system-aarch64 -machine virt -m 1024M -cpu cortex-a53 -nographic -kernel build/miniEuler -s
调试支持
GDB简单调试方法
通过QEMU运行程序并启动调试服务器,默认端口1234:
qemu-system-aarch64 -machine virt,gic-version=2 -m 1024M -cpu cortex-a53 -nographic -kernel build/miniEuler -s -S
启动调试客户端:
aarch64-none-elf-gdb build/miniEuler
注意此时要同时打开两个终端!
设置调试参数,开始调试:
(gdb) target remote localhost:1234
(gdb) disassemble
(gdb) n
将调试集成到vscode
打开 main.c 文件,点击 vscode左侧的运行和调试按钮,弹出对话框选择创建 launch.json文件,增加如下配置:
在左边面板顶部选择刚添加的 aarch64-gdb 选项,点击旁边的绿色 开始调试(F5) 按钮开始调试:
在调试时,可以在调试控制台执行gdb命令,如:
自动化脚本
之后编译及运行程序只需要执行:
sh makeMiniEuler.sh
sh runMiniEuler.sh
作业
-
商业操作系统都有复杂的构建系统,试简要分析 UniProton 的构建系统。(提示:UniProton 通过在根目录下执行 python build.py m4 (m4是指目标平台,还有如hi3093等)进行构建,所以构建系统的分析可从 build.py 入手进行。)
在网上找到了build.py文件,接下来进行分段分析:
import os import sys import time import shutil import subprocess import platform from sys import argv UniProton_home = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, "%s/cmake/common/build_auxiliary_script"%UniProton_home) from make_buildef import make_buildef sys.path.insert(0, "%s/build/uniproton_ci_lib"%UniProton_home) import globle from logs import BuilderNolog, log_msg from get_config_info import *
这段是导入模块,脚本开始时导入了一些标准库和自定义的模块,包括os、sys、time等标准库,例如:
import os
: 导入了名为os
的模块,用于与操作系统进行交互。import time
: 导入了名为time
的模块,用于处理时间相关的功能。from make_buildef import make_buildef
: 导入了自定义的make_buildef
函数,这个函数可能用于构建相关的定义文件。
class Compile:
# 根据makechoice获取config的配置的环境,compile_mode, lib_type, def get_config(self, cpu_type, cpu_plat): self.compile_mode = get_compile_mode() self.lib_type, self.plam_type, self.hcc_path, self.kconf_dir, self.system, self.core = get_cpu_info(cpu_type, cpu_plat, self.build_machine_platform) if not self.compile_mode and self.lib_type and self.plam_type and self.hcc_path and self.kconf_dir: log_msg('error', 'load config.xml env error') sys.exit(0) self.config_file_path = '%s/build/uniproton_config/config_%s'%(self.home_path, self.kconf_dir) self.objcopy_path = self.hcc_path def setCmdEnv(self): self.build_time_tag = time.strftime('%Y-%m-%d_%H:%M:00') self.log_dir = '%s/logs/%s' % (self.build_dir, self.cpu_type) self.log_file = '%s.log' % self.kconf_dir def SetCMakeEnviron(self): os.environ["CPU_TYPE"] = self.cpu_type os.environ["PLAM_TYPE"] = self.plam_type os.environ["LIB_TYPE"] = self.lib_type os.environ["COMPILE_OPTION"] = self.compile_option os.environ["HCC_PATH"] = self.hcc_path os.environ["UNIPROTON_PACKING_PATH"] = self.UniProton_packing_path os.environ["CONFIG_FILE_PATH"] = self.config_file_path os.environ["LIB_RUN_TYPE"] = self.lib_run_type os.environ["HOME_PATH"] = self.home_path os.environ["COMPILE_MODE"] = self.compile_mode os.environ["BUILD_MACHINE_PLATFORM"] = self.build_machine_platform os.environ["SYSTEM"] = self.system os.environ["CORE"] = self.core os.environ["OBJCOPY_PATH"] = self.objcopy_path os.environ['PATH'] = '%s:%s' % (self.hcc_path, os.getenv('PATH')) # 环境准备,准备执行cmake,make,makebuildfile,CmakeList需要的环境 # 每次compile之前请调用该函数 def prepare_env(self, cpu_type, choice): # makebuildfile需要的环境kconf_dir # cmake需要的环境cmake_env_path,home_path(cmakelist所在的路径),home_path, # make cmd拼接需要的环境:home_path,UniProton_make_jx,log_dir,log_file,build_time_tag, UniProton_make_jx #根据cpu_type, choice从config文件中获取并初始化初始化hcc_path, plam_type, kconf_dir #根据输入分支获取 #从编译镜像环境获取 self.get_config(cpu_type, choice) self.setCmdEnv() self.SetCMakeEnviron() #获取编译环境是arm64还是x86,用户不感知,并将其写入环境变量。 def getOsPlatform(self): self.cmake_env_path = get_tool_info('cmake', 'tool_path') if platform.uname()[-1] == 'aarch64': self.build_machine_platform = 'arm64' else: self.build_machine_platform = 'x86' # 获取当前编译的路径信息,配置文件信息,编译选项信息 def __init__(self, cpu_type: str, make_option="normal", lib_run_type="FPGA", choice="ALL", make_phase="ALL", UniProton_packing_path=""): # 当前路径信息 self.system = "" self.objcopy_path = "" self.builder = None self.compile_mode = "" self.core = "" self.plam_type = "" self.kconf_dir = "" self.build_tmp_dir = "" self.log_dir = "" self.lib_type = "" self.hcc_path = "" self.log_file = "" self.config_file_path = "" self.build_time_tag = "" self.build_dir = globle.build_dir self.home_path = globle.home_path self.kbuild_path = globle.kbuild_path # 当前选项信息 self.cpu_type = cpu_type self.compile_option = make_option self.lib_run_type = lib_run_type self.make_choice = choice.lower() self.make_phase = make_phase self.UniProton_packing_path = UniProton_packing_path if make_phase == "CREATE_CMAKE_FILE" else '%s/output'%self.home_path self.UniProton_binary_dir = os.getenv('RPROTON_BINARY_DIR') self.UniProton_install_file_option = os.getenv('RPROTON_INSTALL_FILE_OPTION') self.UniProton_make_jx = 'VERBOSE=1' if self.UniProton_install_file_option == 'SUPER_BUILD' else 'VERBOSE=1 -j' + str(os.cpu_count()) # 当前编译平台信息 self.getOsPlatform()
这一段定义了一个名为 Compile
的类,该类封装了一系列与编译相关的方法。这些方法包括获取配置信息、设置环境变量、准备环境、判断编译平台等。这些方法在后续的构建过程中会被调用,用于配置和准备构建所需的环境和参数。比如:
setCmdEnv(self)
函数的作用是设置编译过程中所需的环境变量。SetCMakeEnviron(self)
函数的作用是设置与 CMake 相关的环境变量。__init__
函数用于初始化Compile
类的实例对象,在创建对象时设置了对象的一些初始状态,以便后续的编译过程中使用。
#调用cmake生成Makefile文件,需要
def CMake(self):
if self.UniProton_binary_dir:
self.build_tmp_dir = '%s/output/tmp/%s' % (self.UniProton_binary_dir, self.kconf_dir)
else:
self.build_tmp_dir = '%s/output/tmp/%s' % (self.build_dir, self.kconf_dir)
os.environ['BUILD_TMP_DIR'] = self.build_tmp_dir
if not os.path.exists(self.build_tmp_dir):
os.makedirs(self.build_tmp_dir)
if not os.path.exists(self.log_dir):
os.makedirs(self.log_dir)
log_msg('info', 'BUILD_TIME_TAG %s' % self.build_time_tag)
self.builder = BuilderNolog(os.path.join(self.log_dir, self.log_file))
if self.make_phase in ['CREATE_CMAKE_FILE', 'ALL']:
real_path = os.path.realpath(self.build_tmp_dir)
if os.path.exists(real_path):
shutil.rmtree(real_path)
os.makedirs(self.build_tmp_dir)
#拼接cmake命令
if self.compile_option == 'fortify':
cmd = '%s/cmake %s -DCMAKE_TOOLCHAIN_FILE=%s/cmake/tool_chain/uniproton_tool_chain.cmake ' \
'-DCMAKE_C_COMPILER_LAUNCHER="sourceanalyzer;-b;%sproject" ' \
'-DCMAKE_INSTALL_PREFIX=%s &> %s/%s' % (
self.cmake_env_path, self.home_path, self.home_path, self.cpu_type,
self.UniProton_packing_path, self.log_dir, self.log_file)
elif self.compile_option == 'hllt':
cmd = '%s/cmake %s -DCMAKE_TOOLCHAIN_FILE=%s/cmake/tool_chain/uniproton_tool_chain.cmake ' \
'-DCMAKE_C_COMPILER_LAUNCHER="lltwrapper" -DCMAKE_INSTALL_PREFIX=%s &> %s/%s' % (
self.cmake_env_path, self.home_path, self.home_path, self.UniProton_packing_path, self.log_dir, self.log_file)
else:
cmd = '%s/cmake %s -DCMAKE_TOOLCHAIN_FILE=%s/cmake/tool_chain/uniproton_tool_chain.cmake ' \
'-DCMAKE_INSTALL_PREFIX=%s &> %s/%s' % (
self.cmake_env_path, self.home_path, self.home_path, self.UniProton_packing_path, self.log_dir, self.log_file)
#执行cmake命令
if self.builder.run(cmd, cwd=self.build_tmp_dir, env=None):
log_msg('error', 'generate makefile failed!')
return False
log_msg('info', 'generate makefile succeed.')
return True
def UniProton_clean(self):
for foldername,subfoldernames,filenames in os.walk(self.build_dir):
for subfoldername in subfoldernames:
if subfoldername in ['logs','output','__pycache__']:
folder_path = os.path.join(foldername,subfoldername)
shutil.rmtree(os.path.relpath(folder_path))
for filename in filenames:
if filename == 'prt_buildef.h':
file_dir = os.path.join(foldername,filename)
os.remove(os.path.relpath(file_dir))
if os.path.exists('%s/cmake/common/build_auxiliary_script/__pycache__'%self.home_path):
shutil.rmtree('%s/cmake/common/build_auxiliary_script/__pycache__'%self.home_path)
if os.path.exists('%s/output'%self.home_path):
shutil.rmtree('%s/output'%self.home_path)
if os.path.exists('%s/tools/SRE/x86-win32/sp_makepatch/makepatch'%self.home_path):
os.remove('%s/tools/SRE/x86-win32/sp_makepatch/makepatch'%self.home_path)
if os.path.exists('%s/build/prepare/__pycache__'%self.home_path):
shutil.rmtree('%s/build/prepare/__pycache__'%self.home_path)
return True
def make(self):
if self.make_phase in ['EXECUTING_MAKE', 'ALL']:
self.builder.run('make clean', cwd=self.build_tmp_dir, env=None)
tmp = sys.argv
if self.builder.run(
'make all %s &>> %s/%s' % (
self.UniProton_make_jx, self.log_dir, self.log_file), cwd=self.build_tmp_dir, env=None):
log_msg('error', 'make %s %s failed!' % (self.cpu_type, self.plam_type))
return False
sys.argv = tmp
if self.compile_option in ['normal', 'coverity', 'single']:
if self.builder.run('make install %s &>> %s/%s' % (self.UniProton_make_jx, self.log_dir, self.log_file), cwd=self.build_tmp_dir, env=None):
log_msg('error', 'make install failed!')
return False
if os.path.exists('%s/%s' % (self.log_dir, self.log_file)):
self.builder.log_format()
log_msg('info', 'make %s %s succeed.' % (self.cpu_type, self.plam_type))
return True
def SdkCompaile(self)->bool:
# 判断该环境中是否需要编译
if self.hcc_path == 'None':
return True
self.MakeBuildef()
if self.CMake() and self.make():
log_msg('info', 'make %s %s lib succeed!' % (self.cpu_type, self.make_choice))
return True
else:
log_msg('info', 'make %s %s lib failed!' % (self.cpu_type, self.make_choice))
return False
# 对外函数,调用后根据类初始化时的值进行编译
def UniProtonCompile(self):
#清除UniProton缓存
if self.cpu_type == 'clean':
log_msg('info', 'UniProton clean')
return self.UniProton_clean()
# 根据cpu的编译平台配置相应的编译环境。
if self.make_choice == "all":
for make_choice in globle.cpu_plat[self.cpu_type]:
self.prepare_env(self.cpu_type, make_choice)
if not self.SdkCompaile():
return False
else:
self.prepare_env(self.cpu_type, self.make_choice)
if not self.SdkCompaile():
return False
return True
def MakeBuildef(self):
if not make_buildef(globle.home_path,self.kconf_dir,"CREATE"):
sys.exit(1)
log_msg('info', 'make_buildef_file.sh %s successfully.' % self.kconf_dir)
这一段定义了一些编译相关的方法,包括执行 CMake、清理构建环境、执行 Make、编译 UniProton、生成构建定义文件等。这些方法会被 Compile
类的实例对象调用,用于执行实际的编译任务。比如:
CMake(self)
方法的作用是启动 CMake 工具来生成项目的构建文件,为后续的编译过程做准备。UniProton_clean(self)
方法的作用是清理 UniProton 项目的构建环境,即删除之前生成的临时文件、日志文件和构建产物,以便进行全新的编译。MakeBuildef(self)
方法的作用是生成构建定义文件(builddef 文件)。
# argv[1]: cpu_plat 表示要编译的平台:
# argv[2]: compile_option 控制编译选项,调用不同的cmake参数,目前只有normal coverity hllt fortify single(是否编译安全c,组件化独立构建需求)
# argv[3]: lib_run_type lib库要跑的平台 faga sim等
# argv[4]: make_choice
# argv[5]: make_phase 全量构建选项
# argv[6]: UniProton_packing_path lib库的安装位置
if __name__ == "__main__":
default_para = ("all", "normal", "FPGA", "ALL", "ALL", "")
if len(argv) == 1:
para = [default_para[i] for i in range(0, len(default_para))]
else:
para = [argv[i+1] if i < len(argv) - 1 else default_para[i] for i in range(0,len(default_para))]
cur_cpu_type = para[0].lower()
cur_compile_option = para[1].lower()
cur_lib_run_type = para[2]
cur_make_choice = para[3]
cur_make_phase = para[4]
cur_UniProton_packing_path = para[5]
for plat in globle.cpus_[cur_cpu_type]:
UniProton_build = Compile(plat, cur_compile_option, cur_lib_run_type, cur_make_choice, cur_make_phase, cur_UniProton_packing_path)
if not UniProton_build.UniProtonCompile():
sys.exit(1)
sys.exit(0)
这一段是程序的入口,它根据命令行参数调用 Compile
类进行编译。根据命令行参数解析出编译的相关参数,然后根据这些参数创建 Compile
类的实例对象,并调用 UniProtonCompile
方法进行编译。
- 学习如何调试项目。
以.c文件为例。
-
向编译器(如gcc 或 g++) 添加-g选项来实现,生成带有调试信息的可执行文件(gcc -g program.c -o program)
-
启动gdb (gdb program)
-
设置断点,当执行运行到这时,gdb会暂停执行(break main)
-
使用r命令运行程序,程序会在你设置的第一个断点处暂停
-
单步执行: 使用n命令执行下一行代码
-
x指令可以用来查看内存的值,p指令可以用来查看变量的值,bt指令用来查看堆栈信息
-
一些其他命令
- 运行命令
- 断点
- 运行信息
-
使用q指令退出调试
在vscode上调试:
- 查看指定地址的内存内容。在调试控制台执行 -exec x/20xw 0x40000000 即可,其中 x表示查看命令,20表示查看数量,x表示格式,可选格式包括 Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),t(binary), f(float), a(address), i(instruction), c(char) and s(string).Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).,最后的 w表示字宽,b表示单字节,h表示双字节,w表示四字节,g表示八字节。还可以是指令:-exec x/20i 0x40000000; 字符串:-exec x/20s 0x40000000
- 显示所有寄存器。-exec info all-registers
- 查看寄存器内容。-exec p/x $pc
- 修改寄存器内容。-exec set $x24 = 0x5
更多推荐
所有评论(0)