7. 执行一段程序后正常退出 (上)

1 概述

  • 实现全部目标指令集 (RV32I, Zicsr):使用从 riscv-opcodes 生成的文件为模板,编写代码。从 riscv-opcodes 生成的文件放在 resources/ 中。
  • 实现 load ELF 文件的功能.
  • 成功运行一段程序.

2 使用 riscv-opcodes 生成文件

2.1 使用 riscv-opcodes 生成各个指令集的 const 文件

  • make_for_rrv_iss.sh 是在 riscv-opcodes 中执行的脚本
#!/bin/bash
#
# make_for_rrv_iss.sh
#
# G -- IMAFDZicsr Zifencei
# c
#

# Define extensions to be built
# extensions=("rv_i" "rv32_i" "rv64_i")

extensions=("rv_i" "rv32_i" "rv64_i"
    "rv_m" "rv32_m" "rv64_m"
    "rv_a" "rv32_a" "rv64_a"
    "rv_f" "rv32_f" "rv64_f"
    "rv_d" "rv32_d" "rv64_d"
    "rv_zicsr" "rv32_zicsr" "rv64_zicsr"
    "rv_zifencei" "rv32_zifencei" "rv64_zifencei"
    "rv_c" "rv32_c" "rv64_c"
    "rv_system")

# Clean up before building
make clean
rm -f inst*.rs

# Function to process specific patterns
process_pattern() {
    local pattern=$1
    local output_file=$2

    # Extract lines starting with the given pattern from inst_$ext.rs and append them to the output file
    grep -h "^$pattern" "inst_$ext.rs" >>"$output_file"

    # Use sed to delete lines starting with the given pattern in place
    sed -i "/^$pattern/d" "inst_$ext.rs"
    # echo "Lines starting with '$pattern' have been removed from inst_$ext.rs"
}

# Process each extension
for ext in "${extensions[@]}"; do
    # Build with current extension
    make EXTENSIONS="$ext" inst.rs || {
        echo "Error: make failed for extension $ext"
        exit 1
    }

    mv inst.rs "inst_$ext.rs" || {
        echo "Error: Failed to rename file for extension $ext"
        exit 1
    }

    process_pattern "const CSR_" "inst_${ext}_csr_reg.rs"
    process_pattern "const CAUSE_" "inst_${ext}_cause.rs"
done

# Confirm completion
echo ">>>>>>>>> All extensions processed successfully."

# Function to remove duplicate files and rename the unique one
remove_duplicates_and_rename() {
    local pattern=$1
    local target_name=$2
    local files=($(ls $pattern 2>/dev/null))

    if [ ${#files[@]} -eq 0 ]; then
        echo "No files found matching pattern: $pattern"
        return
    fi

    local base_file="${files[0]}"
    local keep_file="$base_file"

    for file in "${files[@]:1}"; do
        if cmp -s "$base_file" "$file"; then
            # Remove duplicate file
            rm "$file"
        else
            # Update base_file to compare with the next files
            base_file="$file"
        fi
    done

    # Rename the kept file
    if [ "$keep_file" != "$target_name" ]; then
        mv "$keep_file" "$target_name"
    fi
}

# Call the function with patterns and target names
remove_duplicates_and_rename "inst_*_csr_reg.rs" "inst_csr_reg.rs"
remove_duplicates_and_rename "inst_*_cause.rs" "inst_cause.rs"

# Iterate over all .rs files in the current directory
for file in inst*.rs; do
    # Use the grep command to check if the file contains the 'const ' keyword
    # -q option makes grep run quietly, without outputting any text
    # -F treats the pattern as a fixed string, not a regular expression
    if ! grep -qF 'const ' "$file"; then
        # If 'const ' is not found in the file, delete the file
        rm "$file"
        echo "Deleted empty file: $file"
    else
        # Create a temporary file
        temp_file=$(mktemp)

        # Use awk to replace multiple consecutive blank lines with a single blank line
        awk 'BEGIN {RS=""; FS="\n"} {gsub(/\n+/,"\n"); print}' "$file" >"$temp_file"

        mv "$temp_file" "$file"
    fi
done
>>>>>>>>> All extensions processed successfully.
Deleted empty file: inst_rv32_a.rs
Deleted empty file: inst_rv32_d.rs
Deleted empty file: inst_rv32_f.rs
Deleted empty file: inst_rv32_m.rs
Deleted empty file: inst_rv32_zicsr.rs
Deleted empty file: inst_rv32_zifencei.rs
Deleted empty file: inst_rv64_zicsr.rs
Deleted empty file: inst_rv64_zifencei.rs

2.2 生成执行指令的桩函数

#!/bin/bash
#
# stub_for_rrv_iss.sh
#

# Ensure the execute directory exists
mkdir -p execute

# Traverse all files matching the pattern inst*.rs in the current directory
for file in inst_*.rs; do
    # Extract the base name of the file without the 'inst' prefix (e.g., inst1.rs -> 1.rs)
    base_name=$(basename "$file" .rs | sed 's/^inst_//')

    # Use grep to find all unique MATCH_* patterns in the file
    matches=$(grep -oP 'MATCH_\w+' "$file" | sort -u)

    # Initialize the output file content
    output_content=""

    for match in $matches; do
        # Convert MATCH_* to lowercase and replace underscores with spaces
        func_name=$(echo "$match" | sed -e 's/MATCH_//' -e 's/_/ /g' -e 's/.*/\L&/')

        # Convert spaces to underscores for the function name
        func_name=$(echo "$func_name" | sed -e 's/ /_/g')

        # Create the corresponding Rust code
        rust_code=$(printf '\npub(crate) fn execute_%s(\n  raw: MachineInstruction,\n  core: &mut Core,\n  _bus: &mut Bus,\n  disasm: bool,\n) -> Result<Option<ExecutionReturnData>, RvCoreError> {\n  todo!("Not implemented")\n}\n' "$func_name")

        # Append the Rust code to the output content
        output_content+="$rust_code"
    done

    # Check if output_content contains non-empty characters before saving
    if [ -n "$output_content" ]; then
        # Save the accumulated Rust code to the corresponding file in the execute directory
        echo -e "$output_content" > "execute/${base_name}.rs"
    else
        echo "Warning: output_content is empty, skipping save for ${base_name}.rs"
    fi    
done

echo "All MATCH_* patterns have been processed and corresponding Rust files created in the execute directory."

3 实现生成文件中的桩函数

  • inst_rv_i.rs, inst_rv_system.rs, inst_rv_zicsr.rs, inst_rv32_i.rs 文件中的每个 const 常量添加 pub(crate).
  • 实现 rv_i.rs, rv_system.rs, rv_zicsr.rs, rv32_i.rs 中未实现的函数.
  • 优化代码: 消除重复, 使用 inst_cause.rs, inst_csr_reg.rs.

4 实现 load ELF 文件的功能

  • 借助 goblin 来解析 ELF 文件.
  • 使用下面 C 程序,生成的 ELF 文件,来测试 load ELF 文件的功能:
static int cnt1;
static int cnt2 = 0x10;
int main(void) {
    return 0;
}

5 运行 hello world

5.1 riscv64-unknown-elf-gcc 生成的程序,是怎样退出的?

  • 结合 ELF 的反汇编代码, newlib,spike,pk源代碼,查看spike的退出過程,相关源码摘录见下面的代码片段.
  • 总结: 调用 exit(), exit() 通过 ecall(系统调用号是 93) 调用 pk 中实现的系统调用; pk 中的退出代码会对 tohost 和 fromhost 做一些处理,然后,进入死循环.
  • 在 RRV-ISS 中, 将退出简化为: ISS 拦截 target 中, 编号为 93 的 ecall, 从而退出模拟器.
// #define SYS_exit 93
// newlib 中的調用鏈
void exit (int code)
    void _exit(int exit_status)
        #define syscall_errno(N, ARGC, A0, A1, A2, A3, A4, A5) 
            __internal_syscall
                ecall // ecall NO is SYS_exit

// pk
void handle_trap(trapframe_t* tf)
    static void handle_syscall(trapframe_t* tf)
        long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, unsigned long n)
            void sys_exit(int code)
                void shutdown(int code)

void shutdown(int code)
{
  frontend_syscall(SYS_exit, code, 0, 0, 0, 0, 0, 0);
  while (1);
}


long frontend_syscall(long n, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6)
    htif_syscall()


void htif_syscall(uintptr_t arg)
{
  do_tohost_fromhost(0, 0, arg);
}

volatile uint64_t tohost __attribute__((section(".htif")));
volatile uint64_t fromhost __attribute__((section(".htif")));

#define FROMHOST_DEV(fromhost_value) ((uint64_t)(fromhost_value) >> 56)
#define FROMHOST_CMD(fromhost_value) ((uint64_t)(fromhost_value) << 8 >> 56)
#define FROMHOST_DATA(fromhost_value) ((uint64_t)(fromhost_value) << 16 >> 16)

static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
{
  spinlock_lock(&htif_lock);
    __set_tohost(dev, cmd, data);

    while (1) {
      uint64_t fh = fromhost;
      if (fh) {
        if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) {
          fromhost = 0;
          break;
        }
        __check_fromhost();
      }
    }
  spinlock_unlock(&htif_lock);
}

5.2 riscv64-unknown-elf-gcc 生成的程序,是怎样处理 printf 的?

  • 调用系统调用

5.3 运行 helloworld C 程序

  • 放弃运行 helloworld C 程序, 原因如下:
    • riscv64-unknown-elf-gcc 使用的启动代码包含 RISC-V 的 c 扩展指令
    • riscv64-unknown-elf-gcc 使用的 printf 使用了多个系统调用
  • 打算使用计算 MD5 的程序来验证,可以不用打印信息.
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容