Message ID | 1452808031-706-11-git-send-email-konrad.wilk@oracle.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
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.
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.
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 --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) } + +}
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