diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a8e2ef9d..40d69e9f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -9,14 +9,7 @@ jobs:
matrix:
compiler: [gcc, clang]
architecture: [arm, riscv]
- link_mode: [static]
- include:
- - compiler: gcc
- architecture: arm
- link_mode: dynamic
- - compiler: clang
- architecture: arm
- link_mode: dynamic
+ link_mode: [static, dynamic]
steps:
- name: Checkout code
uses: actions/checkout@v4
@@ -27,6 +20,9 @@ jobs:
sudo apt-get install -q -y qemu-user
sudo apt-get install -q -y build-essential
sudo apt-get install -q -y gcc-arm-linux-gnueabihf
+ sudo wget -q https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2026.04.05/riscv32-glibc-ubuntu-24.04-gcc.tar.xz
+ sudo tar Jxf riscv32-glibc-ubuntu-24.04-gcc.tar.xz -C /opt
+ echo "/opt/riscv/bin" >> "$GITHUB_PATH"
- name: Determine static or dynamic linking mode
id: determine-mode
run: |
diff --git a/Makefile b/Makefile
index f79d6650..8e2e8ebf 100644
--- a/Makefile
+++ b/Makefile
@@ -49,10 +49,6 @@ STAGE0_FLAGS ?= --dump-ir
STAGE1_FLAGS ?=
DYNLINK ?= 0
ifeq ($(DYNLINK),1)
- ifeq ($(ARCH),riscv)
- # TODO: implement dynamic linking for RISC-V.
- $(error "Dynamic linking mode is not implemented for RISC-V")
- endif
STAGE0_FLAGS += --dynlink
STAGE1_FLAGS += --dynlink
endif
@@ -108,8 +104,10 @@ check-sanitizer: $(OUT)/$(STAGE0)-sanitizer tests/driver.sh
$(Q)rm $(OUT)/shecc
check-snapshots: $(OUT)/$(STAGE0) $(SNAPSHOTS) tests/check-snapshots.sh
+ # static linking
$(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config check-snapshot ARCH=$(SNAPSHOT_ARCH) DYNLINK=0 --silent;)
- $(Q)$(MAKE) distclean config check-snapshot ARCH=arm DYNLINK=1 --silent
+ # dynamic linking
+ $(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config check-snapshot ARCH=$(SNAPSHOT_ARCH) DYNLINK=1 --silent;)
$(VECHO) "Switching backend back to %s (DYNLINK=0)\n" arm
$(Q)$(MAKE) distclean config ARCH=arm DYNLINK=0 --silent
@@ -134,8 +132,10 @@ check-abi-stage2: $(OUT)/$(STAGE2)
fi
update-snapshots: tests/update-snapshots.sh
+ # static linking
$(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config update-snapshot ARCH=$(SNAPSHOT_ARCH) DYNLINK=0 --silent;)
- $(Q)$(MAKE) distclean config update-snapshot ARCH=arm DYNLINK=1 --silent
+ # dynamic linking
+ $(Q)$(foreach SNAPSHOT_ARCH, $(ARCHS), $(MAKE) distclean config update-snapshot ARCH=$(SNAPSHOT_ARCH) DYNLINK=1 --silent;)
$(VECHO) "Switching backend back to %s (DYNLINK=0)\n" arm
$(Q)$(MAKE) distclean config ARCH=arm DYNLINK=0 --silent
diff --git a/mk/arm.mk b/mk/arm.mk
index 53fe0899..1210ce18 100644
--- a/mk/arm.mk
+++ b/mk/arm.mk
@@ -16,6 +16,7 @@ ARCH_DEFS = \
\#define LIBC_SO \"libc.so.6\"\n$\
\#define PLT_FIXUP_SIZE 20\n$\
\#define PLT_ENT_SIZE 12\n$\
+ \#define RESERVED_GOT_NUM 3\n$\
\#define R_ARCH_JUMP_SLOT 0x16\n$\
\#define MAX_ARGS_IN_REG 4\n$\
"
diff --git a/mk/riscv.mk b/mk/riscv.mk
index 536d2c0e..9dd55907 100644
--- a/mk/riscv.mk
+++ b/mk/riscv.mk
@@ -7,13 +7,40 @@ ARCH_DEFS = \
\#define ARCH_PREDEFINED \"__riscv\" /* Older versions of the GCC toolchain defined __riscv__ */\n$\
\#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$\
\#define LIBC_SO \"libc.so.6\"\n$\
- \#define PLT_FIXUP_SIZE 20\n$\
- \#define PLT_ENT_SIZE 12\n$\
+ \#define PLT_FIXUP_SIZE 32\n$\
+ \#define PLT_ENT_SIZE 16\n$\
+ \#define RESERVED_GOT_NUM 2\n$\
\#define R_ARCH_JUMP_SLOT 0x5\n$\
\#define MAX_ARGS_IN_REG 8\n$\
"
-# TODO: Set this variable for RISC-V architecture
-RUNNER_LD_PREFIX=
+ifeq ($(USE_QEMU),1)
+ ifeq ($(DYNLINK),1)
+ CROSS_COMPILE = riscv32-unknown-linux-gnu-
+ RISCV_CC = $(CROSS_COMPILE)gcc
+ RISCV_CC := $(shell which $(RISCV_CC))
+ ifndef RISCV_CC
+ $(error "Unable to find ARM GNU toolchain.")
+ endif
+
+ LD_LINUX_PATH := $(shell cd $(shell $(RISCV_CC) --print-sysroot) 2>/dev/null && pwd)
+ ifeq ("$(LD_LINUX_PATH)","/")
+ LD_LINUX_PATH := $(shell dirname "$(shell which $(RISCV_CC))")/..
+ LD_LINUX_PATH := $(shell cd $(LD_LINUX_PATH) 2>/dev/null && pwd)
+ LD_LINUX_PATH := $(LD_LINUX_PATH)/$(shell echo $(CROSS_COMPILE) | sed s'/.$$//')/libc
+ LD_LINUX_PATH := $(shell cd $(LD_LINUX_PATH) 2>/dev/null && pwd)
+ ifndef LD_LINUX_PATH
+ LD_LINUX_PATH = /usr/$(shell echo $(CROSS_COMPILE) | sed s'/.$$//')
+ LD_LINUX_PATH := $(shell cd $(LD_LINUX_PATH) 2>/dev/null && pwd)
+ endif
+ endif
+
+ ifndef LD_LINUX_PATH
+ $(error "Dynamic linking mode requires ld-linux.so")
+ endif
+
+ RUNNER_LD_PREFIX = -L $(LD_LINUX_PATH)
+ endif
+endif
diff --git a/src/arch-lower.c b/src/arch-lower.c
index 1c70e7d2..96f7ea85 100644
--- a/src/arch-lower.c
+++ b/src/arch-lower.c
@@ -59,9 +59,9 @@ void riscv_lower(void)
/* Entry point: dispatch to the active architecture. */
void arch_lower(void)
{
-#if ELF_MACHINE == 0x28 /* ARM */
+#if ELF_MACHINE == ELF_MACHINE_ARM32
arm_lower();
-#elif ELF_MACHINE == 0xf3 /* RISC-V */
+#elif ELF_MACHINE == ELF_MACHINE_RV32
riscv_lower();
#else
/* Unknown architecture: keep behavior as-is. */
diff --git a/src/defs.h b/src/defs.h
index b0b79340..d43d39c9 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -43,6 +43,7 @@
#define MAX_DYNSYM 1024
#define MAX_DYNSTR 1024
#define MAX_RELPLT 1024
+#define MAX_RELAPLT 1024
#define MAX_PLT 1024
#define MAX_GOTPLT 1024
#define MAX_CONSTANTS 1024
@@ -101,6 +102,9 @@
#define ALIGN_UP(val, align) (((val) + (align) - 1) & ~((align) - 1))
#endif
+#define ELF_MACHINE_ARM32 0x28
+#define ELF_MACHINE_RV32 0xf3
+
/* Common data structures */
typedef struct arena_block {
char *memory;
@@ -682,15 +686,28 @@ typedef struct {
strbuf_t *elf_dynsym;
strbuf_t *elf_dynstr;
strbuf_t *elf_relplt;
+ strbuf_t *elf_relaplt;
strbuf_t *elf_plt;
strbuf_t *elf_got;
int elf_interp_start;
int elf_relplt_start;
+ int elf_relaplt_start;
int elf_plt_start;
int elf_got_start;
int relplt_size;
+ int relaplt_size;
int plt_size;
int got_size;
+
+ /* Currently, we don't consider the scenarios involving
+ * a mixture of REL and RELA relocation entries.
+ *
+ * Therefore, use a flag to determine the type of
+ * relocation entries to be processed:
+ * - true: use RELA relocation entries
+ * - false: use REL relocation entries
+ */
+ bool use_relaplt;
} dynamic_sections_t;
/* For .dynsym section. */
@@ -709,6 +726,12 @@ typedef struct {
int r_info;
} elf32_rel_t;
+typedef struct {
+ int r_offset;
+ int r_info;
+ int r_addend;
+} elf32_rela_t;
+
/* For .dynamic section */
typedef struct {
int d_tag;
diff --git a/src/elf.c b/src/elf.c
index 17e2244a..ac2ff253 100644
--- a/src/elf.c
+++ b/src/elf.c
@@ -75,17 +75,20 @@ void elf_generate_header(void)
* - number of section headers = 15
* - section header index of .shstrtab = 14
*/
+ int elf_relplt_size = dynamic_sections.use_relaplt
+ ? dynamic_sections.elf_relaplt->size
+ : dynamic_sections.elf_relplt->size;
phnum = 4;
shnum = 15;
shstrndx = 14;
- shoff =
- elf_header_len + elf_code->size + elf_data->size +
- elf_rodata->size + elf_symtab->size + elf_strtab->size +
- elf_shstrtab->size + dynamic_sections.elf_interp->size +
- dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size +
- dynamic_sections.elf_got->size + dynamic_sections.elf_dynstr->size +
- dynamic_sections.elf_dynsym->size +
- dynamic_sections.elf_dynamic->size;
+ shoff = elf_header_len + elf_code->size + elf_data->size +
+ elf_rodata->size + elf_symtab->size + elf_strtab->size +
+ elf_shstrtab->size + dynamic_sections.elf_interp->size +
+ elf_relplt_size + dynamic_sections.elf_plt->size +
+ dynamic_sections.elf_got->size +
+ dynamic_sections.elf_dynstr->size +
+ dynamic_sections.elf_dynsym->size +
+ dynamic_sections.elf_dynamic->size;
} else {
/* In static linking mode:
* - number of program headers = 2
@@ -187,9 +190,12 @@ void elf_generate_header(void)
void elf_generate_program_headers(void)
{
+ strbuf_t *elf_relplt = dynamic_sections.use_relaplt
+ ? dynamic_sections.elf_relaplt
+ : dynamic_sections.elf_relplt;
if (!elf_program_header || !elf_code || !elf_data || !elf_rodata ||
(dynlink &&
- (!dynamic_sections.elf_interp || !dynamic_sections.elf_relplt ||
+ (!dynamic_sections.elf_interp || !elf_relplt ||
!dynamic_sections.elf_plt || !dynamic_sections.elf_got ||
!dynamic_sections.elf_dynstr || !dynamic_sections.elf_dynsym ||
!dynamic_sections.elf_dynamic))) {
@@ -232,10 +238,8 @@ void elf_generate_program_headers(void)
phdr.p_flags = 5; /* flags */
phdr.p_align = PAGESIZE; /* alignment */
if (dynlink) {
- phdr.p_filesz +=
- dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size;
- phdr.p_memsz +=
- dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size;
+ phdr.p_filesz += elf_relplt->size + dynamic_sections.elf_plt->size;
+ phdr.p_memsz += elf_relplt->size + dynamic_sections.elf_plt->size;
}
elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t));
@@ -250,8 +254,7 @@ void elf_generate_program_headers(void)
phdr.p_flags = 6; /* flags */
phdr.p_align = PAGESIZE; /* alignment */
if (dynlink) {
- phdr.p_offset +=
- dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size;
+ phdr.p_offset += elf_relplt->size + dynamic_sections.elf_plt->size;
phdr.p_vaddr = dynamic_sections.elf_interp_start;
phdr.p_paddr = dynamic_sections.elf_interp_start;
phdr.p_filesz += dynamic_sections.elf_interp->size +
@@ -272,7 +275,7 @@ void elf_generate_program_headers(void)
/* program header - program interpreter (.interp section) */
phdr.p_type = 3; /* PT_INTERP */
phdr.p_offset = elf_header_len + elf_code->size + elf_rodata->size +
- dynamic_sections.elf_relplt->size +
+ elf_relplt->size +
dynamic_sections.elf_plt->size; /* offset of segment */
phdr.p_vaddr = dynamic_sections.elf_interp_start; /* virtual address */
phdr.p_paddr = dynamic_sections.elf_interp_start; /* physical address */
@@ -286,7 +289,7 @@ void elf_generate_program_headers(void)
phdr.p_type = 2; /* PT_DYNAMIC */
phdr.p_offset =
elf_header_len + elf_code->size + elf_rodata->size +
- dynamic_sections.elf_relplt->size + dynamic_sections.elf_plt->size +
+ elf_relplt->size + dynamic_sections.elf_plt->size +
dynamic_sections.elf_interp->size + dynamic_sections.elf_got->size +
dynamic_sections.elf_dynstr->size +
dynamic_sections.elf_dynsym->size; /* offset of segment */
@@ -308,11 +311,14 @@ void elf_generate_program_headers(void)
void elf_generate_section_headers(void)
{
+ strbuf_t *elf_relplt = dynamic_sections.use_relaplt
+ ? dynamic_sections.elf_relaplt
+ : dynamic_sections.elf_relplt;
/* Check for null pointers to prevent crashes */
if (!elf_section_header || !elf_code || !elf_data || !elf_rodata ||
!elf_symtab || !elf_strtab || !elf_shstrtab ||
(dynlink &&
- (!dynamic_sections.elf_interp || !dynamic_sections.elf_relplt ||
+ (!dynamic_sections.elf_interp || !elf_relplt ||
!dynamic_sections.elf_plt || !dynamic_sections.elf_got ||
!dynamic_sections.elf_dynstr || !dynamic_sections.elf_dynsym ||
!dynamic_sections.elf_dynamic))) {
@@ -396,20 +402,37 @@ void elf_generate_section_headers(void)
sh_name += strlen(".rodata") + 1;
if (dynlink) {
- /* .rel.plt */
+ /* .rel.plt or .rela.plt */
+ int sh_type, sh_addr, sh_size, sh_entsize, __ofs, __sh_name;
+
+ if (dynamic_sections.use_relaplt) {
+ sh_type = 4; /* SHT_RELA */
+ sh_addr = dynamic_sections.elf_relaplt_start;
+ sh_size = dynamic_sections.elf_relaplt->size;
+ sh_entsize = sizeof(elf32_rela_t);
+ __ofs = dynamic_sections.elf_relaplt->size;
+ __sh_name = strlen(".rela.plt") + 1;
+ } else {
+ sh_type = 9; /* SHT_REL */
+ sh_addr = dynamic_sections.elf_relplt_start;
+ sh_size = dynamic_sections.elf_relplt->size;
+ sh_entsize = sizeof(elf32_rel_t);
+ __ofs = dynamic_sections.elf_relplt->size;
+ __sh_name = strlen(".rel.plt") + 1;
+ }
shdr.sh_name = sh_name;
- shdr.sh_type = 9; /* SHT_REL */
+ shdr.sh_type = sh_type;
shdr.sh_flags = 0x42; /* 0x40 | SHF_ALLOC */
- shdr.sh_addr = dynamic_sections.elf_relplt_start;
+ shdr.sh_addr = sh_addr;
shdr.sh_offset = ofs;
- shdr.sh_size = dynamic_sections.elf_relplt->size;
+ shdr.sh_size = sh_size;
shdr.sh_link = 8; /* The section header index of .dynsym. */
shdr.sh_info = 6; /* The section header index of .got. */
shdr.sh_addralign = 4;
- shdr.sh_entsize = sizeof(elf32_rel_t);
+ shdr.sh_entsize = sh_entsize;
elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t));
- ofs += dynamic_sections.elf_relplt->size;
- sh_name += strlen(".rel.plt") + 1;
+ ofs += __ofs;
+ sh_name += __sh_name;
/* .plt */
shdr.sh_name = sh_name;
@@ -597,9 +620,12 @@ void elf_align(strbuf_t *elf_array)
void elf_generate_sections(void)
{
+ strbuf_t *elf_relplt = dynamic_sections.use_relaplt
+ ? dynamic_sections.elf_relaplt
+ : dynamic_sections.elf_relplt;
if (!elf_shstrtab ||
(dynlink &&
- (!dynamic_sections.elf_interp || !dynamic_sections.elf_relplt ||
+ (!dynamic_sections.elf_interp || !elf_relplt ||
!dynamic_sections.elf_plt || !dynamic_sections.elf_got ||
!dynamic_sections.elf_dynstr || !dynamic_sections.elf_dynsym ||
!dynamic_sections.elf_dynamic))) {
@@ -609,21 +635,20 @@ void elf_generate_sections(void)
if (dynlink) {
/* In dynamic linking mode, elf_generate_sections() also generates
- * .interp, .dynsym, .dynstr, .relplt, .got and dynamic sections.
+ * .interp, .dynsym, .dynstr, .rel.plt (.rela.plt), .got and dynamic
+ * sections.
*
* .plt section is generated at the code generation phase.
- *
- * TODO:
- * Define a new structure named 'elf32_rela_t' and use it to generate
- * relocation entries for RISC-V architecture.
*/
elf32_sym_t sym;
elf32_dyn_t dyn;
elf32_rel_t rel;
+ elf32_rela_t rela;
int dymsym_idx = 1, func_plt_ofs, func_got_ofs, st_name = 0;
memset(&sym, 0, sizeof(elf32_sym_t));
memset(&dyn, 0, sizeof(elf32_dyn_t));
memset(&rel, 0, sizeof(elf32_rel_t));
+ memset(&rela, 0, sizeof(elf32_rela_t));
/* .interp section */
elf_write_str(dynamic_sections.elf_interp, DYN_LINKER);
@@ -651,9 +676,20 @@ void elf_generate_sections(void)
* Since __libc_start_main is not added to the function list,
* it must be handled additionally first.
*/
- rel.r_offset = dynamic_sections.elf_got_start + PTR_SIZE * 3;
- rel.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT;
- elf_write_blk(dynamic_sections.elf_relplt, &rel, sizeof(elf32_rel_t));
+ if (dynamic_sections.use_relaplt) {
+ rela.r_offset =
+ dynamic_sections.elf_got_start + PTR_SIZE * RESERVED_GOT_NUM;
+ rela.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT;
+ rela.r_addend = 0;
+ elf_write_blk(dynamic_sections.elf_relaplt, &rela,
+ sizeof(elf32_rela_t));
+ } else {
+ rel.r_offset =
+ dynamic_sections.elf_got_start + PTR_SIZE * RESERVED_GOT_NUM;
+ rel.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT;
+ elf_write_blk(dynamic_sections.elf_relplt, &rel,
+ sizeof(elf32_rel_t));
+ }
sym.st_name = st_name;
sym.st_info = ELF32_ST_INFO(1, 2); /* STB_GLOBAL = 1, STT_FUNC = 2 */
@@ -664,58 +700,80 @@ void elf_generate_sections(void)
elf_write_byte(dynamic_sections.elf_dynstr, 0);
st_name += strlen("__libc_start_main") + 1;
- /* Because PLT[1] and GOT[3] are reserved for __libc_start_main,
- * its plt_offset and got_offset must be PLT_FIXUP_SIZE and
- * PTR_SIZE * 3, respectively. Therefore, no offset assignment is
- * required for this function.
+ /* Because PLT[1] and GOT[RESERVED_GOT_NUM] are reserved for
+ * __libc_start_main, its plt_offset and got_offset must be
+ * PLT_FIXUP_SIZE and PTR_SIZE * RESERVED_GOT_NUM, respectively.
+ * Therefore, no offset assignment is required for this function.
*/
func_plt_ofs = PLT_FIXUP_SIZE + PLT_ENT_SIZE;
func_got_ofs = PTR_SIZE << 2;
for (func_t *func = FUNC_LIST.head; func; func = func->next) {
- if (func->is_used && !func->bbs) {
+ if (!func->is_used || func->bbs)
+ continue;
+ /* If the function is used and has no basic block,
+ * consider it to be an external function.
+ */
+
+ if (dynamic_sections.use_relaplt) {
+ rela.r_offset += PTR_SIZE;
+ rela.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT;
+ rela.r_addend = 0;
+ elf_write_blk(dynamic_sections.elf_relaplt, &rela,
+ sizeof(elf32_rela_t));
+ } else {
rel.r_offset += PTR_SIZE;
rel.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT;
elf_write_blk(dynamic_sections.elf_relplt, &rel,
sizeof(elf32_rel_t));
+ }
- sym.st_name = st_name;
- sym.st_info =
- ELF32_ST_INFO(1, 2); /* STB_GLOBAL = 1, STT_FUNC = 2 */
- elf_write_blk(dynamic_sections.elf_dynsym, &sym,
- sizeof(elf32_sym_t));
- dymsym_idx += 1;
+ sym.st_name = st_name;
+ sym.st_info =
+ ELF32_ST_INFO(1, 2); /* STB_GLOBAL = 1, STT_FUNC = 2 */
+ elf_write_blk(dynamic_sections.elf_dynsym, &sym,
+ sizeof(elf32_sym_t));
+ dymsym_idx += 1;
- elf_write_str(dynamic_sections.elf_dynstr,
- func->return_def.var_name);
- elf_write_byte(dynamic_sections.elf_dynstr, 0);
- st_name += strlen(func->return_def.var_name) + 1;
+ elf_write_str(dynamic_sections.elf_dynstr,
+ func->return_def.var_name);
+ elf_write_byte(dynamic_sections.elf_dynstr, 0);
+ st_name += strlen(func->return_def.var_name) + 1;
- func->plt_offset = func_plt_ofs;
- func->got_offset = func_got_ofs;
+ func->plt_offset = func_plt_ofs;
+ func->got_offset = func_got_ofs;
- func_plt_ofs += PLT_ENT_SIZE;
- func_got_ofs += PTR_SIZE;
- }
+ func_plt_ofs += PLT_ENT_SIZE;
+ func_got_ofs += PTR_SIZE;
}
/* Ensure proper alignment for .dynstr section. */
elf_align(dynamic_sections.elf_dynstr);
/* .got section
*
- * - GOT[0] holds the virtual address of .dynamic section.
- * - GOT[1] and GOT[2] are reserved for link_map and resolver,
- * and are initialized to 0.
+ * - Arm architecture:
+ * - GOT[0] holds the virtual address of .dynamic section.
+ * - GOT[1] and GOT[2] are reserved for link_map and resolver,
+ * and are initialized to 0.
+ * - RISC-V architecture:
+ * - GOT[0] and GOT[1] are reserved for link_map and resolver,
+ * and are initialized to 0.
* - The remaining entries are initialized to &PLT[0].
*/
- elf_write_int(dynamic_sections.elf_got,
- dynamic_sections.elf_got_start +
- dynamic_sections.got_size +
- dynamic_sections.elf_dynstr->size +
- dynamic_sections.elf_dynsym->size);
- 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) {
+ case ELF_MACHINE_ARM32:
+ elf_write_int(dynamic_sections.elf_got,
+ dynamic_sections.elf_got_start +
+ dynamic_sections.got_size +
+ dynamic_sections.elf_dynstr->size +
+ dynamic_sections.elf_dynsym->size);
+ case ELF_MACHINE_RV32:
+ elf_write_int(dynamic_sections.elf_got, 0);
+ elf_write_int(dynamic_sections.elf_got, 0);
+ break;
+ }
+ for (int i = PTR_SIZE * RESERVED_GOT_NUM; i < dynamic_sections.got_size;
+ i += PTR_SIZE)
elf_write_int(dynamic_sections.elf_got,
dynamic_sections.elf_plt_start);
@@ -740,37 +798,79 @@ void elf_generate_sections(void)
dyn.d_un = sizeof(elf32_sym_t); /* Size of an entry. */
elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
- dyn.d_tag = 0x11; /* REL */
- dyn.d_un = dynamic_sections
- .elf_relplt_start; /* The virtual address of .rel.plt. */
- elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
-
- dyn.d_tag = 0x12; /* RELSZ */
- dyn.d_un = dynamic_sections.relplt_size;
- elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
-
- dyn.d_tag = 0x13; /* RELENT */
- dyn.d_un = sizeof(elf32_rel_t);
- elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
+ if (dynamic_sections.use_relaplt) {
+ dyn.d_tag = 0x7; /* RELA */
+ dyn.d_un =
+ dynamic_sections
+ .elf_relaplt_start; /* The virtual address of .rela.plt. */
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ dyn.d_tag = 0x8; /* RELASZ */
+ dyn.d_un = dynamic_sections.relaplt_size;
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ dyn.d_tag = 0x9; /* RELAENT */
+ dyn.d_un = sizeof(elf32_rela_t);
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ dyn.d_tag = 0x2; /* PLTRELSZ */
+ dyn.d_un = dynamic_sections.relaplt_size;
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ dyn.d_tag = 0x14; /* PLTREL */
+ dyn.d_un = 0x7;
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ /* The virtual address of .rela.plt. */
+ dyn.d_tag = 0x17; /* JMPREL */
+ dyn.d_un = dynamic_sections.elf_relaplt_start;
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+ } else {
+ dyn.d_tag = 0x11; /* REL */
+ dyn.d_un =
+ dynamic_sections
+ .elf_relplt_start; /* The virtual address of .rel.plt. */
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ dyn.d_tag = 0x12; /* RELSZ */
+ dyn.d_un = dynamic_sections.relplt_size;
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ dyn.d_tag = 0x13; /* RELENT */
+ dyn.d_un = sizeof(elf32_rel_t);
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ dyn.d_tag = 0x2; /* PLTRELSZ */
+ dyn.d_un = dynamic_sections.relplt_size;
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ dyn.d_tag = 0x14; /* PLTREL */
+ dyn.d_un = 0x11;
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+
+ /* The virtual address of .rel.plt. */
+ dyn.d_tag = 0x17; /* JMPREL */
+ dyn.d_un = dynamic_sections.elf_relplt_start;
+ elf_write_blk(dynamic_sections.elf_dynamic, &dyn,
+ sizeof(elf32_dyn_t));
+ }
dyn.d_tag = 0x3; /* PLTGOT */
dyn.d_un =
dynamic_sections.elf_got_start; /* The virtual address of .got.*/
elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
- dyn.d_tag = 0x2; /* PLTRELSZ */
- dyn.d_un = dynamic_sections.relplt_size;
- elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
-
- dyn.d_tag = 0x14; /* PLTREL */
- dyn.d_un = 0x11;
- elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
-
- dyn.d_tag = 0x17; /* JMPREL */
- dyn.d_un = dynamic_sections
- .elf_relplt_start; /* The virtual address of .rel.plt. */
- elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
-
dyn.d_tag = 0x1; /* NEEDED */
dyn.d_un = 0x1; /* The index of "libc.so.6" in .dynstr. */
elf_write_blk(dynamic_sections.elf_dynamic, &dyn, sizeof(elf32_dyn_t));
@@ -789,7 +889,10 @@ void elf_generate_sections(void)
elf_write_str(elf_shstrtab, ".rodata");
elf_write_byte(elf_shstrtab, 0);
if (dynlink) {
- elf_write_str(elf_shstrtab, ".rel.plt");
+ if (dynamic_sections.use_relaplt)
+ elf_write_str(elf_shstrtab, ".rela.plt");
+ else
+ elf_write_str(elf_shstrtab, ".rel.plt");
elf_write_byte(elf_shstrtab, 0);
elf_write_str(elf_shstrtab, ".plt");
elf_write_byte(elf_shstrtab, 0);
@@ -844,47 +947,67 @@ void elf_preprocess(void)
elf_code_start = ELF_START + elf_header_len;
elf_rodata_start = elf_code_start + elf_offset;
if (dynlink) {
- /* Precalculate the sizes of .rel.plt, .plt and .got sections.
+ /* Precalculate the sizes of .rel.plt (.rela.plt), .plt and .got
+ * sections.
*
* Suppose the compiled program has n external functions:
- * - .rel.plt contains n entries.
+ * - .rel.plt (.rela.plt) contains n entries.
* - .plt has n entries plus one fixup entry.
- * - .got includes n + 3 entries
- * - GOT[0] holds the virtual address of .dynamic section.
- * - GOT[1] and GOT[2] are reserved for link_map and resolver
- * (both set to 0).
- * - The remaining entries correspond to all external functions.
+ * - .got includes n + RESERVED_GOT_NUM entries
+ * - Arm architecture:
+ * - GOT[0] holds the virtual address of .dynamic section.
+ * - GOT[1] and GOT[2] are reserved for link_map and resolver
+ * (both set to 0).
+ * - RISC-V architecture:
+ * - GOT[0] and GOT[1] are reserved for resolver and link_map.
+ * - Common:
+ * - The remaining entries correspond to all external functions.
*
* Next, consider the case of __libc_start_main before initializing
* the sizes:
- * - .rel.plt has the one entry for __libc_start_main.
+ * - .rel.plt (.rela.plt) has the one entry for __libc_start_main.
* - .plt includes one fixup entry plus one entry for __libc_start_main.
- * - .got has 3 + 1 entries.
- * - 3 entries for GOT[0] - GOT[2].
- * - 1 entry (GOT[3]) reserved for __libc_start_main.
+ * - .got has RESERVED_GOT_NUM + 1 entries.
+ * - RESERVED_GOT_NUM entries for GOT[0] - GOT[RESERVED_GOT_NUM - 1].
+ * - 1 entry (GOT[RESERVED_GOT_NUM]) reserved for __libc_start_main.
*
* Therefore, the following code initialize the section sizes based on
* the layout described above, and then traverse the function list in a
* for loop to increment the sizes for each newly found external
* function.
*/
- dynamic_sections.relplt_size = sizeof(elf32_rel_t);
+ if (dynamic_sections.use_relaplt)
+ dynamic_sections.relaplt_size = sizeof(elf32_rela_t);
+ else
+ dynamic_sections.relplt_size = sizeof(elf32_rel_t);
dynamic_sections.plt_size = PLT_FIXUP_SIZE + PLT_ENT_SIZE;
- dynamic_sections.got_size = PTR_SIZE * 3 + PTR_SIZE;
+ dynamic_sections.got_size = PTR_SIZE * RESERVED_GOT_NUM + PTR_SIZE;
for (func_t *func = FUNC_LIST.head; func; func = func->next) {
- if (func->is_used && !func->bbs) {
+ if (!func->is_used || func->bbs)
+ continue;
+ if (dynamic_sections.use_relaplt)
+ dynamic_sections.relaplt_size += sizeof(elf32_rela_t);
+ else
dynamic_sections.relplt_size += sizeof(elf32_rel_t);
- dynamic_sections.plt_size += PLT_ENT_SIZE;
- dynamic_sections.got_size += PTR_SIZE;
- }
+ dynamic_sections.plt_size += PLT_ENT_SIZE;
+ dynamic_sections.got_size += PTR_SIZE;
}
/* Set the starting addresses of the three sections. */
int elf_interp_size = strlen(DYN_LINKER) + 1;
elf_interp_size = ALIGN_UP(elf_interp_size, 4);
- dynamic_sections.elf_relplt_start = elf_rodata_start + elf_rodata->size;
- dynamic_sections.elf_plt_start =
- dynamic_sections.elf_relplt_start + dynamic_sections.relplt_size;
+ if (dynamic_sections.use_relaplt) {
+ dynamic_sections.elf_relaplt_start =
+ elf_rodata_start + elf_rodata->size;
+ dynamic_sections.elf_plt_start =
+ dynamic_sections.elf_relaplt_start +
+ dynamic_sections.relaplt_size;
+ } else {
+ dynamic_sections.elf_relplt_start =
+ elf_rodata_start + elf_rodata->size;
+ dynamic_sections.elf_plt_start = dynamic_sections.elf_relplt_start +
+ dynamic_sections.relplt_size;
+ }
/* Since the first section of the second load segment is .interp
* when using dynamic linking mode, adding PAGESIZE to elf_interp_start
* is to ensure that two load segments don't share a common page.
@@ -944,8 +1067,13 @@ void elf_generate(const char *outfile)
if (dynlink) {
/* Read-only sections */
- for (int i = 0; i < dynamic_sections.elf_relplt->size; i++)
- fputc(dynamic_sections.elf_relplt->elements[i], fp);
+ if (dynamic_sections.use_relaplt)
+ for (int i = 0; i < dynamic_sections.elf_relaplt->size; i++)
+ fputc(dynamic_sections.elf_relaplt->elements[i], fp);
+ else {
+ for (int i = 0; i < dynamic_sections.elf_relplt->size; i++)
+ fputc(dynamic_sections.elf_relplt->elements[i], fp);
+ }
for (int i = 0; i < dynamic_sections.elf_plt->size; i++)
fputc(dynamic_sections.elf_plt->elements[i], fp);
/* Readable and writable sections */
diff --git a/src/globals.c b/src/globals.c
index f284d24c..98cfeb36 100644
--- a/src/globals.c
+++ b/src/globals.c
@@ -1308,11 +1308,23 @@ void global_init(void)
elf_bss_size = 0;
elf_shstrtab = strbuf_create(MAX_SHSTR);
elf_section_header = strbuf_create(MAX_SECTION_HEADER);
+
+ switch (ELF_MACHINE) {
+ case ELF_MACHINE_ARM32:
+ dynamic_sections.use_relaplt = false;
+ break;
+ case ELF_MACHINE_RV32:
+ dynamic_sections.use_relaplt = true;
+ break;
+ }
dynamic_sections.elf_interp = strbuf_create(MAX_INTERP);
dynamic_sections.elf_dynamic = strbuf_create(MAX_DYNAMIC);
dynamic_sections.elf_dynsym = strbuf_create(MAX_DYNSYM);
dynamic_sections.elf_dynstr = strbuf_create(MAX_DYNSTR);
- dynamic_sections.elf_relplt = strbuf_create(MAX_RELPLT);
+ if (dynamic_sections.use_relaplt)
+ dynamic_sections.elf_relaplt = strbuf_create(MAX_RELAPLT);
+ else
+ dynamic_sections.elf_relplt = strbuf_create(MAX_RELPLT);
dynamic_sections.elf_plt = strbuf_create(MAX_PLT);
dynamic_sections.elf_got = strbuf_create(MAX_GOTPLT);
}
@@ -1449,7 +1461,10 @@ void global_release(void)
strbuf_free(dynamic_sections.elf_dynamic);
strbuf_free(dynamic_sections.elf_dynsym);
strbuf_free(dynamic_sections.elf_dynstr);
- strbuf_free(dynamic_sections.elf_relplt);
+ if (dynamic_sections.use_relaplt)
+ strbuf_free(dynamic_sections.elf_relaplt);
+ else
+ strbuf_free(dynamic_sections.elf_relplt);
strbuf_free(dynamic_sections.elf_plt);
strbuf_free(dynamic_sections.elf_got);
}
diff --git a/src/riscv-codegen.c b/src/riscv-codegen.c
index ee641b60..213c1b81 100644
--- a/src/riscv-codegen.c
+++ b/src/riscv-codegen.c
@@ -10,6 +10,8 @@
#include "globals.c"
#include "riscv.c"
+#define RV32_ALIGNMENT 16
+
void update_elf_offset(ph2_ir_t *ph2_ir)
{
switch (ph2_ir->op) {
@@ -118,12 +120,19 @@ void update_elf_offset(ph2_ir_t *ph2_ir)
void cfg_flatten(void)
{
- func_t *func = find_func("__syscall");
- /* Prologue ~ 6 instructions (24 bytes). Place __syscall right after. */
- func->bbs->elf_offset = 24;
+ func_t *func;
+
+ if (dynlink)
+ elf_offset = 68;
+ else {
+ func = find_func("__syscall");
+ /* Prologue ~ 6 instructions (24 bytes). Place __syscall right after. */
+ func->bbs->elf_offset = 24;
+ /* Reserve space for prologue (24) + syscall trampoline (36) = 60 bytes.
+ */
+ elf_offset = 60;
+ }
- /* Reserve space for prologue (24) + syscall trampoline (36) = 60 bytes. */
- elf_offset = 60;
GLOBAL_FUNC->bbs->elf_offset = elf_offset;
for (ph2_ir_t *ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir;
@@ -132,7 +141,10 @@ void cfg_flatten(void)
}
/* prepare 'argc' and 'argv', then proceed to 'main' function */
- elf_offset += 24;
+ if (dynlink)
+ elf_offset += 32;
+ else
+ elf_offset += 24;
for (func = FUNC_LIST.head; func; func = func->next) {
/* Skip function declarations without bodies */
@@ -144,6 +156,14 @@ void cfg_flatten(void)
flatten_ir->src0 = func->stack_size;
strncpy(flatten_ir->func_name, func->return_def.var_name, MAX_VAR_LEN);
+ /* Except for local variables, it must allocate additional space
+ * to preserve the content of ra at each function entry point.
+ *
+ * 'stack_size' doesn't include the additional space, so an extra
+ * number '4' is added to 'stack_size'.
+ */
+ int stack_top_ofs = ALIGN_UP(func->stack_size + 4, RV32_ALIGNMENT);
+
for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) {
bb->elf_offset = elf_offset;
@@ -154,9 +174,23 @@ void cfg_flatten(void)
for (ph2_ir_t *insn = bb->ph2_ir_list.head; insn;
insn = insn->next) {
- /* TODO: recalculate the offset for instructions with the
- * 'ofs_based_on_stack_top' flag set.
- */
+ if (insn->ofs_based_on_stack_top) {
+ switch (insn->op) {
+ case OP_load:
+ case OP_address_of:
+ insn->src0 = insn->src0 + stack_top_ofs;
+ break;
+ case OP_store:
+ insn->src1 = insn->src1 + stack_top_ofs;
+ break;
+ default:
+ /* Ignore opcodes with the ofs_based_on_stack_top
+ * flag set since only the three opcodes above needs
+ * to access a variable's address.
+ */
+ break;
+ }
+ }
flatten_ir = add_existed_ph2_ir(insn);
if (insn->op == OP_return) {
@@ -193,9 +227,10 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir)
switch (ph2_ir->op) {
case OP_define:
+ ofs = ALIGN_UP(ph2_ir->src0 + 4, RV32_ALIGNMENT);
emit(__sw(__ra, __sp, -4));
- emit(__lui(__t0, rv_hi(ph2_ir->src0 + 4)));
- emit(__addi(__t0, __t0, rv_lo(ph2_ir->src0 + 4)));
+ emit(__lui(__t0, rv_hi(ofs)));
+ emit(__addi(__t0, __t0, rv_lo(ofs)));
emit(__sub(__sp, __sp, __t0));
return;
case OP_load_constant:
@@ -274,7 +309,16 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir)
return;
case OP_call:
func = find_func(ph2_ir->func_name);
- emit(__jal(__ra, func->bbs->elf_offset - elf_code->size));
+ if (func->bbs)
+ ofs = func->bbs->elf_offset - elf_code->size;
+ else if (dynlink) {
+ ofs = (dynamic_sections.elf_plt_start + func->plt_offset) -
+ (elf_code_start + elf_code->size);
+ } else {
+ printf("The '%s' function is not implemented\n", ph2_ir->func_name);
+ abort();
+ }
+ emit(__jal(__ra, ofs));
return;
case OP_load_data_address:
emit(__lui(rd, rv_hi(elf_data_start + ph2_ir->src0)));
@@ -286,7 +330,14 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir)
return;
case OP_address_of_func:
func = find_func(ph2_ir->func_name);
- ofs = elf_code_start + func->bbs->elf_offset;
+ if (func->bbs)
+ ofs = elf_code_start + func->bbs->elf_offset;
+ else if (dynlink)
+ ofs = dynamic_sections.elf_plt_start + func->plt_offset;
+ else {
+ printf("The '%s' function is not implemented\n", ph2_ir->func_name);
+ abort();
+ }
emit(__lui(__t0, rv_hi(ofs)));
emit(__addi(__t0, __t0, rv_lo(ofs)));
emit(__sw(__t0, rs1, 0));
@@ -302,8 +353,9 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir)
emit(__addi(__zero, __zero, 0));
else
emit(__addi(__a0, rs1, 0));
- emit(__lui(__t0, rv_hi(ph2_ir->src1 + 4)));
- emit(__addi(__t0, __t0, rv_lo(ph2_ir->src1 + 4)));
+ ofs = ALIGN_UP(ph2_ir->src1 + 4, RV32_ALIGNMENT);
+ emit(__lui(__t0, rv_hi(ofs)));
+ emit(__addi(__t0, __t0, rv_lo(ofs)));
emit(__add(__sp, __sp, __t0));
emit(__lw(__ra, __sp, -4));
emit(__jalr(__zero, __ra, 0));
@@ -482,26 +534,100 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir)
}
}
+void plt_generate(void);
void code_generate(void)
{
- /* start: save original sp in s0; allocate global stack; run init */
- emit(__addi(__s0, __sp, 0));
- emit(__lui(__t0, rv_hi(GLOBAL_FUNC->stack_size)));
- emit(__addi(__t0, __t0, rv_lo(GLOBAL_FUNC->stack_size)));
+ int ofs;
+
+ if (dynlink) {
+ plt_generate();
+ /* - Initial stack layout when the program starts:
+ *
+ * +----------------+ (high address)
+ * | ... |
+ * +----------------+
+ * | argv[argc - 1] |
+ * +----------------+
+ * | ... |
+ * +----------------+
+ * | argv[0] |
+ * +----------------+
+ * | argc |
+ * +----------------+ <- sp points to this location.
+ *
+ * - At the program entry point, it must call __libc_start_main()
+ * under dynamic linking. The function prototype is as follows:
+ *
+ * int __libc_start_main(int (*main) (int, char **, char **),
+ * int argc, char **argv,
+ * void (*init) (void),
+ * void (*fini) (void),
+ * void (*rtld_fini) (void),
+ * void (*stack_end));
+ *
+ * Currently, to execute a dynamically linked program with the
+ * minimal effort required, we perform the following call:
+ * -> __libc_start_main(main_wrapper, argc, argv, NULL,
+ * NULL, NULL, stack_end)
+ */
+ emit(__lui(__a0, rv_hi(elf_code_start + 36)));
+ emit(__addi(__a0, __a0, rv_lo(elf_code_start + 36)));
+ emit(__lw(__a1, __sp, 0));
+ emit(__addi(__a2, __sp, 4));
+ emit(__addi(__a3, __zero, 0));
+ emit(__addi(__a4, __zero, 0));
+ emit(__addi(__a5, __zero, 0));
+ emit(__addi(__a6, __sp, 0));
+
+ /* 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));
+
+ /* The main wrapper is located here under the dynamic linking mode
+ *
+ * Use t1 and t2 registers to temporarily store 'argc' and 'argv',
+ * while preserving ra on the stack.
+ *
+ * After the main function completes its execution, it must use
+ * the content of ra to transfer control back to __libc_start_main().
+ */
+ emit(__addi(__t1, __a0, 0));
+ emit(__addi(__t2, __a1, 0));
+ emit(__sw(__ra, __sp, -4));
+ ofs = ALIGN_UP(GLOBAL_FUNC->stack_size + 4, RV32_ALIGNMENT);
+ } else {
+ /* When using static linking, the starting address
+ * of the main wrapper is here.
+ *
+ * Save original sp in s0 first.
+ */
+ ofs = ALIGN_UP(GLOBAL_FUNC->stack_size, RV32_ALIGNMENT);
+ emit(__addi(__s0, __sp, 0));
+ }
+ /* Next, the main wrapper performs:
+ * 1. allocate global stack
+ * 2. run init
+ * 3. call the main function
+ */
+ emit(__lui(__t0, rv_hi(ofs)));
+ emit(__addi(__t0, __t0, rv_lo(ofs)));
emit(__sub(__sp, __sp, __t0));
emit(__addi(__gp, __sp, 0)); /* Set up global pointer */
emit(__jal(__ra, GLOBAL_FUNC->bbs->elf_offset - elf_code->size));
- /* syscall trampoline for __syscall - must be at offset 24 */
- emit(__addi(__a7, __a0, 0));
- emit(__addi(__a0, __a1, 0));
- emit(__addi(__a1, __a2, 0));
- emit(__addi(__a2, __a3, 0));
- emit(__addi(__a3, __a4, 0));
- emit(__addi(__a4, __a5, 0));
- emit(__addi(__a5, __a6, 0));
- emit(__ecall());
- emit(__jalr(__zero, __ra, 0));
+ if (!dynlink) {
+ /* syscall trampoline for __syscall */
+ emit(__addi(__a7, __a0, 0));
+ emit(__addi(__a0, __a1, 0));
+ emit(__addi(__a1, __a2, 0));
+ emit(__addi(__a2, __a3, 0));
+ emit(__addi(__a3, __a4, 0));
+ emit(__addi(__a4, __a5, 0));
+ emit(__addi(__a5, __a6, 0));
+ emit(__ecall());
+ emit(__jalr(__zero, __ra, 0));
+ }
ph2_ir_t *ph2_ir;
for (ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir;
@@ -509,16 +635,31 @@ void code_generate(void)
emit_ph2_ir(ph2_ir);
/* prepare 'argc' and 'argv', then proceed to 'main' function */
- /* use original sp saved in s0 to get argc/argv */
if (MAIN_BB) {
- emit(__addi(__t0, __s0, 0));
- emit(__lw(__a0, __t0, 0));
- emit(__addi(__a1, __t0, 4));
- emit(__jal(__ra, MAIN_BB->elf_offset - elf_code->size));
+ if (dynlink) {
+ emit(__addi(__a0, __t1, 0));
+ emit(__addi(__a1, __t2, 0));
+ emit(__jal(__ra, MAIN_BB->elf_offset - elf_code->size));
- /* exit with main's return value in a0 */
- emit(__addi(__a7, __zero, 93));
- emit(__ecall());
+ /* Restore sp and transfer control back to __libc_start_main()
+ * using the preserved ra.
+ */
+ emit(__lui(__t0, rv_hi(ofs)));
+ emit(__addi(__t0, __t0, rv_lo(ofs)));
+ emit(__add(__sp, __sp, __t0));
+ emit(__lw(__ra, __sp, -4));
+ emit(__jalr(__zero, __ra, 0));
+ } else {
+ /* use original sp saved in s0 to get argc/argv */
+ emit(__addi(__t0, __s0, 0));
+ emit(__lw(__a0, __t0, 0));
+ emit(__addi(__a1, __t0, 4));
+ emit(__jal(__ra, MAIN_BB->elf_offset - elf_code->size));
+
+ /* exit with main's return value in a0 */
+ emit(__addi(__a7, __zero, 93));
+ emit(__ecall());
+ }
}
for (int i = 0; i < ph2_ir_idx; i++) {
@@ -526,3 +667,63 @@ void code_generate(void)
emit_ph2_ir(ph2_ir);
}
}
+
+void plt_generate()
+{
+ int addr_of_plt = dynamic_sections.elf_plt_start;
+ int addr_of_got = dynamic_sections.elf_got_start;
+ int end = dynamic_sections.plt_size - PLT_FIXUP_SIZE;
+ int ofs, pcrel_hi, pcrel_lo;
+
+ ofs = addr_of_got - addr_of_plt;
+ pcrel_hi = ofs & ~0xFFF;
+ pcrel_lo = ofs & 0xFFF;
+ if (pcrel_lo > 2047) {
+ pcrel_hi += 1;
+ pcrel_lo -= 0x1000;
+ }
+
+ /* t0 -> link map (GOT[1])
+ * t1 -> .got offset
+ * t2 -> %pcrel_hi(.got.plt) (but it is unused)
+ * t3 -> _dl_runtime_resolve (GOT[0])
+ */
+ elf_write_int(dynamic_sections.elf_plt, __auipc(__t2, pcrel_hi));
+ elf_write_int(dynamic_sections.elf_plt, __sub(__t1, __t1, __t3));
+ elf_write_int(dynamic_sections.elf_plt, __lw(__t3, __t2, pcrel_lo));
+ elf_write_int(dynamic_sections.elf_plt, __addi(__t1, __t1, -44));
+ elf_write_int(dynamic_sections.elf_plt, __addi(__t0, __t2, pcrel_lo));
+ elf_write_int(dynamic_sections.elf_plt, __srli(__t1, __t1, 2));
+ elf_write_int(dynamic_sections.elf_plt, __lw(__t0, __t0, 4));
+ elf_write_int(dynamic_sections.elf_plt, __jalr(__zero, __t3, 0));
+ for (int i = 0; i * PLT_ENT_SIZE < end; i++) {
+ /* elf_generate() ensures that the .got section is placed
+ * a higher memory address than the plt section.
+ *
+ * Consequently, 'ofs' must always be positive.
+ *
+ * t1 = &PLT[N] + 12 (address of the 'nop' instruction)
+ *
+ * First call:
+ * t3 = &PLT[0]
+ *
+ * Subsequent calls:
+ * t3 = Address of the function
+ */
+ addr_of_plt =
+ dynamic_sections.elf_plt_start + PLT_FIXUP_SIZE + PLT_ENT_SIZE * i;
+ addr_of_got = dynamic_sections.elf_got_start + PTR_SIZE * (i + 2);
+ ofs = addr_of_got - addr_of_plt;
+ pcrel_hi = ofs & ~0xFFF;
+ pcrel_lo = ofs & 0xFFF;
+ if (pcrel_lo > 2047) {
+ pcrel_hi += 1;
+ pcrel_lo -= 0x1000;
+ }
+
+ elf_write_int(dynamic_sections.elf_plt, __auipc(__t3, pcrel_hi));
+ elf_write_int(dynamic_sections.elf_plt, __lw(__t3, __t3, pcrel_lo));
+ elf_write_int(dynamic_sections.elf_plt, __jalr(__t1, __t3, 0));
+ elf_write_int(dynamic_sections.elf_plt, __addi(__zero, __zero, 0));
+ }
+}
diff --git a/tests/snapshots/fib-riscv-dynamic.json b/tests/snapshots/fib-riscv-dynamic.json
new file mode 100644
index 00000000..34eede5f
--- /dev/null
+++ b/tests/snapshots/fib-riscv-dynamic.json
@@ -0,0 +1 @@
+{"_subgraph_cnt":13,"directed":true,"edges":[{"_gvid":0,"head":14,"headport":"n","tail":13,"tailport":"s"},{"_gvid":1,"head":15,"tail":14,"weight":"100"},{"_gvid":2,"head":16,"tail":15,"weight":"100"},{"_gvid":3,"head":17,"headport":"n","tail":16,"tailport":"sw"},{"_gvid":4,"head":22,"headport":"n","tail":16,"tailport":"se"},{"_gvid":5,"head":18,"tail":17,"weight":"100"},{"_gvid":6,"head":19,"headport":"n","tail":18,"tailport":"s"},{"_gvid":7,"head":19,"headport":"n","tail":20,"tailport":"s"},{"_gvid":8,"head":19,"headport":"n","tail":21,"tailport":"s"},{"_gvid":9,"head":23,"headport":"n","tail":22,"tailport":"s"},{"_gvid":10,"head":24,"tail":23,"weight":"100"},{"_gvid":11,"head":25,"tail":24,"weight":"100"},{"_gvid":12,"head":26,"headport":"n","tail":25,"tailport":"sw"},{"_gvid":13,"head":27,"headport":"n","tail":25,"tailport":"se"},{"_gvid":14,"head":20,"tail":26,"weight":"100"},{"_gvid":15,"head":28,"headport":"n","tail":27,"tailport":"s"},{"_gvid":16,"head":29,"tail":28,"weight":"100"},{"_gvid":17,"head":30,"tail":29,"weight":"100"},{"_gvid":18,"head":31,"tail":30,"weight":"100"},{"_gvid":19,"head":32,"tail":31,"weight":"100"},{"_gvid":20,"head":33,"tail":32,"weight":"100"},{"_gvid":21,"head":34,"tail":33,"weight":"100"},{"_gvid":22,"head":35,"tail":34,"weight":"100"},{"_gvid":23,"head":36,"tail":35,"weight":"100"},{"_gvid":24,"head":37,"tail":36,"weight":"100"},{"_gvid":25,"head":38,"tail":37,"weight":"100"},{"_gvid":26,"head":21,"tail":38,"weight":"100"},{"_gvid":27,"head":40,"tail":39,"weight":"100"},{"_gvid":28,"head":41,"tail":40,"weight":"100"},{"_gvid":29,"head":42,"tail":41,"weight":"100"},{"_gvid":30,"head":43,"tail":42,"weight":"100"},{"_gvid":31,"head":44,"tail":43,"weight":"100"},{"_gvid":32,"head":45,"tail":44,"weight":"100"},{"_gvid":33,"head":46,"tail":45,"weight":"100"},{"_gvid":34,"head":47,"tail":46,"weight":"100"},{"_gvid":35,"head":48,"tail":47,"weight":"100"},{"_gvid":36,"head":49,"headport":"n","tail":48,"tailport":"s"}],"label":"","name":"CFG","objects":[{"_gvid":0,"edges":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26],"nodes":[13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"subgraphs":[1,2,3,4,5,6,7,8,9]},{"_gvid":1,"edges":[],"nodes":[13],"subgraphs":[]},{"_gvid":2,"edges":[1,2],"nodes":[14,15,16],"subgraphs":[]},{"_gvid":3,"edges":[5],"nodes":[17,18],"subgraphs":[]},{"_gvid":4,"edges":[],"nodes":[19],"subgraphs":[]},{"_gvid":5,"edges":[],"nodes":[22],"subgraphs":[]},{"_gvid":6,"edges":[10,11],"nodes":[23,24,25],"subgraphs":[]},{"_gvid":7,"edges":[14],"nodes":[20,26],"subgraphs":[]},{"_gvid":8,"edges":[],"nodes":[27],"subgraphs":[]},{"_gvid":9,"edges":[16,17,18,19,20,21,22,23,24,25,26],"nodes":[21,28,29,30,31,32,33,34,35,36,37,38],"subgraphs":[]},{"_gvid":10,"edges":[27,28,29,30,31,32,33,34,35,36],"nodes":[39,40,41,42,43,44,45,46,47,48,49],"subgraphs":[11,12]},{"_gvid":11,"edges":[27,28,29,30,31,32,33,34,35],"nodes":[39,40,41,42,43,44,45,46,47,48],"subgraphs":[]},{"_gvid":12,"edges":[],"nodes":[49],"subgraphs":[]},{"_gvid":13,"edges":[],"label":"pseudo","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":14,"edges":[],"label":".t00 := CONST 0","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":15,"edges":[],"label":".t10 := n0 == .t00","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":16,"edges":[],"label":"BRANCH .t10","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":17,"edges":[],"label":".t20 := CONST 0","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":18,"edges":[],"label":"RETURN .t20","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":19,"edges":[],"label":"pseudo","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":20,"edges":[],"label":"RETURN .t50","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":21,"edges":[],"label":"RETURN .t120","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":22,"edges":[],"label":"pseudo","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":23,"edges":[],"label":".t30 := CONST 1","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":24,"edges":[],"label":".t40 := n0 == .t30","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":25,"edges":[],"label":"BRANCH .t40","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":26,"edges":[],"label":".t50 := CONST 1","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":27,"edges":[],"label":"pseudo","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":28,"edges":[],"label":".t60 := CONST 1","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":29,"edges":[],"label":".t70 := n0 - .t60","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":30,"edges":[],"label":"PUSH .t70","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":31,"edges":[],"label":"CALL @fib","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":32,"edges":[],"label":".t80 := RETURN VALUE","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":33,"edges":[],"label":".t90 := CONST 2","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":34,"edges":[],"label":".t100 := n0 - .t90","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":35,"edges":[],"label":"PUSH .t100","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":36,"edges":[],"label":"CALL @fib","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":37,"edges":[],"label":".t110 := RETURN VALUE","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":38,"edges":[],"label":".t120 := .t80 + .t110","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":39,"edges":[],"label":".t130 := [.rodata] + 0","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":40,"edges":[],"label":".t140 := CONST 10","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":41,"edges":[],"label":"PUSH .t140","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":42,"edges":[],"label":"CALL @fib","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":43,"edges":[],"label":".t150 := RETURN VALUE","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":44,"edges":[],"label":"PUSH .t130","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":45,"edges":[],"label":"PUSH .t150","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":46,"edges":[],"label":"CALL @printf","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":47,"edges":[],"label":".t160 := CONST 0","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":48,"edges":[],"label":"RETURN .t160","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":49,"edges":[],"label":"pseudo","nodes":[],"shape":"box","subgraphs":[]}],"strict":true}
diff --git a/tests/snapshots/hello-riscv-dynamic.json b/tests/snapshots/hello-riscv-dynamic.json
new file mode 100644
index 00000000..f005855f
--- /dev/null
+++ b/tests/snapshots/hello-riscv-dynamic.json
@@ -0,0 +1 @@
+{"_subgraph_cnt":3,"directed":true,"edges":[{"_gvid":0,"head":4,"tail":3,"weight":"100"},{"_gvid":1,"head":5,"tail":4,"weight":"100"},{"_gvid":2,"head":6,"tail":5,"weight":"100"},{"_gvid":3,"head":7,"tail":6,"weight":"100"},{"_gvid":4,"head":8,"tail":7,"weight":"100"},{"_gvid":5,"head":9,"tail":8,"weight":"100"},{"_gvid":6,"head":10,"tail":9,"weight":"100"},{"_gvid":7,"head":11,"tail":10,"weight":"100"},{"_gvid":8,"head":12,"headport":"n","tail":11,"tailport":"s"}],"label":"","name":"CFG","objects":[{"_gvid":0,"edges":[0,1,2,3,4,5,6,7,8],"nodes":[3,4,5,6,7,8,9,10,11,12],"subgraphs":[1,2]},{"_gvid":1,"edges":[0,1,2,3,4,5,6,7],"nodes":[3,4,5,6,7,8,9,10,11],"subgraphs":[]},{"_gvid":2,"edges":[],"nodes":[12],"subgraphs":[]},{"_gvid":3,"edges":[],"label":".t00 := [.rodata] + 0","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":4,"edges":[],"label":"PUSH .t00","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":5,"edges":[],"label":"PUSH argc0","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":6,"edges":[],"label":"CALL @printf","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":7,"edges":[],"label":".t10 := [.rodata] + 4","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":8,"edges":[],"label":"PUSH .t10","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":9,"edges":[],"label":"CALL @printf","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":10,"edges":[],"label":".t20 := CONST 0","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":11,"edges":[],"label":"RETURN .t20","nodes":[],"shape":"box","subgraphs":[]},{"_gvid":12,"edges":[],"label":"pseudo","nodes":[],"shape":"box","subgraphs":[]}],"strict":true}