chen-charles / psp2cldr Goto Github PK
View Code? Open in Web Editor NEWLoading userspace PSP2 VELFs
License: MIT License
Loading userspace PSP2 VELFs
License: MIT License
master@13427e2ae745f9c5a665174b245d6dcebcf159d9
Suspected to be caused by coregrind/m_sigframe/sigframe-arm-linux.c:synth_ucontext
not passing CPSR at all.
x86-linux
does
sc->eflags = LibVEX_GuestX86_get_eflags(&tst->arch.vex);
so I suppose we could probably do something similar, probably,
sc->arm_cpsr = LibVEX_GuestARM_get_cpsr(&tst->arch.vex);
to at least observe a valid cpsr register.
There is probably something else wrong regarding how valgrind is implementing signals. According to this msg from qemu-devel, arm_pc
should actually always have LSB clear and use arm_cpsr.T
to indicate the thumb state. Currently, valgrind doesn't even process CPSR at all...
According to DDI0406C_d_armv7ar_arm B1.8.10 Exception return
, returning from an exception will use CPSR to determine the target instruction set and ignores the corresponding bits that doesn't align with the mode selected by CPSR (such behavior has been deprecated as well).
The linux kernel doesn't really do anything about PC and CPSR in uc
(arch/arm/kernel/signal.c:sys_rt_sigreturn
), it just takes them as is. This likely indicates valgrind is the one at fault here.
I can see why valgrind doesn't care about CPSR, it's because valgrind doesn't use CPSR as a register, but more of as a state tracker. The actual thumb bit information is directly encoded in Vex's PC (guest_R15T
).
Suspecting this worked in the past because if the user is not changing PC in ucontext, they would be returning the same PC value, which would actually have the LSB set/unset accordingly.
// compile with `gcc a.c`
#include <assert.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#define TESTBIT(var, pos) ((var) & ((uintptr_t)1 << (pos)))
#define SETBIT(var, pos) ((var) | ((uintptr_t)1 << (pos)))
#define CLEARBIT(var, pos) ((var) & ~((uintptr_t)1 << (pos)))
volatile sig_atomic_t count = 0;
uintptr_t ptr;
int pagesize;
void sigill_handler(int sig, siginfo_t *info, void *ucontext)
{
ucontext_t *ctx = (ucontext_t *)(ucontext);
if (count == 0)
{
count = 1;
// On linux, this will goto SIGILL, because the processor will decide based on CPSR.T, not PC's LSB.
// see DDI0406C_d_armv7ar_arm B1.8.10 Exception return
// https://developer.arm.com/documentation/ddi0406/cd/?lang=en
// On qemu, anything could happen, because strictly speaking, ARM stated that,
// "ARM deprecates any dependence on the requirements that the hardware ignores bits of the address."
// also see https://lists.gnu.org/archive/html/qemu-devel/2021-04/msg02211.html
// On valgrind, we would head to Thumb and SEGV.
ctx->uc_mcontext.arm_pc = ptr + pagesize;
ctx->uc_mcontext.arm_pc = SETBIT(ctx->uc_mcontext.arm_pc, 0);
ctx->uc_mcontext.arm_cpsr = CLEARBIT(ctx->uc_mcontext.arm_cpsr, 5);
// alternatively, on qemu and linux, if we do the followings,
// ctx->uc_mcontext.arm_pc = ptr + pagesize;
// ctx->uc_mcontext.arm_cpsr = SETBIT(ctx->uc_mcontext.arm_cpsr, 5);
// we will see SEGV because we would head to Thumb
// it's probably unfair to mention valgrind's behavior because valgrind ignores cpsr altogether
}
else
{
// make sure we landed because we were on an ARM instruction
assert(TESTBIT(ctx->uc_mcontext.arm_pc, 0) == 0);
assert(ctx->uc_mcontext.arm_pc == ptr + pagesize);
_exit(0);
}
}
int main(int argc, char *argv[])
{
/**
* ARM: (SIGILL)
* udf #0x44
*
* THUMB: (SEGV if we protect the page ahead of us)
* lsls r4, r6, #0x13
* b 0xffffffe6
*/
static const uint32_t INSTR_UDF44_ARM = 0xe7f004f0;
pagesize = sysconf(_SC_PAGE_SIZE);
assert(pagesize != -1);
ptr = (uintptr_t)mmap(NULL, 2 * pagesize, PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_POPULATE | MAP_PRIVATE, -1, 0);
assert(TESTBIT(ptr, 0) == 0);
*(uint32_t *)(ptr + pagesize) = INSTR_UDF44_ARM;
assert(mprotect((void *)ptr, pagesize, PROT_NONE) != -1);
struct sigaction action, oldaction;
sigfillset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = sigill_handler;
assert(sigaction(SIGILL, &action, &oldaction) == 0);
__asm__("udf #0xff");
return 0;
}
Unfortunately, valgrind's sigframe
implementation is incomplete/problematic in certain ways (coregrind/m_sigframe/sigframe-arm-linux.c:VG_(sigframe_destroy)
). Even x86-linux
doesn't know how to restore eflags and fp states from uc
.
Simply adding a line to pass cpsr
in uc
may/should resolve the first issue here.
Probably an additional block to interpret CPSR returned from the signal handler might be necessary to align with the standard behaviors.
Platform: Windows 11 22000.469
When attempted to run psp2cldr
on Docker / qemu-arm, a noticeable memory leak accumulates very quickly when threads are created.
Narrowed down the issue further to the following sample program,
// compile with `gcc a.c -pthread`
#include <assert.h>
#include <pthread.h>
#define MAGIC_RETURN ((void *)42)
void *thread_main(void *arg)
{
return MAGIC_RETURN;
}
int main(int argc, char *argv[])
{
size_t i;
for (i = 0;; i++)
{
pthread_t thread;
assert(pthread_create(&thread, NULL, thread_main, NULL) == 0);
void *ret;
assert(pthread_join(thread, &ret) == 0);
assert(ret == MAGIC_RETURN);
}
return 0;
}
A pmap -XX
is attached (a.log) for the above sample on arm32v7/fedora:35
.
Notice that the total mapped memory in vaddr space is 16066068 KiB
.
Reproducible with multiple distributions from docker hub, including arm32v7/alpine:3.15.0
(musl), arm32v7/ubuntu:focal-20220113
(glibc), and arm32v7/fedora:35
(glibc).
Also reproducible on qemu-arm version 6.1.0 (qemu-6.1.0-10.fc35)
with gcc-arm-10.2-2020.11-x86_64-arm-none-linux-gnueabihf
10.2-2020.11 from ARM's GNU Toolchain for the A-profile Architecture.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.