Anonymous
Problem nach dem Erstellen von Signalhandler mit 'Sigaktion' in einer eigenständigen Multi-Thread-ausführbare Datei. Sig
Post
by Anonymous » 27 Feb 2025, 05:23
Ich versuche, eine eigenständige (ohne libc) mutlithreading ausführbare Datei in Linux zu erstellen, die mit Signalen und Unix-Domain-Socket behandelt werden kann, aber ich habe Probleme, den Signalhandler einzurichten.
Code: Select all
#include
#include
#include
#include
#include
#include
#include
#include
#define STACKSIZE 16384
#define CORES_COUNT 1
#define STARTING_CORE 1
struct thread_props {
pid_t tid;
pid_t tret;
int32_t coreID;
int32_t stopped;
struct timespec start;
struct timespec end;
struct clone_args clargs;
};
typedef uint32_t cpuset_part_t;
#define MAX_CPUSET_BITS 32
#define CPUSET_PART_BITS (sizeof(cpuset_part_t) * 8)
#define CPUSET_PARTS ((MAX_CPUSET_BITS/8)/sizeof(cpuset_part_t))
#define CPUSET_ZERO(cpuset) memset((uint8_t *) cpuset, 0, CPUSET_PARTS * sizeof(cpuset_part_t))
#define CPUSET_ADD(cpuset, cpu) (cpuset[cpu/CPUSET_PART_BITS] = BIT_SET(cpuset[cpu/CPUSET_PART_BITS], (cpu % CPUSET_PART_BITS) + 1))
#define CPUSET_RM(cpuset, cpu) (cpuset[cpu/CPUSET_PART_BITS] = BIT_CLR(cpuset[cpu/CPUSET_PART_BITS], (cpu % CPUSET_PART_BITS) + 1))
#define CPUSET_TOGGLE(cpuset, cpu) (cpuset[cpu/CPUSET_PART_BITS] = BIT_TOGGLE(cpuset[cpu/CPUSET_PART_BITS], (cpu % CPUSET_PART_BITS) + 1))
#define CPUSET_ALLON(cpuset) memset((uint8_t *) cpuset, (uint64_t) -1, CPUSET_PARTS * sizeof(cpuset_part_t))
static int32_t thread_function(struct thread_props *tp)
{
cpuset_part_t cpuset[CPUSET_PARTS];
CPUSET_ZERO(cpuset);
CPUSET_ADD(cpuset, tp->coreID);
sched_setaffinity(0, sizeof(cpuset), (uint64_t) cpuset);
printf("before stop: tid %sl:d\n", tp->tid);
futex((uint64_t) &tp->stopped, FUTEX_WAIT, 1, (uint64_t) NULL, (uint64_t) NULL, 0);
clock_gettime(CLOCK_THREAD_CPUTIME_ID, (uint64_t) &tp->start);
printf("doing some works here\n")
clock_gettime(CLOCK_THREAD_CPUTIME_ID, (uint64_t) &tp->end);
tp->tid = 0;
futex((uint64_t) &tp->tid, FUTEX_WAKE, 1, (uint64_t) NULL, (uint64_t) NULL, 0);
return 0;
}
void create_thread(struct thread_props *tp) {
printf("Started\n");
tp->stopped = 1;
tp->clargs.flags = (CLONE_SYSVSEM | CLONE_IO | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD);
tp->clargs.exit_signal = 0;
tp->clargs.parent_tid = 0;
tp->clargs.pidfd = 0;
tp->clargs.cgroup = 0;
tp->clargs.set_tid = 0;
tp->clargs.set_tid_size = 0;
tp->clargs.tls = 0;
tp->clargs.child_tid = 0;
tp->clargs.stack = (uint64_t) mmap((uint64_t) NULL, STACKSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if(tp->clargs.stack == (uint64_t) -1) {
printf("mmap failed");
return;
}
printf("Initialized: coreID = %sl:d\n", tp->coreID);
tp->clargs.stack_size = STACKSIZE;
int64_t clret = 0;
clret = clone3((uint64_t) &tp->clargs, (uint64_t) sizeof(struct clone_args));
if(clret == 0) {
tp->tid = gettid();
tp->tret = tp->tid;
printf("entered tid: %sl:d\n", tp->tret);
thread_function(tp);
printf("exiting tid: %sl:d\n", gettid());
exit(0);
}
}
void wait_thread(struct thread_props *tp) {
futex((uint64_t) &tp->tid, FUTEX_WAIT, tp->tret, (uint64_t) NULL, (uint64_t) NULL, 0);
}
static struct thread_props tprops[CORES_COUNT];
void wake_threads(int32_t signo) {
printf("signaled\n");
for(int32_t core = 0; core < CORES_COUNT; ++core)
futex((uint64_t) &tprops[core].stopped, FUTEX_WAKE, CORES_COUNT, (uint64_t) NULL, (uint64_t) NULL, 0);
}
int32_t threads_test() {
cpuset_part_t cpuset[CPUSET_PARTS];
CPUSET_ZERO(cpuset);
CPUSET_ADD(cpuset, 0); // core 0 here
sched_setaffinity(0, sizeof(cpuset), (uint64_t) cpuset);
printf("entered tgid: %sl:d\n", gettid());
struct sigaction siganew = {};
struct sigaction sigaold = {};
siganew.sa_handler = wake_threads;
siganew.sa_restorer = NULL;
siganew.sa_mask = 0;
siganew.sa_flags = SA_RESTART; // for the futexes to restart after signal
rt_sigaction(SIGUSR1, (uint64_t) &siganew, (uint64_t) &sigaold, sizeof(sigset_t));
for(int32_t core = 0; core < CORES_COUNT; ++core) {
tprops[core].coreID = STARTING_CORE + core;
create_thread(&tprops[core]);
}
struct timespec waittime;
waittime.tv_sec = 1;
waittime.tv_nsec = 0;
// this will effectively give enough time for the threads to hook up on their stop futex
clock_nanosleep(CLOCK_MONOTONIC, 0, (uint64_t) &waittime, (uint64_t) NULL);
for(int32_t core = 0; core < CORES_COUNT; ++core)
wait_thread(&tprops[core]);
printf("exiting tgid: %sl:d\n", gettid());
return 0;
}
int32_t main() {
printf("entering threads test\n");
threads_test();
printf("finished threads test\n");
return 0;
}
Ich habe GDB verwendet, um herauszufinden, was passiert, aber das Sigsegv wird aus dem Kernel gesendet, nachdem die Ausgabe des Strace . Es wartet einfach richtig auf ein Signal < /p>
Code: Select all
$ strace -f output64/test.threads64
execve("output64/test.threads64", ["output64/test.threads64"], 0x7ffd61325aa8 /* 44 vars */) = 0
write(1, "entering threads test\n", 22entering threads test
) = 22
sched_setaffinity(0, 4, [0]) = 0
gettid() = 23568
write(1, "entered tgid: 23568\n", 20entered tgid: 23568
) = 20
rt_sigaction(SIGUSR1, {sa_handler=0x7fb47a12f720, sa_mask=[], sa_flags=SA_RESTART}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
write(1, "Started\n", 8Started
) = 8
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fb47a121000
write(1, "Initialized: coreID = 1\n", 24Initialized: coreID = 1
) = 24
clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_IO, exit_signal=0, stack=0x7fb47a121000, stack_size=0x4000}, 88strace: Process 23569 attached
) = 23569
[pid 23569] gettid(
[pid 23568] clock_nanosleep(CLOCK_MONOTONIC, 0, {tv_sec=1, tv_nsec=0},
[pid 23569] ) = 23569
[pid 23569] write(1, "entered tid: 23569\n", 19entered tid: 23569
) = 19
[pid 23569] sched_setaffinity(0, 4, [1]) = 0
[pid 23569] write(1, "before stop: tid 23569\n", 23before stop: tid 23569
) = 23
[pid 23569] futex(0x7fb47a132014, FUTEX_WAIT, 1, NULL
[pid 23568] NULL) = 0
[pid 23568] futex(0x7fb47a132000, FUTEX_WAIT, 23569, NULL
Dann von einer anderen Konsole und sendet ein Signal mit Kill :
und der Strace beendet mit:
Code: Select all
[pid 23568] --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=23649, si_uid=1000} ---
[pid 23568] --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=NULL} ---
[pid 23569] ) = ?
[pid 23569] +++ killed by SIGSEGV +++
+++ killed by SIGSEGV +++
< /code>
Ich kompile den Code mit < /p>
gcc -c -nostdinc -m64 -march=raptorlake -mtune=raptorlake -fno-stack-protector -ffreestanding -fno-builtin -pipe -O0 -g3 -ggdb -I /usr/lib/modules/6.13.1/build/headers/include -I include -Wall -Wextra -o output64/threads.c.o test/threads.c
< /code>
und link schließlich mit < /p>
verlinkenld output64/libc.c.o output64/arch/x86/start64.S.o output64/threads.c.o -x -pie -nostdlib --no-dynamic-linker -m elf_x86_64 -o output64/test.threads64
beendet normal, nachdem ich den Stop FUTEX des untergeordneten Threads herausgenommen habe. Das Sigsegv tritt nur dann auf, wenn es das Signal empfängt.
1740630211
Anonymous
Ich versuche, eine eigenständige (ohne libc) mutlithreading ausführbare Datei in Linux zu erstellen, die mit Signalen und Unix-Domain-Socket behandelt werden kann, aber ich habe Probleme, den Signalhandler einzurichten.[code]#include #include #include #include #include #include #include #include #define STACKSIZE 16384 #define CORES_COUNT 1 #define STARTING_CORE 1 struct thread_props { pid_t tid; pid_t tret; int32_t coreID; int32_t stopped; struct timespec start; struct timespec end; struct clone_args clargs; }; typedef uint32_t cpuset_part_t; #define MAX_CPUSET_BITS 32 #define CPUSET_PART_BITS (sizeof(cpuset_part_t) * 8) #define CPUSET_PARTS ((MAX_CPUSET_BITS/8)/sizeof(cpuset_part_t)) #define CPUSET_ZERO(cpuset) memset((uint8_t *) cpuset, 0, CPUSET_PARTS * sizeof(cpuset_part_t)) #define CPUSET_ADD(cpuset, cpu) (cpuset[cpu/CPUSET_PART_BITS] = BIT_SET(cpuset[cpu/CPUSET_PART_BITS], (cpu % CPUSET_PART_BITS) + 1)) #define CPUSET_RM(cpuset, cpu) (cpuset[cpu/CPUSET_PART_BITS] = BIT_CLR(cpuset[cpu/CPUSET_PART_BITS], (cpu % CPUSET_PART_BITS) + 1)) #define CPUSET_TOGGLE(cpuset, cpu) (cpuset[cpu/CPUSET_PART_BITS] = BIT_TOGGLE(cpuset[cpu/CPUSET_PART_BITS], (cpu % CPUSET_PART_BITS) + 1)) #define CPUSET_ALLON(cpuset) memset((uint8_t *) cpuset, (uint64_t) -1, CPUSET_PARTS * sizeof(cpuset_part_t)) static int32_t thread_function(struct thread_props *tp) { cpuset_part_t cpuset[CPUSET_PARTS]; CPUSET_ZERO(cpuset); CPUSET_ADD(cpuset, tp->coreID); sched_setaffinity(0, sizeof(cpuset), (uint64_t) cpuset); printf("before stop: tid %sl:d\n", tp->tid); futex((uint64_t) &tp->stopped, FUTEX_WAIT, 1, (uint64_t) NULL, (uint64_t) NULL, 0); clock_gettime(CLOCK_THREAD_CPUTIME_ID, (uint64_t) &tp->start); printf("doing some works here\n") clock_gettime(CLOCK_THREAD_CPUTIME_ID, (uint64_t) &tp->end); tp->tid = 0; futex((uint64_t) &tp->tid, FUTEX_WAKE, 1, (uint64_t) NULL, (uint64_t) NULL, 0); return 0; } void create_thread(struct thread_props *tp) { printf("Started\n"); tp->stopped = 1; tp->clargs.flags = (CLONE_SYSVSEM | CLONE_IO | CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD); tp->clargs.exit_signal = 0; tp->clargs.parent_tid = 0; tp->clargs.pidfd = 0; tp->clargs.cgroup = 0; tp->clargs.set_tid = 0; tp->clargs.set_tid_size = 0; tp->clargs.tls = 0; tp->clargs.child_tid = 0; tp->clargs.stack = (uint64_t) mmap((uint64_t) NULL, STACKSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); if(tp->clargs.stack == (uint64_t) -1) { printf("mmap failed"); return; } printf("Initialized: coreID = %sl:d\n", tp->coreID); tp->clargs.stack_size = STACKSIZE; int64_t clret = 0; clret = clone3((uint64_t) &tp->clargs, (uint64_t) sizeof(struct clone_args)); if(clret == 0) { tp->tid = gettid(); tp->tret = tp->tid; printf("entered tid: %sl:d\n", tp->tret); thread_function(tp); printf("exiting tid: %sl:d\n", gettid()); exit(0); } } void wait_thread(struct thread_props *tp) { futex((uint64_t) &tp->tid, FUTEX_WAIT, tp->tret, (uint64_t) NULL, (uint64_t) NULL, 0); } static struct thread_props tprops[CORES_COUNT]; void wake_threads(int32_t signo) { printf("signaled\n"); for(int32_t core = 0; core < CORES_COUNT; ++core) futex((uint64_t) &tprops[core].stopped, FUTEX_WAKE, CORES_COUNT, (uint64_t) NULL, (uint64_t) NULL, 0); } int32_t threads_test() { cpuset_part_t cpuset[CPUSET_PARTS]; CPUSET_ZERO(cpuset); CPUSET_ADD(cpuset, 0); // core 0 here sched_setaffinity(0, sizeof(cpuset), (uint64_t) cpuset); printf("entered tgid: %sl:d\n", gettid()); struct sigaction siganew = {}; struct sigaction sigaold = {}; siganew.sa_handler = wake_threads; siganew.sa_restorer = NULL; siganew.sa_mask = 0; siganew.sa_flags = SA_RESTART; // for the futexes to restart after signal rt_sigaction(SIGUSR1, (uint64_t) &siganew, (uint64_t) &sigaold, sizeof(sigset_t)); for(int32_t core = 0; core < CORES_COUNT; ++core) { tprops[core].coreID = STARTING_CORE + core; create_thread(&tprops[core]); } struct timespec waittime; waittime.tv_sec = 1; waittime.tv_nsec = 0; // this will effectively give enough time for the threads to hook up on their stop futex clock_nanosleep(CLOCK_MONOTONIC, 0, (uint64_t) &waittime, (uint64_t) NULL); for(int32_t core = 0; core < CORES_COUNT; ++core) wait_thread(&tprops[core]); printf("exiting tgid: %sl:d\n", gettid()); return 0; } int32_t main() { printf("entering threads test\n"); threads_test(); printf("finished threads test\n"); return 0; } [/code] Ich habe GDB verwendet, um herauszufinden, was passiert, aber das Sigsegv wird aus dem Kernel gesendet, nachdem die Ausgabe des Strace . Es wartet einfach richtig auf ein Signal < /p> [code]$ strace -f output64/test.threads64 execve("output64/test.threads64", ["output64/test.threads64"], 0x7ffd61325aa8 /* 44 vars */) = 0 write(1, "entering threads test\n", 22entering threads test ) = 22 sched_setaffinity(0, 4, [0]) = 0 gettid() = 23568 write(1, "entered tgid: 23568\n", 20entered tgid: 23568 ) = 20 rt_sigaction(SIGUSR1, {sa_handler=0x7fb47a12f720, sa_mask=[], sa_flags=SA_RESTART}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0 write(1, "Started\n", 8Started ) = 8 mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fb47a121000 write(1, "Initialized: coreID = 1\n", 24Initialized: coreID = 1 ) = 24 clone3({flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_IO, exit_signal=0, stack=0x7fb47a121000, stack_size=0x4000}, 88strace: Process 23569 attached ) = 23569 [pid 23569] gettid( [pid 23568] clock_nanosleep(CLOCK_MONOTONIC, 0, {tv_sec=1, tv_nsec=0}, [pid 23569] ) = 23569 [pid 23569] write(1, "entered tid: 23569\n", 19entered tid: 23569 ) = 19 [pid 23569] sched_setaffinity(0, 4, [1]) = 0 [pid 23569] write(1, "before stop: tid 23569\n", 23before stop: tid 23569 ) = 23 [pid 23569] futex(0x7fb47a132014, FUTEX_WAIT, 1, NULL [pid 23568] NULL) = 0 [pid 23568] futex(0x7fb47a132000, FUTEX_WAIT, 23569, NULL [/code] Dann von einer anderen Konsole und sendet ein Signal mit Kill : [code]$ kill -SIGUSR1 23568 [/code] und der Strace beendet mit: [code][pid 23568] --- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_USER, si_pid=23649, si_uid=1000} --- [pid 23568] --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=NULL} --- [pid 23569] ) = ? [pid 23569] +++ killed by SIGSEGV +++ +++ killed by SIGSEGV +++ < /code> Ich kompile den Code mit < /p> gcc -c -nostdinc -m64 -march=raptorlake -mtune=raptorlake -fno-stack-protector -ffreestanding -fno-builtin -pipe -O0 -g3 -ggdb -I /usr/lib/modules/6.13.1/build/headers/include -I include -Wall -Wextra -o output64/threads.c.o test/threads.c < /code> und link schließlich mit < /p> verlinkenld output64/libc.c.o output64/arch/x86/start64.S.o output64/threads.c.o -x -pie -nostdlib --no-dynamic-linker -m elf_x86_64 -o output64/test.threads64 [/code] beendet normal, nachdem ich den Stop FUTEX des untergeordneten Threads herausgenommen habe. Das Sigsegv tritt nur dann auf, wenn es das Signal empfängt.[code]sigset_t sigset = (sigset_t)(1