赞
踩
写在前面:
从2024年开始,HNU信息院计科和智能专业统一使用李老师基于UniProton编写的新实验,一共有八个实验,如果仅是按照指导书的内容将实验跑通还是比较容易的(第一个实验除外),而对于想要在将来走系统方向的同学,或者遇上比较苛刻的助教,需要将整个实验的代码完全看懂,那就是一件任务量很大的事情了。我在完成这一系列实验的过程也十分坎坷,获得了很多同学的帮助,向他们表示感谢,本文旨在为下一届的同学提供一些参考和帮助,需要注意的是,每个实验的作业题没有标准的答案,仅由我与其他同学讨论得到,欢迎指正。
1.安装交叉工具链(aarch64)以及安装QEMU模拟器
2.创建裸机(Bare Metal)程序
3.构件工程
4.完成调试支持
5.建立自动化脚本
- 下载工具链,以下载工具链版本为11.2,宿主机为x86 64位 Linux机器为例
- wget https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/binrel/gcc-arm-11.2-2022.02-x86_64-aarch64-none-elf.tar.xz
- 解压工具链
- tar -xf gcc-arm-(按Tab键补全)
- 重命名工具链目录
- mv gcc-arm-(按Tab键补全) aarch64-none-elf
这里的话需要在终端的命令行中输入vim ~/.bashrc指令,然后在最底下新增加一行将自己的工具链目录添加到环境变量,例如此处为/home/moonwine/arrch64-none-elf/bin,需要根据自己实际的目录进行替换,最后完成编辑后,在命令行输入source ~/.bashrc进行环境变量设置。
使用最常用的查看版本指令,如果正常显示则表明成功安装,如果报错多半是环境变量没有添加成功,检查工具链目录是否正确设置。
- sudo apt-get update
- sudo apt-get install qemu
- sudo apt-get install qemu-system
sudo apt-get install cmake
src目录下放置了所有的的源代码,src目录下包含bsp目录以及include目录,bsp目录存放与硬件关系密切的代码,本实验中包含CMakeList.txt以及两个汇编文件start.S和prt_reset_vector.S,include目录中存放项目的绝大部分头文件,如prt_typedef.h文件,该文件中实现了UniProton使用的基本数据类型和结构的定义。
就直接复制指导书就好
- /*
- * Copyright (c) 2009-2022 Huawei Technologies Co., Ltd. All rights reserved.
- *
- * UniProton is licensed under Mulan PSL v2.
- * You can use this software according to the terms and conditions of the Mulan PSL v2.
- * You may obtain a copy of Mulan PSL v2 at:
- * http://license.coscl.org.cn/MulanPSL2
- * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
- * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
- * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
- * See the Mulan PSL v2 for more details.
- * Create: 2009-12-22
- * Description: 定义基本数据类型和数据结构。
- */
- #ifndef PRT_TYPEDEF_H
- #define PRT_TYPEDEF_H
-
- #include <stddef.h>
- #include <stdint.h>
- #include <stdbool.h>
-
- #ifdef __cplusplus
- #if __cplusplus
- extern "C" {
- #endif /* __cpluscplus */
- #endif /* __cpluscplus */
-
- typedef unsigned char U8;
- typedef unsigned short U16;
- typedef unsigned int U32;
- typedef unsigned long long U64;
- typedef signed char S8;
- typedef signed short S16;
- typedef signed int S32;
- typedef signed long long S64;
-
- typedef void *VirtAddr;
- typedef void *PhyAddr;
-
- #ifndef OS_SEC_ALW_INLINE
- #define OS_SEC_ALW_INLINE
- #endif
-
- #ifndef INLINE
- #define INLINE static __inline __attribute__((always_inline))
- #endif
-
- #ifndef OS_EMBED_ASM
- #define OS_EMBED_ASM __asm__ __volatile__
- #endif
-
- /* 参数不加void表示可以传任意个参数 */
- typedef void (*OsVoidFunc)(void);
-
- #define ALIGN(addr, boundary) (((uintptr_t)(addr) + (boundary) - 1) & ~((uintptr_t)(boundary) - 1))
- #define TRUNCATE(addr, size) ((addr) & ~((uintptr_t)(size) - 1))
-
- #ifdef YES
- #undef YES
- #endif
- #define YES 1
-
- #ifdef NO
- #undef NO
- #endif
- #define NO 0
-
- #ifndef FALSE
- #define FALSE ((bool)0)
- #endif
-
- #ifndef TRUE
- #define TRUE ((bool)1)
- #endif
-
- #ifndef NULL
- #define NULL ((void *)0)
- #endif
-
- #define OS_ERROR (U32)(-1)
- #define OS_INVALID (-1)
-
- #ifndef OS_OK
- #define OS_OK 0
- #endif
-
- #ifndef OS_FAIL
- #define OS_FAIL 1
- #endif
-
- #ifndef U8_INVALID
- #define U8_INVALID 0xffU
- #endif
-
- #ifndef U12_INVALID
- #define U12_INVALID 0xfffU
- #endif
-
- #ifndef U16_INVALID
- #define U16_INVALID 0xffffU
- #endif
-
- #ifndef U32_INVALID
- #define U32_INVALID 0xffffffffU
- #endif
-
- #ifndef U64_INVALID
- #define U64_INVALID 0xffffffffffffffffUL
- #endif
-
- #ifndef U32_MAX
- #define U32_MAX 0xFFFFFFFFU
- #endif
-
- #ifndef S32_MAX
- #define S32_MAX 0x7FFFFFFF
- #endif
-
- #ifndef S32_MIN
- #define S32_MIN (-S32_MAX-1)
- #endif
-
- #ifndef LIKELY
- #define LIKELY(x) __builtin_expect(!!(x), 1)
- #endif
- #ifndef UNLIKELY
- #define UNLIKELY(x) __builtin_expect(!!(x), 0)
- #endif
-
- #ifdef __cplusplus
- #if __cplusplus
- }
- #endif /* __cpluscplus */
- #endif /* __cpluscplus */
-
- #endif /* PRT_TYPEDEF_H */
- .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
- 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
脚本较长,此处仅展示需要理解的部分:
- 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 = .;
-
- ... ... ...
- }
- 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/moonshine/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}) # 可执行文件
此处需要将set(TOOLCHAIN_PATH)中的路径更改为实际交叉工具链所在位置,此处为:“/home/moonwine/arrch64-none-elf”
- set(SRCS start.S prt_reset_vector.S )
- add_library(bsp OBJECT ${SRCS}) # OBJECT类型只编译生成.o目标文件,但不实际链接成库
此处需要将我们所创建的文件纳入构建系统,即在set中包含start.S和prt_reset_vector.S文件,同时在add_library中加入当前目录bsp
在lab1目录下创建目录build用于存放编译过程产生的一系列中间文件,并进入build目录执行指令(注意一定要在build目录下,如果错在其他目录下执行了指令,需要将生成的中间文件全部删除,否则会报错):
- cmake ../src
- cmake --build .
对于大多数同学,这个地方第一次执行指令的时候都会产生报错“error while loading shared libraries: libpython3.6m.so.1.0: cannot open shared object file: No such file or directory”,原因是缺少相应的库,需要使用指令进行安装(这里是本实验最难的部分,需要安装一系列的依赖库,目的是为了后面调试使用):
- sudo apt-get update sudo apt-get install -y build-essential libssl-dev libffi-dev libbz2-dev libreadline-dev libsqlite3-dev libncurses5-dev libncursesw5-dev xz-utils tk-dev
- 下载并解压python3.6的源码
- wget -c https://www.python.org/ftp/python/3.6.15/Python-3.6.15.tar.xz tar -xf Python-3.6.15.tar.xz
- 编译安装
- cd Python-3.6.15 LDFLAGS="-L/usr/lib/x86_64-linux-gnu" ./configure ./configure --enable-shared
- make sudo
- make install
- sudo ldconfig
如果使用cmake指令后没有任何报错,显示built target miniEuler,表明已经完成裸机的创建,接着进入lab1目录下(不在这个目录下运行会报错)运行指令:
qemu-system-aarch64 -machine virt -m 1024M -cpu cortex-a53 -nographic -kernel build/miniEuler -s
正常运行后屏幕上会显示AArch Bare Metal,然后卡住不动,这就是正常的(当时我还以为是什么bug),因为我们的裸机现在只具有打印的功能,打印完之后就进入无限循环。如果想退出裸机内核需要同时按下ctrl和字母键a,再按下x。
- sh makeMiniEuler.sh
- sh runMiniEuler.sh
至此,整个内核的构建已经完成。
我这里使用的是运行指令,使用自动化脚本也是一样的效果。
qemu-system-aarch64代表启动armv8架构的虚拟机
-machine virt 来指定虚拟机类型为 virt
-m 1024M 来指定虚拟机内存大小为 1024M
-cpu cortex-a53 来指定虚拟机的 CPU 类型为 cortex-a53
-nographic 来禁用图形界面
-kernel build/miniEuler 来指定内核映像文件为我们自己的操作系统内核miniEuler
-S 选项表示在启动时暂停虚拟机并等待 gdb 连接。默认服务器端口为1234
定义了一个宏UART_REG_WRITE,用于实现将字符写入地址为0x9000000的位置,main.c文件的主题功能就是将字符串AArch64 Bare Metal逐个输入0x9000000处,实现字符串输出。(关于为什么向内存地址0x9000000输入字符串就可以实现字符串输入,这是一种操作系统与硬件交互的规定,这个地址被规定为操作系统与I/O设备之间的借口而非我们平常理解的地址,想要具体深入理解的同学可以自行上网查阅)
首先声明两个外部定义的变量OsEnterMain 和 __os_sys_sp_end,其中OsEnterMain来自于prt_reset_vector.S,__os_sys_sp_end来自于脚本aarch64-qemu.ld,然后定义.text.bspinit包含本行之后的代码,表示一个可分配可执行的段,接着定义整个程序的入口OsElxState,程序运行时会跳转到此处开始执行(入口的定义在链接脚本aarch64-qemu.ld中实现)程序开始运行时,首先会执行指令MRS x6,CurrentEL,将系统寄存器的值读入到通用寄存器x6(MRS表示Move to register from system),然后比较该寄存器的低32位w6与0x4是否相等,由于CurrentEL系统寄存器中的索引第2位和第三位表示EL级别,所以等价于判断当前的EL级别是否为1,即内核态,若当前级别处于内核态,则进入start区,首先利用链接文件中定义的全局栈低指针__os_sys_sp_end对栈区进行初始化,然后跳转到prt_reset_vector.S中的OsEntermain进行执行;若当前级别处于用户态,则无法陷入操作系统内核执行内核指令,进入死循环。
跳转进入操作系统内核的主程序main.c,执行完毕后返回(BL指令返回),由于此时还未设置中断处理,故将DAIF寄存器中的Debug、SError、IRQ和FIQ位禁用,最后进入死循环。
本文件为链接脚本文件,首先通过ENTRY声明整个程序的入口为__text_start,接着定义堆区、栈区大小均为0x10000,然后使用MEMORY定义两个内存空间IMU_SRAM与MMU_MEM,并定义了它们的起始位置分别为0x40000000,0x40800000、大小均为0x800000以及权限均为“rwx”,最后在SECTIONS中定义了__text_start的具体位置,然后将start.S文件中的代码段.text.bspinit插入__text_start后,如此操作,实质上则定义了整个程序的入口为start.S文件中的OsElxState,以及定义了堆区空间和栈区空间的起始位置和结束位置,并将起始代码段、堆栈空间保存于内存空间IMU_SRAM。
. = ALIGN(8);表示将链接器的当前位置对齐为8的倍数
PROVIDE表示定义起始位置和结束位置
. = . + _heap_size(_stack_size);将链接器的当前位置向前偏移堆栈空间的大小,以便为程序的堆栈分配空间。
UniProton通过在根目录下执行 python build.py m4 (m4是指目标平台,还有如hi3093等)进行构建,在这个python文件的主函数中,会根据命令行参数创建一个Compile类的实例,并调用这个实例的UniProtonCompile()方法来开始编译过程,完成项目的构建。
分析构建的核心文件build.py:
- #!/usr/bin/env python3
- # coding=utf-8
- # The build entrance of UniProton.
- # Copyright (c) 2009-2023 Huawei Technologies Co., Ltd. All rights reserved.
-
- 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 *
-
-
- 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()
-
- #调用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)
-
- # 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(),从而实现使用cmake进行构建,对类中的各方法进行分析:
首先在打开一个wsl终端(对应虚拟机终端),并启动程序,加上-S指令冻结CPU,使程序在入口停止等待调试:
然后再新打开一个wsl终端(这里对应虚拟机的终端),在新的终端中启动调试客户端:
在gdb下输入指令:target remote localhost:1234远程连接到第一个终端中运行的程序,并使用disassemble进行反汇编:
使用指令i r查看所有寄存器的值:
使用si指令进行单步运行:
使用x/20xw 0x40000000可以查看从地址0x40000000起始的20个四字节的地址空间中的值,以16进制形式表示,0x40000000是第一条指令对应的机器码的起始地址:
可以使用指令set $x24 = 0x7来设置寄存器x24的值为7,使用p指令可以查看寄存器的内容:
使用指令layout asm可以在运行时实时查看当前运行到具体的哪一条指令:
首先在终端中的lab1目录下输入指令code .,在vscode中远程连接虚拟机:
然后与直接在终端中调试相同,在终端处运行程序(或者使用自动化脚本),加上-S指令冻结CPU,使程序在入口停止等待调试:
点击左侧的调试选项,最初会弹出一个launch.json文件,点击创建后会自动填充相关信息,注意将miDebuggerPath修改为自己实际的交叉调试器路径,格式可以参考如下:
- {
- "version": "0.2.0",
- "configurations": [
- {
- "name": "(gdb) Windows 上的 Bash 启动",
- "type": "cppdbg",
- "request": "launch",
- "program": "输入程序名称,例如 ${workspaceFolder}/a.exe",
- "args": [],
- "stopAtEntry": false,
- "cwd": "${fileDirname}",
- "environment": [],
- "externalConsole": false,
- "pipeTransport": {
- "debuggerPath": "/usr/bin/gdb",
- "pipeProgram": "${env:windir}\\system32\\bash.exe",
- "pipeArgs": ["-c"],
- "pipeCwd": ""
- },
- "setupCommands": [
- {
- "description": "为 gdb 启用整齐打印",
- "text": "-enable-pretty-printing",
- "ignoreFailures": true
- },
- {
- "description": "将反汇编风格设置为 Intel",
- "text": "-gdb-set disassembly-flavor intel",
- "ignoreFailures": true
- }
- ]
- },
-
-
- {
- "name": "aarch64-gdb",
- "type": "cppdbg",
- "request": "launch",
- "program": "${workspaceFolder}/build/miniEuler",
- "stopAtEntry": true,
- "cwd": "${fileDirname}",
- "environment": [],
- "externalConsole": false,
- "launchCompleteCommand": "exec-run",
- "MIMode": "gdb",
- "miDebuggerPath":"/home/moonwine/arrch64-none-elf/bin/aarch64-none-elf-gdb", // 修改成交叉调试器gdb对应位置
- "miDebuggerServerAddress": "localhost:1234",
- "setupCommands": [
- {
- "description": "Enable pretty-printing for gdb",
- "text": "-enable-pretty-printing",
- "ignoreFailures": true
- }
- ]
- }
- ],
-
- }
准备工作就绪后,开始调试,可以看到程序顺利在入口处停止:
在调试控制台处输入指令-exec x/20xw 0x40000000可以查看从地址0x40000000起始的20个四字节的地址空间中的值,以16进制形式表示:
此外,交叉编译工具链中可以执行gdb的所有调试指令,不过要在gdb指令前加上-exec,如:
显示所有寄存器。-exec info all-registers
查看寄存器内容。-exec p/x $pc
修改寄存器内容。-exec set $x24 = 0x5 (将寄存器x24的值设置为0x5)
修改指定内存位置的内容。-exec set {int}0x4000000 = 0x1 或者 -exec set *((int *) 0x4000000) = 0x1
修改指定MMIO 寄存器的内容。 -exec set *((volatile int *) 0x08010004) = 0x1
退出调试 -exec q
单步执行代码,直到完成输出后进入无限循环:
1.对操作系统的理解更加深入。
2.深入理解操作系统内核的底层原理。
3.学会对qemu模拟器运行的程序进行调试,对vscode的json配置更加清楚。
4.对Ubuntu的使用也更熟悉。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。