mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-01-23 02:14:37 +00:00
x86: Use PTRACE_GET_THREAD_AREA instead of sys_get_thread_area()
To minimize things done in parasite, PTRACE_GET_THREAD_AREA can be used to get remote tls. That also removes an additional compat stack (de)allocation in the parasite (also asm-coded syscall). In order to use PTRACE_GET_THREAD_AREA, the dumpee should be stopped. So, let's move this from criu to compel to non-seized state and put tls into thread info on x86. Signed-off-by: Dmitry Safonov <dima@arista.com>
This commit is contained in:
parent
72dc328502
commit
ffb848e6d9
15 changed files with 164 additions and 85 deletions
|
|
@ -28,6 +28,10 @@ lib-y += src/lib/infect-util.o
|
|||
lib-y += src/lib/infect.o
|
||||
lib-y += src/lib/ptrace.o
|
||||
|
||||
ifeq ($(ARCH),x86)
|
||||
lib-y += arch/$(ARCH)/src/lib/thread_area.o
|
||||
endif
|
||||
|
||||
# handle_elf() has no support of ELF relocations on ARM (yet?)
|
||||
ifneq ($(filter arm aarch64,$(ARCH)),)
|
||||
CFLAGS += -DNO_RELOCS
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@
|
|||
typedef struct user_pt_regs user_regs_struct_t;
|
||||
typedef struct user_fpsimd_state user_fpregs_struct_t;
|
||||
|
||||
#define __compel_arch_fetch_thread_area(tid, th) 0
|
||||
#define compel_arch_fetch_thread_area(tctl) 0
|
||||
#define compel_arch_get_tls_task(ctl, tls)
|
||||
#define compel_arch_get_tls_thread(tctl, tls)
|
||||
|
||||
#define REG_RES(r) ((uint64_t)(r).regs[0])
|
||||
#define REG_IP(r) ((uint64_t)(r).pc)
|
||||
#define REG_SP(r) ((uint64_t)((r).sp))
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ typedef struct {
|
|||
long uregs[18];
|
||||
} user_regs_struct_t;
|
||||
|
||||
#define __compel_arch_fetch_thread_area(tid, th) 0
|
||||
#define compel_arch_fetch_thread_area(tctl) 0
|
||||
#define compel_arch_get_tls_task(ctl, tls)
|
||||
#define compel_arch_get_tls_thread(tctl, tls)
|
||||
|
||||
typedef struct user_vfp user_fpregs_struct_t;
|
||||
|
||||
#define ARM_cpsr uregs[16]
|
||||
|
|
|
|||
|
|
@ -54,6 +54,10 @@ static inline bool user_regs_native(user_regs_struct_t *pregs)
|
|||
return true;
|
||||
}
|
||||
|
||||
#define __compel_arch_fetch_thread_area(tid, th) 0
|
||||
#define compel_arch_fetch_thread_area(tctl) 0
|
||||
#define compel_arch_get_tls_task(ctl, tls)
|
||||
#define compel_arch_get_tls_thread(tctl, tls)
|
||||
|
||||
#define REG_RES(regs) ((regs).MIPS_v0)
|
||||
#define REG_IP(regs) ((regs).cp0_epc)
|
||||
|
|
|
|||
|
|
@ -83,4 +83,9 @@ typedef struct {
|
|||
|
||||
#define __NR(syscall, compat) ({ (void)compat; __NR_##syscall; })
|
||||
|
||||
#define __compel_arch_fetch_thread_area(tid, th) 0
|
||||
#define compel_arch_fetch_thread_area(tctl) 0
|
||||
#define compel_arch_get_tls_task(ctl, tls)
|
||||
#define compel_arch_get_tls_thread(tctl, tls)
|
||||
|
||||
#endif /* UAPI_COMPEL_ASM_TYPES_H__ */
|
||||
|
|
|
|||
|
|
@ -84,4 +84,9 @@ struct mmap_arg_struct {
|
|||
unsigned long offset;
|
||||
};
|
||||
|
||||
#define __compel_arch_fetch_thread_area(tid, th) 0
|
||||
#define compel_arch_fetch_thread_area(tctl) 0
|
||||
#define compel_arch_get_tls_task(ctl, tls)
|
||||
#define compel_arch_get_tls_thread(tctl, tls)
|
||||
|
||||
#endif /* UAPI_COMPEL_ASM_TYPES_H__ */
|
||||
|
|
|
|||
|
|
@ -9,6 +9,29 @@
|
|||
#define SIGMAX 64
|
||||
#define SIGMAX_OLD 31
|
||||
|
||||
#define ARCH_HAS_PTRACE_GET_THREAD_AREA
|
||||
|
||||
/*
|
||||
* Linux preserves three TLS segments in GDT.
|
||||
* Offsets in GDT differ between 32-bit and 64-bit machines.
|
||||
* For 64-bit x86 those GDT offsets are the same
|
||||
* for native and compat tasks.
|
||||
*/
|
||||
#define GDT_ENTRY_TLS_MIN 12
|
||||
#define GDT_ENTRY_TLS_MAX 14
|
||||
#define GDT_ENTRY_TLS_NUM 3
|
||||
typedef struct {
|
||||
user_desc_t desc[GDT_ENTRY_TLS_NUM];
|
||||
} tls_t;
|
||||
|
||||
struct thread_ctx;
|
||||
struct parasite_ctl;
|
||||
struct parasite_thread_ctl;
|
||||
extern int __compel_arch_fetch_thread_area(int tid, struct thread_ctx *th);
|
||||
extern int compel_arch_fetch_thread_area(struct parasite_thread_ctl *tctl);
|
||||
extern void compel_arch_get_tls_thread(struct parasite_thread_ctl *tctl, tls_t *out);
|
||||
extern void compel_arch_get_tls_task(struct parasite_ctl *ctl, tls_t *out);
|
||||
|
||||
typedef struct {
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
|
|
|
|||
88
compel/arch/x86/src/lib/thread_area.c
Normal file
88
compel/arch/x86/src/lib/thread_area.c
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include "log.h"
|
||||
#include "asm/infect-types.h"
|
||||
#include "infect.h"
|
||||
#include "infect-priv.h"
|
||||
|
||||
#ifndef PTRACE_GET_THREAD_AREA
|
||||
# define PTRACE_GET_THREAD_AREA 25
|
||||
#endif
|
||||
|
||||
/*
|
||||
* For 64-bit applications, TLS (fs_base for Glibc) is in MSR,
|
||||
* which are dumped with the help of ptrace() and restored with
|
||||
* arch_prctl(ARCH_SET_FS/ARCH_SET_GS).
|
||||
*
|
||||
* But SET_FS_BASE will update GDT if base pointer fits in 4 bytes.
|
||||
* Otherwise it will set only MSR, which allows for mixed 64/32-bit
|
||||
* code to use: 2 MSRs as TLS base _and_ 3 GDT entries.
|
||||
* Having in sum 5 TLS pointers, 3 of which are four bytes and
|
||||
* other two eight bytes:
|
||||
* struct thread_struct {
|
||||
* struct desc_struct tls_array[3];
|
||||
* ...
|
||||
* #ifdef CONFIG_X86_64
|
||||
* unsigned long fsbase;
|
||||
* unsigned long gsbase;
|
||||
* #endif
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* Most x86_64 applications don't use GDT, but mixed code (i.e. Wine)
|
||||
* can use it. Be pessimistic and dump it for 64-bit applications too.
|
||||
*/
|
||||
int __compel_arch_fetch_thread_area(int tid, struct thread_ctx *th)
|
||||
{
|
||||
bool native_mode = user_regs_native(&th->regs);
|
||||
tls_t *ptls = &th->tls;
|
||||
int err, i;
|
||||
|
||||
/* Initialise as not present by default */
|
||||
for (i = 0; i < GDT_ENTRY_TLS_NUM; i++) {
|
||||
user_desc_t *d = &ptls->desc[i];
|
||||
|
||||
memset(d, 0, sizeof(user_desc_t));
|
||||
d->seg_not_present = 1;
|
||||
d->entry_number = GDT_ENTRY_TLS_MIN + i;
|
||||
}
|
||||
|
||||
for (i = 0; i < GDT_ENTRY_TLS_NUM; i++)
|
||||
{
|
||||
user_desc_t *d = &ptls->desc[i];
|
||||
|
||||
err = ptrace(PTRACE_GET_THREAD_AREA, tid,
|
||||
GDT_ENTRY_TLS_MIN + i, d);
|
||||
/*
|
||||
* Ignoring absent syscall on !CONFIG_IA32_EMULATION
|
||||
* where such mixed code can't run.
|
||||
* XXX: Add compile CONFIG_X86_IGNORE_64BIT_TLS
|
||||
* (for x86_64 systems with CONFIG_IA32_EMULATION)
|
||||
*/
|
||||
if (err == -EIO && native_mode)
|
||||
return 0;
|
||||
if (err) {
|
||||
pr_perror("get_thread_area failed for %d\n", tid);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int compel_arch_fetch_thread_area(struct parasite_thread_ctl *tctl)
|
||||
{
|
||||
return __compel_arch_fetch_thread_area(tctl->tid, &tctl->th);
|
||||
}
|
||||
|
||||
void compel_arch_get_tls_task(struct parasite_ctl *ctl, tls_t *out)
|
||||
{
|
||||
memcpy(out, &ctl->orig.tls, sizeof(tls_t));
|
||||
}
|
||||
|
||||
void compel_arch_get_tls_thread(struct parasite_thread_ctl *tctl, tls_t *out)
|
||||
{
|
||||
memcpy(out, &tctl->th.tls, sizeof(tls_t));
|
||||
}
|
||||
|
|
@ -8,6 +8,9 @@
|
|||
struct thread_ctx {
|
||||
k_rtsigset_t sigmask;
|
||||
user_regs_struct_t regs;
|
||||
#ifdef ARCH_HAS_PTRACE_GET_THREAD_AREA
|
||||
tls_t tls;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* parasite control block */
|
||||
|
|
|
|||
|
|
@ -681,6 +681,11 @@ static int parasite_start_daemon(struct parasite_ctl *ctl)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (__compel_arch_fetch_thread_area(pid, &ctl->orig)) {
|
||||
pr_err("Can't get thread area of %d\n", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ictx->make_sigframe(ictx->regs_arg, ctl->sigframe, ctl->rsigframe, &ctl->orig.sigmask))
|
||||
return -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef __ASM_PARASITE_H__
|
||||
#define __ASM_PARASITE_H__
|
||||
|
||||
/* kuser_get_tls() kernel-provided user-helper, the address is emulated */
|
||||
static inline void arch_get_tls(tls_t *ptls)
|
||||
{
|
||||
*ptls = ((tls_t (*)(void))0xffff0fe0)();
|
||||
|
|
|
|||
|
|
@ -1,77 +1,10 @@
|
|||
#ifndef __ASM_PARASITE_H__
|
||||
#define __ASM_PARASITE_H__
|
||||
|
||||
#include <compel/plugins/std/string.h>
|
||||
#include <compel/plugins/std/syscall-codes.h>
|
||||
#include "asm/compat.h"
|
||||
|
||||
static int arch_get_user_desc(user_desc_t *desc)
|
||||
{
|
||||
int ret = __NR32_get_thread_area;
|
||||
/*
|
||||
* For 64-bit applications, TLS (fs_base for Glibc) is
|
||||
* in MSR, which are dumped with the help of arch_prctl().
|
||||
*
|
||||
* But SET_FS_BASE will update GDT if base pointer fits in 4 bytes.
|
||||
* Otherwise it will set only MSR, which allows for mixed 64/32-bit
|
||||
* code to use: 2 MSRs as TLS base _and_ 3 GDT entries.
|
||||
* Having in sum 5 TLS pointers, 3 of which are four bytes and
|
||||
* other two bigger than four bytes:
|
||||
* struct thread_struct {
|
||||
* struct desc_struct tls_array[3];
|
||||
* ...
|
||||
* #ifdef CONFIG_X86_64
|
||||
* unsigned long fsbase;
|
||||
* unsigned long gsbase;
|
||||
* #endif
|
||||
* ...
|
||||
* };
|
||||
*/
|
||||
asm volatile (
|
||||
" mov %0,%%eax \n"
|
||||
" mov %1,%%rbx \n"
|
||||
" int $0x80 \n"
|
||||
" mov %%eax,%0 \n"
|
||||
: "+m"(ret)
|
||||
: "m"(desc)
|
||||
: "rax", "rbx", "r8", "r9", "r10", "r11", "memory");
|
||||
|
||||
if (ret)
|
||||
pr_err("Failed to dump TLS descriptor #%d: %d\n",
|
||||
desc->entry_number, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void arch_get_tls(tls_t *ptls)
|
||||
{
|
||||
void *syscall_mem;
|
||||
int i;
|
||||
|
||||
syscall_mem = alloc_compat_syscall_stack();
|
||||
if (!syscall_mem) {
|
||||
pr_err("Failed to allocate memory <4Gb for compat syscall\n");
|
||||
|
||||
for (i = 0; i < GDT_ENTRY_TLS_NUM; i++) {
|
||||
user_desc_t *d = &ptls->desc[i];
|
||||
|
||||
d->seg_not_present = 1;
|
||||
d->entry_number = GDT_ENTRY_TLS_MIN + i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < GDT_ENTRY_TLS_NUM; i++)
|
||||
{
|
||||
user_desc_t *d = syscall_mem;
|
||||
|
||||
memset(d, 0, sizeof(user_desc_t));
|
||||
d->seg_not_present = 1;
|
||||
d->entry_number = GDT_ENTRY_TLS_MIN + i;
|
||||
arch_get_user_desc(d);
|
||||
memcpy(&ptls->desc[i], d, sizeof(user_desc_t));
|
||||
}
|
||||
|
||||
free_compat_syscall_stack(syscall_mem);
|
||||
}
|
||||
/*
|
||||
* TLS is accessed through PTRACE_GET_THREAD_AREA,
|
||||
* see compel_arch_fetch_thread_area().
|
||||
*/
|
||||
static inline void arch_get_tls(tls_t *ptls) { (void)ptls; }
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "asm/types.h"
|
||||
#include <compel/asm/fpu.h>
|
||||
#include <compel/asm/infect-types.h>
|
||||
#include "images/core.pb-c.h"
|
||||
#include <compel/plugins/std/syscall-codes.h>
|
||||
#include <compel/asm/sigframe.h>
|
||||
|
|
|
|||
|
|
@ -36,17 +36,4 @@ static inline void *decode_pointer(u64 v) { return (void*)(long)v; }
|
|||
#define AT_VECTOR_SIZE 44
|
||||
typedef uint64_t auxv_t;
|
||||
|
||||
/*
|
||||
* Linux preserves three TLS segments in GDT.
|
||||
* Offsets in GDT differ between 32-bit and 64-bit machines.
|
||||
* For 64-bit x86 those GDT offsets are the same
|
||||
* for native and compat tasks.
|
||||
*/
|
||||
#define GDT_ENTRY_TLS_MIN 12
|
||||
#define GDT_ENTRY_TLS_MAX 14
|
||||
#define GDT_ENTRY_TLS_NUM 3
|
||||
typedef struct {
|
||||
user_desc_t desc[GDT_ENTRY_TLS_NUM];
|
||||
} tls_t;
|
||||
|
||||
#endif /* __CR_ASM_TYPES_H__ */
|
||||
|
|
|
|||
|
|
@ -156,6 +156,8 @@ int parasite_dump_thread_leader_seized(struct parasite_ctl *ctl, int pid, CoreEn
|
|||
return -1;
|
||||
}
|
||||
|
||||
compel_arch_get_tls_task(ctl, &args->tls);
|
||||
|
||||
return dump_thread_core(pid, core, args);
|
||||
}
|
||||
|
||||
|
|
@ -190,6 +192,14 @@ int parasite_dump_thread_seized(struct parasite_thread_ctl *tctl,
|
|||
goto err_rth;
|
||||
}
|
||||
|
||||
ret = compel_arch_fetch_thread_area(tctl);
|
||||
if (ret) {
|
||||
pr_err("Can't obtain thread area of %d\n", pid);
|
||||
goto err_rth;
|
||||
}
|
||||
|
||||
compel_arch_get_tls_thread(tctl, &args->tls);
|
||||
|
||||
ret = compel_run_in_thread(tctl, PARASITE_CMD_DUMP_THREAD);
|
||||
if (ret) {
|
||||
pr_err("Can't init thread in parasite %d\n", pid);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue