深入解析 FESVR(Front-End Server)
url: https://github.com/riscv/riscv-isa-sim.git
commid: fcbdbe7946079650d0e656fa3d353e3f652d471f
目录
- FESVR 概述
- FESVR 代码结构分析
- ELF 加载机制
在RISC-V ISA Simulator系列之fesvr<1-4>中我们已经完成了
1. FESVR 概述
2. FESVR 代码结构分析
1. ELF 相关文件
2. HTIF(Host-Target Interface)
3. 设备模拟
3.1. `device.h` / `device.cc`
3.2. `rfb.h` / `rfb.cc`
内容的,下面我们继续完成下列内容。
2. FESVR 代码结构分析
fesvr
的代码主要位于 riscv-isa-sim/fesvr/
目录,主要文件及作用如下:
fesvr
目录下的源码文件清单,涉及 ELF 加载、设备仿真、HTIF 交互、系统调用 等多个模块。下面是各文件的简要介绍:
3. 设备模拟
device.h
/device.cc
:模拟目标系统中的 I/O 设备,如 UART、存储器等。rfb.h
/rfb.cc
:是远程帧缓冲(Remote Frame Buffer)相关的仿真代码。memif.h
/memif.cc
:提供对目标内存的读写访问,供 ELF 加载器和 HTIF 使用。
3.3 memif.h
/ memif.cc
memif.h
class chunked_memif_t
{
public:
virtual void read_chunk(addr_t taddr, size_t len, void* dst) = 0;
virtual void write_chunk(addr_t taddr, size_t len, const void* src) = 0;
virtual void clear_chunk(addr_t taddr, size_t len) = 0;
virtual size_t chunk_align() = 0;
virtual size_t chunk_max_size() = 0;
virtual endianness_t get_target_endianness() const {
return endianness_little;
}
virtual ~chunked_memif_t() = default;
};
chunked_memif_t
是一个抽象基类,定义了对内存块进行操作的接口。它的成员函数包括:
read_chunk()
: 用于从内存中读取指定长度的数据到目标地址。write_chunk()
: 用于将指定长度的数据写入到内存中。clear_chunk()
: 用于清除内存中的指定数据。chunk_align()
: 返回数据块的对齐方式。chunk_max_size()
: 返回数据块的最大大小。get_target_endianness()
: 返回目标平台的字节序,默认返回小端字节序。
这些虚函数提供了操作内存的通用接口,而具体的实现由派生类提供。
class memif_t
{
public:
memif_t(chunked_memif_t* _cmemif) : cmemif(_cmemif) {}
virtual ~memif_t(){}
virtual void read(addr_t addr, size_t len, void* bytes);
virtual void write(addr_t addr, size_t len, const void* bytes);
virtual target_endian<uint8_t> read_uint8(addr_t addr);
virtual target_endian<int8_t> read_int8(addr_t addr);
virtual void write_uint8(addr_t addr, target_endian<uint8_t> val);
virtual void write_int8(addr_t addr, target_endian<int8_t> val);
virtual target_endian<uint16_t> read_uint16(addr_t addr);
virtual target_endian<int16_t> read_int16(addr_t addr);
virtual void write_uint16(addr_t addr, target_endian<uint16_t> val);
virtual void write_int16(addr_t addr, target_endian<int16_t> val);
virtual target_endian<uint32_t> read_uint32(addr_t addr);
virtual target_endian<int32_t> read_int32(addr_t addr);
virtual void write_uint32(addr_t addr, target_endian<uint32_t> val);
virtual void write_int32(addr_t addr, target_endian<int32_t> val);
virtual target_endian<uint64_t> read_uint64(addr_t addr);
virtual target_endian<int64_t> read_int64(addr_t addr);
virtual void write_uint64(addr_t addr, target_endian<uint64_t> val);
virtual void write_int64(addr_t addr, target_endian<int64_t> val);
virtual endianness_t get_target_endianness() const {
return cmemif->get_target_endianness();
}
protected:
chunked_memif_t* cmemif;
};
memif_t
是一个更高级的内存接口类,提供了多种数据类型的内存读写操作,包括字节(uint8_t
)、16位(uint16_t
)、32位(uint32_t
)、64位(uint64_t
)等类型。这个类包含的函数通过 chunked_memif_t
提供的接口来实现内存的读写,并支持目标端字节序(大端或小端)。它提供了以下功能:
read()
和write()
:用于从内存读取和向内存写入指定长度的字节数组。read_uint8()
、write_uint8()
等:这些函数是为不同的数据类型(如 8 位、16 位、32 位、64 位)提供读写操作。get_target_endianness()
:返回目标平台的字节序。
该类的设计允许用户在更高层次上进行内存操作,同时确保数据访问的正确性。
class incompat_xlen : public std::exception {
public:
const unsigned expected_xlen;
const unsigned actual_xlen;
incompat_xlen(unsigned _expected_xlen, unsigned _actual_xlen) : expected_xlen(_expected_xlen), actual_xlen(_actual_xlen) {}
};
incompat_xlen
是一个自定义异常类,用于处理数据宽度(XLen)不匹配的情况。该类包含两个成员变量:expected_xlen
和 actual_xlen
,它们分别表示期望的数据宽度和实际的数据宽度。如果在处理数据时发生了宽度不匹配的情况,系统将抛出此异常。它继承自 std::exception
,使得异常可以与其他标准异常类型兼容。
memif.cc
1. read
函数
void memif_t::read(addr_t addr, size_t len, void* bytes)
{
size_t align = cmemif->chunk_align(); // 获取内存对齐要求
- 通过调用
cmemif->chunk_align()
获取内存对齐的要求。align
变量存储了每个内存块需要的对齐方式。对齐方式决定了从内存中读取数据时,每次读取的起始地址必须满足对齐要求。
if (len && (addr & (align-1))) // 如果地址不对齐
{
size_t this_len = std::min(len, align - size_t(addr & (align-1))); // 计算需要读取的首块长度
uint8_t chunk[align]; // 分配一个内存块,用于读取数据
- 这段代码检查
addr
地址是否对齐。如果地址不是align
的整数倍(即addr & (align-1)
非零),说明地址不对齐。 - 如果不对齐,
this_len
计算出从当前位置到对齐边界的长度,确保读取的第一个块长度不会超过剩余的读取长度。 chunk
数组用于暂时存储从内存中读取的整块数据。
cmemif->read_chunk(addr & ~(align-1), align, chunk); // 从内存中读取一整块数据
memcpy(bytes, chunk + (addr & (align-1)), this_len); // 将数据从块中复制到目标地址
cmemif->read_chunk
读取的是从addr & ~(align-1)
开始的对齐内存块。这是通过清除addr
中的对齐掩码部分来获取对齐地址。- 然后将读取的块数据
chunk
中的数据复制到目标内存bytes
中,从chunk
中的偏移位置(addr & (align-1)
)开始,确保读取的部分对齐。
bytes = (char*)bytes + this_len; // 更新目标地址
addr += this_len; // 更新源地址
len -= this_len; // 更新剩余长度
}
- 更新目标地址
bytes
和源地址addr
,并减少剩余要读取的数据长度len
。
if (len & (align-1)) // 如果剩余长度未对齐
{
size_t this_len = len & (align-1); // 计算未对齐部分的长度
size_t start = len - this_len; // 计算起始位置
uint8_t chunk[align]; // 分配内存块
- 这里是处理剩余数据是否对齐。如果剩余的
len
不是对齐的(即len & (align-1)
非零),则计算出剩余未对齐部分的长度。
cmemif->read_chunk(addr + start, align, chunk); // 读取未对齐部分的数据
memcpy((char*)bytes + start, chunk, this_len); // 复制数据到目标地址
- 从内存中读取未对齐的块,并将读取的数据复制到目标内存。
len -= this_len; // 更新剩余长度
}
- 更新剩余要读取的长度
len
。
// 现在对齐了
for (size_t pos = 0; pos < len; pos += cmemif->chunk_max_size()) // 逐块读取剩余数据
cmemif->read_chunk(addr + pos, std::min(cmemif->chunk_max_size(), len - pos), (char*)bytes + pos);
}
- 数据已经对齐,接下来就是按块读取数据。
cmemif->chunk_max_size()
返回单个块的最大大小,根据剩余长度len
逐块读取数据并复制到目标内存。
2. write
函数
void memif_t::write(addr_t addr, size_t len, const void* bytes)
{
size_t align = cmemif->chunk_align(); // 获取内存对齐要求
- 和
read
函数一样,首先获取内存块的对齐要求。
if (len && (addr & (align-1))) // 如果地址不对齐
{
size_t this_len = std::min(len, align - size_t(addr & (align-1))); // 计算需要写入的首块长度
uint8_t chunk[align]; // 分配一个内存块,用于写入数据
- 检查地址是否对齐。如果地址不对齐,计算出需要写入的首块长度。
cmemif->read_chunk(addr & ~(align-1), align, chunk); // 从内存中读取一整块数据
memcpy(chunk + (addr & (align-1)), bytes, this_len); // 将数据从源地址复制到内存块中
cmemif->write_chunk(addr & ~(align-1), align, chunk); // 写入修改后的内存块
- 读取一整块内存,然后将新数据复制到内存块中,最后写回修改后的内存块。
bytes = (char*)bytes + this_len; // 更新源地址
addr += this_len; // 更新目标地址
len -= this_len; // 更新剩余长度
}
- 更新源地址、目标地址和剩余写入的数据长度。
if (len & (align-1)) // 如果剩余长度未对齐
{
size_t this_len = len & (align-1); // 计算未对齐部分的长度
size_t start = len - this_len; // 计算起始位置
uint8_t chunk[align]; // 分配内存块
- 处理剩余未对齐部分的数据,首先计算未对齐部分的长度。
cmemif->read_chunk(addr + start, align, chunk); // 读取未对齐部分的数据
memcpy(chunk, (char*)bytes + start, this_len); // 将数据复制到内存块中
cmemif->write_chunk(addr + start, align, chunk); // 将修改后的数据写回内存
- 读取并写入未对齐部分的数据。
len -= this_len; // 更新剩余长度
}
- 更新剩余的写入长度。
// 现在对齐了
bool all_zero = len != 0; // 检查数据是否全为零
for (size_t i = 0; i < len; i++) // 检查每个字节是否为零
all_zero &= ((const char*)bytes)[i] == 0;
- 检查数据是否全部为零。如果是,直接清除内存区域。
if (all_zero) { // 如果数据全为零
cmemif->clear_chunk(addr, len); // 清除内存块
} else { // 否则按块写入数据
size_t max_chunk = cmemif->chunk_max_size();
for (size_t pos = 0; pos < len; pos += max_chunk)
cmemif->write_chunk(addr + pos, std::min(max_chunk, len - pos), (char*)bytes + pos);
}
}
- 如果数据全部为零,则通过
cmemif->clear_chunk
清除指定的内存区域。 - 否则,逐块写入内存,直到所有数据写入完毕。
3. MEMIF_READ_FUNC
和 MEMIF_WRITE_FUNC
宏
#define MEMIF_READ_FUNC \
if(addr & (sizeof(val)-1)) \
throw std::runtime_error("misaligned address"); \
this->read(addr, sizeof(val), &val); \
return val
- 该宏在进行读取操作时,首先检查地址是否对齐。如果地址不对齐,则抛出
misaligned address
异常。如果对齐,调用read
函数读取数据并返回。
#define MEMIF_WRITE_FUNC \
if(addr & (sizeof(val)-1)) \
throw std::runtime_error("misaligned address"); \
this->write(addr, sizeof(val), &val)
- 该宏在进行写入操作时,首先检查地址是否对齐。如果不对齐,则抛出异常。如果对齐,调用
write
函数写入数据。
4. 类型特定的读写函数
这些函数都使用了 MEMIF_READ_FUNC
和 MEMIF_WRITE_FUNC
宏来读取和写入特定类型的数据,例如 uint8_t
, int8_t
, uint16_t
, int16_t
等。
target_endian<uint8_t> memif_t::read_uint8(addr_t addr)
{
target_endian<uint8_t> val;
MEMIF_READ_FUNC;
}
- 该函数读取
uint8_t
类型的数据。如果地址不对齐,抛出异常。如果对齐,调用read
函数进行数据读取。
类似的处理也应用于其他类型(如 int8_t
, uint16_t
, int16_t
, 等)。
总结:
read
和write
函数的核心逻辑是处理内存对齐,确保数据按对齐要求读写。对于不对齐的数据,进行块读写,并处理剩余未对齐部分的数据。- 使用宏
MEMIF_READ_FUNC
和MEMIF_WRITE_FUNC
来简化对不同数据类型的读写操作,并保证地址对齐。 - 在写操作中,若数据全为零,会优化为清除内存,而不是逐字节写入。
4. 系统调用
syscall.h
/syscall.cc
:处理ecall
指令,将 RISC-V 系统调用映射到宿主机的系统调用。
syscall.h
这是一个名为 syscall_t
的 C++ 类定义,主要用于处理系统调用,特别是与文件系统、进程控制和 I/O 操作相关的系统调用。
类结构和成员变量
class syscall_t : public device_t
{
public:
syscall_t(htif_t*);
~syscall_t();
void set_chroot(const char* where);
private:
const char* identity() { return "syscall_proxy"; }
htif_t* htif; // 高级接口,通常用来与主机或外部设备通信
memif_t* memif; // 内存接口,可能用于直接操作内存(例如读取、写入)
std::vector<syscall_func_t> table; // 系统调用表,存储不同系统调用的处理函数
fds_t fds; // 文件描述符管理
std::vector<reg_t> fds_index; // 文件描述符索引,用于跟踪打开的文件描述符
void handle_syscall(command_t cmd); // 处理系统调用
void dispatch(addr_t mm); // 调度系统调用,可能涉及内存操作
std::string chroot; // 存储当前的 chroot 路径
std::string do_chroot(const char* fn); // 执行 chroot 操作
std::string undo_chroot(const char* fn); // 恢复 chroot 操作
// 各种系统调用函数声明,处理不同的系统调用
reg_t sys_exit(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_openat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_read(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_pread(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_write(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_pwrite(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_close(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_lseek(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_fstat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_lstat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_statx(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_fstatat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_faccessat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_fcntl(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_ftruncate(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_renameat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_linkat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_unlinkat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_mkdirat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_getcwd(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_getmainvars(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_chdir(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
reg_t sys_readlinkat(reg_t, reg_t, reg_t, reg_t, reg_t, reg_t, reg_t);
};
成员变量
htif_t* htif;
:指向htif_t
类型的指针,可能用于处理主机或外部接口。htif
可能代表一种 “host-target interface”(主机-目标接口)用于与外部设备进行通信。memif_t* memif;
:指向memif_t
类型的指针,可能用于进行内存操作。它提供了对内存的直接访问,如读写内存。std::vector<syscall_func_t> table;
:存储了系统调用函数的列表。syscall_func_t
是一个函数指针类型,指向每个系统调用的处理函数。fds_t fds;
:文件描述符管理类,负责分配和释放文件描述符。std::vector<reg_t> fds_index;
:文件描述符索引,可能用于跟踪哪些文件描述符已被分配。
成员函数
set_chroot(const char* where);
:设置当前进程的chroot
路径。chroot
是一个将进程限制在指定目录中的机制。identity()
:返回设备的身份字符串,这里是 “syscall_proxy”。handle_syscall(command_t cmd);
:处理一个系统调用的函数,接受一个command_t
类型的命令。dispatch(addr_t mm);
:根据内存地址调度系统调用。do_chroot(const char* fn);
和undo_chroot(const char* fn);
:执行和撤销chroot
操作。
系统调用处理函数
sys_exit
:处理exit
系统调用,退出程序。sys_openat
:处理openat
系统调用,用于打开一个文件。sys_read
:处理read
系统调用,用于读取文件。sys_write
:处理write
系统调用,用于写入文件。sys_close
:处理close
系统调用,关闭文件描述符。sys_lseek
:处理lseek
系统调用,定位文件指针。- 其他类似的函数处理各类文件系统操作和 I/O 系统调用。
fds_t
类
class fds_t
{
public:
reg_t alloc(int fd); // 分配文件描述符
void dealloc(reg_t fd); // 释放文件描述符
int lookup(reg_t fd); // 查找文件描述符的状态
private:
std::vector<int> fds; // 存储文件描述符
};
alloc()
:分配一个文件描述符。dealloc()
:释放指定的文件描述符。lookup()
:查找文件描述符的状态。
系统调用表
table
是一个存储系统调用函数指针的向量。系统调用的每个函数(如 sys_exit
、sys_openat
等)都定义了不同的功能,它们接收不同的参数并返回不同类型的结果。每个系统调用都会按照表中的索引调用相应的函数。
其他成员函数
set_chroot
和do_chroot
/undo_chroot
:提供了对chroot
操作的封装,使得进程可以在受限的目录树中运行。handle_syscall
:处理具体的系统调用命令,可能涉及根据命令选择不同的系统调用函数。dispatch
:根据内存地址或命令调度对应的系统调用处理函数。
总结
syscall_t
类定义了一个系统调用处理代理,主要包括处理与文件操作、进程管理相关的系统调用。它通过函数指针表管理和调用不同的系统调用函数,且提供了对文件描述符的分配和管理。chroot
操作的封装也使得进程可以在特定的目录环境中执行。
syscall.cc
- 结构体构造函数
riscv_stat::riscv_stat(const struct stat& s, htif_t* htif)
riscv_stat(const struct stat& s, htif_t* htif)
: dev(htif->to_target<uint64_t>(s.st_dev)),
ino(htif->to_target<uint64_t>(s.st_ino)),
mode(htif->to_target<uint32_t>(s.st_mode)),
nlink(htif->to_target<uint32_t>(s.st_nlink)),
uid(htif->to_target<uint32_t>(s.st_uid)),
gid(htif->to_target<uint32_t>(s.st_gid)),
rdev(htif->to_target<uint64_t>(s.st_rdev)), __pad1(),
size(htif->to_target<uint64_t>(s.st_size)),
blksize(htif->to_target<uint32_t>(s.st_blksize)), __pad2(),
blocks(htif->to_target<uint64_t>(s.st_blocks)),
atime(htif->to_target<uint64_t>(s.st_atime)), __pad3(),
mtime(htif->to_target<uint64_t>(s.st_mtime)), __pad4(),
ctime(htif->to_target<uint64_t>(s.st_ctime)), __pad5(),
__unused4(), __unused5() {}
};
- 功能:将标准
struct stat
结构体的数据转换为riscv_stat
结构体,并且将数据转换为目标端格式。 - 参数:
s
:标准struct stat
结构体,包含文件的状态信息。htif
:htif_t
类型的指针,用于进行端序转换。
- 实现:将
s
中的每个成员通过htif->to_target
方法转换为目标端格式,并赋值给riscv_stat
的对应成员。
riscv_statx::riscv_statx(const struct statx& s, htif_t* htif)
(仅在 HAVE_STATX
定义时有效)
riscv_statx(const struct statx& s, htif_t* htif)
: mask(htif->to_target<uint32_t>(s.stx_mask)),
blksize(htif->to_target<uint32_t>(s.stx_blksize)),
attributes(htif->to_target<uint64_t>(s.stx_attributes)),
nlink(htif->to_target<uint32_t>(s.stx_nlink)),
uid(htif->to_target<uint32_t>(s.stx_uid)),
gid(htif->to_target<uint32_t>(s.stx_gid)),
mode(htif->to_target<uint16_t>(s.stx_mode)), __spare0(),
ino(htif->to_target<uint64_t>(s.stx_ino)),
size(htif->to_target<uint64_t>(s.stx_size)),
blocks(htif->to_target<uint64_t>(s.stx_blocks)),
attributes_mask(htif->to_target<uint64_t>(s.stx_attributes_mask)),
atime {
htif->to_target<int64_t>(s.stx_atime.tv_sec),
htif->to_target<uint32_t>(s.stx_atime.tv_nsec)
},
btime {
htif->to_target<int64_t>(s.stx_btime.tv_sec),
htif->to_target<uint32_t>(s.stx_btime.tv_nsec)
},
ctime {
htif->to_target<int64_t>(s.stx_ctime.tv_sec),
htif->to_target<uint32_t>(s.stx_ctime.tv_nsec)
},
mtime {
htif->to_target<int64_t>(s.stx_mtime.tv_sec),
htif->to_target<uint32_t>(s.stx_mtime.tv_nsec)
},
rdev_major(htif->to_target<uint32_t>(s.stx_rdev_major)),
rdev_minor(htif->to_target<uint32_t>(s.stx_rdev_minor)),
dev_major(htif->to_target<uint32_t>(s.stx_dev_major)),
dev_minor(htif->to_target<uint32_t>(s.stx_dev_minor)),
#ifdef HAVE_STATX_MNT_ID
mnt_id(htif->to_target<uint64_t>(s.stx_mnt_id)),
__spare2(), __spare3()
#else
__spare2()
#endif
{}
};
- 功能:将标准
struct statx
结构体的数据转换为riscv_statx
结构体,并且将数据转换为目标端格式。 - 参数:
s
:标准struct statx
结构体,包含文件的扩展状态信息。htif
:htif_t
类型的指针,用于进行端序转换。
- 实现:将
s
中的每个成员通过htif->to_target
方法转换为目标端格式,并赋值给riscv_statx
的对应成员。对于时间戳成员,分别处理秒和纳秒部分。
syscall_t
类的成员函数
syscall_t::syscall_t(htif_t* htif)
syscall_t::syscall_t(htif_t* htif)
: htif(htif), memif(&htif->memif()), table(2048)
{
table[17] = &syscall_t::sys_getcwd;
table[25] = &syscall_t::sys_fcntl;
table[34] = &syscall_t::sys_mkdirat;
table[35] = &syscall_t::sys_unlinkat;
table[37] = &syscall_t::sys_linkat;
table[38] = &syscall_t::sys_renameat;
table[46] = &syscall_t::sys_ftruncate;
table[48] = &syscall_t::sys_faccessat;
table[49] = &syscall_t::sys_chdir;
table[56] = &syscall_t::sys_openat;
table[57] = &syscall_t::sys_close;
table[62] = &syscall_t::sys_lseek;
table[63] = &syscall_t::sys_read;
table[64] = &syscall_t::sys_write;
table[67] = &syscall_t::sys_pread;
table[68] = &syscall_t::sys_pwrite;
table[78] = &syscall_t::sys_readlinkat;
table[79] = &syscall_t::sys_fstatat;
table[80] = &syscall_t::sys_fstat;
table[93] = &syscall_t::sys_exit;
table[291] = &syscall_t::sys_statx;
table[1039] = &syscall_t::sys_lstat;
table[2011] = &syscall_t::sys_getmainvars;
register_command(0, std::bind(&syscall_t::handle_syscall, this, _1), "syscall");
int stdin_fd = dup(0), stdout_fd0 = dup(1), stdout_fd1 = dup(1);
if (stdin_fd < 0 || stdout_fd0 < 0 || stdout_fd1 < 0)
throw std::runtime_error("could not dup stdin/stdout");
fds_index.push_back(fds.alloc(stdin_fd)); // stdin -> stdin
fds_index.push_back(fds.alloc(stdout_fd0)); // stdout -> stdout
fds_index.push_back(fds.alloc(stdout_fd1)); // stderr -> stdout
}
- 功能:
syscall_t
类的构造函数,初始化系统调用表,注册系统调用处理命令,复制标准输入输出文件描述符。 - 参数:
htif
:htif_t
类型的指针,代表硬件接口。
- 实现:
- 初始化
htif
、memif
和table
成员变量。 - 将系统调用号映射到对应的成员函数,填充
table
数组。 - 注册系统调用处理命令,使用
std::bind
绑定handle_syscall
方法。 - 复制标准输入输出文件描述符,并将其分配到
fds
中。
- 初始化
syscall_t::~syscall_t()
syscall_t::~syscall_t() {
for (auto i: fds_index) {
close(fds.lookup(i));
fds.dealloc(i);
}
}
- 功能:
syscall_t
类的析构函数,关闭所有打开的文件描述符并释放资源。 - 实现:遍历
fds_index
,关闭对应的文件描述符,并从fds
中释放索引。
std::string syscall_t::do_chroot(const char* fn)
std::string syscall_t::do_chroot(const char* fn)
{
if (!chroot.empty() && *fn == '/')
return chroot + fn;
return fn;
}
- 功能:如果设置了根目录,将绝对路径转换为包含根目录的路径。
- 参数:
fn
:文件路径。
- 实现:如果
chroot
不为空且fn
是绝对路径,则返回chroot
与fn
拼接后的路径;否则返回原路径。
std::string syscall_t::undo_chroot(const char* fn)
std::string syscall_t::undo_chroot(const char* fn)
{
if (chroot.empty())
return fn;
if (strncmp(fn, chroot.c_str(), chroot.size()) == 0
&& (chroot.back() == '/' || fn[chroot.size()] == '/'))
return fn + chroot.size() - (chroot.back() == '/');
return "/";
}
- 功能:如果设置了根目录,将包含根目录的路径转换为相对路径。
- 参数:
fn
:文件路径。
- 实现:如果
chroot
为空,返回原路径;否则,检查fn
是否以chroot
开头,如果是,则返回去除chroot
后的路径;否则返回根路径/
。
void syscall_t::handle_syscall(command_t cmd)
void syscall_t::handle_syscall(command_t cmd)
{
if (cmd.payload() & 1) // test pass/fail
{
htif->exitcode = cmd.payload();
if (htif->exit_code())
std::cerr << "*** FAILED *** (tohost = " << htif->exit_code() << ")" << std::endl;
return;
}
else // proxied system call
dispatch(cmd.payload());
cmd.respond(1);
}
- 功能:处理系统调用命令,根据命令负载判断是测试结果还是代理系统调用。
- 参数:
cmd
:command_t
类型的命令对象。
- 实现:
- 如果
cmd.payload()
的最低位为 1,表示测试结果,设置htif->exitcode
并输出错误信息(如果测试失败)。 - 否则,调用
dispatch
方法处理代理系统调用,并回复命令。
- 如果
reg_t syscall_t::sys_exit(reg_t code, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_exit(reg_t code, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
htif->htif_exit(code << 1 | 1);
return 0;
}
- 功能:实现
exit
系统调用,设置退出码。 - 参数:
code
:退出码。a1 - a6
:其他参数(未使用)。
- 实现:设置
htif->exitcode
为code << 1 | 1
,并返回 0。
static reg_t sysret_errno(sreg_t ret)
static reg_t sysret_errno(sreg_t ret)
{
return ret == -1 ? -errno : ret;
}
- 功能:将系统调用的返回值转换为包含错误码的返回值。
- 参数:
ret
:系统调用的原始返回值。
- 实现:如果
ret
为 -1,表示系统调用失败,返回-errno
;否则返回ret
。
reg_t syscall_t::sys_read(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_read(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> buf(len);
ssize_t ret = read(fds.lookup(fd), buf.data(), len);
reg_t ret_errno = sysret_errno(ret);
if (ret > 0)
memif->write(pbuf, ret, buf.data());
return ret_errno;
}
- 功能:实现
read
系统调用,从文件描述符读取数据。 - 参数:
fd
:文件描述符。pbuf
:缓冲区地址。len
:读取的字节数。a3 - a6
:其他参数(未使用)。
- 实现:
- 创建一个长度为
len
的缓冲区。 - 调用
read
函数从文件描述符读取数据。 - 如果读取成功,将数据写入
pbuf
指向的内存。 - 返回处理后的返回值。
- 创建一个长度为
reg_t syscall_t::sys_pread(reg_t fd, reg_t pbuf, reg_t len, reg_t off, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_pread(reg_t fd, reg_t pbuf, reg_t len, reg_t off, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> buf(len);
ssize_t ret = pread(fds.lookup(fd), buf.data(), len, off);
reg_t ret_errno = sysret_errno(ret);
if (ret > 0)
memif->write(pbuf, ret, buf.data());
return ret_errno;
}
- 功能:实现
pread
系统调用,从文件描述符指定偏移量处读取数据。 - 参数:
fd
:文件描述符。pbuf
:缓冲区地址。len
:读取的字节数。off
:偏移量。a4 - a6
:其他参数(未使用)。
- 实现:与
sys_read
类似,只是调用pread
函数并指定偏移量。
reg_t syscall_t::sys_write(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_write(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> buf(len);
memif->read(pbuf, len, buf.data());
reg_t ret = sysret_errno(write(fds.lookup(fd), buf.data(), len));
return ret;
}
- 功能:实现
write
系统调用,向文件描述符写入数据。 - 参数:
fd
:文件描述符。pbuf
:缓冲区地址。len
:写入的字节数。a3 - a6
:其他参数(未使用)。
- 实现:
- 创建一个长度为
len
的缓冲区。 - 从
pbuf
指向的内存读取数据到缓冲区。 - 调用
write
函数将数据写入文件描述符。 - 返回处理后的返回值。
- 创建一个长度为
reg_t syscall_t::sys_pwrite(reg_t fd, reg_t pbuf, reg_t len, reg_t off, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_pwrite(reg_t fd, reg_t pbuf, reg_t len, reg_t off, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> buf(len);
memif->read(pbuf, len, buf.data());
reg_t ret = sysret_errno(pwrite(fds.lookup(fd), buf.data(), len, off));
return ret;
}
- 功能:实现
pwrite
系统调用,向文件描述符指定偏移量处写入数据。 - 参数:
fd
:文件描述符。pbuf
:缓冲区地址。len
:写入的字节数。off
:偏移量。a4 - a6
:其他参数(未使用)。
- 实现:与
sys_write
类似,只是调用pwrite
函数并指定偏移量。
reg_t syscall_t::sys_close(reg_t fd, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_close(reg_t fd, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
if (close(fds.lookup(fd)) < 0)
return sysret_errno(-1);
fds.dealloc(fd);
return 0;
}
- 功能:实现
close
系统调用,关闭文件描述符。 - 参数:
fd
:文件描述符。a1 - a6
:其他参数(未使用)。
- 实现:
- 调用
close
函数关闭文件描述符。 - 如果关闭失败,返回处理后的错误码;否则,从
fds
中释放索引并返回 0。
- 调用
reg_t syscall_t::sys_lseek(reg_t fd, reg_t ptr, reg_t dir, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_lseek(reg_t fd, reg_t ptr, reg_t dir, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
return sysret_errno(lseek(fds.lookup(fd), ptr, dir));
}
- 功能:实现
lseek
系统调用,设置文件偏移量。 - 参数:
fd
:文件描述符。ptr
:偏移量。dir
:偏移基准。a3 - a6
:其他参数(未使用)。
- 实现:调用
lseek
函数并返回处理后的返回值。
reg_t syscall_t::sys_fstat(reg_t fd, reg_t pbuf, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_fstat(reg_t fd, reg_t pbuf, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
struct stat buf;
reg_t ret = sysret_errno(fstat(fds.lookup(fd), &buf));
if (ret != (reg_t)-1)
{
riscv_stat rbuf(buf, htif);
memif->write(pbuf, sizeof(rbuf), &rbuf);
}
return ret;
}
- 功能:实现
fstat
系统调用,获取文件状态信息。 - 参数:
fd
:文件描述符。pbuf
:缓冲区地址。a2 - a6
:其他参数(未使用)。
- 实现:
- 调用
fstat
函数获取文件状态信息。 - 如果获取成功,将状态信息转换为
riscv_stat
结构体并写入pbuf
指向的内存。 - 返回处理后的返回值。
- 调用
reg_t syscall_t::sys_fcntl(reg_t fd, reg_t cmd, reg_t arg, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_fcntl(reg_t fd, reg_t cmd, reg_t arg, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
return sysret_errno(fcntl(fds.lookup(fd), cmd, arg));
}
- 功能:实现
fcntl
系统调用,对文件描述符进行控制操作。 - 参数:
fd
:文件描述符。cmd
:操作命令。arg
:操作参数。a3 - a6
:其他参数(未使用)。
- 实现:调用
fcntl
函数并返回处理后的返回值。
reg_t syscall_t::sys_ftruncate(reg_t fd, reg_t len, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_ftruncate(reg_t fd, reg_t len, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
return sysret_errno(ftruncate(fds.lookup(fd), len));
}
- 功能:实现
ftruncate
系统调用,截断文件到指定长度。 - 参数:
fd
:文件描述符。len
:截断长度。a2 - a6
:其他参数(未使用)。
- 实现:调用
ftruncate
函数并返回处理后的返回值。
reg_t syscall_t::sys_lstat(reg_t pname, reg_t len, reg_t pbuf, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_lstat(reg_t pname, reg_t len, reg_t pbuf, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> name(len);
memif->read(pname, len, name.data());
struct stat buf;
reg_t ret = sysret_errno(lstat(do_chroot(name.data()).c_str(), &buf));
if (ret != (reg_t)-1)
{
riscv_stat rbuf(buf, htif);
memif->write(pbuf, sizeof(rbuf), &rbuf);
}
return ret;
}
- 功能:实现
lstat
系统调用,获取文件或符号链接的状态信息。 - 参数:
pname
:文件名地址。len
:文件名长度。pbuf
:缓冲区地址。a3 - a6
:其他参数(未使用)。
- 实现:
- 从
pname
指向的内存读取文件名。 - 调用
lstat
函数获取文件状态信息。 - 如果获取成功,将状态信息转换为
riscv_stat
结构体并写入pbuf
指向的内存。 - 返回处理后的返回值。
- 从
reg_t syscall_t::sys_statx(reg_t fd, reg_t pname, reg_t len, reg_t flags, reg_t mask, reg_t pbuf, reg_t a6)
(仅在 HAVE_STATX
定义时有效)
reg_t syscall_t::sys_statx(reg_t fd, reg_t pname, reg_t len, reg_t flags, reg_t mask, reg_t pbuf, reg_t a6)
{
#ifndef HAVE_STATX
return -ENOSYS;
#else
std::vector<char> name(len);
memif->read(pname, len, name.data());
struct statx buf;
reg_t ret = sysret_errno(statx(fds.lookup(fd), do_chroot(name.data()).c_str(), flags, mask, &buf));
if (ret != (reg_t)-1)
{
riscv_statx rbuf(buf, htif);
memif->write(pbuf, sizeof(rbuf), &rbuf);
}
return ret;
#endif
}
- 功能:实现
statx
系统调用,获取文件的扩展状态信息。 - 参数:
fd
:文件描述符。pname
:文件名地址。len
:文件名长度。flags
:标志位。mask
:掩码。pbuf
:缓冲区地址。a6
:其他参数(未使用)。
- 实现:
- 从
pname
指向的内存读取文件名。 - 调用
statx
函数获取文件状态信息。 - 如果获取成功,将状态信息转换为
riscv_statx
结构体并写入pbuf
指向的内存。 - 返回处理后的返回值。
- 从
reg_t syscall_t::sys_openat(reg_t dirfd, reg_t pname, reg_t len, reg_t flags, reg_t mode, reg_t a5, reg_t a6)
#define AT_SYSCALL(syscall, fd, name, ...) \
(syscall(fds.lookup(fd), int(fd) == RISCV_AT_FDCWD ? do_chroot(name).c_str() : (name), __VA_ARGS__))
reg_t syscall_t::sys_openat(reg_t dirfd, reg_t pname, reg_t len, reg_t flags, reg_t mode, reg_t a5, reg_t a6)
{
std::vector<char> name(len);
memif->read(pname, len, name.data());
int fd = sysret_errno(AT_SYSCALL(openat, dirfd, name.data(), flags, mode));
if (fd < 0)
return sysret_errno(-1);
return fds.alloc(fd);
}
- 功能:实现
openat
系统调用,打开文件。 - 参数:
dirfd
:目录文件描述符。pname
:文件名地址。len
:文件名长度。flags
:打开标志。mode
:文件权限。a5 - a6
:其他参数(未使用)。
- 实现:
- 从
pname
指向的内存读取文件名。 - 调用
openat
函数打开文件。 - 如果打开成功,分配一个新的文件描述符索引并返回;否则返回处理后的错误码。
- 从
reg_t syscall_t::sys_fstatat(reg_t dirfd, reg_t pname, reg_t len, reg_t pbuf, reg_t flags, reg_t a5, reg_t a6)
reg_t syscall_t::sys_fstatat(reg_t dirfd, reg_t pname, reg_t len, reg_t pbuf, reg_t flags, reg_t a5, reg_t a6)
{
std::vector<char> name(len);
memif->read(pname, len, name.data());
struct stat buf;
reg_t ret = sysret_errno(AT_SYSCALL(fstatat, dirfd, name.data(), &buf, flags));
if (ret != (reg_t)-1)
{
riscv_stat rbuf(buf, htif);
memif->write(pbuf, sizeof(rbuf), &rbuf);
}
return ret;
}
- 功能:实现
fstatat
系统调用,获取文件状态信息。 - 参数:
dirfd
:目录文件描述符。pname
:文件名地址。len
:文件名长度。pbuf
:缓冲区地址。flags
:标志位。a5 - a6
:其他参数(未使用)。
- 实现:
- 从
pname
指向的内存读取文件名。 - 调用
fstatat
函数获取文件状态信息。 - 如果获取成功,将状态信息转换为
riscv_stat
结构体并写入pbuf
指向的内存。 - 返回处理后的返回值。
- 从
reg_t syscall_t::sys_faccessat(reg_t dirfd, reg_t pname, reg_t len, reg_t mode, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_faccessat(reg_t dirfd, reg_t pname, reg_t len, reg_t mode, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> name(len);
memif->read(pname, len, name.data());
return sysret_errno(AT_SYSCALL(faccessat, dirfd, name.data(), mode, 0));
}
- 功能:实现
faccessat
系统调用,检查文件的访问权限。 - 参数:
dirfd
:目录文件描述符。pname
:文件名地址。len
:文件名长度。mode
:访问模式。a4 - a6
:其他参数(未使用)。
- 实现:
- 从
pname
指向的内存读取文件名。 - 调用
faccessat
函数检查文件访问权限。 - 返回处理后的返回值。
- 从
reg_t syscall_t::sys_renameat(reg_t odirfd, reg_t popath, reg_t olen, reg_t ndirfd, reg_t pnpath, reg_t nlen, reg_t a6)
reg_t syscall_t::sys_renameat(reg_t odirfd, reg_t popath, reg_t olen, reg_t ndirfd, reg_t pnpath, reg_t nlen, reg_t a6)
{
std::vector<char> opath(olen), npath(nlen);
memif->read(popath, olen, opath.data());
memif->read(pnpath, nlen, npath.data());
return sysret_errno(renameat(fds.lookup(odirfd), int(odirfd) == RISCV_AT_FDCWD ? do_chroot(opath.data()).c_str() : opath.data(),
fds.lookup(ndirfd), int(ndirfd) == RISCV_AT_FDCWD ? do_chroot(npath.data()).c_str() : npath.data()));
}
- 功能:实现
renameat
系统调用,重命名文件或目录。 - 参数:
odirfd
:旧目录文件描述符。popath
:旧文件名地址。olen
:旧文件名长度。ndirfd
:新目录文件描述符。pnpath
:新文件名地址。nlen
:新文件名长度。a6
:其他参数(未使用)。
- 实现:
- 从
popath
和pnpath
指向的内存读取旧文件名和新文件名。 - 调用
renameat
函数重命名文件或目录。 - 返回处理后的返回值。
- 从
reg_t syscall_t::sys_linkat(reg_t odirfd, reg_t poname, reg_t olen, reg_t ndirfd, reg_t pnname, reg_t nlen, reg_t flags)
reg_t syscall_t::sys_linkat(reg_t odirfd, reg_t poname, reg_t olen, reg_t ndirfd, reg_t pnname, reg_t nlen, reg_t flags)
{
std::vector<char> oname(olen), nname(nlen);
memif->read(poname, olen, oname.data());
memif->read(pnname, nlen, nname.data());
return sysret_errno(linkat(fds.lookup(odirfd), int(odirfd) == RISCV_AT_FDCWD ? do_chroot(oname.data()).c_str() : oname.data(),
fds.lookup(ndirfd), int(ndirfd) == RISCV_AT_FDCWD ? do_chroot(nname.data()).c_str() : nname.data(),
flags));
}
- 功能:实现
linkat
系统调用,创建硬链接。 - 参数:
odirfd
:旧目录文件描述符。poname
:旧文件名地址。olen
:旧文件名长度。ndirfd
:新目录文件描述符。pnname
:新文件名地址。nlen
:新文件名长度。flags
:标志位。
- 实现:
- 从
poname
和pnname
指向的内存读取旧文件名和新文件名。 - 调用
linkat
函数创建硬链接。 - 返回处理后的返回值。
- 从
reg_t syscall_t::sys_unlinkat(reg_t dirfd, reg_t pname, reg_t len, reg_t flags, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_unlinkat(reg_t dirfd, reg_t pname, reg_t len, reg_t flags, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> name(len);
memif->read(pname, len, name.data());
return sysret_errno(AT_SYSCALL(unlinkat, dirfd, name.data(), flags));
}
- 功能:实现
unlinkat
系统调用,删除文件或目录。 - 参数:
dirfd
:目录文件描述符。pname
:文件名地址。len
:文件名长度。flags
:标志位。a4 - a6
:其他参数(未使用)。
- 实现:
- 从
pname
指向的内存读取文件名。 - 调用
unlinkat
函数删除文件或目录。 - 返回处理后的返回值。
- 从
reg_t syscall_t::sys_mkdirat(reg_t dirfd, reg_t pname, reg_t len, reg_t mode, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_mkdirat(reg_t dirfd, reg_t pname, reg_t len, reg_t mode, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> name(len);
memif->read(pname, len, name.data());
return sysret_errno(AT_SYSCALL(mkdirat, dirfd, name.data(), mode));
}
- 功能:实现
mkdirat
系统调用,创建目录。 - 参数:
dirfd
:目录文件描述符。pname
:目录名地址。len
:目录名长度。mode
:目录权限。a4 - a6
:其他参数(未使用)。
- 实现:
- 从
pname
指向的内存读取目录名。 - 调用
mkdirat
函数创建目录。 - 返回处理后的返回值。
- 从
reg_t syscall_t::sys_getcwd(reg_t pbuf, reg_t size, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_getcwd(reg_t pbuf, reg_t size, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<char> buf(size);
char* ret = getcwd(buf.data(), size);
if (ret == NULL)
return sysret_errno(-1);
std::string tmp = undo_chroot(buf.data());
if (size <= tmp.size())
return -ENOMEM;
memif->write(pbuf, tmp.size() + 1, tmp.data());
return tmp.size() + 1;
}
- 功能:实现
getcwd
系统调用,获取当前工作目录。 - 参数:
pbuf
:缓冲区地址。size
:缓冲区大小。a2 - a6
:其他参数(未使用)。
- 实现:
- 调用
getcwd
函数获取当前工作目录。 - 如果获取成功,将路径转换为相对路径并写入
pbuf
指向的内存。 - 返回路径长度。
- 调用
reg_t syscall_t::sys_getmainvars(reg_t pbuf, reg_t limit, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_getmainvars(reg_t pbuf, reg_t limit, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<std::string> args = htif->target_args();
std::vector<target_endian<uint64_t>> words(args.size() + 3);
words[0] = htif->to_target<uint64_t>(args.size());
words[args.size()+1] = target_endian<uint64_t>::zero; // argv[argc] = NULL
words[args.size()+2] = target_endian<uint64_t>::zero; // envp[0] = NULL
size_t sz = (args.size() + 3) * sizeof(words[0]);
for (size_t i = 0; i < args.size(); i++)
{
words[i+1] = htif->to_target<uint64_t>(sz + pbuf);
sz += args[i].length() + 1;
}
std::vector<char> bytes(sz);
memcpy(bytes.data(), words.data(), sizeof(words[0]) * words.size());
for (size_t i = 0; i < args.size(); i++)
strcpy(&bytes[htif->from_target(words[i+1]) - pbuf], args[i].c_str());
if (bytes.size() > limit)
return -ENOMEM;
memif->write(pbuf, bytes.size(), bytes.data());
return 0;
}
- 功能:获取目标参数并写入内存。
- 参数:
pbuf
:缓冲区地址。limit
:缓冲区大小限制。a2 - a6
:其他参数(未使用)。
- 实现:
- 获取目标参数。
- 构造一个包含参数信息的数组。
- 将参数信息和参数值写入缓冲区。
- 如果缓冲区大小超过限制,返回
-ENOMEM
;否则返回 0。
reg_t syscall_t::sys_chdir(reg_t path, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
reg_t syscall_t::sys_chdir(reg_t path, reg_t a1, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
size_t size = 0;
while (memif->read_uint8(path + size++))
;
std::vector<char> buf(size);
for (size_t offset = 0;; offset++)
{
buf[offset] = memif->read_uint8(path + offset);
if (!buf[offset])
break;
}
return sysret_errno(chdir(buf.data()));
}
- 功能:实现
chdir
系统调用,更改当前工作目录。 - 参数:
path
:目录路径地址。a1 - a6
:其他参数(未使用)。
- 实现:
- 从
path
指向的内存读取目录路径。 - 调用
chdir
函数更改当前工作目录。 - 返回处理后的返回值。
- 从
void syscall_t::dispatch(reg_t mm)
void syscall_t::dispatch(reg_t mm)
{
target_endian<reg_t> magicmem[8];
memif->read(mm, sizeof(magicmem), magicmem);
reg_t n = htif->from_target(magicmem[0]);
if (n >= table.size() || !table[n])
throw std::runtime_error("bad syscall #" + std::to_string(n));
magicmem[0] = htif->to_target((this->*table[n])(htif->from_target(magicmem[1]), htif->from_target(magicmem[2]), htif->from_target(magicmem[3]), htif->from_target(magicmem[4]), htif->from_target(magicmem[5]), htif->from_target(magicmem[6]), htif->from_target(magicmem[7])));
memif->write(mm, sizeof(magicmem), magicmem);
}
- 功能:根据系统调用号调用对应的成员函数。
- 参数:
mm
:系统调用信息的内存地址。
- 实现:
- 从
mm
指向的内存读取系统调用号和参数。 - 根据系统调用号查找对应的成员函数。
- 调用成员函数并将结果写回
mm
指向的内存。
- 从
fds_t
类的成员函数
reg_t fds_t::alloc(int fd)
reg_t fds_t::alloc(int fd)
{
reg_t i;
for (i = 0; i < fds.size(); i++)
if (fds[i] == -1)
break;
if (i == fds.size())
fds.resize(i+1);
fds[i] = fd;
return i;
}
- 功能:分配一个新的文件描述符索引。
- 参数:
fd
:实际的文件描述符。
- 实现:
- 查找
fds
数组中第一个空闲的索引。 - 如果没有空闲索引,扩展
fds
数组。 - 将
fd
存储到该索引位置,并返回索引。
- 查找
void fds_t::dealloc(reg_t fd)
void fds_t::dealloc(reg_t fd)
{
fds[fd] = -1;
}
- 功能:释放文件描述符索引。
- 参数:
fd
:文件描述符索引。
- 实现:将
fds[fd]
设置为 -1,表示该索引空闲。
int fds_t::lookup(reg_t fd)
int fds_t::lookup(reg_t fd)
{
if (int(fd) == RISCV_AT_FDCWD)
return AT_FDCWD;
return fd >= fds.size() ? -1 : fds[fd];
}
- 功能:根据索引查找对应的文件描述符。
- 参数:
fd
:文件描述符索引。
- 实现:如果
fd
等于RISCV_AT_FDCWD
,返回AT_FDCWD
;否则,如果fd
在fds
数组范围内,返回fds[fd]
;否则返回 -1。
syscall_t::set_chroot(const char* where)
void syscall_t::set_chroot(const char* where)
{
char buf1[PATH_MAX], buf2[PATH_MAX];
if (getcwd(buf1, sizeof(buf1)) == NULL
|| chdir(where) != 0
|| getcwd(buf2, sizeof(buf2)) == NULL
|| chdir(buf1) != 0)
{
fprintf(stderr, "could not chroot to %s\n", where);
exit(-1);
}
chroot = buf2;
}
- 功能:设置根目录,用于文件路径的转换。
- 参数:
where
:根目录路径。
- 实现:
- 保存当前工作目录。
- 切换到指定的根目录并获取新的工作目录。
- 切换回原来的工作目录。
- 如果任何步骤失败,输出错误信息并退出程序;否则,将新的工作目录设置为
chroot
。
这段代码实现了 syscall_t
类中的 sys_readlinkat
成员函数,其主要功能是模拟 readlinkat
系统调用,用于读取符号链接的内容。
reg_t syscall_t::sys_readlinkat(reg_t dirfd, reg_t ppathname, reg_t ppathname_size, reg_t pbuf, reg_t bufsiz, reg_t a5, reg_t a6)
reg_t syscall_t::sys_readlinkat(reg_t dirfd, reg_t ppathname, reg_t ppathname_size,
reg_t pbuf, reg_t bufsiz, reg_t a5, reg_t a6)
- 参数说明:
dirfd
:目录文件描述符,用于指定相对路径的基准目录。ppathname
:指向符号链接路径名的内存地址。ppathname_size
:符号链接路径名的大小。pbuf
:用于存储符号链接内容的缓冲区的内存地址。bufsiz
:缓冲区的大小。a5
和a6
:未使用的额外参数。
- 读取符号链接路径名
std::vector<char> pathname(ppathname_size);
memif->read(ppathname, ppathname_size, pathname.data());
- 创建一个大小为
ppathname_size
的std::vector<char>
类型的pathname
容器,用于存储符号链接的路径名。 - 调用
memif
对象的read
方法,从ppathname
指向的内存地址读取ppathname_size
字节的数据到pathname
容器中。
- 创建存储符号链接内容的缓冲区
std::vector<char> buf(bufsiz);
- 创建一个大小为
bufsiz
的std::vector<char>
类型的buf
容器,用于存储符号链接的内容。
- 调用
readlinkat
系统调用
ssize_t ret = sysret_errno(AT_SYSCALL(readlinkat, dirfd, pathname.data(), buf.data(), bufsiz));
- 使用
AT_SYSCALL
宏调用readlinkat
系统调用,尝试读取符号链接的内容到buf
容器中。 sysret_errno
函数用于处理系统调用的返回值,如果系统调用失败(返回 -1),则将错误码转换为合适的返回值。
- 将读取的内容写入指定缓冲区
if (ret > 0)
memif->write(pbuf, ret, buf.data());
- 如果
readlinkat
系统调用成功,且返回的字节数大于 0,则调用memif
对象的write
方法,将buf
容器中的内容写入到pbuf
指向的内存地址。
- 返回结果
return ret;
- 返回
readlinkat
系统调用的结果,可能是读取的字节数或者错误码。
下一篇:RISC-V ISA Simulator系列之fesvr<6>