Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support passing argc and argv to target program #185

Merged
merged 4 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ci/gdbstub-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ if [ -z ${GDB} ]; then
exit 1
fi

build/rv32emu --gdbstub build/puzzle.elf &
build/rv32emu -g build/puzzle.elf &
PID=$!

# Before starting GDB, we should ensure rv32emu is still running.
Expand Down
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ CFLAGS += -Wno-unused-label
CFLAGS += -include src/common.h

# Set the default stack pointer
CFLAGS += -D DEFAULT_STACK_ADDR=0xFFFFF000
CFLAGS += -D DEFAULT_STACK_ADDR=0xFFFFE000
# Set the default args starting address
CFLAGS += -D DEFAULT_ARGS_ADDR=0xFFFFF000

OBJS_EXT :=

Expand Down Expand Up @@ -140,7 +142,7 @@ check: $(BIN)
EXPECTED_aes = Dec 15 2022 16:35:12 Test results AES-128 ECB encryption: PASSED! AES-128 ECB decryption: PASSED! AES-128 CBC encryption: PASSED! AES-128 CBC decryption: PASSED! AES-128 CFB encryption: PASSED! AES-128 CFB decryption: PASSED! AES-128 OFB encryption: PASSED! AES-128 OFB decryption: PASSED! AES-128 CTR encryption: PASSED! AES-128 CTR decryption: PASSED! AES-128 XTS encryption: PASSED! AES-128 XTS decryption: PASSED! AES-128 validate CMAC : PASSED! AES-128 Poly-1305 mac : PASSED! AES-128 GCM encryption: PASSED! AES-128 GCM decryption: PASSED! AES-128 CCM encryption: PASSED! AES-128 CCM decryption: PASSED! AES-128 OCB encryption: PASSED! AES-128 OCB decryption: PASSED! AES-128 SIV encryption: PASSED! AES-128 SIV decryption: PASSED! AES-128 GCMSIV encrypt: PASSED! AES-128 GCMSIV decrypt: PASSED! AES-128 EAX encryption: PASSED! AES-128 EAX decryption: PASSED! AES-128 key wrapping : PASSED! AES-128 key unwrapping: PASSED! AES-128 FF1 encryption: PASSED! AES-128 FPE decryption: PASSED! +-> Let's do some extra tests AES-128 OCB encryption: PASSED! AES-128 OCB decryption: PASSED! AES-128 GCMSIV encrypt: PASSED! AES-128 GCMSIV decrypt: PASSED! AES-128 GCMSIV encrypt: PASSED! AES-128 GCMSIV decrypt: PASSED! AES-128 SIV encryption: PASSED! AES-128 SIV decryption: PASSED! AES-128 SIV encryption: PASSED! AES-128 SIV decryption: PASSED! AES-128 EAX encryption: PASSED! AES-128 EAX decryption: PASSED! AES-128 EAX encryption: PASSED! AES-128 EAX decryption: PASSED! AES-128 Poly-1305 mac : PASSED! inferior exit code 0
misalign: $(BIN)
$(Q)$(PRINTF) "Running aes.elf ... ";
$(Q)if [ "$(shell $(BIN) --misalign $(OUT)/aes.elf | uniq)" = "$(EXPECTED_aes)" ]; then \
$(Q)if [ "$(shell $(BIN) -m $(OUT)/aes.elf | uniq)" = "$(EXPECTED_aes)" ]; then \
$(call notice, [OK]); \
else \
$(PRINTF) "Failed.\n"; \
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ a limited number of [GDB Remote Serial Protocol](https://sourceware.org/gdb/onli
You must first build the emulator and set `ENABLE_GDBSTUB` to `1` in the `Makefile` in order
to activate this feature. After that, you might execute it using the command below.
```shell
build/rv32emu --gdbstub <binary>
build/rv32emu -g <binary>
```

The `<binary>` should be the ELF file in RISC-V 32 bit format. Additionally, it is advised
Expand All @@ -162,14 +162,14 @@ command line is available, you can communicate with `rv32emu`.

### Dump registers as JSON

If an option `--dump-registers [filename]` is specified, the emulator outputs registers as JSON format.
If an option `-d [filename]` is specified, the emulator outputs registers as JSON format.
This can be used for tests using the emulator, such as compiler tests.

You can use the option with `--quiet` to use the output directly.
You can use the option with `-q` to use the output directly.

```
# Read the register x10 (a0).
$ build/rv32emu --dump-registers - out.elf --quiet | jq .x10
$ build/rv32emu -d - out.elf -q | jq .x10
```

## Contributing
Expand Down
112 changes: 52 additions & 60 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "elf.h"
#include "state.h"
Expand All @@ -32,6 +33,11 @@ static bool opt_quiet_outputs = false;
/* target executable */
static const char *opt_prog_name = "a.out";

/* target argc and argv */
static int prog_argc;
static char **prog_args;
static const char *optstr = "tgqmhd:a:";

/* enable misaligned memory access */
static bool opt_misaligned = false;

Expand Down Expand Up @@ -91,79 +97,64 @@ static void print_usage(const char *filename)
"RV32I[MA] Emulator which loads an ELF file to execute.\n"
"Usage: %s [options] [filename]\n"
"Options:\n"
" --trace : print executable trace\n"
" -t : print executable trace\n"
#if RV32_HAS(GDBSTUB)
" --gdbstub : allow remote GDB connections (as gdbstub)\n"
" -g : allow remote GDB connections (as gdbstub)\n"
#endif
" --dump-registers [filename]: dump registers as JSON to the "
" -d [filename]: dump registers as JSON to the "
"given file or `-` (STDOUT)\n"
" --quiet : Suppress outputs other than `dump-registers`\n"
" --arch-test [filename] : dump signature to the given file, "
"required by arch-test test\n",
" -q : Suppress outputs other than `dump-registers`\n"
" -a [filename] : dump signature to the given file, "
"required by arch-test test\n"
" -m : enable misaligned memory access\n"
" -h : show this message\n",
filename);
}

static bool parse_args(int argc, char **args)
{
/* parse each argument in turn */
for (int i = 1; i < argc; ++i) {
const char *arg = args[i];
/* parse flags */
if (arg[0] == '-') {
if (!strcmp(arg, "--help"))
return false;
if (!strcmp(arg, "--trace")) {
opt_trace = true;
continue;
}
int opt;
int emu_argc = 0;

while ((opt = getopt(argc, args, optstr)) != -1) {
emu_argc++;

switch (opt) {
case 't':
opt_trace = true;
break;
#if RV32_HAS(GDBSTUB)
if (!strcmp(arg, "--gdbstub")) {
opt_gdbstub = true;
continue;
}
case 'g':
opt_gdbstub = true;
break;
#endif
if (!strcmp(arg, "--dump-registers")) {
opt_dump_regs = true;
if (i + 1 >= argc) {
fprintf(stderr,
"Filename for registers output required by "
"dump-registers.\n");
return false;
}
registers_out_file = args[++i];
continue;
}

if (!strcmp(arg, "--quiet")) {
opt_quiet_outputs = true;
continue;
}

if (!strcmp(arg, "--arch-test")) {
opt_arch_test = true;
if (i + 1 >= argc) {
fprintf(stderr,
"Filename for signature output required by "
"arch-test.\n");
return false;
}
signature_out_file = args[++i];
continue;
}

if (!strcmp(arg, "--misalign")) {
opt_misaligned = true;
continue;
}

/* otherwise, error */
fprintf(stderr, "Unknown argument '%s'\n", arg);
case 'q':
opt_quiet_outputs = true;
break;
case 'h':
return false;
case 'm':
opt_misaligned = true;
break;
case 'd':
opt_dump_regs = true;
registers_out_file = optarg;
emu_argc++;
break;
case 'a':
opt_arch_test = true;
signature_out_file = optarg;
emu_argc++;
break;
default:
return false;
}
/* set the executable */
opt_prog_name = arg;
}

prog_argc = argc - emu_argc - 1;
prog_args = &args[optind]; /* optind points to first non-option string so it
should be target program */
opt_prog_name = prog_args[0];
return true;
}

Expand Down Expand Up @@ -234,7 +225,8 @@ int main(int argc, char **args)
state->break_addr = end->st_value;

/* create the RISC-V runtime */
riscv_t *rv = rv_create(&io, state, !opt_quiet_outputs);
riscv_t *rv =
rv_create(&io, state, prog_argc, prog_args, !opt_quiet_outputs);
if (!rv) {
fprintf(stderr, "Unable to create riscv emulator\n");
return 1;
Expand Down
99 changes: 97 additions & 2 deletions src/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <string.h>

#include "riscv_private.h"
#include "state.h"

/* initialize the block map */
static void block_map_init(block_map_t *map, const uint8_t bits)
Expand Down Expand Up @@ -78,6 +79,8 @@ riscv_word_t rv_get_reg(riscv_t *rv, uint32_t reg)

riscv_t *rv_create(const riscv_io_t *io,
riscv_user_t userdata,
int argc,
char **args,
bool output_exit_code)
{
assert(io);
Expand All @@ -96,7 +99,7 @@ riscv_t *rv_create(const riscv_io_t *io,
block_map_init(&rv->block_map, 10);

/* reset */
rv_reset(rv, 0U);
rv_reset(rv, 0U, argc, args);

return rv;
}
Expand Down Expand Up @@ -124,7 +127,7 @@ void rv_delete(riscv_t *rv)
free(rv);
}

void rv_reset(riscv_t *rv, riscv_word_t pc)
void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args)
{
assert(rv);
memset(rv->X, 0, sizeof(uint32_t) * RV_N_REGS);
Expand All @@ -135,6 +138,98 @@ void rv_reset(riscv_t *rv, riscv_word_t pc)
/* set the default stack pointer */
rv->X[rv_reg_sp] = DEFAULT_STACK_ADDR;

/*
* store argc and args of target program to state->mem
* thus, we can use offset technique for emulating
* 32/64-bit target program on 64-bit emulator
*
* memory layout of arguments as below:
* -----------------------
* | NULL |
* -----------------------
* | envp[n] |
* -----------------------
* | envp[n - 1] |
* -----------------------
* | ... |
* -----------------------
* | envp[0] |
* -----------------------
* | NULL |
* -----------------------
* | args[n] |
* -----------------------
* | args[n - 1] |
* -----------------------
* | ... |
* -----------------------
* | args[0] |
* -----------------------
* | argc |
* -----------------------
*
* TODO: access to envp
*/

int i;
state_t *s = rv_userdata(rv);

/* copy args to DRAM */
uintptr_t args_size = (1 + argc + 1) * sizeof(uint32_t);
uintptr_t args_bottom = DEFAULT_ARGS_ADDR;
uintptr_t args_top = args_bottom - args_size;
args_top &= 16;

/* argc */
uintptr_t *args_p = (uintptr_t *) args_top;
memory_write(s->mem, (uintptr_t) args_p, (void *) &argc, sizeof(int));
args_p++;

/* args */
size_t args_space[256]; /* for calculating the offset of args when pushing
to stack */
size_t args_space_idx = 0;
size_t args_len;
size_t args_len_total = 0;
for (i = 0; i < argc; i++) {
const char *arg = args[i];
args_len = strlen(arg);
memory_write(s->mem, (uintptr_t) args_p, (void *) arg,
(args_len + 1) * sizeof(uint8_t));
args_space[args_space_idx++] = args_len + 1;
args_p = (uintptr_t *) ((uintptr_t) args_p + args_len + 1);
args_len_total += args_len + 1;
}
args_p = (uintptr_t *) ((uintptr_t) args_p - args_len_total);
args_p--; /* point to argc */

/* ready to push argc, args to stack */
int stack_size = (1 + argc + 1) * sizeof(uint32_t);
uintptr_t stack_bottom = (uintptr_t) rv->X[rv_reg_sp];
uintptr_t stack_top = stack_bottom - stack_size;
stack_top &= -16;

/* argc */
uintptr_t *sp = (uintptr_t *) stack_top;
memory_write(s->mem, (uintptr_t) sp,
(void *) (s->mem->mem_base + (uintptr_t) args_p), sizeof(int));
args_p++;
sp = (uintptr_t *) ((uint32_t *) sp + 1); /* keep argc and args[0] within
one word due to RV32 ABI */

/* args */
for (i = 0; i < argc; i++) {
uintptr_t offset = (uintptr_t) args_p;
memory_write(s->mem, (uintptr_t) sp, (void *) &offset,
sizeof(uintptr_t));
args_p = (uintptr_t *) ((uintptr_t) args_p + args_space[i]);
sp = (uintptr_t *) ((uint32_t *) sp + 1);
}
memory_fill(s->mem, (uintptr_t) sp, sizeof(uint32_t), 0);

/* reset sp pointing to argc */
rv->X[rv_reg_sp] = stack_top;

/* reset the csrs */
rv->csr_mtvec = 0;
rv->csr_cycle = 0;
Expand Down
4 changes: 3 additions & 1 deletion src/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,15 @@ typedef struct {
/* create a RISC-V emulator */
riscv_t *rv_create(const riscv_io_t *io,
riscv_user_t user_data,
int argc,
char **args,
bool output_exit_code);

/* delete a RISC-V emulator */
void rv_delete(riscv_t *rv);

/* reset the RISC-V processor */
void rv_reset(riscv_t *rv, riscv_word_t pc);
void rv_reset(riscv_t *rv, riscv_word_t pc, int argc, char **args);

#if RV32_HAS(GDBSTUB)
/* Run the RISC-V emulator as gdbstub */
Expand Down
2 changes: 1 addition & 1 deletion tests/arch-test-target/rv32emu/riscof_rv32emu.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def runTests(self, testList):
# echo statement.
if self.target_run:
# set up the simulation command. Template is for spike. Please change.
simcmd = self.dut_exe + ' --arch-test {0} {1}'.format(sig_file, elf)
simcmd = self.dut_exe + ' -a {0} {1}'.format(sig_file, elf)
else:
simcmd = 'echo "NO RUN"'

Expand Down
3 changes: 1 addition & 2 deletions tests/readelf/readelf.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ void read_elf_data(const char *path, int flags)

int main(int argc, char *argv[])
{
/* FIXME: use correct argv[0] */
read_elf_data("build/readelf.elf", FLAG_ELF_HEADER | FLAG_SECTION_HEADER);
read_elf_data(argv[0], FLAG_ELF_HEADER | FLAG_SECTION_HEADER);

return 0;
}