[CRIU] [PATCHv1 25/26] x86: dump TLS entries from GDT
Dmitry Safonov
dsafonov at virtuozzo.com
Thu Jun 16 03:53:42 PDT 2016
Dump TLS with the help of SYS_get_thread_area.
Primary for 32-bit applications, but this also may be used by
mixed 64/32 bit code. I do not enable dumping for 64 bit unless
we'll meet such code, but include 3 user_desc entries of TLS
as not present in 64-bit images.
That's arguable and I may include user_descs only for compat tasks.
Cc: Cyrill Gorcunov <gorcunov at openvz.org>
Signed-off-by: Dmitry Safonov <dsafonov at virtuozzo.com>
---
criu/arch/x86/crtools.c | 17 +++++++++++-
criu/arch/x86/include/asm/dump.h | 25 +++++++++++++++++-
criu/arch/x86/include/asm/parasite.h | 51 +++++++++++++++++++++++++++++++++++-
criu/arch/x86/include/asm/restorer.h | 5 +++-
criu/arch/x86/include/asm/types.h | 14 +++++++++-
images/core-x86.proto | 15 +++++++++++
6 files changed, 122 insertions(+), 5 deletions(-)
diff --git a/criu/arch/x86/crtools.c b/criu/arch/x86/crtools.c
index 2b97011996cd..5b2f2191a638 100644
--- a/criu/arch/x86/crtools.c
+++ b/criu/arch/x86/crtools.c
@@ -375,6 +375,18 @@ int ptrace_set_regs(pid_t pid, user_regs_struct_t *regs)
return ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov);
}
+static void alloc_tls(ThreadInfoX86 *ti, void **mempool)
+{
+ int i;
+
+ ti->tls = xptr_pull_s(mempool, GDT_ENTRY_TLS_NUM*sizeof(UserDescT*));
+ ti->n_tls = GDT_ENTRY_TLS_NUM;
+ for (i = 0; i < GDT_ENTRY_TLS_NUM; i++) {
+ ti->tls[i] = xptr_pull(mempool, UserDescT);
+ user_desc_t__init(ti->tls[i]);
+ }
+}
+
int arch_alloc_thread_info(CoreEntry *core)
{
size_t sz;
@@ -385,7 +397,9 @@ int arch_alloc_thread_info(CoreEntry *core)
with_fpu = cpu_has_feature(X86_FEATURE_FPU);
- sz = sizeof(ThreadInfoX86) + sizeof(UserX86RegsEntry);
+ sz = sizeof(ThreadInfoX86) + sizeof(UserX86RegsEntry) +
+ GDT_ENTRY_TLS_NUM*sizeof(UserDescT) +
+ GDT_ENTRY_TLS_NUM*sizeof(UserDescT*);
if (with_fpu) {
sz += sizeof(UserX86FpregsEntry);
with_xsave = cpu_has_feature(X86_FEATURE_XSAVE);
@@ -401,6 +415,7 @@ int arch_alloc_thread_info(CoreEntry *core)
thread_info_x86__init(ti);
ti->gpregs = xptr_pull(&m, UserX86RegsEntry);
user_x86_regs_entry__init(ti->gpregs);
+ alloc_tls(ti, &m);
if (with_fpu) {
UserX86FpregsEntry *fpregs;
diff --git a/criu/arch/x86/include/asm/dump.h b/criu/arch/x86/include/asm/dump.h
index 02ec20042cb0..9c3555236ce6 100644
--- a/criu/arch/x86/include/asm/dump.h
+++ b/criu/arch/x86/include/asm/dump.h
@@ -9,6 +9,29 @@ extern void arch_free_thread_info(CoreEntry *core);
extern int ptrace_get_regs(pid_t pid, user_regs_struct_t *regs);
extern int ptrace_set_regs(pid_t pid, user_regs_struct_t *regs);
-#define core_put_tls(core, tls)
+static inline void core_put_tls(CoreEntry *core, tls_t tls)
+{
+ ThreadInfoX86 *ti = core->thread_info;
+ int i;
+
+ for (i = 0; i < GDT_ENTRY_TLS_NUM; i++)
+ {
+ user_desc_t *from = &tls.desc[i];
+ UserDescT *to = ti->tls[i];
+
+#define COPY_TLS(field) to->field = from->field
+ COPY_TLS(entry_number);
+ COPY_TLS(base_addr);
+ COPY_TLS(limit);
+ COPY_TLS(seg_32bit);
+ to->contents_h = from->contents & 0x2;
+ to->contents_l = from->contents & 0x1;
+ COPY_TLS(read_exec_only);
+ COPY_TLS(limit_in_pages);
+ COPY_TLS(seg_not_present);
+ COPY_TLS(useable);
+#undef COPY_TLS
+ }
+}
#endif
diff --git a/criu/arch/x86/include/asm/parasite.h b/criu/arch/x86/include/asm/parasite.h
index 669ae63e26cc..d238327dbbeb 100644
--- a/criu/arch/x86/include/asm/parasite.h
+++ b/criu/arch/x86/include/asm/parasite.h
@@ -5,6 +5,55 @@
# define __parasite_entry __attribute__((regparm(3)))
#endif
-static inline void arch_get_tls(tls_t *ptls) { (void)ptls; }
+#ifdef CONFIG_X86_32
+static void arch_get_user_desc(user_desc_t *desc)
+{
+ if (sys_get_thread_area(desc))
+ pr_err("Failed to dump TLS descriptor #%d\n",
+ desc->entry_number);
+}
+#else /* !X86_32 */
+static void arch_get_user_desc(user_desc_t *desc)
+{
+ /*
+ * 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
+ * ...
+ * };
+ *
+ * For this mixed code we may want to call get_thread_area
+ * 32-bit syscall. But as additional three calls to kernel
+ * will slow dumping, I omit it here.
+ */
+ desc->seg_not_present = 1;
+}
+#endif /* !X86_32 */
+
+static void arch_get_tls(tls_t *ptls)
+{
+ int i;
+
+ for (i = 0; i < GDT_ENTRY_TLS_NUM; i++)
+ {
+ user_desc_t *d = &ptls->desc[i];
+
+ memset(d, 0, sizeof(user_desc_t));
+ d->entry_number = GDT_ENTRY_TLS_MIN + i;
+ arch_get_user_desc(d);
+ }
+}
#endif
diff --git a/criu/arch/x86/include/asm/restorer.h b/criu/arch/x86/include/asm/restorer.h
index 5b3346e149b0..f4ba22180a39 100644
--- a/criu/arch/x86/include/asm/restorer.h
+++ b/criu/arch/x86/include/asm/restorer.h
@@ -334,7 +334,10 @@ int restore_nonsigframe_gpregs(UserX86RegsEntry *r);
int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe,
struct rt_sigframe *rsigframe);
-static inline void restore_tls(tls_t *ptls) { (void)ptls; }
+static inline void restore_tls(tls_t *ptls)
+{
+ (void)ptls;
+}
int ptrace_set_breakpoint(pid_t pid, void *addr);
int ptrace_flush_breakpoints(pid_t pid);
diff --git a/criu/arch/x86/include/asm/types.h b/criu/arch/x86/include/asm/types.h
index d516b05a067c..1d3d00976eeb 100644
--- a/criu/arch/x86/include/asm/types.h
+++ b/criu/arch/x86/include/asm/types.h
@@ -194,7 +194,19 @@ typedef struct {
static inline unsigned long task_size() { return TASK_SIZE; }
typedef u64 auxv_t;
-typedef u32 tls_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;
#define REG_RES(regs) get_user_reg(®s, ax)
#define REG_IP(regs) get_user_reg(®s, ip)
diff --git a/images/core-x86.proto b/images/core-x86.proto
index fbd592b40ae0..453bbdef62e7 100644
--- a/images/core-x86.proto
+++ b/images/core-x86.proto
@@ -63,8 +63,23 @@ message user_x86_fpregs_entry {
optional user_x86_xsave_entry xsave = 13;
}
+message user_desc_t {
+ required uint32 entry_number = 1;
+ /* this is for GDT, not for MSRs - 32-bit base */
+ required uint32 base_addr = 2;
+ required uint32 limit = 3;
+ required bool seg_32bit = 4;
+ required bool contents_h = 5;
+ required bool contents_l = 6;
+ required bool read_exec_only = 7 [default = true];
+ required bool limit_in_pages = 8;
+ required bool seg_not_present = 9 [default = true];
+ required bool useable = 10;
+}
+
message thread_info_x86 {
required uint64 clear_tid_addr = 1[(criu).hex = true];
required user_x86_regs_entry gpregs = 2[(criu).hex = true];
required user_x86_fpregs_entry fpregs = 3;
+ repeated user_desc_t tls = 4;
}
--
2.8.3
More information about the CRIU
mailing list