Below link is pretty good.

 

https://www.cs.cornell.edu/courses/cs3410/2019sp/riscv/interpreter/#

 

RISC-V Interpreter

Credit to Danny Qiu for the creation of the original MIPS interpreter.

www.cs.cornell.edu

 

 

Code walkthrough 

 

MP:FFFFFFFF800030B8|ret_from_exception:     c.ldsp     x8,0x100(x2)   ; x8,256(x2)
MP:FFFFFFFF800030BA|                        csrci      sstatus,0x2   ; sstatus,2
MP:FFFFFFFF800030BE|                        andi       x8,x8,0x100   ; x8,x8,256
MP:FFFFFFFF800030C2|                        c.bnez     x8,0xFFFFFFFF800030D8   ; x8,restore_all
MP:FFFFFFFF800030C4|resume_userspace:       ld         x8,0x0(x4)    ; x8,0(x4)
MP:FFFFFFFF800030C8|                        andi       x9,x8,0x60E   ; x9,x8,1550
MP:FFFFFFFF800030CC|                        c.bnez     x9,0xFFFFFFFF8000312A   ; x9,work_pending
MP:FFFFFFFF800030CE|                        c.addi4spn x8,0x120      ; x8,288
MP:FFFFFFFF800030D0|                        sd         x8,0x10(x4)   ; x8,16(x4)
MP:FFFFFFFF800030D4|                        csrw       sscratch,x4
MP:FFFFFFFF800030D8|restore_all:            c.ldsp     x10,0x100(x2)   ; x10,256(x2)
MP:FFFFFFFF800030DA|                        c.ldsp     x12,0x0(x2)   ; x12,0(x2)
MP:FFFFFFFF800030DC|                        sc.d       x0,x12,(x2)
MP:FFFFFFFF800030E0|                        csrw       sstatus,x10
MP:FFFFFFFF800030E4|                        csrw       sepc,x12
MP:FFFFFFFF800030E8|                        c.ldsp     x1,0x8(x2)    ; x1,8(x2)
MP:FFFFFFFF800030EA|                        c.ldsp     x3,0x18(x2)   ; x3,24(x2)
MP:FFFFFFFF800030EC|                        c.ldsp     x4,0x20(x2)   ; x4,32(x2)
MP:FFFFFFFF800030EE|                        c.ldsp     x5,0x28(x2)   ; x5,40(x2)
MP:FFFFFFFF800030F0|                        c.ldsp     x6,0x30(x2)   ; x6,48(x2)
MP:FFFFFFFF800030F2|                        c.ldsp     x7,0x38(x2)   ; x7,56(x2)
MP:FFFFFFFF800030F4|                        c.ldsp     x8,0x40(x2)   ; x8,64(x2)
...
MP:FFFFFFFF80003122|                        c.ldsp     x31,0xF8(x2)   ; x31,248(x2)
MP:FFFFFFFF80003124|                        c.ldsp     x2,0x10(x2)   ; x2,16(x2)
MP:FFFFFFFF80003126|                        sret

https://elixir.bootlin.com/linux/v6.8-rc1/source/arch/riscv/kernel/entry.S
SYM_CODE_START(handle_exception)
/*
 * If coming from userspace, preserve the user thread pointer and load
 * the kernel thread pointer.  If we came from the kernel, the scratch
 * register will contain 0, and we should continue on the current TP.
 */
csrrw tp, CSR_SCRATCH, tp
bnez tp, .Lsave_context
...
/*
 * MSB of cause differentiates between
 * interrupts and exceptions
 */
bge s4, zero, 1f

/* Handle interrupts */
tail do_irq


https://elixir.bootlin.com/linux/v6.8-rc1/source/arch/riscv/kernel/traps.c#L366
asmlinkage void noinstr do_irq(struct pt_regs *regs)
{
irqentry_state_t state = irqentry_enter(regs);

if (IS_ENABLED(CONFIG_IRQ_STACKS) && on_thread_stack())
call_on_irq_stack(regs, handle_riscv_irq);
else
handle_riscv_irq(regs);

irqentry_exit(regs, state);
}

https://elixir.bootlin.com/linux/v6.8-rc1/source/arch/riscv/kernel/traps.c#L355
static void noinstr handle_riscv_irq(struct pt_regs *regs)
{
struct pt_regs *old_regs;

irq_enter_rcu();
old_regs = set_irq_regs(regs);
handle_arch_irq(regs);
set_irq_regs(old_regs);
irq_exit_rcu();
}

https://elixir.bootlin.com/linux/v6.8-rc1/source/drivers/irqchip/irq-riscv-intc.c#L127
(where)
static int __init riscv_intc_init_common(struct fwnode_handle *fn)
{
...
rc = set_handle_irq(&riscv_intc_irq);
if (rc) {
pr_err("failed to set irq handler\n");
return rc;
}

https://elixir.bootlin.com/linux/v6.8-rc1/source/drivers/irqchip/irq-riscv-intc.c#L23
static asmlinkage void riscv_intc_irq(struct pt_regs *regs)
{
unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG;

if (unlikely(cause >= BITS_PER_LONG))
panic("unexpected interrupt cause");

generic_handle_domain_irq(intc_domain, cause);
}

https://elixir.bootlin.com/linux/v6.8-rc1/source/kernel/irq/irqdesc.c#L726
int generic_handle_domain_irq(struct irq_domain *domain, unsigned int hwirq)
{
return handle_irq_desc(irq_resolve_mapping(domain, hwirq));
}

Interrupt handling routines are architecture-dependent. The way interrupts are managed and processed can vary significantly between different computer architectures. Interrupt handling routines can be different between RISC-V and Armv8.

Understanding interrupt handling routines is crucial when dealing with real-time operating systems (RTOS) and the Linux kernel. Since RTOS and Linux kernel rely heavily on interrupt handling to efficiently manage and respond to events in a timely manner.

Here's a general outline of how interrupt handling might be implemented in Linux kernel based on RISC-V architecture:


[1] When interrupt is triggered from peripheral like device signals interrupt, interrupt handling routine is performed to handle interrupt request. Based on RISC-V architecture, 'handle_exception' is exception handler.

arch/riscv/kernel/entry.S
SYM_CODE_START(handle_exception)
...
la ra, ret_from_exception

/*
 * MSB of cause differentiates between
 * interrupts and exceptions
 */
bge s4, zero, 1f

/* Handle interrupts */
tail do_irq

[2] The call do_irq() function is made inside handle_exception as above.

do_irq() -> handle_riscv_irq() -> riscv_intc_irq() -> generic_handle_domain_irq()

[3] If IRQ is considered to be inter-processor interrupt, sbi_ipi_handle() is called.
Where sbi_ipi_handle() is set to chained handler in arch/riscv/kernel/sbi-ipi.c
 
[4] If IRQ is triggered from peripheral device(like sensor, touch, etc), plic_handle_irq() is called.
Where plic_handle_irq() is set to chained handler in drivers/irqchip/irq-sifive-plic.c.

[5] It is known that timer interrupt is handled in RISC-V in different ways. If timer IRQ is triggered, handle_percpu_devid_irq () is called.

The subroutine of [3],[4][5] will call the corresponding interrupt handler to take care of interrupt handling.

          <idle>-0       [000] d.h..   643.355357: irq_handler_entry: irq=16 name=virtio2
          <idle>-0       [000] d.h..   643.355397: <stack trace>
 => trace_event_raw_event_irq_handler_entry+0x96/0xf6
 => __traceiter_irq_handler_entry+0x2e/0x44
 => __handle_irq_event_percpu+0x13a/0x196
 => handle_irq_event+0x78/0xd2
 => handle_fasteoi_irq+0xa8/0x236
 => generic_handle_domain_irq+0x28/0x36
 => plic_handle_irq+0x86/0xfe
 => generic_handle_domain_irq+0x28/0x36
 => riscv_intc_irq+0x34/0x4c
 => do_irq+0x56/0x8e
 => ret_from_exception+0x0/0x64
 => default_idle_call+0x3a/0xd0
 => do_idle+0x214/0x23e
 => cpu_startup_entry+0x2e/0x30
 => kernel_init+0x0/0x168
 => arch_post_acpi_subsys_init+0x0/0x28
 => console_on_rootfs+0x0/0x70

'RISC-V' 카테고리의 다른 글

[RISC-V] system call return (s mode -> u mode)  (0) 2024.02.29
[RISC-V]: Interrupt handling - code walkthrough  (0) 2024.01.29

'current' macro is mainly used to read 'task_struct' address of current process.

arch/riscv/include/asm/current.h
register struct task_struct *riscv_current_is_tp __asm__("tp");

static __always_inline struct task_struct *get_current(void)
{
return riscv_current_is_tp;
}

#define current get_current()

Body of current is 'get_current()' macro. We can deduce that "tp"(X4) holds start address of current.

Core routine of context switching in RISC-V Linux kernel is below.

ENTRY(__switch_to)
/* Save context into prev->thread */
li    a4,  TASK_THREAD_RA
add   a3, a0, a4
add   a4, a1, a4
REG_S ra,  TASK_THREAD_RA_RA(a3)
REG_S sp,  TASK_THREAD_SP_RA(a3)
REG_S s0,  TASK_THREAD_S0_RA(a3)
REG_S s1,  TASK_THREAD_S1_RA(a3)
REG_S s2,  TASK_THREAD_S2_RA(a3)
REG_S s3,  TASK_THREAD_S3_RA(a3)
REG_S s4,  TASK_THREAD_S4_RA(a3)
REG_S s5,  TASK_THREAD_S5_RA(a3)
REG_S s6,  TASK_THREAD_S6_RA(a3)
REG_S s7,  TASK_THREAD_S7_RA(a3)
REG_S s8,  TASK_THREAD_S8_RA(a3)
REG_S s9,  TASK_THREAD_S9_RA(a3)
REG_S s10, TASK_THREAD_S10_RA(a3)
REG_S s11, TASK_THREAD_S11_RA(a3)
/* Restore context from next->thread */
REG_L ra,  TASK_THREAD_RA_RA(a4)
REG_L sp,  TASK_THREAD_SP_RA(a4)
REG_L s0,  TASK_THREAD_S0_RA(a4)
REG_L s1,  TASK_THREAD_S1_RA(a4)
REG_L s2,  TASK_THREAD_S2_RA(a4)
REG_L s3,  TASK_THREAD_S3_RA(a4)
REG_L s4,  TASK_THREAD_S4_RA(a4)
REG_L s5,  TASK_THREAD_S5_RA(a4)
REG_L s6,  TASK_THREAD_S6_RA(a4)
REG_L s7,  TASK_THREAD_S7_RA(a4)
REG_L s8,  TASK_THREAD_S8_RA(a4)
REG_L s9,  TASK_THREAD_S9_RA(a4)
REG_L s10, TASK_THREAD_S10_RA(a4)
REG_L s11, TASK_THREAD_S11_RA(a4)
/* Swap the CPU entry around. */
lw a3, TASK_TI_CPU(a0)
lw a4, TASK_TI_CPU(a1)
sw a3, TASK_TI_CPU(a1)
sw a4, TASK_TI_CPU(a0)
/* The offset of thread_info in task_struct is zero. */
move tp, a1
ret
ENDPROC(__switch_to)
   

'move tp, a1' instruction corresponding to 'move X4, X11' is added to save the starting address of 'task_struct.

Body of 'alloc_fd' function.

fs/file.c
static int alloc_fd(unsigned start, unsigned end, unsigned flags)
{
struct files_struct *files = current->files;

The 'current' macro is used as a way to read starting address of 'task_struct'.

SP:FFFFFFFF8014E5B0|711D      alloc_fd: c.addi16sp -0x60         ; -96
SP:FFFFFFFF8014E5B2|E8A2                c.sdsp     x8,0x50(x2)   ; x8,80(x2)
SP:FFFFFFFF8014E5B4|F852                c.sdsp     x20,0x30(x2)   ; x20,48(x2)
SP:FFFFFFFF8014E5B6|F456                c.sdsp     x21,0x28(x2)   ; x21,40(x2)
SP:FFFFFFFF8014E5B8|F05A                c.sdsp     x22,0x20(x2)   ; x22,32(x2)
SP:FFFFFFFF8014E5BA|EC5E                c.sdsp     x23,0x18(x2)   ; x23,24(x2)
SP:FFFFFFFF8014E5BC|E862                c.sdsp     x24,0x10(x2)   ; x24,16(x2)
SP:FFFFFFFF8014E5BE|EC86                c.sdsp     x1,0x58(x2)   ; x1,88(x2)
SP:FFFFFFFF8014E5C0|E4A6                c.sdsp     x9,0x48(x2)   ; x9,72(x2)
SP:FFFFFFFF8014E5C2|E0CA                c.sdsp     x18,0x40(x2)   ; x18,64(x2)
SP:FFFFFFFF8014E5C4|FC4E                c.sdsp     x19,0x38(x2)   ; x19,56(x2)
SP:FFFFFFFF8014E5C6|E466                c.sdsp     x25,0x8(x2)   ; x25,8(x2)
SP:FFFFFFFF8014E5C8|1080                c.addi4spn x8,0x60       ; x8,96
SP:FFFFFFFF8014E5CA|67823903            ld         x18,0x678(x4)   ; x18,1656(x4)

After 'ld x18,0x678(x4)' instruction is executed, x18 is holding the &(current->files) where 0x678 is offset of 'struct task_struct.files.
This is true because x4 holds the entry address of 'task_struct'.

fs/file.c
int __close_range(unsigned fd, unsigned max_fd, unsigned int flags)
{
struct task_struct *me = current;
struct files_struct *cur_fds = me->files, *fds = NULL;
 
      SP:FFFFFFFF8014EC14|__close_range:    c.addi16sp -0x60         ; -96
      SP:FFFFFFFF8014EC16|                  c.sdsp     x8,0x50(x2)   ; x8,80(x2)
      SP:FFFFFFFF8014EC18|                  c.sdsp     x1,0x58(x2)   ; x1,88(x2)
      SP:FFFFFFFF8014EC1A|                  c.sdsp     x21,0x28(x2)   ; x21,40(x2)
      SP:FFFFFFFF8014EC1C|                  c.addi4spn x8,0x60       ; x8,96
      SP:FFFFFFFF8014EC1E|                  ld         x15,0x460(x4)   ; x15,1120(x4)
      SP:FFFFFFFF8014EC22|                  sd         x15,-0x58(x8)   ; x15,-88(x8)
      SP:FFFFFFFF8014EC26|                  c.li       x15,0x0       ; x15,0

From:
https://msyksphinz-self.github.io/riscv-isadoc/html/index.html

...
MP:FFFFFFFF80002FEA|                    csrrc      x9,sstatus,x5  // atomic read and clear bits in CSR.
MP:FFFFFFFF80002FEE|                    csrr       x18,sepc
MP:FFFFFFFF80002FF2|                    csrr       x19,stval
MP:FFFFFFFF80002FF6|                    csrr       x20,scause
MP:FFFFFFFF80002FFA|                    csrr       x21,sscratch

void handle_exception() 
{
   word scause;
   word offset_vect;
   void *exception_func;
   bool interrupt_status;

   scause = sys_csr_reg_read(scause); // csrr       x20,scause

   interrupt_status = scause >> (SXLEN -1); // 

   if ( interrupt_status == true) {
       la_reg = ret_from_exception;  // la ra, ret_from_exception
       handle_arch_irq(); 
   }
   else {
      la_reg = ret_from_exception;  // la ra, ret_from_exception
      if (scause & 0b1000) {
         handle_syscall();
      }
      else {
         offset_vect = get_exception_reason(scause);
         exception_func = excp_vect_table + offset_vect;
         (void*)exception_func; // c.jr x5
      }
   }
}

From: https://doc-en.rvspace.org/VisionFive2/SWTRM/VisionFive2_SW_TRM/swtrm_compiling_linux_kernel%20-%20vf2.html

이번 포스트에서는 RISC-V 툴체인을 설치해 리눅스 커널을 빌드하는 방법을 소개합니다.

먼저 RISC-V 툴체인을 설치하는 명령어를 입력합시다.

RISC-V 툴체인 소스를 내려받기  

다음 명령어를 입력해 RISC-V 툴체인 소스를 내려받습니다.

$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
 
아래는 리눅스 터미널에서 위 명령어를 입력한 후의 출력 결과입니다. 소스를 내려받는데 1시간 정도 걸리네요.

austindh.kim:~/src/risc-v_toolchain$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
Cloning into 'riscv-gnu-toolchain'...
remote: Enumerating objects: 21, done.
remote: Counting objects: 100% (21/21), done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 7795 (delta 7), reused 16 (delta 4), pack-reused 7774
Receiving objects: 100% (7795/7795), 4.75 MiB | 970.00 KiB/s, done.
Resolving deltas: 100% (3940/3940), done.
Submodule 'qemu' (https://git.qemu.org/git/qemu.git) registered for path 'qemu'
Submodule 'riscv-binutils' (https://github.com/riscv/riscv-binutils-gdb.git) registered for path 'riscv-binutils'
Submodule 'riscv-dejagnu' (https://github.com/riscv/riscv-dejagnu.git) registered for path 'riscv-dejagnu'
Submodule 'riscv-gcc' (https://github.com/riscv/riscv-gcc.git) registered for path 'riscv-gcc'
Submodule 'riscv-gdb' (https://github.com/riscv/riscv-binutils-gdb.git) registered for path 'riscv-gdb'
...

시간이 오래 걸리니 밥 먹기 전에 명령어를 입력해 놔야 겠습니다.

RISC-V 툴체인 소스 빌드하고 설치하기 

소스를 전부 내려받은 다음에 아래와 같이 디렉터리를 생성합니다.

$ mkdir -p opt/riscv
$ mkdir build
$ cd build
$ ../configure --prefix=/home001/austindh.kim/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv --enable-multilib

혹은

$ ../configure --prefix=/home001/austindh.kim/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv  



아래는 리눅스 터미널에서 위 명령어를 실행한 후 출력 결과입니다. 빌드되는데 2시간 정도가 소요됩니다.

austindh.kim:~/src/risc-v_toolchain$ mkdir -p opt/riscv
austindh.kim:~/src/risc-v_toolchain$ mkdir build
austindh.kim:~/src/risc-v_toolchain$ cd build
austindh.kim:~/src/risc-v_toolchain/build$ ../configure --prefix=/home001/austindh.kim/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv 
...

 

이번에는 다음 명령어를 실행해서 빌드를 수행합니다.

 

austindh.kim:~/src/risc-v_toolchain/build$ make linux


빌드가 끝나고 "~src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv/bin" 디렉터리로 이동하니, 다음과 같은 파일이 생성됐음을 확인할 수 있습니다.

austindh.kim:~/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv/bin$ ls
riscv64-unknown-linux-gnu-addr2line  riscv64-unknown-linux-gnu-gcc         riscv64-unknown-linux-gnu-gdb            riscv64-unknown-linux-gnu-objcopy  ... 
riscv64-unknown-linux-gnu-elfedit    riscv64-unknown-linux-gnu-gcov-dump   riscv64-unknown-linux-gnu-lto-dump       riscv64-unknown-linux-gnu-strings
riscv64-unknown-linux-gnu-g++        riscv64-unknown-linux-gnu-gcov-tool   riscv64-unknown-linux-gnu-nm             riscv64-unknown-linux-gnu-strip

이제 RISC-V 툴체인 소스 빌드를 마무리했습니다.

 RISC-V 툴체인으로 리눅스 커널 빌드하기 

다음 명령어를 입력해 리눅스 커널 소스 코드를 내려 받습니다.

$ git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/next/linux-next

소스를 내려 받은 다음에 확인한 디렉터리 정보는 다음과 같습니다.

austindh.kim:~/src/dev_kernel/59_linux_kernel$ ls
linux-next

'riscv_kernel_build.sh' 이름으로 셸 스크립트 파일을 생성한 다음에, 아래 빌드 스크립트를 입력합니다.

 

#!/bin/bash

export PATH=$PATH:/home001/austindh.kim/src/risc-v_toolchain/riscv-gnu-toolchain/opt/riscv/bin

echo "configure build output path"
TOP_PATH=$( cd "$(dirname "$0")" ; pwd )
OUTPUT="$TOP_PATH/out"

BUILD_LOG="$TOP_PATH/rpi_build_log.txt"

OUTPUT_PATH=$( cd "$(dirname "$0")" ; pwd )
OUTPUT="$OUTPUT_PATH/out"

pushd linux-next > /dev/null

# configure defconfig
make ARCH=riscv O=$OUTPUT CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig -j16  2>&1

# build kernel with riscv tool-chain
make ARCH=riscv O=$OUTPUT CROSS_COMPILE=riscv64-unknown-linux-gnu- modules dtbs -j16  2>&1 | tee $BUILD_LOG

popd > /dev/null


위 빌드 스크립트에서 가장 중요한 명령어는 아래와 같습니다.


# configure defconfig
make ARCH=riscv O=$OUTPUT CROSS_COMPILE=riscv64-unknown-linux-gnu- defconfig -j16  2>&1

# build kernel with riscv tool-chain
make ARCH=riscv O=$OUTPUT CROSS_COMPILE=riscv64-unknown-linux-gnu- modules dtbs -j16  2>&1 | tee $BUILD_LOG



'arch/riscv/configs/defconfig' 파일을 다음과 같이 수정해 vmlinux에 디버깅 심벌이 추가되도록 합시다.

diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
index d58c93e..6ea8e83 100644
--- a/arch/riscv/configs/defconfig
+++ b/arch/riscv/configs/defconfig
@@ -20,6 +20,7 @@ CONFIG_SMP=y
 CONFIG_JUMP_LABEL=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
+CONFIG_DEBUG_INFO=y
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y


'riscv_kernel_build.sh' 빌드 셸 스크립트를 실행해, RISCV 툴 체인으로 커널을 빌드합니다.

아래는 'riscv_kernel_build.sh' 빌드 셸 스크립트를 터미널에서 실행한 출력 화면입니다.
austindh.kim:~/src/dev_kernel/59_linux_kernel$ ./riscv_kernel_build.sh
configure build output path
make[1]: Entering directory '/home001/austindh.kim/src/dev_kernel/59_linux_kernel/out'
  GEN     Makefile
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
...
  KSYMS   .tmp_vmlinux.kallsyms2.S
  AS      .tmp_vmlinux.kallsyms2.S
  LD      vmlinux
  SYSMAP  System.map
  MODPOST Module.symvers
  CC [M]  fs/nfs/flexfilelayout/nfs_layout_flexfiles.mod.o
  LD [M]  fs/nfs/flexfilelayout/nfs_layout_flexfiles.ko


'out' 폴더를 가니 제대로 커널이 빌드됐음을 확인할 수 있습니다.

austindh.kim:~/src/dev_kernel/59_linux_kernel/out$ ls
arch   crypto   include  kernel    mm                       modules.order   scripts   source      virt       vmlinux.symvers
block  drivers  init     lib       modules.builtin          Module.symvers  security  System.map  vmlinux
certs  fs       ipc      Makefile  modules.builtin.modinfo  net             sound     usr         vmlinux.o


RISC-V 바이너리 유틸리티 사용해보기 

이번엔 다음 명령어를 입력해 vmlinux의 셉션 정보를 확인해보겠습니다.

  
$ ./riscv64-unknown-linux-gnu-objdump -x vmlinux | more


아래는 './riscv64-unknown-linux-gnu-objdump -x vmlinux | more' 빌드 셸 스크립트를 터미널에서 실행한 출력 화면입니다.

austindh.kim:~/src/dev_kernel/59_linux_kernel/out$ ./riscv64-unknown-linux-gnu-objdump -x vmlinux | more

vmlinux:     file format elf64-littleriscv
vmlinux
architecture: riscv:rv64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0xffffffe000000000

Program Header:
    LOAD off    0x0000000000001000 vaddr 0xffffffe000000000 paddr 0x0000000000000000 align 2**12
         filesz 0x0000000000025af4 memsz 0x0000000000025af4 flags r-x
    LOAD off    0x0000000000027000 vaddr 0xffffffe000026000 paddr 0x0000000000026000 align 2**12
         filesz 0x0000000000019de8 memsz 0x0000000000019de8 flags rwx
    LOAD off    0x0000000000041000 vaddr 0xffffffe000200000 paddr 0x0000000000200000 align 2**12
         filesz 0x00000000006ce0a2 memsz 0x00000000006ce0a2 flags r-x
    LOAD off    0x0000000000710000 vaddr 0xffffffe000a00000 paddr 0x0000000000a00000 align 2**12
         filesz 0x000000000022a9fc memsz 0x000000000022a9fc flags rw-
    LOAD off    0x000000000093b000 vaddr 0xffffffe000e00000 paddr 0x0000000000e00000 align 2**12
         filesz 0x0000000000002400 memsz 0x0000000000002400 flags r--
    LOAD off    0x000000000093e000 vaddr 0xffffffe001000000 paddr 0x0000000001000000 align 2**12
         filesz 0x00000000000e19b4 memsz 0x0000000000131920 flags rw-
    NOTE off    0x000000000093a9c0 vaddr 0xffffffe000c2a9c0 paddr 0x0000000000c2a9c0 align 2**2
         filesz 0x000000000000003c memsz 0x000000000000003c flags r--
   STACK off    0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**4
         filesz 0x0000000000000000 memsz 0x0000000000000000 flags rw-

  
출력 결과를 보니 아키텍처의 정보는 'riscv:rv64'이고 스타트업 코드의 위치는 0xffffffe000000000 주소임을 알 수 있습니다.

+ Recent posts