diff mbox

[v2,10/13] xen_hello_world.xsplice: Test payload for patching 'xen_extra_version'.

Message ID 1452808031-706-11-git-send-email-konrad.wilk@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Konrad Rzeszutek Wilk Jan. 14, 2016, 9:47 p.m. UTC
This change demonstrates how to generate an xSplice ELF payload.

The idea here is that we want to patch in the hypervisor
the 'xen_version_extra' function with an function that will
return 'Hello World'. The 'xl info | grep extraversion'
will reflect the new value after the patching.

To generate this ELF payload file we need:
 - C code of the new code.
 - C code generating the .xsplice.func structure.
 - The address of the old code (xen_extra_version). We
   do it by using 'nm' but that is a bit of hack.

The linker script file:
 - Discards .debug* and .comments* sections.
 - Changes the name of .data.local.xsplice_hello_world to
   .xsplice.func
 - Figures out the size of the new code.

Also if you are curious on the input/output sections
magic the linker does, add these to the GCC line:
  -Wl,-M  -Wl,-t -Wl,-verbose
which are: print linking map, provide trace and be verbose.

The use-case is simple:

$xen-xsplice load /usr/lib/xen/bin/xen_hello_world.xsplice
$xen-xsplice list
 ID                                     | status
----------------------------------------+------------
xen_hello_world                           APPLIED
$xl info | grep extra
xen_extra              : Hello World
$xen-xsplice revert xen_hello_world
Performing revert: completed
$xen-xsplice unload xen_hello_world
Performing unload: completed
$xl info | grep extra
xen_extra              : -unstable

Note that it does not build under a 32-bit toolstack as
there is no access to the hypervisor (xen-syms).

We also force it to be built every time - as the hypervisor
may have been rebuilt.

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 docs/misc/xsplice.markdown   | 50 ++++++++++++++++++++++++++++++++++++++++++++
 tools/misc/Makefile          | 25 +++++++++++++++++++++-
 tools/misc/xen_hello_world.c | 15 +++++++++++++
 tools/misc/xsplice.h         | 12 +++++++++++
 tools/misc/xsplice.lds       | 11 ++++++++++
 5 files changed, 112 insertions(+), 1 deletion(-)
 create mode 100644 tools/misc/xen_hello_world.c
 create mode 100644 tools/misc/xsplice.h
 create mode 100644 tools/misc/xsplice.lds

Comments

Wei Liu Jan. 19, 2016, 11:14 a.m. UTC | #1
On Thu, Jan 14, 2016 at 04:47:08PM -0500, Konrad Rzeszutek Wilk wrote:
> This change demonstrates how to generate an xSplice ELF payload.
> 
> The idea here is that we want to patch in the hypervisor
> the 'xen_version_extra' function with an function that will
> return 'Hello World'. The 'xl info | grep extraversion'
> will reflect the new value after the patching.
> 
> To generate this ELF payload file we need:
>  - C code of the new code.
>  - C code generating the .xsplice.func structure.
>  - The address of the old code (xen_extra_version). We
>    do it by using 'nm' but that is a bit of hack.
> 
> The linker script file:
>  - Discards .debug* and .comments* sections.
>  - Changes the name of .data.local.xsplice_hello_world to
>    .xsplice.func
>  - Figures out the size of the new code.
> 
> Also if you are curious on the input/output sections
> magic the linker does, add these to the GCC line:
>   -Wl,-M  -Wl,-t -Wl,-verbose
> which are: print linking map, provide trace and be verbose.
> 
> The use-case is simple:
> 
> $xen-xsplice load /usr/lib/xen/bin/xen_hello_world.xsplice
> $xen-xsplice list
>  ID                                     | status
> ----------------------------------------+------------
> xen_hello_world                           APPLIED
> $xl info | grep extra
> xen_extra              : Hello World
> $xen-xsplice revert xen_hello_world
> Performing revert: completed
> $xen-xsplice unload xen_hello_world
> Performing unload: completed
> $xl info | grep extra
> xen_extra              : -unstable
> 
> Note that it does not build under a 32-bit toolstack as
> there is no access to the hypervisor (xen-syms).
> 
> We also force it to be built every time - as the hypervisor
> may have been rebuilt.
> 
> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> ---
>  docs/misc/xsplice.markdown   | 50 ++++++++++++++++++++++++++++++++++++++++++++
>  tools/misc/Makefile          | 25 +++++++++++++++++++++-
>  tools/misc/xen_hello_world.c | 15 +++++++++++++
>  tools/misc/xsplice.h         | 12 +++++++++++
>  tools/misc/xsplice.lds       | 11 ++++++++++

Please put the files of this test case into a dedicated directory.

Wei.
Ross Lagerwall Jan. 19, 2016, 2:57 p.m. UTC | #2
On 01/14/2016 09:47 PM, Konrad Rzeszutek Wilk wrote:
> This change demonstrates how to generate an xSplice ELF payload.
>
> The idea here is that we want to patch in the hypervisor
> the 'xen_version_extra' function with an function that will
> return 'Hello World'. The 'xl info | grep extraversion'
> will reflect the new value after the patching.
>
snip
> +### Example
> +
> +A simple example of what a payload file can be:
> +
> +<pre>
> +/* MUST be in sync with hypervisor. */
> +struct xsplice_patch_func {
> +    const char *name;
> +    unsigned long new_addr;
> +    const unsigned long old_addr;
> +    uint32_t new_size;
> +    const uint32_t old_size;
> +    uint8_t pad[32];
> +};
> +
> +/* Our replacement function for xen_extra_version. */
> +const char *xen_hello_world(void)
> +{
> +    return "Hello World";
> +}
> +
> +struct xsplice_patch_func xsplice_hello_world = {
> +    .name = "xen_extra_version",
> +    .new_addr = &xen_hello_world,
> +    .old_addr = 0xffff82d08013963c, /* Extracted from xen-syms. */
> +    .new_size = 13, /* To be be computed by scripts. */
> +    .old_size = 13, /* -----------""---------------  */
> +};
> +</pre>
> +
> +With the linker script as follow to change the `xsplice_hello_world`
> +do be `.xsplice.funcs` :
> +
> +<pre>
> +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
> +OUTPUT_ARCH(i386:x86-64)
> +ENTRY(xsplice_hello_world)
> +SECTIONS
> +{
> +    /* The hypervisor expects ".xsplice.func", so change
> +     * the ".data.xsplice_hello_world" to it. */
> +
> +    .xsplice.funcs : { *(*.xsplice_hello_world) }
> +    }
> +}
> +</pre>

You should be able to use __attribute__((__section__(".xsplice.funcs"))) 
on the structure to avoid needing to use a linker script.

> +
> +Code must be compiled with -fPIC.
> +
>   ## Hypercalls
>
>   We will employ the sub operations of the system management hypercall (sysctl).
> diff --git a/tools/misc/Makefile b/tools/misc/Makefile
> index c46873e..8385830 100644
> --- a/tools/misc/Makefile
> +++ b/tools/misc/Makefile
> @@ -36,6 +36,10 @@ INSTALL_SBIN += $(INSTALL_SBIN-y)
>   # Everything to be installed in a private bin/
>   INSTALL_PRIVBIN                += xenpvnetboot
>
> +# We need the hypervisor - and only 64-bit builds have it.
> +ifeq ($(XEN_COMPILE_ARCH),x86_64)
> +INSTALL_PRIVBIN                += xen_hello_world.xsplice
> +endif
>   # Everything to be installed
>   TARGETS_ALL := $(INSTALL_BIN) $(INSTALL_SBIN) $(INSTALL_PRIVBIN)
>
> @@ -49,7 +53,7 @@ TARGETS_COPY += xenpvnetboot
>   # Everything which needs to be built
>   TARGETS_BUILD := $(filter-out $(TARGETS_COPY),$(TARGETS_ALL))
>
> -.PHONY: all build
> +.PHONY: all build xsplice
>   all build: $(TARGETS_BUILD)
>
>   .PHONY: install
> @@ -111,4 +115,23 @@ gtraceview: gtraceview.o
>   xencov: xencov.o
>   	$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
>
> +.PHONY: xsplice
> +xsplice:
> +ifeq ($(XEN_COMPILE_ARCH),x86_64)
> +	# We MUST regenerate the file everytime we build - in case the hypervisor
> +	# is rebuilt too.
> +	$(RM) *.xplice
> +	$(MAKE) xen_hello_world.xsplice

Can't you depend on xen-syms to avoid recompiling this every time.

> +endif
> +
> +XEN_EXTRA_VERSION_ADDR=$(shell nm --defined $(XEN_ROOT)/xen/xen-syms | grep xen_extra_version | awk '{print "0x"$$1}')
> +
> +xen_hello_world.xsplice: xen_hello_world.c
> +	$(CC) -DOLD_CODE=$(XEN_EXTRA_VERSION_ADDR) -I$(XEN_ROOT)/tools/include \
> +		-fPIC -Wl,--emit-relocs \
> +		-Wl,-r -Wl,--entry=xsplice_hello_world \
> +		-fdata-sections -ffunction-sections \
> +		-nostdlib -Txsplice.lds \
> +		-o $@ $<
> +	@objdump -x --section=.xsplice.funcs $@

If you use __attribute__((__section__(".xsplice.funcs"))) on the struct, 
you can drop the custom linker script and simplify the command-line to 
something like:
$(CC) -DOLD_CODE=$(XEN_EXTRA_VERSION_ADDR) -I$(XEN_ROOT)/tools/include \
	-c -o $@ $< $(CFLAGS)

Having mostly the same CFLAGS that Xen uses is important because it 
contains things like -mno-red-zone, -fno-asynchronous-unwind-tables, and 
-mno-sse, etc which affect the way the code is compiled.
Ross Lagerwall Jan. 19, 2016, 4:47 p.m. UTC | #3
On 01/14/2016 09:47 PM, Konrad Rzeszutek Wilk wrote:
snip
> diff --git a/tools/misc/xen_hello_world.c b/tools/misc/xen_hello_world.c
> new file mode 100644
> index 0000000..8c24d8f
> --- /dev/null
> +++ b/tools/misc/xen_hello_world.c
> @@ -0,0 +1,15 @@
> +#include "xsplice.h"
> +
> +/* Our replacement function for xen_extra_version. */
> +const char *xen_hello_world(void)
> +{
> +    return "Hello World";
> +}
> +
> +struct xsplice_patch_func xsplice_hello_world = {
> +    .name = "xen_extra_version",
> +    .new_addr = &xen_hello_world,

This line introduces a warning:
xen_hello_world.c:11:17: warning: initialization makes integer from 
pointer without a cast [-Wint-conversion]
      .new_addr = &xen_hello_world,
                  ^
xen_hello_world.c:11:17: note: (near initialization for 
‘xsplice_hello_world.new_addr’)
diff mbox

Patch

diff --git a/docs/misc/xsplice.markdown b/docs/misc/xsplice.markdown
index beb452e..e2cdcff 100644
--- a/docs/misc/xsplice.markdown
+++ b/docs/misc/xsplice.markdown
@@ -312,11 +312,61 @@  size.
 
 When applying the patch the hypervisor iterates over each `xsplice_patch_func`
 structure and the core code inserts a trampoline at `old_addr` to `new_addr`.
+The `new_addr` is altered when the ELF payload is loaded.
 
 When reverting a patch, the hypervisor iterates over each `xsplice_patch_func`
 and the core code copies the data from the undo buffer (private internal copy)
 to `old_addr`.
 
+### Example
+
+A simple example of what a payload file can be:
+
+<pre>
+/* MUST be in sync with hypervisor. */  
+struct xsplice_patch_func {  
+    const char *name;  
+    unsigned long new_addr;  
+    const unsigned long old_addr;  
+    uint32_t new_size;  
+    const uint32_t old_size;  
+    uint8_t pad[32];  
+};  
+
+/* Our replacement function for xen_extra_version. */  
+const char *xen_hello_world(void)  
+{  
+    return "Hello World";  
+}  
+
+struct xsplice_patch_func xsplice_hello_world = {  
+    .name = "xen_extra_version",  
+    .new_addr = &xen_hello_world,  
+    .old_addr = 0xffff82d08013963c, /* Extracted from xen-syms. */  
+    .new_size = 13, /* To be be computed by scripts. */  
+    .old_size = 13, /* -----------""---------------  */  
+};  
+</pre>
+
+With the linker script as follow to change the `xsplice_hello_world`
+do be `.xsplice.funcs` :
+
+<pre>
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")  
+OUTPUT_ARCH(i386:x86-64)  
+ENTRY(xsplice_hello_world)  
+SECTIONS  
+{  
+    /* The hypervisor expects ".xsplice.func", so change  
+     * the ".data.xsplice_hello_world" to it. */  
+
+    .xsplice.funcs : { *(*.xsplice_hello_world) }  
+    }  
+}  
+</pre>
+
+Code must be compiled with -fPIC.
+
 ## Hypercalls
 
 We will employ the sub operations of the system management hypercall (sysctl).
diff --git a/tools/misc/Makefile b/tools/misc/Makefile
index c46873e..8385830 100644
--- a/tools/misc/Makefile
+++ b/tools/misc/Makefile
@@ -36,6 +36,10 @@  INSTALL_SBIN += $(INSTALL_SBIN-y)
 # Everything to be installed in a private bin/
 INSTALL_PRIVBIN                += xenpvnetboot
 
+# We need the hypervisor - and only 64-bit builds have it.
+ifeq ($(XEN_COMPILE_ARCH),x86_64)
+INSTALL_PRIVBIN                += xen_hello_world.xsplice
+endif
 # Everything to be installed
 TARGETS_ALL := $(INSTALL_BIN) $(INSTALL_SBIN) $(INSTALL_PRIVBIN)
 
@@ -49,7 +53,7 @@  TARGETS_COPY += xenpvnetboot
 # Everything which needs to be built
 TARGETS_BUILD := $(filter-out $(TARGETS_COPY),$(TARGETS_ALL))
 
-.PHONY: all build
+.PHONY: all build xsplice
 all build: $(TARGETS_BUILD)
 
 .PHONY: install
@@ -111,4 +115,23 @@  gtraceview: gtraceview.o
 xencov: xencov.o
 	$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
 
+.PHONY: xsplice
+xsplice:
+ifeq ($(XEN_COMPILE_ARCH),x86_64)
+	# We MUST regenerate the file everytime we build - in case the hypervisor
+	# is rebuilt too.
+	$(RM) *.xplice
+	$(MAKE) xen_hello_world.xsplice
+endif
+
+XEN_EXTRA_VERSION_ADDR=$(shell nm --defined $(XEN_ROOT)/xen/xen-syms | grep xen_extra_version | awk '{print "0x"$$1}')
+
+xen_hello_world.xsplice: xen_hello_world.c
+	$(CC) -DOLD_CODE=$(XEN_EXTRA_VERSION_ADDR) -I$(XEN_ROOT)/tools/include \
+		-fPIC -Wl,--emit-relocs \
+		-Wl,-r -Wl,--entry=xsplice_hello_world \
+		-fdata-sections -ffunction-sections \
+		-nostdlib -Txsplice.lds \
+		-o $@ $<
+	@objdump -x --section=.xsplice.funcs $@
 -include $(DEPS)
diff --git a/tools/misc/xen_hello_world.c b/tools/misc/xen_hello_world.c
new file mode 100644
index 0000000..8c24d8f
--- /dev/null
+++ b/tools/misc/xen_hello_world.c
@@ -0,0 +1,15 @@ 
+#include "xsplice.h"
+
+/* Our replacement function for xen_extra_version. */
+const char *xen_hello_world(void)
+{
+    return "Hello World";
+}
+
+struct xsplice_patch_func xsplice_hello_world = {
+    .name = "xen_extra_version",
+    .new_addr = &xen_hello_world,
+    .old_addr = OLD_CODE,
+    .new_size = 13, /* TODO: Compute. */
+    .old_size = 13, /* TODO: Compute. */
+};
diff --git a/tools/misc/xsplice.h b/tools/misc/xsplice.h
new file mode 100644
index 0000000..6ce8bae
--- /dev/null
+++ b/tools/misc/xsplice.h
@@ -0,0 +1,12 @@ 
+#include <stdint.h>
+#include <sys/types.h>
+
+/* MUST be in sync with hypervisor. */
+struct xsplice_patch_func {
+    const char *name;
+    unsigned long new_addr;
+    const unsigned long old_addr;
+    uint32_t new_size;
+    const uint32_t old_size;
+    uint8_t pad[32];
+};
diff --git a/tools/misc/xsplice.lds b/tools/misc/xsplice.lds
new file mode 100644
index 0000000..f52eb8c
--- /dev/null
+++ b/tools/misc/xsplice.lds
@@ -0,0 +1,11 @@ 
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(xsplice_hello_world)
+SECTIONS
+{
+    /* The hypervisor expects ".xsplice.func", so change
+     * the ".data.xsplice_hello_world" to it. */
+
+    .xsplice.funcs : { *(*.xsplice_hello_world) }
+
+}