From 65b3dd7e525e0cbecc0144f917affec25bec34f5 Mon Sep 17 00:00:00 2001 From: John Reiser Date: Sat, 1 Nov 2025 08:59:05 -0700 Subject: [PATCH] risc-v 64-bit stubs build successfully in developer environment This is the "bottom half" to add support for riscv64 to UPX. Need update to stubtools for: gcc objcopy objdump ld Now using local cross tools /usr/bin/riscv64-linux-gnu-* on Ubuntu 24.04. Need fix for riscv64-linux.elf-fold.h being 224 KB. TODO: extend p_lx_elf.cpp linker.cpp TODO: implement nrv2d_d.S nrv2e_d.S lzma_d.S TODO: testing (using qemu-riscv64 and gdb-multiarch on Ubuntu 24.04) https://github.com/upx/upx/discussions/793 modified: src/stub/Makefile modified: src/stub/scripts/xstrip.py modified: src/stub/src/amd64-linux.elf-main2.c modified: src/stub/src/arch/amd64/macros.S new file: src/stub/src/arch/riscv/64/Makefile new file: src/stub/src/arch/riscv/64/Makefile.extra new file: src/stub/src/arch/riscv/64/macros.S new file: src/stub/src/arch/riscv/64/nrv2b_d.S new file: src/stub/src/arch/riscv/64/regs.h new file: src/stub/src/riscv64-expand.S new file: src/stub/src/riscv64-linux.elf-entry.S new file: src/stub/src/riscv64-linux.elf-fold.S new file: src/stub/src/riscv64-linux.elf-fold.lds new file: src/stub/src/riscv64-linux.elf-help_umf.S new file: src/stub/src/riscv64-linux.elf-main2.c modified: src/stub/src/upxfd_linux.c --- src/stub/Makefile | 70 ++++- src/stub/scripts/xstrip.py | 4 +- src/stub/src/amd64-linux.elf-main2.c | 44 ++- src/stub/src/arch/amd64/macros.S | 1 - src/stub/src/arch/riscv/64/Makefile | 3 + src/stub/src/arch/riscv/64/Makefile.extra | 55 ++++ src/stub/src/arch/riscv/64/macros.S | 96 ++++++ src/stub/src/arch/riscv/64/nrv2b_d.S | 85 ++++++ src/stub/src/arch/riscv/64/regs.h | 22 ++ src/stub/src/riscv64-expand.S | 211 ++++++++++++++ src/stub/src/riscv64-linux.elf-entry.S | 287 ++++++++++++++++++ src/stub/src/riscv64-linux.elf-fold.S | 340 ++++++++++++++++++++++ src/stub/src/riscv64-linux.elf-fold.lds | 37 +++ src/stub/src/riscv64-linux.elf-help_umf.S | 5 + src/stub/src/riscv64-linux.elf-main2.c | 1 + src/stub/src/upxfd_linux.c | 10 + 16 files changed, 1266 insertions(+), 5 deletions(-) create mode 100644 src/stub/src/arch/riscv/64/Makefile create mode 100644 src/stub/src/arch/riscv/64/Makefile.extra create mode 100644 src/stub/src/arch/riscv/64/macros.S create mode 100644 src/stub/src/arch/riscv/64/nrv2b_d.S create mode 100644 src/stub/src/arch/riscv/64/regs.h create mode 100644 src/stub/src/riscv64-expand.S create mode 100644 src/stub/src/riscv64-linux.elf-entry.S create mode 100644 src/stub/src/riscv64-linux.elf-fold.S create mode 100644 src/stub/src/riscv64-linux.elf-fold.lds create mode 100644 src/stub/src/riscv64-linux.elf-help_umf.S create mode 100644 src/stub/src/riscv64-linux.elf-main2.c diff --git a/src/stub/Makefile b/src/stub/Makefile index 59b6762d..356eb93f 100644 --- a/src/stub/Makefile +++ b/src/stub/Makefile @@ -42,6 +42,8 @@ endif # ************************************************************************/ ifndef STUBS +STUBS += riscv64-linux.elf-entry.h +STUBS += riscv64-linux.elf-fold.h STUBS += amd64-darwin.dylib-entry.h STUBS += amd64-darwin.macho-entry.h STUBS += amd64-darwin.macho-fold.h @@ -320,7 +322,7 @@ define tc.default.f-embed_objinfo_without_xstrip_keep_dot_text chmod a-x $1 $(call tc,objcopy) -R .data -R .bss $1 $(call tc,objcopy) -R .comment -R .note -R .note.GNU-stack -R .reginfo $1 - $(call tc,objcopy) --strip-unneeded --keep-symbol=_start --keep-symbol=upx_so_main $1 + $(call tc,objcopy) --strip-unneeded --keep-symbol=_start --keep-symbol=upx_so_main --keep-symbol=str_upx $1 # # Disassemble for human readability # objdump @@ -2182,6 +2184,72 @@ powerpc64-linux.kernel.vmlinux-head.h : $(srcdir)/src/$$T.S $(call tc,objcopy) --output-target binary --only-section .text tmp/$T.o tmp/$T.bin $(call tc,bin2h) tmp/$T.bin $@ +# ===== +# /*********************************************************************** +# // riscv64-linux.elf +# ************************************************************************/ + +riscv64-linux.elf%.h : tc_list = riscv64-linux.elf default +riscv64-linux.elf%.h : tc_bfdname = elf64-littleriscv64 + +tc.riscv64-linux.elf.gcc = /usr/bin/riscv64-linux-gnu-gcc -nostdinc -MMD -MT $@ +tc.riscv64-linux.elf.gcc += -fno-exceptions -fno-asynchronous-unwind-tables # -fno-stack-protector +tc.riscv64-linux.elf.gcc += -Wall -W -Wcast-align -Wcast-qual -Wstrict-prototypes -Wwrite-strings -Werror + +tc.riscv64-linux.elf.objcopy = /usr/bin/riscv64-linux-gnu-objcopy +# Override of objcopy is needed because: +# multiarch-objcopy-2.17 -F elf64-littleriscv64 --strip-unneeded --keep-symbol=_start --keep-symbol=upx_so_main tmp/riscv64-linux.elf-entry.bin +# multiarch-objcopy-2.17: tmp/riscv64-linux.elf-entry.bin: Invalid bfd target + +tc.riscv64-linux.elf.objdump = /usr/bin/riscv64-linux-gnu-objdump +# Override of objdump is needed because: +# multiarch-objdump-2.17 -b elf64-littleriscv64 -Dr tmp/riscv64-linux.elf-entry.bin | sed -e 's/[ ]*$//' > tmp/riscv64-linux.elf-entry.bin.disasm +# multiarch-objdump-2.17: tmp/riscv64-linux.elf-entry.bin: Invalid bfd target +# multiarch-objdump-2.17 -b elf64-littleriscv64 -htr -w tmp/riscv64-linux.elf-entry.bin | sed -e 's/[ ]*$//' | cat --squeeze-blank | sed -e '1{/^$/d}' -e '${/^$/d}' | sed -e '1s/^.*: *file format/file format/' -e 's/ / /g' -e 's/ 00*/ 0/g' -e 's/CONTENTS.*/CONTENTS/' > tmp/riscv64-linux.elf-entry.bin.dump +# multiarch-objdump-2.17: tmp/riscv64-linux.elf-entry.bin: Invalid bfd target + + +tc.riscv64-linux.elf.ld = /usr/bin/riscv64-linux-gnu-ld +# Override of ld is needed because: +# multiarch-objcopy-2.17 -F elf64-littleriscv64 --strip-unneeded --keep-symbol=_start --keep-symbol=upx_so_main tmp/riscv64-linux.elf-entry.bin +# multiarch-objcopy-2.17: tmp/riscv64-linux.elf-entry.bin: Invalid bfd target + +riscv64-linux.elf-entry.h : $(srcdir)/src/$$T.S + $(call tc,gcc) -c $< -o tmp/$T.bin + $(call tc,f-embed_objinfo,tmp/$T.bin) + $(call tc,bin2h) tmp/$T.bin $@ + +riscv64-linux.elf-fold.h : $(srcdir)/src/$$T.lds \ + tmp/$$T.o \ + tmp/riscv64-linux.elf-help_umf.o \ + tmp/riscv64-linux.elf-upxfd_linux.o \ + tmp/riscv64-expand.o \ + tmp/riscv64-linux.elf-main2.o + $(call tc,ld) -r -Map tmp/$T.map $(filter %.o,$^) -o tmp/$T.bin + $(call tc,f-embed_objinfo_without_xstrip_keep_dot_text,tmp/$T.bin) + $(call tc,bin2h) tmp/$T.bin $@ + +tmp/riscv64-linux.elf-fold.o : $(srcdir)/src/$$T.S + $(call tc,gcc) -c $< -o $@ + $(call tc,f-objstrip,$@) + +tmp/riscv64-linux.elf-main2.o : $(srcdir)/src/$$T.c $(srcdir)/src/i386-linux.elf-main2.c + $(call tc,gcc) -c $< -o $@ + $(call tc,f-objstrip,$@) + +tmp/riscv64-linux.elf-help_umf.o: $(srcdir)/src/$$T.S + $(call tc,gcc) -c $< -o $@ + +tmp/riscv64-linux.elf-upxfd_linux.o : $(srcdir)/src/upxfd_linux.c + $(call tc,gcc) -c -O $< -o $@ + $(call tc,objcopy) --strip-unneeded --rename-section .text=UMF_LINUX \ + -R .riscv.attributes -R .data -R .bss -R .note.GNU-stack $@ + $(call tc,objdump) -Dr $(tc_objdump_disasm_options) $@ | $(RTRIM) > $@.disasm + +tmp/riscv64-expand.o: $(srcdir)/src/$$T.S + $(call tc,gcc) -c $< -o $@ + +# ===== # /*********************************************************************** # // dependencies diff --git a/src/stub/scripts/xstrip.py b/src/stub/scripts/xstrip.py index 5b629e3a..79fc9664 100644 --- a/src/stub/scripts/xstrip.py +++ b/src/stub/scripts/xstrip.py @@ -157,7 +157,7 @@ def do_file(fn): # ELF64 LE eh, idata = idata[:64], idata[64:] e_shnum, e_shstrndx = struct.unpack("= e_shnum elif idata[4:7] == "\x02\x02\x01": # ELF64 BE eh, idata = idata[:64], idata[64:] @@ -178,6 +178,8 @@ def do_file(fn): assert pos >= len(odata), ("unexpected strip_with_dump", pos, len(odata)) elif re.search(r"^arm64-", os.path.basename(fn)): assert pos >= len(odata), ("unexpected strip_with_dump", pos, len(odata)) + elif re.search(r"^riscv", os.path.basename(fn)): + assert pos >= len(odata), ("unexpected strip_with_dump", pos, len(odata)) else: assert pos == len(odata), ("unexpected strip_with_dump", pos, len(odata)) else: diff --git a/src/stub/src/amd64-linux.elf-main2.c b/src/stub/src/amd64-linux.elf-main2.c index 84f8e4a4..b512ce5e 100644 --- a/src/stub/src/amd64-linux.elf-main2.c +++ b/src/stub/src/amd64-linux.elf-main2.c @@ -77,6 +77,16 @@ extern void my_bkpt(void *, ...); /*und*/ : "x30"); \ str; \ }) +#elif defined(__riscv) //}{ +#define ANDROID_FRIEND 0 +#define addr_string(string) ({ \ + char const *str; \ + asm("jal 0f; .string \"" string "\"; .balign 4; 0: mv %0,ra" \ +/*out*/ : "=r"(str) \ +/* in*/ : \ +/*und*/ : "x30"); \ + str; \ +}) #else //}{ #error; #endif //} @@ -279,6 +289,36 @@ make_hatch( DPRINTF("hatch=%%p\\n", hatch); return hatch; } +#elif defined(__riscv) //{ +static void * +make_hatch( + ElfW(Phdr) const *const phdr, + char *next_unc, + unsigned const frag_mask +) +{ + unsigned long a = (unsigned long)next_unc; + int *hatch = (int *)((3u & -a) + a); + int code[3] = { + 0,0,0 //NYI illegal instruction + }; + DPRINTF("make_hatch %%p %%p %%x\\n", phdr, next_unc, frag_mask); + if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) { + if (sizeof(code) <= (unsigned)(frag_mask & -(long)hatch)) { + hatch[0] = code[0]; + hatch[1] = code[1]; + hatch[2] = code[2]; + } + else { // Does not fit at hi end of .text, so must use a new page "permanently" + int mfd = upxfd_create(addr_string("upx"), MFD_EXEC); // the directory entry + write(mfd, &code, sizeof(code)); + hatch = mmap(0, sizeof(code), PROT_READ|PROT_EXEC, MAP_SHARED, mfd, 0); + close(mfd); + } + } + DPRINTF("hatch=%%p\\n", hatch); + return hatch; +} #elif defined(__powerpc64__) //}{ static void * make_hatch( @@ -660,7 +700,7 @@ upx_main2( // returns entry address /*arg4*/ ElfW(auxv_t) *const av #if defined(__x86_64) //{ /*arg5*/ , ElfW(Addr) elfaddr // In: &ElfW(Ehdr) for stub -#elif defined(__aarch64__) //}{ +#elif defined(__aarch64__) || defined(__riscv) //}{ /*arg5*/ , ElfW(Addr) elfaddr #elif defined(__powerpc64__) //}{ /*arg5*/ , ElfW(Addr) *p_reloc // In: &ElfW(Ehdr) for stub; Out: 'slide' for PT_INTERP @@ -681,7 +721,7 @@ upx_main2( // returns entry address // ehdr = Uncompress Ehdr and Phdrs unpackExtent(&xi2, &xo); // never filtered? -#if defined(__x86_64) || defined(__aarch64__) //{ +#if defined(__x86_64) || defined(__aarch64__) || defined(__riscv) //{ ElfW(Addr) *const p_reloc = &elfaddr; #endif //} ElfW(Addr) page_mask = get_page_mask(); (void)page_mask; diff --git a/src/stub/src/arch/amd64/macros.S b/src/stub/src/arch/amd64/macros.S index d0215861..3e025617 100644 --- a/src/stub/src/arch/amd64/macros.S +++ b/src/stub/src/arch/amd64/macros.S @@ -34,7 +34,6 @@ #undef x86_64 //.altmacro // not supported by x86_64-linux-as-2.15 - .code64 .macro section name .section \name diff --git a/src/stub/src/arch/riscv/64/Makefile b/src/stub/src/arch/riscv/64/Makefile new file mode 100644 index 00000000..413a5328 --- /dev/null +++ b/src/stub/src/arch/riscv/64/Makefile @@ -0,0 +1,3 @@ +# convenience Makefile +default %: + $(MAKE) -C ../../.. $@ diff --git a/src/stub/src/arch/riscv/64/Makefile.extra b/src/stub/src/arch/riscv/64/Makefile.extra new file mode 100644 index 00000000..67e6b92b --- /dev/null +++ b/src/stub/src/arch/riscv/64/Makefile.extra @@ -0,0 +1,55 @@ +MAKEFLAGS += -rR +.SUFFIXES: +.SECONDEXPANSION: + +ifndef top_srcdir +__dir_list = . .. ../.. ../../.. ../../../.. ../../../../.. +__dir_list += ../../../../../.. ../../../../../../.. ../../../../../../../.. +__dir_search = $(firstword $(foreach v,$1,$(if $(wildcard $v/$2),$v)) $3) +top_srcdir := $(call __dir_search,$(__dir_list),src/bele.h,NOT_FOUND) +endif +include $(wildcard $(top_srcdir)/Makevars-global.mk ./Makevars-local.mk) +vpath %.c $(top_srcdir)/src/stub/src/c + +STUBS = +include $(top_srcdir)/src/stub/src/c/Makevars-lzma.mk +ifneq ($(UPX_LZMA_VERSION),) +STUBS += lzma_d_cf.S lzma_d_cs.S lzma_d_cn.S +endif + +default.targets = all +ifeq ($(strip $(STUBS)),) +STUBS = NO_STUBS +all.targets = +endif +include $(top_srcdir)/src/stub/Makefile + + +# /*********************************************************************** +# // method-lzma +# ************************************************************************/ + +lzma_d_c%.S : tc_list = method-lzma riscv64-linux.elf default +lzma_d_c%.S : tc_bfdname = elf64-riscv64 + +c := tc.method-lzma.gcc +$c = $(tc.riscv64-linux.elf.gcc) +$c += -Os -fomit-frame-pointer +$c += -fno-unit-at-a-time +$c += -ffunction-sections +$c += -fwrapv +$c += -DWITH_LZMA=$(UPX_LZMA_VERSION) +$c += -I$(UPX_LZMADIR) +$c += -I$(top_srcdir)/src + +lzma_d_c%.S : lzma_d_c.c + $(call tc,gcc) $(PP_FLAGS) -c $< -o tmp/$T.o + $(call tc,f-objstrip,tmp/$T.o) + $(call tc,objcopy) -O binary --only-section .text.LzmaDecode tmp/$T.o tmp/$T.bin + head -c-1 tmp/$T.bin > tmp/$T.out + $(call tc,objdump) -b binary -m i386:x86-64 -D tmp/$T.out | $(RTRIM) > tmp/$T.out.disasm + $(call tc,bin2h) --mode=gas tmp/$T.out $@ + +lzma_d_cf.% : PP_FLAGS = -DFAST +lzma_d_cs.% : PP_FLAGS = -DSMALL +lzma_d_cn.% : PP_FLAGS = -DFAST -mno-red-zone diff --git a/src/stub/src/arch/riscv/64/macros.S b/src/stub/src/arch/riscv/64/macros.S new file mode 100644 index 00000000..185fbf2e --- /dev/null +++ b/src/stub/src/arch/riscv/64/macros.S @@ -0,0 +1,96 @@ +/* +; macros.S -- +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2025 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2025 Laszlo Molnar +; All Rights Reserved. +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them under the terms of the GNU General Public License as +; published by the Free Software Foundation; either version 2 of +; the License, or (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; +; +*/ + +.macro section name + .section \name +.endm + +.macro syscall num + li a7,\num; ecall +.endm + +NBPW= 8 + +.macro PUSH4 a,b,c,d + addi sp,sp,-4*NBPW + sd \a,0*NBPW(sp) + sd \b,1*NBPW(sp) + sd \c,2*NBPW(sp) + sd \d,3*NBPW(sp) +.endm + +.macro POP4 a,b,c,d + ld \a,0*NBPW(sp) + ld \b,1*NBPW(sp) + ld \c,2*NBPW(sp) + ld \d,3*NBPW(sp) + addi sp,sp,4*NBPW +.endm + +.macro PUSH3 a,b,c + addi sp,sp,-4*NBPW + sd \a,0*NBPW(sp) + sd \b,1*NBPW(sp) + sd \c,2*NBPW(sp) +.endm + +.macro POP3 a,b,c + ld \a,0*NBPW(sp) + ld \b,1*NBPW(sp) + ld \c,2*NBPW(sp) + addi sp,sp,4*NBPW +.endm + +.macro PUSH2 a,b + addi sp,sp,-2*NBPW + sd \a,0*NBPW(sp) + sd \b,1*NBPW(sp) +.endm + +.macro POP2 a,b,c + ld \a,0*NBPW(sp) + ld \b,1*NBPW(sp) + addi sp,sp,2*NBPW +.endm + +.macro PUSH1 a + addi sp,sp,-2*NBPW + sd \a,0*NBPW(sp) +.endm + +.macro POP1 a + ld \a,0*NBPW(sp) + addi sp,sp,2*NBPW +.endm + +.macro SUB2 dst,src + sub \dst,\dst,\src +.endm + +// vi:ts=8:et:nowrap diff --git a/src/stub/src/arch/riscv/64/nrv2b_d.S b/src/stub/src/arch/riscv/64/nrv2b_d.S new file mode 100644 index 00000000..4bff6c2f --- /dev/null +++ b/src/stub/src/arch/riscv/64/nrv2b_d.S @@ -0,0 +1,85 @@ +/* riscv64 nrv2b_d.S -- RISCV64 decompressor for NRV2B + + This file is part of the UPX executable compressor. + + Copyright (C) 1996-2025 Markus Franz Xaver Johannes Oberhumer + Copyright (C) 1996-2025 Laszlo Molnar + Copyright (C) 2000-2025 John F. Reiser + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + + + John F. Reiser + +*/ + + .text +#ifndef NO_METHOD_CHECK + cmpb $ M_NRV2B_LE32,methb; jne not_n2b +#endif + li bits,1<<31 // empty; force refill + li len,0 + li disp,-1 // initial displacement + j top_n2b + +#undef EOF +#define EOF eof_n2b + +lit_n2b: + addi src,src,1; sb bytpre,(dst) + addi dst,dst,1 +top_n2b: + lbu bytpre,(src) # prefetch: literal, or bottom 8 bits of offset + jnextb1yp lit_n2b + li off,1 +offmore_n2b: + getnextbp(off) + jnextb0np offmore_n2b + + addi off,off,-3; blt off,zero,len_n2b # use previous offset + slli off,off,8 + or off,off,bytpre; addi src,src,1 + xori disp,off,~0; beq disp,zero,EOF +len_n2b: + li off,1 + getnextb(len); getnextb(len) # two bits; cc set on result + bne len,zero,gotlen_n2b # raw 1,2,3 ==> 2,3,4 + mv len,off # len= 1, the msb + addi off,off,3-1 # raw 2.. ==> 5.. +lenmore_n2b: + getnextb(len) + jnextb0n lenmore_n2b +gotlen_n2b: + li t0,0x300; lui t0,0xfffff # -0xd00 + sltu retbit,t0,disp + add len,len,off; add len,len,retbit # len += off + (disp < -0xd00) + //NYI call copy@plt; .extern copy +bot_n2b: # In: 0==len + j top_n2b + +#ifndef NO_METHOD_CHECK //{ +not_n2b: + push %rdi; pop %rsi # src = arg1 +#endif //} + + # fall into daisy chain +/* +vi:ts=8:et:nowrap +*/ + diff --git a/src/stub/src/arch/riscv/64/regs.h b/src/stub/src/arch/riscv/64/regs.h new file mode 100644 index 00000000..0dc1214c --- /dev/null +++ b/src/stub/src/arch/riscv/64/regs.h @@ -0,0 +1,22 @@ +#if 0 //{ pre-defined +#define ra x1 /* return address */ +#define sp x2 /* stack ptr */ +#define gp x3 /* global ptr */ +#define tp x4 /* thread ptr */ +#endif //} +#define t0 x5 +#define t1 x6 +#define t2 x7 + +#define s0 x8 +#define s1 x9 + +#define rv a0 /* return value */ + +/* NOTE: THE FIRST ARGUMENT IS arg1, NOT arg0. */ +#define arg1 x10 /* same as rv */ +#define arg2 x11 +#define arg3 x12 +#define arg4 x13 +#define arg5 x14 +#define arg6 x15 diff --git a/src/stub/src/riscv64-expand.S b/src/stub/src/riscv64-expand.S new file mode 100644 index 00000000..2728f006 --- /dev/null +++ b/src/stub/src/riscv64-expand.S @@ -0,0 +1,211 @@ +/* +; riscv64-expand.S -- decompressors for riscv64 +; +; This file is part of the UPX executable compressor. +; +; Copyright (C) 1996-2021 Markus Franz Xaver Johannes Oberhumer +; Copyright (C) 1996-2021 Laszlo Molnar +; Copyright (C) 2000-2021 John F. Reiser +; All Rights Reserved. +; +; UPX and the UCL library are free software; you can redistribute them +; and/or modify them under the terms of the GNU General Public License as +; published by the Free Software Foundation; either version 2 of +; the License, or (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; see the file COPYING. +; If not, write to the Free Software Foundation, Inc., +; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +; +; Markus F.X.J. Oberhumer Laszlo Molnar +; +; +; John F. Reiser +; +; +*/ + +NBPW = 8 +#include "arch/riscv/64/macros.S" +.macro call subr; jal \subr; .endm + + section EXP_HEAD + +sz_unc= 0 +sz_cpr= 4 +b_method= 8 +b_ftid= 9 +b_cto8= 10 +b_extra= 11 +sz_binfo= 12 + +f_expand: .globl f_expand // f_expand(b_info *, dst, &dstlen) +// Supervisor for de-compression, un-filter, and sync_cache +// Input to supervisor: +#define fx_src a0 +#define fx_dst a1 +#define fx_dstlen a2 + PUSH4 fx_src,fx_dst,fx_dstlen,ra // MATCH_95 params to unfilter and sync_cache + +// Input to de-compressor: +#define xsrc a0 +#define xsrclen a1 +#define xdst a2 +#define xdstlen a3 +#define meth a4 + lbu meth,b_method(fx_src) + mv xdstlen,fx_dstlen // arg4 + mv xdst,fx_dst // arg3 + lw xsrclen,sz_cpr(xsrc) // arg2 + addi xsrc,fx_src,sz_binfo // arg1 + call decompress + mv a3,a0 // save retval + + POP4 fx_src,fx_dst,fx_dstlen,ra // MATCH_95 fx_src,fx_dst,fx_dstlen,ra + lw fx_dst,(fx_dst) // actual length used by decompressor + PUSH3 fx_src,fx_dst,a2 // MATCH_96 params for sync_cache + lbu a3,b_ftid(sp) + lbu a2,b_cto8(sp) + beqz a3,no_unf +//NYI #include "arch/arm64/v8/bxx.S" // unfilter code; args in registers, fall-through return +no_unf: + + POP2 a0,a1 // MATCH_96 dst, len + add a1,a1,a0 // lo, hi + //NYI sync_cache // in macros.S + POP1 a0 // MATCH_96 retval from decompress + ret +#undef fx_src +#undef fx_dst +#undef fx_dstlen +#undef xsrc +#undef xsrclen +#undef xdst +#undef xdstlen + +decompress: // (src *, cpr_len, dst *, &dstlen); +// sections NRV2B, etc, inserted here by addLoader() from ::buildLinuxLoader() + + section EXP_TAIL +// Fall through: daisy chain had no matching method + li a0,-1 + mv a1,meth + ebreak // EXP_TAIL daisy chain fail + +#define src a0 +#define bits a1 +#define dst a2 +#define off a3 +#define len a4 +#define disp a5 +#define retbit t0 +#define bytpre t1 + + + .globl eof + +// sync_cache is done in tail of f_expand, after possible unfilter +// NYI: eof_n2b, eof_n2d, eof_n2e should be unified. +eof_n2b: // .globl eof_n2b .type eof_n2b,%function +eof: // MATCH_90 end of a compressed extent; need sync_cache after unfilter + ld a2,0*NBPW(sp) // &input_eof + sub a0,src,a3 // src -= eof; // return 0: good; else: bad + ld a1,1*NBPW(sp) // original dst + sub dst,dst,a1 // dst -= original dst; actual length of output + ld a1,2*NBPW(sp) // &dstlen + sw dst,(a1) // actual length used at dst XXX: 4GB + ld ra,3*NBPW(sp) + addi sp,sp,4*NBPW + ret + + +// WINDOWS_BACK compatibility seems to be broken +// if POP3 replaces POP2+POP1 (MATCH_92, MATCH_91) +#define srclim a7 +eof_n2d: // .globl eof_n2d +eof_n2e: // .globl eof_n2e + POP2 a3,a4 // MATCH_92 r3= orig_dst; r4= plen_dst + SUB2 src,srclim // 0 if actual src length equals expected length + SUB2 dst,a3 // actual dst length + sw dst,(a4) + POP1 ra // MATCH_91 + ret + +eof_lzma: .globl eof_lzma + POP4 x2,x3, fp,ra // MATCH_94 x2= orig_dst; x3= plen_dst + ret + +upx_mmap_and_fd: .globl upx_mmap_and_fd + // UMF_LINUX goes here + +#define M_NRV2B_LE32 2 +#define M_NRV2B_8 3 +#define M_NRV2D_LE32 5 +#define M_NRV2D_8 6 +#define M_NRV2E_LE32 8 +#define M_NRV2E_8 9 +#define M_CL1B_LE32 11 +#define M_LZMA 14 + +#define NO_METHOD_CHECK 1 /* subsumed here by daisy chain */ + +getbit: + srli retbit,bits,63 # hi bit of 64 + slli bits,bits,1; beq bits,zero,refill + ret +/* On refill: prefetch next byte, for latency reduction on literals and offsets. */ +refill: + lw bits,(src); addi src,src,4 + srli retbit,bits,31 # hi bit of 4 bytes fetched + slli bits,bits,1; lbu bytpre,(src) # prefetch + ori bits,bits,1 # the flag bit + slli bits,bits,32 # hi 32 bits in 64-bit register + ret + +/* jump on next bit {0,1} with prediction {y==>likely, n==>unlikely} */ +/* Prediction omitted for now. */ + +#define jnextb0np jnextb0yp +#define jnextb0yp GETBITp; beq retbit,zero, +#define jnextb1np jnextb1yp +#define jnextb1yp GETBITp; bne retbit,zero, +#define GETBITp \ + call getbit + +/* Same, but without prefetch (not useful for length of match.) */ +#define jnextb0n jnextb0y +#define jnextb0y GETBIT; beq retbit,zero, +#define jnextb1n jnextb1y +#define jnextb1y GETBIT; bne retbit,zero, +#define GETBIT \ + call getbit + +/* rotate next bit into bottom bit of reg */ +#define getnextbp(reg) GETBITp; slli reg,reg,1; or reg,reg,retbit +#define getnextb(reg) getnextbp(reg) + + section NRV2E + li t0,M_NRV2E_LE32; bne meth,t0,not_nrv2e +//NYI #include "arch/arm64/v8/nrv2e_d.S" +not_nrv2e: + + section NRV2D + li t0,M_NRV2D_LE32; bne meth,t0,not_nrv2d +//NYI #include "arch/arm64/v8/nrv2d_d.S" +not_nrv2d: + + section NRV2B + li t0,M_NRV2B_LE32; bne meth,t0,not_nrv2b +#include "arch/riscv/64/nrv2b_d.S" +not_nrv2b: + + section LZMA_DAISY + li t0,M_LZMA; bne meth,t0,not_lzma +//NYI #include "arch/arm64/v8/lzma_d.S" +not_lzma: diff --git a/src/stub/src/riscv64-linux.elf-entry.S b/src/stub/src/riscv64-linux.elf-entry.S new file mode 100644 index 00000000..ae836343 --- /dev/null +++ b/src/stub/src/riscv64-linux.elf-entry.S @@ -0,0 +1,287 @@ +/* riscv64-linux.elf-entry.S -- Linux program entry point & decompressor (Elf binary) +* +* This file is part of the UPX executable compressor. +* +* Copyright (C) 1996-2025 Markus Franz Xaver Johannes Oberhumer +* Copyright (C) 1996-2025 Laszlo Molnar +* Copyright (C) 2000-2025 John F. Reiser +* All Rights Reserved. +* +* UPX and the UCL library are free software; you can redistribute them +* and/or modify them under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. +* If not, write to the Free Software Foundation, Inc., +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* Markus F.X.J. Oberhumer Laszlo Molnar +* +* +* John F. Reiser +* +*/ + +#include "arch/riscv/64/macros.S" +#include "arch/riscv/64/regs.h" +NBPW= 8 + +/* These from /usr/include/unistd_64.h */ +__NR_close= 57 +__NR_exit= 93 +__NR_ftruncate= 64 +__NR_memfd_create= 279 +__NR_mmap= 222 +__NR_mprotect= 226 +__NR_msync= 227 +__NR_openat= 56 +__NR_write= 64 + +sz_Ehdr= 64 +e_phnum= 56 +sz_Phdr= 56 + +sz_l_info= 12 + l_lsize= 8 + +sz_p_info= 12 + +sz_b_info= 12 + sz_unc= 0 + sz_cpr= 4 + b_method= 8 + +AT_PAGESZ= 6 + +PROT_READ= 1 +PROT_WRITE= 2 +PROT_EXEC= 4 + +MAP_SHARED= 1 +MAP_PRIVATE= 2 +MAP_FIXED= 0x10 +MAP_ANONYMOUS= 0x20 + +MFD_EXEC= 0x0010 + +SYS_mmap= 9 // 64-bit mode only! + +FD_stderr= 2 + +M_NRV2B_LE32=2 // ../conf.h +M_NRV2D_LE32=5 +M_NRV2E_LE32=8 + + +// https://www.uclibc.org/docs/psABI-x86_64.pdf + section ELFMAINX +sz_pack2= .-4 +_start: .globl _start +//// nop; int3 # uncomment for debugging + + ld s0,0(sp) // argc + addi s1,sp,NBPW // argv + //NYI lea cancel_dummy(%rip),%r15 + + section ELFMAINX2 + +// same as in riscv64-linux.elf-fold.S +F_FRAME= 8*NBPW +F_EOF= 7*NBPW +F_ENTR= 6*NBPW; F_PMASK= F_ENTR +F_RDX= 5*NBPW +F_LENU= 4*NBPW +F_ADRU= 3*NBPW +F_ELFA= 2*NBPW +F_LENX= 1*NBPW +F_ADRX= 0*NBPW + +D_FOLD= 2*NBPW // .data space at start of unfold +D_PMASK= 0*NBPW +D_XSIGSEGV= 1*NBPW + +// find auxv + slli t0,s0,3 // NBPW * argc + add t0,t0,a1 // end argv +0: // find end of env + addi t0,t0,NBPW // &env[k] + ld t1,(t0); bnez t1,0b + +// find AT_PAGESZ in auxv + addi t0,t0,NBPW // &auxv + li a7,AT_PAGESZ // desired key +0: + ld t1,(t0); ld t2,NBPW(t0); addi t0,t0,2*NBPW + beq t1,a7,1f // found it + bne t1,zero,0b // keep searching + li t2,0x1000 // default PAGE_SIZE +1: + sub a7,zero,t2 // PAGE_MASK +2: +// Create anonymous temporary file on mfd; like upxfd_create + jal arg1,0f +str_upx: + .asciz "upx" +0: +// lla arg1,str_upx + li arg2,MFD_EXEC // flags + syscall __NR_memfd_create + bne a0,zero,ok_memfd // success +no_memfd: // so try /dev/shm + +O_RDWR= 2 +O_DIRECTORY= 0200000 // 0x010000 +O_TMPFILE= 020000000 // 0x400000 +# lla arg1,str_devmem + jal arg1,0f +str_devmem: + .asciz "/dev/mem" + .balign 4 +0: + li arg2,0700 // mode + li arg3,O_RDWR | O_TMPFILE | O_DIRECTORY + syscall __NR_openat +ok_memfd: +#define mfd s2 + mv mfd,rv + + lla t0,sz_pack2 +#define elfa s3 + lw t1,(t0); sub elfa,t0,t1 +#define binf s4 + lla binf,4+o_binfo +#define lenu s5 + lw lenu,sz_unc(binf) +#define szp2 s6 +#define lenx s7 + sub lenx,szp2,binf +#define adrx s8 + add adrx,elfa,binf + +#define old_sp s9 + mv old_sp,sp +// alloca() + sub sp,sp,lenu; andi sp,sp,-2*NBPW + +// Decompress the rest of this loader, and jump to it. + +#define dst a2 +#define src a0 +#define lsrc a1 + mv dst,sp + lw lsrc,sz_cpr(binf) + lbu t0,b_method(binf); li t1,M_NRV2B_LE32|(0<<8); beq t0,t1,0f; ebreak +0: + add t0,lsrc,src; sw t0,F_EOF(old_sp) + +//%rsp: +// MATCH_10 old_sp +// MATCH_11 &input_eof + +//%rbp === old_sp: array of F_FRAME + +#define src a0 +#define bits a1 +#define dst a2 +#define off a3 +#define len a4 +#define disp a5 +#define retbit t0 +#define bytpre t1 + +/* jump on next bit {0,1} with prediction {y==>likely, n==>unlikely} */ +/* Prediction omitted for now. */ + +#define jnextb0np jnextb0yp +#define jnextb0yp GETBITp; beq retbit,zero, +#define jnextb1np jnextb1yp +#define jnextb1yp GETBITp; bne retbit,zero, +#define GETBITp \ + call getbit + +/* Same, but without prefetch (not useful for length of match.) */ +#define jnextb0n jnextb0y +#define jnextb0y GETBIT; beq retbit,zero, +#define jnextb1n jnextb1y +#define jnextb1y GETBIT; bne retbit,zero, +#define GETBIT \ + call getbit + +/* rotate next bit into bottom bit of reg */ +#define getnextbp(reg) GETBITp; slli reg,reg,1; or reg,reg,retbit +#define getnextb(reg) getnextbp(reg) + +// We already checked the method and no filter. +#define NO_METHOD_CHECK 1 +#include "arch/riscv/64/nrv2b_d.S" + +getbit: + srli retbit,bits,63 # hi bit of 64 + slli bits,bits,1; beq bits,zero,refill + ret +/* On refill: prefetch next byte, for latency reduction on literals and offsets. */ +refill: + lw bits,(src); addi src,src,4 + srli retbit,bits,31 # hi bit of 4 bytes fetched + slli bits,bits,1; lbu bytpre,(src) # prefetch + ori bits,bits,1 # the flag bit + slli bits,bits,32 # hi 32 bits in 64-bit register + ret + +n2b_EOFbad: + ebreak +eof_n2b: + lw t0,F_EOF(old_sp) + bne t0,src,n2b_EOFbad + +// Write de-compressed 'fold' to file + lw t0,F_PMASK(old_sp); sw t0,D_PMASK(sp) // propagate PAGE_MASK + lwu arg3,F_LENU(old_sp) + mv arg2,sp + mv arg1,mfd + mv s2,arg3 // LENU +0: // /dev/shm might be restricted to 8KiB at a time! + syscall __NR_write + add arg2,arg2,rv // ptr += count + sub arg3,arg3,rv // decrement count + bnez arg3,0b // not finished +// de-alloca() + mv sp,old_sp + +// Map unfolded code the SELinux way + li arg6,0 // offset + mv arg5,mfd + li arg4,MAP_SHARED + li arg3,PROT_READ|PROT_EXEC // FIXME: add PROT_WRITE for DEBUG only + mv arg2,s2 // LENU + li arg1,0 // (%arg1)dst = 0; // kernel chooses addr + syscall __NR_mmap + sw rv,F_ADRU(old_sp) + + mv arg1,mfd + syscall __NR_close + +// Use the copy. + ld a0,F_ADRU(sp) // + addi a0,a0,D_FOLD // beyond .data + jr a0 // goto unfolded stub + + // IDENTSTR goes here + + section ELFMAINZ + .balign 4 +o_binfo: + .long O_BINFO // offset of b_info for .text | is_ptinerp | unmap_all_pages +FOLD: + // { b_info={sz_unc, sz_cpr, {4 char}}, folded_loader...} + +/*__XTHEENDX__*/ + +/* vim:set ts=8 sw=8 et: */ diff --git a/src/stub/src/riscv64-linux.elf-fold.S b/src/stub/src/riscv64-linux.elf-fold.S new file mode 100644 index 00000000..4c24d2ef --- /dev/null +++ b/src/stub/src/riscv64-linux.elf-fold.S @@ -0,0 +1,340 @@ +/* riscv64-linux.elf-fold.S -- linkage to C code to process Elf binary +* +* This file is part of the UPX executable compressor. +* +* Copyright (C) 2000-2025 John F. Reiser +* All Rights Reserved. +* +* UPX and the UCL library are free software; you can redistribute them +* and/or modify them under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; see the file COPYING. +* If not, write to the Free Software Foundation, Inc., +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +* +* Markus F.X.J. Oberhumer Laszlo Molnar +* +* +* John F. Reiser +* +*/ + +NBPW= 8 +#include "arch/riscv/64/macros.S" +#include "arch/riscv/64/regs.h" + +PATH_MAX= 4096 // /usr/include/linux/limits.h + +sz_b_info= 12 + sz_unc= 0 + sz_cpr= 4 + +sz_l_info= 12 +sz_p_info= 12 + +MAP_PRIVATE= 0x02 +MAP_FIXED= 0x10 + +PROT_READ= 0x1 + +O_RDONLY= 0 +FD_CWD= -100 + +OVERHEAD=2048 + +/* 64-bit mode only! */ +__NR_read= 0 +__NR_write= 1 +__NR_openat= 2 //NYI +__NR_close= 3 + +__NR_mmap= 9 +__NR_mprotect= 10 +__NR_munmap= 11 +__NR_msync= 26 // 0x1a + MS_SYNC= 4 +__NR_brk= 12 +__NR_memfd_create= 0x13f // 319 +__NR_ftruncate= 0x4d // 77 + +__NR_exit= 60 +__NR_readlink= 89 + +unmap_all_pages= (1<<1) +is_ptinterp= (1<<0) + +// same as in riscv64-linux.elf-entry.S +F_FRAME= 8*NBPW +F_EOF= 7*NBPW // sometimes omitted from FRAME +F_ENTR= 6*NBPW; F_PMASK= F_ENTR +F_RDX= 5*NBPW +F_LENU= 4*NBPW +F_ADRU= 3*NBPW +F_ELFA= 2*NBPW +F_LENX= 1*NBPW +F_ADRX= 0*NBPW + +D_FOLD= 2*NBPW // .data space at start of unfold +D_PMASK= 0*NBPW +D_XSIGSEGV= 1*NBPW + +#define rsp sp /* x2 */ +#define rax a0 /* x10 */ +#define rbx s1 /* x9 */ +#define rbp s0 /* x8 */ +#define rcx t2 /* x7 */ +#define rdx t1 /* x6 */ +#define r12 t0 /* x5 */ + +#define rdi a5 /* x15 */ +#define rsi a4 /* x14 */ +#define r15 a3 /* x13 */ +#define r14 a2 /* x12 */ +#define r13 a1 /* x11 */ + +#define rfd rbp /* file descriptor for /proc/self/exe */ + + .balign 8 +PAGE_MASK: .quad -1<<12 // default +cancel_sigsegv: .quad 0 // subroutine that cancels sigaction(SIGSEGV,) + +// IN: [ADRX,+LENX): compressed data; [ADRU,+LENU): expanded fold (w/ upx_main2) +// %rsp= %rbp= &F_ADRX; %r13= O_BINFO | is_ptinterp | unmap_all_pages + +// no 'section', thus '.text'; also loaded first in riscv64-linux.elf-fold.bin. +// Code from riscv64-linux2.elf-main.c is also .text, and is next. +fold_begin: +//// ebreak # DEBUG +// copy is_interp bit from r13 to F_ELFA(rbp) + ld t0,F_ELFA(rbp); andi t1,r13,is_ptinterp; or t0,t0,t1; + sd t0,F_ELFA(rbp) +// if !unmap_all_pages then need space on stack for result of readlink("/proc/self/exe",...) + mv rsi,rsp + andi t0,r13,unmap_all_pages + bnez t0,5f; li t0,PATH_MAX; sub rsp,rsp,t0; 0: + mv rdi,rsp + li rcx,8 +0: // copy down ADRX,LDRX,ELFA,ADRU,LENU,rdx,%entry,argc + ld t0,(rsi); addi rsi,rsi,NBPW; addi rcx,rcx,-1 + sd t0,(rdi); addi rdi,rdi,NBPW; bnez rcx,0b + +5: +0: // copy down argv + ld t0,(rsi); addi rsi,rsi,NBPW + sd t0,(rdi); addi rdi,rdi,NBPW + bnez t0,0b + mv r14,rdi // remember &new_env[0] + + andi t0,r13,unmap_all_pages; bnez t0,0f + sd zero,(rdi); addi rdi,rdi,NBPW // space for new_env[0] +0: // find old Elf64_auxv + ld t0,(rsi); sd t0,(rdi); bnez t0,0b + mv t2,rdi # &old Elf64_auxv + +0: // find &strings (beyond auxv) + ld t0,(rsi); ld t1,NBPW(rsi); addi rsi,rsi,2*NBPW + sd t0,(rdi); sd t1,NBPW(rdi); addi rdi,rdi,2*NBPW + bnez t0,0b + mv r15,rdi # beyond auxv + mv r13,rsi # beginning of strings + sub rdi,rdi,t2 # -length of auxv + + andi t0,r13,unmap_all_pages; beqz t0,env_pse + ori rfd,rfd,~0; mv r14,rdi; j no_env_pse # no fd; beyond auxv +env_pse: + li arg3,0 # O_RdONLY + lla arg2,proc_self_exe + li arg1,FD_CWD + call openat; mv rfd,rv # fd + + li arg3,-1+ PATH_MAX # buflen + mv arg2,sp # buffer + lla arg1,proc_self_exe + syscall __NR_readlink; bgez rv,0f +// readlink() failed. Set the result equal to the argument. + lla arg2,proc_self_exe # failure result= "/proc/self/exe" + li rcx,15 # sizeof "/proc/self/exe" +0: + // std + addi rdi,r13,-1 # dst last byte + sb zero,(rdi) # terminate + addi rsi,arg2,-1 + add rsi,rsi,rcx # src last byte +0: // slide up (hi end goes first) + lbu t0,(rsi); addi rsi,rsi,-1; addi rcx,rcx,-1 + sb t0,(rdi); addi rdi,rdi,-1; bnez rcx,0b +// add new env " =..." + li t0,' '; sb t0,-3(rdi); sb t0,-2(rdi); sb t0,-1(rdi); addi rdi,rdi,-3 + sd rdi,(r14) # new_env[0] + andi rdi,rdi,-NBPW # align + mv rcx,r15 + sub rsp,rsp,rcx # byte count + + addi rdi,rdi,-NBPW # &last qword of new auxv + addi rsi,r15,-NBPW # &last qword of old auxv + + // end of auxv must move by even number of NBPW + mv t0,rdi + xor t0,t0,rsi + andi t0,t0,NBPW + sub rdi,rdi,t0 + mv r14,rdi # &last qword of new auxv + + srl rcx,rcx,3 +0: + ld t0,(rsi); addi rsi,rsi,-NBPW; addi rcx,rcx,-1 + sd t0,(rdi); addi rdi,rdi,-NBPW; bnez rcx,0b + addi rsp,rdi,NBPW + // cld + + add arg4,r14,r12; addi arg4,arg4,NBPW # &new Elf64_auxv %r12 dead +no_env_pse: + ld arg1,F_ADRX(rbp) + ld arg2,F_LENX(rbp) + andi arg1,arg1,~(is_ptinterp | unmap_all_pages) + + ld arg5,F_ELFA(rbp) # ELFA | is_ptinterp + andi t0,arg5,is_ptinterp; or arg4,arg4,t0 # transfer is_ptinterp to &new_ELF64_auxv + sub arg5,arg5,t0 # and clear from ELFA + mv r13,arg5 # save ELFA + addi rsp,rsp,-OVERHEAD + mv arg3,rsp # &ELf64_Ehdr temporary space + call upx_main2 # Out: rv= entry + +// entry= upx_main2( +// b_info *arg1, {%rdi} +// total_size arg2, {%rsi} +// Elf64_Ehdr *arg3, {%rdx} +// Elf32_Auxv_t *arg4, {%rcx} +// Elf64_Addr elfaddr {%r8} +// ) + +// rsp/ {OVERHEAD},ADRU,LENU,rdx,%entry, argc,argv,0,envp,0,auxv,0,strings + li t0,OVERHEAD + add rsp,rsp,t0 # remove Elf64_Ehdr temporary space +#define r_fd ebx + ld rfd,(rbp) //NYI + sd rv,3*NBPW(rsp) # entry + + //NYI call *cancel_sigsegv(%rip) + +sz_Ehdr= 8*NBPW +e_type= 16 +ET_EXEC= 2 +sz_Phdr= 7*NBPW +p_memsz= 5*NBPW +// Discard pages of compressed data (includes [ADRX,+LENX) ) + addi arg2,r13,p_memsz+sz_Phdr+sz_Ehdr # Phdr[C_TEXT= 1].p_memsz + //cmpw $ET_EXEC, e_type(%r13); jne 1f + mv arg1,r13; call brk // also sets the brk +1: + mv arg1,r13; call munmap # discard C_TEXT compressed data + +// Map 1 page of /proc/self/exe so that the symlink does not disappear. + bltz rfd,no_pse_map + li arg6,0 # 0 offset + mv arg5,rfd + li arg4,MAP_PRIVATE + li arg3,PROT_READ + li arg2,1<<12 + li arg1,0 + call mmap + + mv arg1,rfd + call close + +no_pse_map: + ld arg1,F_ADRU(rbp) # ADRU: unfolded upx_main2 etc. + ld arg2,F_LENU(rbp) + li a7,__NR_munmap + jr r14 # goto: syscall; pop %rdx; ret + +get_page_mask: .globl get_page_mask + ld rv,PAGE_MASK + ret + + section SYSCALLS +my_bkpt: .globl my_bkpt + ebreak // my_bkpt + ret + +proc_self_exe: + .asciz "/proc/self/exe" + .balign 4 + +upxfd_create: .globl upxfd_create // (char *tag, unsigned flags) +0: // try memfd_create + syscall __NR_memfd_create; j ok_memfd //NYI +O_RDWR= 2 +O_DIRECTORY= 0200000 // 0x010000 +O_TMPFILE= 020000000 // 0x400000 + li arg4,0700 # mode + li arg3,O_RDWR|O_DIRECTORY|O_TMPFILE + la arg3,dev_shm + li arg1,FD_CWD + syscall __NR_openat +ok_memfd: + ret + +Pmap: .globl Pmap // page-align the lo end + ld t0,PAGE_MASK; xori t0,t0,~0 // frag mask + and arg1,arg1,t0 // frag + sub arg1,arg1,t0 + add arg2,arg2,t0 +mmap: .globl mmap + li a7,__NR_mmap +sysgo: +sys_check: + ebreak // sys_check + syscall 0 //NYI +//NYI syscall error checking + ret + +Psync: .globl Psync + li a7,__NR_msync + j 5f + +Pprotect: .globl Pprotect + li a7,__NR_mprotect + j 5f + +Punmap: .globl Punmap // page-align the lo end + li a7,__NR_munmap +5: + ld t0,PAGE_MASK; xori t0,t0,~0 // frag mask + and t0,t0,arg1 // frag + sub arg1,arg1,t0 + add arg2,arg2,t0 + j sysgo + +munmap: .globl munmap + li a7,__NR_munmap; 5: j 5f +exit: .globl exit + li a7,__NR_exit; 5: j 5f +brk: .globl brk + li a7,__NR_brk; 5: j 5f +close: .globl close + li a7,__NR_close; 5: j 5f +ftruncate: .globl ftruncate + li a7,__NR_ftruncate; 5: j 5f +open: .globl openat + li a7,__NR_openat; 5: j 5f +mprotect: .globl mprotect + li a7,__NR_mprotect; 5: j 5f +msync: .globl msync + li a7,__NR_msync; 5: j 5f +Pwrite: .globl Pwrite +write: .globl write + li a7,__NR_write; 5: j 5f +read: .globl read + li a7,__NR_read; 5: j sysgo + +// vim:set ts=8 sw=8 et: diff --git a/src/stub/src/riscv64-linux.elf-fold.lds b/src/stub/src/riscv64-linux.elf-fold.lds new file mode 100644 index 00000000..709a454a --- /dev/null +++ b/src/stub/src/riscv64-linux.elf-fold.lds @@ -0,0 +1,37 @@ +/* riscv64-linux.elf-fold.lds -- static linker script to build riscv64-linux.elf-fold.bin + + This file is part of the UPX executable compressor. + + Copyright (C) 2000-2025 John F. Reiser + All Rights Reserved. + + UPX and the UCL library are free software; you can redistribute them + and/or modify them under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer Laszlo Molnar + + + John F. Reiser + + */ + + +OUTPUT_FORMAT("elf64-littleriscv64", "elf64-littleriscv64", "elf64-littleriscv64") +OUTPUT_ARCH(riscv64) + +SECTIONS +{ + . = SIZEOF_HEADERS; +} diff --git a/src/stub/src/riscv64-linux.elf-help_umf.S b/src/stub/src/riscv64-linux.elf-help_umf.S new file mode 100644 index 00000000..866ce6a8 --- /dev/null +++ b/src/stub/src/riscv64-linux.elf-help_umf.S @@ -0,0 +1,5 @@ + .section HUMF_A, "ax" + j upx_mmap_and_fd_android + + .section HUMF_L, "ax" + j upx_mmap_and_fd_linux diff --git a/src/stub/src/riscv64-linux.elf-main2.c b/src/stub/src/riscv64-linux.elf-main2.c new file mode 100644 index 00000000..d2db8bc0 --- /dev/null +++ b/src/stub/src/riscv64-linux.elf-main2.c @@ -0,0 +1 @@ +#include "amd64-linux.elf-main2.c" diff --git a/src/stub/src/upxfd_linux.c b/src/stub/src/upxfd_linux.c index d1935efc..eeac66ed 100644 --- a/src/stub/src/upxfd_linux.c +++ b/src/stub/src/upxfd_linux.c @@ -74,6 +74,16 @@ extern void my_bkpt(void const *, ...); /*und*/ : "x30"); \ str; \ }) +#elif defined(__riscv) //}{ +#define ANDROID_FRIEND 0 +#define addr_string(string) ({ \ + char const *str; \ + asm("jal 0f; .string \"" string "\"; .balign 4; 0: mv %0,ra" \ +/*out*/ : "=r"(str) \ +/* in*/ : \ +/*und*/ : "x30"); \ + str; \ +}) #else //}{ #define ANDROID_FRIEND 0 #error addr_string