compel: rewrite parasite cmd and args manipulation from assembly to C

Previously, __export_parasite_cmd was located in parasite-head.S, and
__export_parasite_args located exactly at the end of the parasite blob.
This is not ideal for various reasons:
1) These two variables work together. It would be preferrable to have
them in the same location
2) This prevent us from allocating another section betweeen the parasite
blob and the args area. We'll need this to allocate a GOT table

This commit changes the allocation of these symbols from assembly/linker
script to a C file.

Moreover, the assembly entry points that invoke parasite_service()
prepares arguments with hand crafted assembly. This is unecessary.
This commit rewrite this logic with regular C code.

Note: if it wasn't for the x86 compat mode, we could remove all
parasite-head.S files and directly jump to parasite_service() via
ptrace.  An int3 architecture specific equivalent could be called at the
end of parasite_service() with an inline asm statement.

Signed-off-by: Nicolas Viennot <Nicolas.Viennot@twosigma.com>
This commit is contained in:
Nicolas Viennot 2020-07-16 23:14:35 +00:00 committed by Dmitry Safonov
parent 67965cb836
commit d7353b9bb7
18 changed files with 54 additions and 107 deletions

View file

@ -2,19 +2,6 @@
.section .head.text, "ax"
ENTRY(__export_parasite_head_start)
adr x2, __export_parasite_head_start // get the address of this instruction
ldr x0, __export_parasite_cmd
ldr x1, parasite_args_ptr
add x1, x1, x2 // fixup __export_parasite_args
bl parasite_service
brk #0 // the instruction BRK #0 generates the signal SIGTRAP in Linux
parasite_args_ptr:
.quad __export_parasite_args
__export_parasite_cmd:
.quad 0
END(__export_parasite_head_start)

View file

@ -29,8 +29,4 @@ SECTIONS
*(.eh_frame*)
*(*)
}
/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(4);
__export_parasite_args = .;
}

View file

@ -2,21 +2,7 @@
.section .head.text, "ax"
ENTRY(__export_parasite_head_start)
sub r2, pc, #8 @ get the address of this instruction
adr r0, __export_parasite_cmd
ldr r0, [r0]
adr r1, parasite_args_ptr
ldr r1, [r1]
add r1, r1, r2 @ fixup __export_parasite_args
bl parasite_service
.byte 0xf0, 0x01, 0xf0, 0xe7 @ the instruction UDF #32 generates the signal SIGTRAP in Linux
parasite_args_ptr:
.long __export_parasite_args
__export_parasite_cmd:
.long 0
END(__export_parasite_head_start)

View file

@ -29,8 +29,4 @@ SECTIONS
*(.eh_frame*)
*(*)
}
/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(4);
__export_parasite_args = .;
}

View file

@ -6,15 +6,10 @@
ENTRY(__export_parasite_head_start)
.set push
.set noreorder
lw a0, __export_parasite_cmd
dla a1, __export_parasite_args
jal parasite_service
nop
.byte 0x0d, 0x00, 0x00, 0x00 //break
.set pop
// .byte 0x40,0x01,0x00,0x00 //pause
__export_parasite_cmd:
.long 0
END(__export_parasite_head_start)

View file

@ -30,8 +30,4 @@ SECTIONS
*(.MIPS.options)
*(.gnu.attributes)
}
/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(32);
__export_parasite_args = .;
}

View file

@ -4,10 +4,6 @@
.align 8
ENTRY(__export_parasite_head_start)
// int __used parasite_service(unsigned int cmd, void *args)
// cmd = r3 = *__export_parasite_cmd (u32 ?)
// args = r4 = @parasite_args_ptr + @pc
bl 0f
0: mflr r2
@ -15,12 +11,6 @@ ENTRY(__export_parasite_head_start)
addis reg,r2,(name - 0b)@ha; \
addi reg,r2,(name - 0b)@l;
LOAD_REG_ADDR(r3,__export_parasite_cmd)
lwz r3,0(r3)
LOAD_REG_ADDR(r4,parasite_args_ptr)
ld r4,0(r4)
LOAD_REG_ADDR(r12,parasite_service_ptr)
ld r12,0(r12)
mtctr r12
@ -28,9 +18,6 @@ ENTRY(__export_parasite_head_start)
bctrl // call parasite_service
twi 31,0,0 // Should generate SIGTRAP
parasite_args_ptr:
.quad __export_parasite_args
parasite_service_ptr:
// We want to run the function prototype to set r2.
// Since the relocation will prefer the local entry
@ -39,7 +26,4 @@ parasite_service_ptr:
// FIXME: There should be a way to specify the global entry here.
.quad parasite_service - 8
__export_parasite_cmd:
.long 0
END(__export_parasite_head_start)

View file

@ -33,8 +33,4 @@ SECTIONS
*(.group*)
*(.eh_frame*)
}
/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(4);
__export_parasite_args = .;
}

View file

@ -2,25 +2,7 @@
.section .head.text, "ax"
/*
* Entry point for parasite_service()
*
* Addresses of symbols are exported in auto-generated criu/pie/parasite-blob.h
*
* Function is called via parasite_run(). The command for parasite_service()
* is stored in global variable __export_parasite_cmd.
*
* Load parameters for parasite_service(unsigned int cmd, void *args):
*
* - Parameter 1 (cmd) : %r2 = *(uint32 *)(__export_parasite_cmd + pc)
* - Parameter 2 (args): %r3 = __export_parasite_args + pc
*/
ENTRY(__export_parasite_head_start)
larl %r14,__export_parasite_cmd
llgf %r2,0(%r14)
larl %r3,__export_parasite_args
brasl %r14,parasite_service
.long 0x00010001 /* S390_BREAKPOINT_U16: Generates SIGTRAP */
__export_parasite_cmd:
.long 0
END(__export_parasite_head_start)

View file

@ -33,8 +33,4 @@ SECTIONS
*(.group*)
*(.eh_frame*)
}
/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(4);
__export_parasite_args = .;
}

View file

@ -11,8 +11,6 @@
andq $~15, %rsp
pushq $\num
movq %rsp, %rbp
movl __export_parasite_cmd(%rip), %edi
leaq __export_parasite_args(%rip), %rsi
call parasite_service
.endm
@ -48,7 +46,3 @@ ENTRY(__export_parasite_head_start)
PARASITE_ENTRY 0
int $0x03
END(__export_parasite_head_start)
.align 8
GLOBAL(__export_parasite_cmd)
.long 0

View file

@ -34,8 +34,4 @@ SECTIONS
*(.group*)
*(.eh_frame*)
}
/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(4);
__export_parasite_args = .;
}

View file

@ -34,8 +34,4 @@ SECTIONS
*(.group*)
*(.eh_frame*)
}
/* Parasite args should have 4 bytes align, as we have futex inside. */
. = ALIGN(4);
__export_parasite_args = .;
}

View file

@ -153,6 +153,7 @@ struct parasite_blob_desc {
size_t nr_gotpcrel;
unsigned long parasite_ip_off;
unsigned long cmd_off;
unsigned long args_ptr_off;
unsigned long args_off;
compel_reloc_t *relocs;
unsigned int nr_relocs;

View file

@ -4,7 +4,10 @@
#include "common/compiler.h"
extern int parasite_get_rpc_sock(void);
extern int __must_check parasite_service(unsigned int cmd, void *args);
extern unsigned int __export_parasite_service_cmd;
extern void * __export_parasite_service_args_ptr;
extern int __must_check parasite_service(void);
/*
* Must be supplied by user plugins.

View file

@ -196,8 +196,22 @@ err:
# define __parasite_entry
#endif
int __used __parasite_entry parasite_service(unsigned int cmd, void *args)
/*
* __export_parasite_service_{cmd,args} serve as arguments to the
* parasite_service() function. We use these global variables to make it
* easier to pass arguments when invoking from ptrace.
*
* We need the linker to allocate these variables. Hence the dummy
* initialization. Otherwise, we end up with COMMON symbols.
*/
unsigned int __export_parasite_service_cmd = 0;
void * __export_parasite_service_args_ptr = NULL;
int __used __parasite_entry parasite_service(void)
{
unsigned int cmd = __export_parasite_service_cmd;
void *args = __export_parasite_service_args_ptr;
pr_info("Parasite cmd %d/%x process\n", cmd, cmd);
if (cmd == PARASITE_CMD_INIT_DAEMON)

View file

@ -699,10 +699,9 @@ int __handle_elf(void *mem, size_t size)
pr_out("\t\tpbd->hdr.parasite_ip_off = "
"%s_sym__export_parasite_head_start_compat;\n", opts.prefix);
pr_out("#endif /* CONFIG_COMPAT */\n");
pr_out("\tpbd->hdr.cmd_off = "
"%s_sym__export_parasite_cmd;\n", opts.prefix);
pr_out("\tpbd->hdr.args_off = "
"%s_sym__export_parasite_args;\n", opts.prefix);
pr_out("\tpbd->hdr.cmd_off = %s_sym__export_parasite_service_cmd;\n", opts.prefix);
pr_out("\tpbd->hdr.args_ptr_off = %s_sym__export_parasite_service_args_ptr;\n", opts.prefix);
pr_out("\tpbd->hdr.args_off = round_up(pbd->hdr.bsize, 4);\n");
pr_out("\tpbd->hdr.relocs = %s_relocs;\n", opts.prefix);
pr_out("\tpbd->hdr.nr_relocs = "
"sizeof(%s_relocs) / sizeof(%s_relocs[0]);\n",

View file

@ -822,6 +822,13 @@ void compel_relocs_apply(void *mem, void *vbase, struct parasite_blob_desc *pbd)
size_t i, j;
/*
* parasite_service() reads the value of __export_parasite_service_args_ptr.
* The reason it is set here is that semantically, we are doing a symbol
* resolution on parasite_service_args, and it turns out to be relocatable.
*/
*(void **)(mem + pbd->hdr.args_ptr_off) = vbase + pbd->hdr.args_off;
#ifdef CONFIG_MIPS
compel_relocs_apply_mips(mem, vbase, pbd);
#else
@ -883,7 +890,30 @@ int compel_infect(struct parasite_ctl *ctl, unsigned long nr_threads, unsigned l
* without using ptrace at all.
*/
parasite_size = ctl->pblob.hdr.bsize;
/*
* The parasite memory layout is the following:
* Low address start first.
* The number in parenthesis denotes the size of the section.
* The arrow on the right shows the different variables that
* corresponds to a given offset.
* +------------------------------------------------------+ <--- 0
* | Parasite blob (sizeof(parasite_blob)) |
* +------------------------------------------------------+ <--- hdr.bsize
* align 4
* +------------------------------------------------------+ <--- hdr.args_off
* | Args area (args_size) |
* +------------------------------------------------------+
* align 64
* +------------------------------------------------------+ <--- ctl->rsigframe
* | sigframe (RESTORE_STACK_SIGFRAME) | ctl->sigframe
* +------------------------------------------------------+
* | main stack (PARASITE_STACK_SIZE) |
* +------------------------------------------------------+ <--- ctl->rstack
* | compel_run_in_thread stack (PARASITE_STACK_SIZE) |
* +------------------------------------------------------+ <--- ctl->r_thread_stack
* map_exchange_size
*/
parasite_size = ctl->pblob.hdr.args_off;
ctl->args_size = args_size;
parasite_size += ctl->args_size;