Implement RISC-V dynamic linking - #323
Conversation
|
As the The updated GitHub Actions also downloads the 32-bit variant to validate RISC-V dynamic linking. |
Evaluate Run 32-bit applications on 64-bit Linux kernel, which is exactly RV32-on-RV64 userspace compatibility, not emulation. |
I'm not sure whether I understand correctly. Do you mean that the proposed changes should be verified on a RISC-V machine? |
See sysprog21/kbox#18 |
446b538 to
44f2f47
Compare
|
RISE RISC-V Runners' documentation explicitly states that binaries must be compiled for riscv64. According to the FAQ - What architectures are supported?.
I created another branch ( Based on both the documentation and my test, it appears that RISE RISC-V runners lack support for 32-bit executables. |
|
RISC-V calling convention:
Item 1: is done by the register allocation phase.The register allocator uses virtual registers (vreg0-vreg7) to allocate reigsters for arguments when encountering a function call. vreg0-vreg7 will be mapped to a0-a7, so first eight arguments are naturally passed to these registers. Since the current shecc only supports up to 8 arguments, no extra arguments need to be passed to the stack. Thus, we can skip this handling. Item 2: is ensured by the RISC-V code generator.When handling the stack pointer, the code generator will guarantee that Item 3:
Therefore, this item can also be considered complete. Further details and explanations can be found in |
2599d74 to
ee9db4a
Compare
de67942 to
1af9140
Compare
|
Since there may be additional requirements to address, I am still keeping this pull request as a draft currently, and will continue to make improvements if necessary. |
5f8b272 to
9be9f62
Compare
Introduce ELF_MACHINE_ARM32 (0x28) and ELF_MACHINE_RV32 (0xf3) to support architecture-specific logic in future developments.
9be9f62 to
14220c1
Compare
About
|
14220c1 to
4a556ea
Compare
jserv
left a comment
There was a problem hiding this comment.
Review notes on the RV32 dynamic linking implementation. Findings are inlined at their locations.
| /* Call __libc_start_main() via PLT[1] */ | ||
| ofs = (dynamic_sections.elf_plt_start + PLT_FIXUP_SIZE) - | ||
| (elf_code_start + elf_code->size); | ||
| emit(__jal(__ra, ofs)); |
There was a problem hiding this comment.
This jal to PLT[1] (and the extern calls at OP_call / OP_address_of_func) spans .text + .rodata + .rel(a).plt to reach .plt, so the offset is roughly code_size + rodata_size. rv_encode_J in src/riscv.c has no range check, unlike rv_encode_I/S/B which all fatal() on overflow. Past the J-type reach of ±1 MB the high bits are silently dropped and the jump lands at the wrong address, a silent miscompile the small hello/fib tests never approach. Add the same guard to rv_encode_J: if (imm > 1048575 || imm < -1048576) fatal("Offset too large"); (src/riscv.c is not part of this PR, so this comment is anchored on the call site.)
| \#define ELF_MACHINE 0xf3\n$\ | ||
| \#define ELF_FLAGS 0\n$\ | ||
| \#define DYN_LINKER \"/lib/ld-linux.so.3\"\n$\ | ||
| \#define DYN_LINKER \"/lib/ld-linux-riscv32-ilp32d.so.1\"\n$\ |
There was a problem hiding this comment.
The interpreter is ld-linux-riscv32-ilp32d.so.1 (double-float ABI) while ELF_FLAGS remains 0, which declares soft-float. The object's e_flags should carry EF_RISCV_FLOAT_ABI_DOUBLE (0x4) to match the ilp32d loader, or the target should select an ILP32 soft-float linker/toolchain. As written the ABI tag contradicts the chosen interpreter and may be rejected or misinterpreted by strict tools.
| elf_write_int(dynamic_sections.elf_got, 0); | ||
| elf_write_int(dynamic_sections.elf_got, 0); | ||
| for (int i = PTR_SIZE * 3; i < dynamic_sections.got_size; i += PTR_SIZE) | ||
| switch (ELF_MACHINE) { |
There was a problem hiding this comment.
GOT init relies on implicit ELF_MACHINE_ARM32 -> ELF_MACHINE_RV32 case fallthrough. It is correct for both arches (ARM writes GOT[0]=&.dynamic then two zeros; RV32 writes two zeros), but it emits -Wimplicit-fallthrough, and a stray break or a case reorder would silently corrupt ARM GOT init with no failure on the RV32 CI leg. Add a /* fall through */ annotation, or write the shared zero entries explicitly per case.
| exit_code=$? | ||
|
|
||
| # Check result | ||
| if [[ $exit_code -eq 0 ]]; then |
There was a problem hiding this comment.
A test is counted as passed on exit_code == 0 alone; an expected_output mismatch is only a warning, and only under VERBOSE. A broken external-call path that still exits 0 passes silently, which is exactly the failure mode this suite is meant to catch. Treat a non-matching expected_output as a failure.
b90b394 to
7e72003
Compare
| - `s1` - `s11` are not used by the code generator, so they are unnecessary to be processed. | ||
| - `s0` is unused during static linking and is only accessed at the program's entry point under dynamic linking, there is no need to save this register. |
There was a problem hiding this comment.
Currently, s0 and s1 may be used under dynamic linking. I will update the above descriptions.
This commit primarily improves the ELF handling and code generator to
enable the compiler to produce a dynamically linked executable targeting
the RISC-V architecture.
- Update the selected dynamic linker path and the ABI flag.
- Allow the ELF handling to generate RELA relocation table.
- Use REL relocation when the target architecture is Arm. Othereise,
use RELA relocation for RISC-V.
- Improve GOT generation process.
- Arm: reserve three entries.
- RISC-V: reserve two entries.
- Implement PLT generation for RISC-V.
- The generation process follows the RISC-V ABI. The first PLT entry
uses 8 instructions to call '_dl_runtime_resolve'. The subsequent
entry uses 4 instructions to perform an indirect function call via
GOT.
- Refine the function call handling for the RISC-V code generator.
- Perform a direct call for internal functions
- Otherwise, use PLT table to peform an indirect call for external
functions.
- Enhance the build system:
- Allow the build system to generate dynamically linked compilers when
targeting the RISC-V architecture.
- Detect the sysroot path of the RISC-V GNU toolchain automatically.
- Remove the unnecessary 'got_offset' field from 'struct func'.
Modify the 'update-snapshots' and 'check-snapshots' make targets to include generation and validation of new snapshots for the RISC-V architecture using dynamic linking.
The update workflow now downloads a RISC-V GNU toolchain to provide necessary dependencies and validate the dynamically linked compiler targeting the RISC-V architecture. This update also improves the download step so that it retrieves the ARM/RISC-V GNU toolchain only when verifying the dynamic linking mode.
Because two architecture-specific makefile fragments contain similar snippets for locating the cross-compilation toolchain path, this commit consolidates them into a shared build logic, thereby reducing code duplication.
A new shell script is introduced to validate whether generated executables targeting RISC-V correct comply with the RISC-V ABI. The tests include: - Parameter Passing: tests function calls with different numbers of arguments. - Stack Alignment: validates whether the stack is always 16-byte aligned when calling a function. - Return Values: confirms if the return value is correct after a function returns. - External Calls: verifies whether dynamically linked programs can call external functions. - Register Preservation: verify whether the contents of function argument registers are properly preserved when calling a function. - Structure Passing: validates if a small structure object can be passed correctly.
- Expand instructions on utilizing dynamic linking for both the Arm and RISC-V architectures. - Describe the stack frame layout for the RISC-V implementation. - Explain caller and callee behaviors when targeting the RISC-V architecture. - Illustrate the RISC-V PLT stub implementation, including assembly code snippets and design intentions. - Add reference links about RISC-V. - glibc implementation of '__dl_runtime_resolve' for RISC-V. - RISC-V ABIs specifications. - Improve the explanation of the runtime execution flow of a dynamically linked program. - Correct the description of callee behavior for the Arm architecture. - Clarify that registers r4-r11 are callee-saved, not caller-saved. - Explain that the saved lr is loaded into pc to return to the caller.
Since the dynamic linking is now supported for the RISC-V architecture, this updates the relevant introductions and usage guides.
Since the jump offset of a 'jal' instruction is restricted to between -1MB and +1MB, this commit adds a validation check to ensure the compiler reports an error if the jump offset is out of range.
7e72003 to
ea51c76
Compare
The proposed changes primarily improve the ELF generation and the RISC-V backend, enabling the build system to generate a dynamically linked shecc targeting the RISC-V architecture.
Although the current changes allow both bootstrapping and test suite to complete successfully, this is still a work in progress. The TODO items are listed as follows:
riscv-abi.sh) to validate the RISC-V ABI.arm.mkandriscv.mkinto a common build logic (e.g.: configureRUNNER_LD_PREFIX).Summary by cubic
Implements RV32 dynamic linking using RELA with an ABI‑compliant PLT/GOT and
__libc_start_main, and switches REL (Arm) vs RELA (RISC‑V) viaELF_MACHINE_ARM32/ELF_MACHINE_RV32. CI now builds/tests static and dynamic forarmandriscv, adds an RV32 ABI suite, and updates docs.New Features
.rela.plt; PLT0=32B, stubs=16B;RESERVED_GOT_NUM(RV32=2, ARM=3);DYN_LINKER/lib/ld-linux-riscv32-ilp32d.so.1;ELF_FLAGS0x4; correctDT_RELA*/DT_REL*,DT_PLTREL,DT_PLTRELSZ,DT_JMPREL.__libc_start_main; 16‑byte stack alignment; JAL ±1MB range check..rel.pltvs.rela.pltviause_relaplt;ELF_MACHINE_*macros used across code; improved GOT/PLT generation; removed unusedgot_offsetfromstruct func.tests/riscv-abi.sh); dynamic snapshots (hello,fib); expanded dynamic linking docs incl. RISC‑V PLT/ABI details.Dependencies
mk/common.mkwith per‑arch candidates and autoRUNNER_LD_PREFIX; GitHub Actions downloads a riscv32 glibc toolchain only for dynamic runs; matrix runs static+dynamic forarmandriscv.Written for commit ea51c76. Summary will update on new commits.