diff mbox

[v3,13/23] XENVER_build_id: Provide ld-embedded build-ids (v10)

Message ID 1455300361-13092-14-git-send-email-konrad.wilk@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Konrad Rzeszutek Wilk Feb. 12, 2016, 6:05 p.m. UTC
The mechanism to get this is via the XENVER hypercall and
we add a new sub-command to retrieve the binary build-id
called XENVER_build_id. The sub-hypercall parameter
allows an arbitrary size (the buffer and len is provided
to the hypervisor). A NULL parameter will probe the hypervisor
for the length of the build-id.

One can also retrieve the value of the build-id by doing
'readelf -n xen-syms'.

For EFI builds we re-use the same build-id that the xen-syms
was built with.

The version of ld that first implemented --build-id is v2.18.
Hence we check for that or later version - if older version
found we do not build the hypervisor with the build-id
(and the return code is -ENODATA for that case).

For x86 we have two binaries - the xen-syms and the xen - an
smaller version with lots of sections removed. To make it possible
for readelf -n xen we also modify mkelf32 and xen.lds.S to include
the PT_NOTE ELF section.

The EFI binary is more complicated. Having any non-recognizable
sections (.note, .data.note, etc) causes the boot to hang.
Moving the .note in the .data section makes it work. It is also
worth noting that the PE/COFF does not have any "comment"
sections to the author.

Suggested-by: Andrew Cooper <andrew.cooper3@citrix.com>
Signed-off-by: Martin Pohlack <mpohlack@amazon.de>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
v1: Rebase it on Martin's initial patch
v2: Move it to XENVER hypercall
v3: Fix EFI building (Ross's fix)
v4: Don't use the third argument for length.
v5: Use new structure for XENVER_build_id with variable buf.
v6: Include Ross's fix.
v7: Include detection of bin-utils for build-id support, add
    probing for size, and return -EPERM for XSM denied calls.
v8: Build xen_build_id under ARM, required adding ELFSIZE in proper file.
v9: Rebase on top XSM version class.
v10: Include the build-id .note in the xen ELF binary.
     s/build_id/build_id_linker/
    For EFI build, moved the --build-id values in .data section
---
 Config.mk                                    |  11 +++
 tools/flask/policy/policy/modules/xen/xen.te |   4 +-
 tools/libxc/xc_private.c                     |   7 ++
 tools/libxc/xc_private.h                     |  10 ++
 xen/arch/arm/Makefile                        |   2 +-
 xen/arch/arm/xen.lds.S                       |  13 +++
 xen/arch/x86/Makefile                        |  31 +++++-
 xen/arch/x86/boot/mkelf32.c                  | 137 +++++++++++++++++++++++----
 xen/arch/x86/xen.lds.S                       |  23 +++++
 xen/common/kernel.c                          |  36 +++++++
 xen/common/version.c                         |  48 ++++++++++
 xen/include/public/version.h                 |  16 +++-
 xen/include/xen/version.h                    |   1 +
 xen/xsm/flask/hooks.c                        |   3 +
 xen/xsm/flask/policy/access_vectors          |   2 +
 15 files changed, 319 insertions(+), 25 deletions(-)

Comments

Daniel De Graaf Feb. 12, 2016, 9:52 p.m. UTC | #1
On 12/02/16 13:05, Konrad Rzeszutek Wilk wrote:
> The mechanism to get this is via the XENVER hypercall and
> we add a new sub-command to retrieve the binary build-id
> called XENVER_build_id. The sub-hypercall parameter
> allows an arbitrary size (the buffer and len is provided
> to the hypervisor). A NULL parameter will probe the hypervisor
> for the length of the build-id.
>
> One can also retrieve the value of the build-id by doing
> 'readelf -n xen-syms'.
>
> For EFI builds we re-use the same build-id that the xen-syms
> was built with.
>
> The version of ld that first implemented --build-id is v2.18.
> Hence we check for that or later version - if older version
> found we do not build the hypervisor with the build-id
> (and the return code is -ENODATA for that case).
>
> For x86 we have two binaries - the xen-syms and the xen - an
> smaller version with lots of sections removed. To make it possible
> for readelf -n xen we also modify mkelf32 and xen.lds.S to include
> the PT_NOTE ELF section.
>
> The EFI binary is more complicated. Having any non-recognizable
> sections (.note, .data.note, etc) causes the boot to hang.
> Moving the .note in the .data section makes it work. It is also
> worth noting that the PE/COFF does not have any "comment"
> sections to the author.
>
> Suggested-by: Andrew Cooper <andrew.cooper3@citrix.com>
> Signed-off-by: Martin Pohlack <mpohlack@amazon.de>
> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>

Acked-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Andrew Cooper Feb. 16, 2016, 8:09 p.m. UTC | #2
On 12/02/16 18:05, Konrad Rzeszutek Wilk wrote:

Building the hypervisor with buildid and making it available via
hypercall really should be split into two different patches, especially
given the complexity in each.

> The mechanism to get this is via the XENVER hypercall and
> we add a new sub-command to retrieve the binary build-id
> called XENVER_build_id. The sub-hypercall parameter
> allows an arbitrary size (the buffer and len is provided
> to the hypervisor). A NULL parameter will probe the hypervisor
> for the length of the build-id.
>
> One can also retrieve the value of the build-id by doing
> 'readelf -n xen-syms'.
>
> For EFI builds we re-use the same build-id that the xen-syms
> was built with.
>
> The version of ld that first implemented --build-id is v2.18.
> Hence we check for that or later version - if older version
> found we do not build the hypervisor with the build-id
> (and the return code is -ENODATA for that case).
>
> For x86 we have two binaries - the xen-syms and the xen - an
> smaller version with lots of sections removed. To make it possible
> for readelf -n xen we also modify mkelf32 and xen.lds.S to include
> the PT_NOTE ELF section.
>
> The EFI binary is more complicated. Having any non-recognizable
> sections (.note, .data.note, etc) causes the boot to hang.
> Moving the .note in the .data section makes it work. It is also
> worth noting that the PE/COFF does not have any "comment"
> sections to the author.
>
> Suggested-by: Andrew Cooper <andrew.cooper3@citrix.com>
> Signed-off-by: Martin Pohlack <mpohlack@amazon.de>
> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
> ---
> v1: Rebase it on Martin's initial patch
> v2: Move it to XENVER hypercall
> v3: Fix EFI building (Ross's fix)
> v4: Don't use the third argument for length.
> v5: Use new structure for XENVER_build_id with variable buf.
> v6: Include Ross's fix.
> v7: Include detection of bin-utils for build-id support, add
>     probing for size, and return -EPERM for XSM denied calls.
> v8: Build xen_build_id under ARM, required adding ELFSIZE in proper file.
> v9: Rebase on top XSM version class.
> v10: Include the build-id .note in the xen ELF binary.
>      s/build_id/build_id_linker/
>     For EFI build, moved the --build-id values in .data section
> ---
>  Config.mk                                    |  11 +++
>  tools/flask/policy/policy/modules/xen/xen.te |   4 +-
>  tools/libxc/xc_private.c                     |   7 ++
>  tools/libxc/xc_private.h                     |  10 ++
>  xen/arch/arm/Makefile                        |   2 +-
>  xen/arch/arm/xen.lds.S                       |  13 +++
>  xen/arch/x86/Makefile                        |  31 +++++-
>  xen/arch/x86/boot/mkelf32.c                  | 137 +++++++++++++++++++++++----
>  xen/arch/x86/xen.lds.S                       |  23 +++++
>  xen/common/kernel.c                          |  36 +++++++
>  xen/common/version.c                         |  48 ++++++++++
>  xen/include/public/version.h                 |  16 +++-
>  xen/include/xen/version.h                    |   1 +
>  xen/xsm/flask/hooks.c                        |   3 +
>  xen/xsm/flask/policy/access_vectors          |   2 +
>  15 files changed, 319 insertions(+), 25 deletions(-)
>
> diff --git a/Config.mk b/Config.mk
> index 429e460..61186e2 100644
> --- a/Config.mk
> +++ b/Config.mk
> @@ -126,6 +126,17 @@ endef
>  check-$(gcc) = $(call cc-ver-check,CC,0x040100,"Xen requires at least gcc-4.1")
>  $(eval $(check-y))
>  
> +ld-ver-build-id = $(shell $(1) --build-id 2>&1 | \
> +					grep -q unrecognized && echo n || echo y)
> +
> +# binutils 2.18 implement build-id.
> +ifeq ($(call ld-ver-build-id,$(LD)),n)
> +build_id_linker :=
> +else
> +CFLAGS += -DBUILD_ID
> +build_id_linker := --build-id=sha1
> +endif
> +
>  # as-insn: Check whether assembler supports an instruction.
>  # Usage: cflags-y += $(call as-insn "insn",option-yes,option-no)
>  as-insn = $(if $(shell echo 'void _(void) { asm volatile ( $(2) ); }' \
> diff --git a/tools/flask/policy/policy/modules/xen/xen.te b/tools/flask/policy/policy/modules/xen/xen.te
> index 9ad648a..2988954 100644
> --- a/tools/flask/policy/policy/modules/xen/xen.te
> +++ b/tools/flask/policy/policy/modules/xen/xen.te
> @@ -79,7 +79,7 @@ allow dom0_t xen_t:xen2 {
>  # Note that dom0 is part of domain_type so this has duplicates.
>  allow dom0_t xen_t:version {
>      version extraversion compile_info capabilities changeset
> -    platform_parameters get_features pagesize guest_handle commandline
> +    platform_parameters get_features pagesize guest_handle commandline build_id
>  };
>  
>  allow dom0_t xen_t:mmu memorymap;
> @@ -146,7 +146,7 @@ if (guest_writeconsole) {
>  # pmu_ctrl is for)
>  allow domain_type xen_t:xen2 pmu_use;
>  
> -# For normal guests all except XENVER_commandline
> +# For normal guests all except XENVER_commandline|build_id
>  allow domain_type xen_t:version {
>      version extraversion compile_info capabilities changeset
>      platform_parameters get_features pagesize guest_handle
> diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c
> index c41e433..d57c39a 100644
> --- a/tools/libxc/xc_private.c
> +++ b/tools/libxc/xc_private.c
> @@ -495,6 +495,13 @@ int xc_version(xc_interface *xch, int cmd, void *arg)
>      case XENVER_commandline:
>          sz = sizeof(xen_commandline_t);
>          break;
> +    case XENVER_build_id:
> +        {
> +            xen_build_id_t *build_id = (xen_build_id_t *)arg;
> +            sz = sizeof(*build_id) + build_id->len;
> +            HYPERCALL_BOUNCE_SET_DIR(arg, XC_HYPERCALL_BUFFER_BOUNCE_BOTH);
> +            break;
> +        }
>      default:
>          ERROR("xc_version: unknown command %d\n", cmd);
>          return -EINVAL;
> diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h
> index aa8daf1..6b592d3 100644
> --- a/tools/libxc/xc_private.h
> +++ b/tools/libxc/xc_private.h
> @@ -191,6 +191,16 @@ enum {
>  #define DECLARE_HYPERCALL_BOUNCE(_ubuf, _sz, _dir) DECLARE_NAMED_HYPERCALL_BOUNCE(_ubuf, _ubuf, _sz, _dir)
>  
>  /*
> + * Change the direction.
> + *
> + * Can only be used if the bounce_pre/bounce_post commands have
> + * not been used.
> + */
> +#define HYPERCALL_BOUNCE_SET_DIR(_buf, _dir) do { if ((HYPERCALL_BUFFER(_buf))->hbuf)         \
> +                                                        assert(1);                            \
> +                                                   (HYPERCALL_BUFFER(_buf))->dir = _dir;      \
> +                                                } while (0)
> +/*
>   * Set the size of data to bounce. Useful when the size is not known
>   * when the bounce buffer is declared.
>   */
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index 35ba293..8491267 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -93,7 +93,7 @@ $(TARGET)-syms: prelink.o xen.lds $(BASEDIR)/common/symbols-dummy.o
>  	$(NM) -pa --format=sysv $(@D)/.$(@F).1 \
>  		| $(BASEDIR)/tools/symbols --sysv --sort >$(@D)/.$(@F).1.S
>  	$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1.o
> -	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o \
> +	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o $(build_id_linker) \
>  	    $(@D)/.$(@F).1.o -o $@
>  	rm -f $(@D)/.$(@F).[0-9]*
>  
> diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
> index f501a2f..5cf180f 100644
> --- a/xen/arch/arm/xen.lds.S
> +++ b/xen/arch/arm/xen.lds.S
> @@ -22,6 +22,9 @@ OUTPUT_ARCH(FORMAT)
>  PHDRS
>  {
>    text PT_LOAD /* XXX should be AT ( XEN_PHYS_START ) */ ;
> +#if defined(BUILD_ID)
> +  note PT_NOTE ;
> +#endif
>  }
>  SECTIONS
>  {
> @@ -53,6 +56,16 @@ SECTIONS
>          _erodata = .;          /* End of read-only data */
>    } :text
>  
> +#if defined(BUILD_ID)
> +  .note : {
> +       __note_gnu_build_id_start = .;
> +       *(.note.gnu.build-id)
> +       __note_gnu_build_id_end = .;
> +       *(.note)
> +       *(.note.*)
> +  } :text
> +#endif

This data really should be contained inside rodata.

> diff --git a/xen/include/public/version.h b/xen/include/public/version.h
> index 44f26b0..adca602 100644
> --- a/xen/include/public/version.h
> +++ b/xen/include/public/version.h
> @@ -30,7 +30,8 @@
>  
>  #include "xen.h"
>  
> -/* NB. All ops return zero on success, except XENVER_{version,pagesize} */
> +/* NB. All ops return zero on success, except
> + * XENVER_{version,pagesize,build_id} */
>  
>  /* arg == NULL; returns major:minor (16:16). */
>  #define XENVER_version      0
> @@ -83,6 +84,19 @@ typedef struct xen_feature_info xen_feature_info_t;
>  #define XENVER_commandline 9
>  typedef char xen_commandline_t[1024];
>  
> +/* Return value is the number of bytes written, or XEN_Exx on error.
> + * Calling with empty parameter returns the size of build_id. */
> +#define XENVER_build_id 10
> +struct xen_build_id {
> +        uint32_t        len; /* IN: size of buf[]. */
> +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
> +        unsigned char   buf[];
> +#elif defined(__GNUC__)
> +        unsigned char   buf[1]; /* OUT: Variable length buffer with build_id. */
> +#endif
> +};
> +typedef struct xen_build_id xen_build_id_t;

I am still against trying to perpetuate this broken interface.  Variable
length structures are a pain for everyone to use.  How about introducing
a brand new hypercall with a separate length and data parameters?

~Andrew
Konrad Rzeszutek Wilk Feb. 16, 2016, 8:22 p.m. UTC | #3
On Tue, Feb 16, 2016 at 08:09:13PM +0000, Andrew Cooper wrote:
> On 12/02/16 18:05, Konrad Rzeszutek Wilk wrote:
> 
> Building the hypervisor with buildid and making it available via
> hypercall really should be split into two different patches, especially
> given the complexity in each.

OK, will do.


.. snip..

> > +/* Return value is the number of bytes written, or XEN_Exx on error.
> > + * Calling with empty parameter returns the size of build_id. */
> > +#define XENVER_build_id 10
> > +struct xen_build_id {
> > +        uint32_t        len; /* IN: size of buf[]. */
> > +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
> > +        unsigned char   buf[];
> > +#elif defined(__GNUC__)
> > +        unsigned char   buf[1]; /* OUT: Variable length buffer with build_id. */
> > +#endif
> > +};
> > +typedef struct xen_build_id xen_build_id_t;
> 
> I am still against trying to perpetuate this broken interface.  Variable
> length structures are a pain for everyone to use.  How about introducing
> a brand new hypercall with a separate length and data parameters?

As in subop to sysctl? I am fine with that (which is what I think was
in the first iteration of this patch had). Or it could go under the
XSPLICE subops :-)

Preferences?
> 
> ~Andrew
Andrew Cooper Feb. 16, 2016, 8:26 p.m. UTC | #4
On 16/02/16 20:22, Konrad Rzeszutek Wilk wrote:
> On Tue, Feb 16, 2016 at 08:09:13PM +0000, Andrew Cooper wrote:
>> On 12/02/16 18:05, Konrad Rzeszutek Wilk wrote:
>>
>> Building the hypervisor with buildid and making it available via
>> hypercall really should be split into two different patches, especially
>> given the complexity in each.
> OK, will do.
>
>
> .. snip..
>
>>> +/* Return value is the number of bytes written, or XEN_Exx on error.
>>> + * Calling with empty parameter returns the size of build_id. */
>>> +#define XENVER_build_id 10
>>> +struct xen_build_id {
>>> +        uint32_t        len; /* IN: size of buf[]. */
>>> +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
>>> +        unsigned char   buf[];
>>> +#elif defined(__GNUC__)
>>> +        unsigned char   buf[1]; /* OUT: Variable length buffer with build_id. */
>>> +#endif
>>> +};
>>> +typedef struct xen_build_id xen_build_id_t;
>> I am still against trying to perpetuate this broken interface.  Variable
>> length structures are a pain for everyone to use.  How about introducing
>> a brand new hypercall with a separate length and data parameters?
> As in subop to sysctl? I am fine with that (which is what I think was
> in the first iteration of this patch had). Or it could go under the
> XSPLICE subops :-)
>
> Preferences?

A completely brand new hypercall.  Then we can deprecate the existing
xenver, including moving the relevent information such as plain version
numbers and leaving the irrelevant information (compile date, etc.).

~Andrew
Konrad Rzeszutek Wilk Feb. 16, 2016, 8:40 p.m. UTC | #5
> >>> +/* Return value is the number of bytes written, or XEN_Exx on error.
> >>> + * Calling with empty parameter returns the size of build_id. */
> >>> +#define XENVER_build_id 10
> >>> +struct xen_build_id {
> >>> +        uint32_t        len; /* IN: size of buf[]. */
> >>> +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
> >>> +        unsigned char   buf[];
> >>> +#elif defined(__GNUC__)
> >>> +        unsigned char   buf[1]; /* OUT: Variable length buffer with build_id. */
> >>> +#endif
> >>> +};
> >>> +typedef struct xen_build_id xen_build_id_t;
> >> I am still against trying to perpetuate this broken interface.  Variable
> >> length structures are a pain for everyone to use.  How about introducing
> >> a brand new hypercall with a separate length and data parameters?
> > As in subop to sysctl? I am fine with that (which is what I think was
> > in the first iteration of this patch had). Or it could go under the
> > XSPLICE subops :-)
> >
> > Preferences?
> 
> A completely brand new hypercall.  Then we can deprecate the existing
> xenver, including moving the relevent information such as plain version
> numbers and leaving the irrelevant information (compile date, etc.).

How would you deprecate the xenver when there are existing guests that
depend on this? Say RHEL5, SLES11 or NetBSD? They are not going to move
over and it would be a bit of silly to deprecate something and actually
never deprecate it because of users still depending on it.

Let me stress out that I have no problems adding a new hypercall (albeit
I think it is an overkill), and adding BUILD_ID in it. However I wouldn't
have the time for Xen 4.7 to add the rest of the sub-ops in it - such as
version, command line, what not.

Keep in mind we have one month left..
diff mbox

Patch

diff --git a/Config.mk b/Config.mk
index 429e460..61186e2 100644
--- a/Config.mk
+++ b/Config.mk
@@ -126,6 +126,17 @@  endef
 check-$(gcc) = $(call cc-ver-check,CC,0x040100,"Xen requires at least gcc-4.1")
 $(eval $(check-y))
 
+ld-ver-build-id = $(shell $(1) --build-id 2>&1 | \
+					grep -q unrecognized && echo n || echo y)
+
+# binutils 2.18 implement build-id.
+ifeq ($(call ld-ver-build-id,$(LD)),n)
+build_id_linker :=
+else
+CFLAGS += -DBUILD_ID
+build_id_linker := --build-id=sha1
+endif
+
 # as-insn: Check whether assembler supports an instruction.
 # Usage: cflags-y += $(call as-insn "insn",option-yes,option-no)
 as-insn = $(if $(shell echo 'void _(void) { asm volatile ( $(2) ); }' \
diff --git a/tools/flask/policy/policy/modules/xen/xen.te b/tools/flask/policy/policy/modules/xen/xen.te
index 9ad648a..2988954 100644
--- a/tools/flask/policy/policy/modules/xen/xen.te
+++ b/tools/flask/policy/policy/modules/xen/xen.te
@@ -79,7 +79,7 @@  allow dom0_t xen_t:xen2 {
 # Note that dom0 is part of domain_type so this has duplicates.
 allow dom0_t xen_t:version {
     version extraversion compile_info capabilities changeset
-    platform_parameters get_features pagesize guest_handle commandline
+    platform_parameters get_features pagesize guest_handle commandline build_id
 };
 
 allow dom0_t xen_t:mmu memorymap;
@@ -146,7 +146,7 @@  if (guest_writeconsole) {
 # pmu_ctrl is for)
 allow domain_type xen_t:xen2 pmu_use;
 
-# For normal guests all except XENVER_commandline
+# For normal guests all except XENVER_commandline|build_id
 allow domain_type xen_t:version {
     version extraversion compile_info capabilities changeset
     platform_parameters get_features pagesize guest_handle
diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c
index c41e433..d57c39a 100644
--- a/tools/libxc/xc_private.c
+++ b/tools/libxc/xc_private.c
@@ -495,6 +495,13 @@  int xc_version(xc_interface *xch, int cmd, void *arg)
     case XENVER_commandline:
         sz = sizeof(xen_commandline_t);
         break;
+    case XENVER_build_id:
+        {
+            xen_build_id_t *build_id = (xen_build_id_t *)arg;
+            sz = sizeof(*build_id) + build_id->len;
+            HYPERCALL_BOUNCE_SET_DIR(arg, XC_HYPERCALL_BUFFER_BOUNCE_BOTH);
+            break;
+        }
     default:
         ERROR("xc_version: unknown command %d\n", cmd);
         return -EINVAL;
diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h
index aa8daf1..6b592d3 100644
--- a/tools/libxc/xc_private.h
+++ b/tools/libxc/xc_private.h
@@ -191,6 +191,16 @@  enum {
 #define DECLARE_HYPERCALL_BOUNCE(_ubuf, _sz, _dir) DECLARE_NAMED_HYPERCALL_BOUNCE(_ubuf, _ubuf, _sz, _dir)
 
 /*
+ * Change the direction.
+ *
+ * Can only be used if the bounce_pre/bounce_post commands have
+ * not been used.
+ */
+#define HYPERCALL_BOUNCE_SET_DIR(_buf, _dir) do { if ((HYPERCALL_BUFFER(_buf))->hbuf)         \
+                                                        assert(1);                            \
+                                                   (HYPERCALL_BUFFER(_buf))->dir = _dir;      \
+                                                } while (0)
+/*
  * Set the size of data to bounce. Useful when the size is not known
  * when the bounce buffer is declared.
  */
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 35ba293..8491267 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -93,7 +93,7 @@  $(TARGET)-syms: prelink.o xen.lds $(BASEDIR)/common/symbols-dummy.o
 	$(NM) -pa --format=sysv $(@D)/.$(@F).1 \
 		| $(BASEDIR)/tools/symbols --sysv --sort >$(@D)/.$(@F).1.S
 	$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1.o
-	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o \
+	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o $(build_id_linker) \
 	    $(@D)/.$(@F).1.o -o $@
 	rm -f $(@D)/.$(@F).[0-9]*
 
diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
index f501a2f..5cf180f 100644
--- a/xen/arch/arm/xen.lds.S
+++ b/xen/arch/arm/xen.lds.S
@@ -22,6 +22,9 @@  OUTPUT_ARCH(FORMAT)
 PHDRS
 {
   text PT_LOAD /* XXX should be AT ( XEN_PHYS_START ) */ ;
+#if defined(BUILD_ID)
+  note PT_NOTE ;
+#endif
 }
 SECTIONS
 {
@@ -53,6 +56,16 @@  SECTIONS
         _erodata = .;          /* End of read-only data */
   } :text
 
+#if defined(BUILD_ID)
+  .note : {
+       __note_gnu_build_id_start = .;
+       *(.note.gnu.build-id)
+       __note_gnu_build_id_end = .;
+       *(.note)
+       *(.note.*)
+  } :text
+#endif
+
   .data : {                    /* Data */
        . = ALIGN(PAGE_SIZE);
        *(.data.page_aligned)
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 8cce4ba..4a19ae9 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -71,9 +71,16 @@  efi-y := $(shell if [ ! -r $(BASEDIR)/include/xen/compile.h -o \
                       -O $(BASEDIR)/include/xen/compile.h ]; then \
                          echo '$(TARGET).efi'; fi)
 
+ifdef build_id_linker
+num_phdrs = 2
+else
+num_phdrs = 1
+endif
+
 $(TARGET): $(TARGET)-syms $(efi-y) boot/mkelf32
 	./boot/mkelf32 $(TARGET)-syms $(TARGET) 0x100000 \
-	`$(NM) -nr $(TARGET)-syms | head -n 1 | sed -e 's/^\([^ ]*\).*/0x\1/'`
+	`$(NM) -nr $(TARGET)-syms | head -n 1 | sed -e 's/^\([^ ]*\).*/0x\1/'` \
+	$(num_phdrs)
 	$(MAKE) -f $(BASEDIR)/Rules.mk -C test
 
 install:
@@ -109,20 +116,27 @@  $(BASEDIR)/common/symbols-dummy.o:
 	$(MAKE) -f $(BASEDIR)/Rules.mk -C $(BASEDIR)/common symbols-dummy.o
 
 $(TARGET)-syms: prelink.o xen.lds $(BASEDIR)/common/symbols-dummy.o
-	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o \
+	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o  \
 	    $(BASEDIR)/common/symbols-dummy.o -o $(@D)/.$(@F).0
 	$(NM) -pa --format=sysv $(@D)/.$(@F).0 \
 		| $(BASEDIR)/tools/symbols --sysv --sort >$(@D)/.$(@F).0.S
 	$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).0.o
-	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o \
+	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o  \
 	    $(@D)/.$(@F).0.o -o $(@D)/.$(@F).1
 	$(NM) -pa --format=sysv $(@D)/.$(@F).1 \
 		| $(BASEDIR)/tools/symbols --sysv --sort --warn-dup >$(@D)/.$(@F).1.S
 	$(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1.o
-	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o \
+	$(LD) $(LDFLAGS) -T xen.lds -N prelink.o $(build_id_linker) \
 	    $(@D)/.$(@F).1.o -o $@
 	rm -f $(@D)/.$(@F).[0-9]*
 
+build_id.o: $(TARGET)-syms
+	$(OBJCOPY) --only-section=.note $< .$@.0
+	# Need to clear the CODE when the build_id.o is put in the .data
+	$(OBJCOPY) --set-section-flags=.note=alloc,load,data  \
+		   --rename-section=.note=.note.gnu.build-id .$@.0 $@
+	rm -f .$@.0
+
 EFI_LDFLAGS = $(patsubst -m%,-mi386pep,$(LDFLAGS)) --subsystem=10
 EFI_LDFLAGS += --image-base=$(1) --stack=0,0 --heap=0,0 --strip-debug
 EFI_LDFLAGS += --section-alignment=0x200000 --file-alignment=0x20
@@ -135,6 +149,13 @@  $(TARGET).efi: VIRT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A VIR
 $(TARGET).efi: ALT_BASE = 0x$(shell $(NM) efi/relocs-dummy.o | sed -n 's, A ALT_START$$,,p')
 # Don't use $(wildcard ...) here - at least make 3.80 expands this too early!
 $(TARGET).efi: guard = $(if $(shell echo efi/dis* | grep disabled),:)
+ifdef build_id_linker
+$(TARGET).efi: build_id.o
+build_id_file := build_id.o
+else
+build_id_file :=
+endif
+
 $(TARGET).efi: prelink-efi.o efi.lds efi/relocs-dummy.o $(BASEDIR)/common/symbols-dummy.o efi/mkreloc
 	$(foreach base, $(VIRT_BASE) $(ALT_BASE), \
 	          $(guard) $(LD) $(call EFI_LDFLAGS,$(base)) -T efi.lds -N $< efi/relocs-dummy.o \
@@ -151,7 +172,7 @@  $(TARGET).efi: prelink-efi.o efi.lds efi/relocs-dummy.o $(BASEDIR)/common/symbol
 		| $(guard) $(BASEDIR)/tools/symbols --sysv --sort >$(@D)/.$(@F).1s.S
 	$(guard) $(MAKE) -f $(BASEDIR)/Rules.mk $(@D)/.$(@F).1r.o $(@D)/.$(@F).1s.o
 	$(guard) $(LD) $(call EFI_LDFLAGS,$(VIRT_BASE)) -T efi.lds -N $< \
-	                $(@D)/.$(@F).1r.o $(@D)/.$(@F).1s.o -o $@
+	                $(@D)/.$(@F).1r.o $(@D)/.$(@F).1s.o $(build_id_file) -o $@
 	if $(guard) false; then rm -f $@; echo 'EFI support disabled'; fi
 	rm -f $(@D)/.$(@F).[0-9]*
 
diff --git a/xen/arch/x86/boot/mkelf32.c b/xen/arch/x86/boot/mkelf32.c
index 890ae6d..da4347a 100644
--- a/xen/arch/x86/boot/mkelf32.c
+++ b/xen/arch/x86/boot/mkelf32.c
@@ -45,9 +45,9 @@  static Elf32_Ehdr out_ehdr = {
     0,                                       /* e_flags */
     sizeof(Elf32_Ehdr),                      /* e_ehsize */
     sizeof(Elf32_Phdr),                      /* e_phentsize */
-    1,                                       /* e_phnum */
+    1,  /* modify based on num_phdrs */      /* e_phnum */
     sizeof(Elf32_Shdr),                      /* e_shentsize */
-    3,                                       /* e_shnum */
+    3,  /* modify based on num_phdrs */      /* e_shnum */
     2                                        /* e_shstrndx */
 };
 
@@ -61,8 +61,20 @@  static Elf32_Phdr out_phdr = {
     PF_R|PF_W|PF_X,                          /* p_flags */
     64                                       /* p_align */
 };
+static Elf32_Phdr note_phdr = {
+    PT_NOTE,                                 /* p_type */
+    DYNAMICALLY_FILLED,                      /* p_offset */
+    DYNAMICALLY_FILLED,                      /* p_vaddr */
+    DYNAMICALLY_FILLED,                      /* p_paddr */
+    DYNAMICALLY_FILLED,                      /* p_filesz */
+    DYNAMICALLY_FILLED,                      /* p_memsz */
+    PF_R,                                    /* p_flags */
+    4                                        /* p_align */
+};
 
 static u8 out_shstrtab[] = "\0.text\0.shstrtab";
+/* If num_phdrs >= 2, we need to tack the .note. */
+static u8 out_shstrtab_extra[] = ".note\0";
 
 static Elf32_Shdr out_shdr[] = {
     { 0 },
@@ -90,6 +102,23 @@  static Elf32_Shdr out_shdr[] = {
     }
 };
 
+/*
+ * The 17 points to the '.note' in the out_shstrtab and out_shstrtab_extra
+ * laid out in the file.
+ */
+static Elf32_Shdr out_shdr_extra = {
+      17,                                    /* sh_name */
+      SHT_NOTE,                              /* sh_type */
+      0,                                     /* sh_flags */
+      DYNAMICALLY_FILLED,                    /* sh_addr */
+      DYNAMICALLY_FILLED,                    /* sh_offset */
+      DYNAMICALLY_FILLED,                    /* sh_size */
+      0,                                     /* sh_link */
+      0,                                     /* sh_info */
+      4,                                     /* sh_addralign */
+      0                                      /* sh_entsize */
+};
+
 /* Some system header files define these macros and pollute our namespace. */
 #undef swap16
 #undef swap32
@@ -228,21 +257,22 @@  static void do_read(int fd, void *data, int len)
 int main(int argc, char **argv)
 {
     u64        final_exec_addr;
-    u32        loadbase, dat_siz, mem_siz;
+    u32        loadbase, dat_siz, mem_siz, note_base, note_sz, offset;
     char      *inimage, *outimage;
     int        infd, outfd = -1;
     char       buffer[1024];
     int        bytes, todo, i, rc = 1;
+    int        num_phdrs;
 
     Elf32_Ehdr in32_ehdr;
 
     Elf64_Ehdr in64_ehdr;
     Elf64_Phdr in64_phdr;
 
-    if ( argc != 5 )
+    if ( argc != 6 )
     {
         fprintf(stderr, "Usage: mkelf32 <in-image> <out-image> "
-                "<load-base> <final-exec-addr>\n");
+                "<load-base> <final-exec-addr> <number of program headers>\n");
         return 1;
     }
 
@@ -250,7 +280,13 @@  int main(int argc, char **argv)
     outimage = argv[2];
     loadbase = strtoul(argv[3], NULL, 16);
     final_exec_addr = strtoull(argv[4], NULL, 16);
-
+    num_phdrs = atoi(argv[5]);
+    if ( num_phdrs > 2 || num_phdrs < 1 )
+    {
+        fprintf(stderr, "Number of program headers MUST be 1 or 2, got %d!\n",
+                num_phdrs);
+        return 1;
+    }
     infd = open(inimage, O_RDONLY);
     if ( infd == -1 )
     {
@@ -285,11 +321,10 @@  int main(int argc, char **argv)
                 (int)in64_ehdr.e_phentsize, (int)sizeof(in64_phdr));
         goto out;
     }
-
-    if ( in64_ehdr.e_phnum != 1 )
+    if ( in64_ehdr.e_phnum != num_phdrs )
     {
-        fprintf(stderr, "Expect precisly 1 program header; found %d.\n",
-                (int)in64_ehdr.e_phnum);
+        fprintf(stderr, "Expect precisly %d program header; found %d.\n",
+                num_phdrs, (int)in64_ehdr.e_phnum);
         goto out;
     }
 
@@ -299,11 +334,36 @@  int main(int argc, char **argv)
 
     (void)lseek(infd, in64_phdr.p_offset, SEEK_SET);
     dat_siz = (u32)in64_phdr.p_filesz;
-
     /* Do not use p_memsz: it does not include BSS alignment padding. */
     /*mem_siz = (u32)in64_phdr.p_memsz;*/
     mem_siz = (u32)(final_exec_addr - in64_phdr.p_vaddr);
 
+    note_sz = note_base = offset = 0;
+    if ( num_phdrs > 1 )
+    {
+        offset = in64_phdr.p_offset;
+        note_base = in64_phdr.p_vaddr;
+
+        (void)lseek(infd, in64_ehdr.e_phoff+sizeof(in64_phdr), SEEK_SET);
+        do_read(infd, &in64_phdr, sizeof(in64_phdr));
+        endianadjust_phdr64(&in64_phdr);
+
+        (void)lseek(infd, offset, SEEK_SET);
+
+        note_sz = in64_phdr.p_memsz;
+        note_base = in64_phdr.p_vaddr - note_base;
+
+        if ( in64_phdr.p_offset > dat_siz || offset > in64_phdr.p_offset )
+        {
+            fprintf(stderr, "Expected .note section within .text section!\n" \
+                    "Offset %ld not within %d!\n",
+                    in64_phdr.p_offset, dat_siz);
+            goto out;
+        }
+        /* Gets us the absolute offset within the .text section. */
+        offset = in64_phdr.p_offset - offset;
+    }
+
     /*
      * End the image on a page boundary. This gets round alignment bugs
      * in the boot- or chain-loader (e.g., kexec on the XenoBoot CD).
@@ -322,6 +382,31 @@  int main(int argc, char **argv)
     out_shdr[1].sh_size   = dat_siz;
     out_shdr[2].sh_offset = RAW_OFFSET + dat_siz + sizeof(out_shdr);
 
+    if ( num_phdrs > 1 )
+    {
+        /* We have two of them! */
+        out_ehdr.e_phnum = num_phdrs;
+        /* Extra .note section. */
+        out_ehdr.e_shnum++;
+
+        /* Fill out the PT_NOTE program header. */
+        note_phdr.p_vaddr   = note_base;
+        note_phdr.p_paddr   = note_base;
+        note_phdr.p_filesz  = note_sz;
+        note_phdr.p_memsz   = note_sz;
+        note_phdr.p_offset  = offset;
+
+        /* Tack on the .note\0 */
+        out_shdr[2].sh_size += sizeof(out_shstrtab_extra);
+        /* And move it past the .note section. */
+        out_shdr[2].sh_offset += sizeof(out_shdr_extra);
+
+        /* Fill out the .note section. */
+        out_shdr_extra.sh_size = note_sz;
+        out_shdr_extra.sh_addr = note_base;
+        out_shdr_extra.sh_offset = RAW_OFFSET + offset;
+    }
+
     outfd = open(outimage, O_WRONLY|O_CREAT|O_TRUNC, 0775);
     if ( outfd == -1 )
     {
@@ -335,8 +420,15 @@  int main(int argc, char **argv)
 
     endianadjust_phdr32(&out_phdr);
     do_write(outfd, &out_phdr, sizeof(out_phdr));
-    
-    if ( (bytes = RAW_OFFSET - sizeof(out_ehdr) - sizeof(out_phdr)) < 0 )
+
+    if ( num_phdrs > 1 )
+    {
+        endianadjust_phdr32(&note_phdr);
+        do_write(outfd, &note_phdr, sizeof(note_phdr));
+    }
+
+    if ( (bytes = RAW_OFFSET - sizeof(out_ehdr) - sizeof(out_phdr) -
+          ( num_phdrs > 1 ? sizeof(note_phdr) : 0 ) ) < 0 )
     {
         fprintf(stderr, "Header overflow.\n");
         goto out;
@@ -355,9 +447,22 @@  int main(int argc, char **argv)
         endianadjust_shdr32(&out_shdr[i]);
     do_write(outfd, &out_shdr[0], sizeof(out_shdr));
 
-    do_write(outfd, out_shstrtab, sizeof(out_shstrtab));
-    do_write(outfd, buffer, 4-((sizeof(out_shstrtab)+dat_siz)&3));
-
+    if ( num_phdrs > 1 )
+    {
+        endianadjust_shdr32(&out_shdr_extra);
+        /* Append the .note section. */
+        do_write(outfd, &out_shdr_extra, sizeof(out_shdr_extra));
+        /* The normal strings - .text\0.. */
+        do_write(outfd, out_shstrtab, sizeof(out_shstrtab));
+        /* Our .note */
+        do_write(outfd, out_shstrtab_extra, sizeof(out_shstrtab_extra));
+        do_write(outfd, buffer, 4-((sizeof(out_shstrtab)+sizeof(out_shstrtab_extra)+dat_siz)&3));
+    }
+    else
+    {
+        do_write(outfd, out_shstrtab, sizeof(out_shstrtab));
+        do_write(outfd, buffer, 4-((sizeof(out_shstrtab)+dat_siz)&3));
+    }
     rc = 0;
 out:
     if ( infd != -1 )
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 3b199ca..99a3fcb 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -31,6 +31,9 @@  OUTPUT_ARCH(i386:x86-64)
 PHDRS
 {
   text PT_LOAD ;
+#if defined(BUILD_ID) && !defined(EFI)
+  note PT_NOTE ;
+#endif
 }
 SECTIONS
 {
@@ -67,6 +70,21 @@  SECTIONS
        *(.rodata.*)
   } :text
 
+#if defined(BUILD_ID) && !defined(EFI)
+/*
+ * No mechanism to put an PT_NOTE in the EFI file - so put
+ * it in .data section.
+ */
+  . = ALIGN(4);
+  .note : {
+       __note_gnu_build_id_start = .;
+       *(.note.gnu.build-id)
+       __note_gnu_build_id_end = .;
+       *(.note)
+       *(.note.*)
+  } :note :text
+#endif
+
   . = ALIGN(SMP_CACHE_BYTES);
   .data.read_mostly : {
        /* Exception table */
@@ -86,6 +104,11 @@  SECTIONS
        __end_schedulers_array = .;
        *(.data.rel.ro)
        *(.data.rel.ro.*)
+#if defined(BUILD_ID) && defined(EFI)
+       __note_gnu_build_id_start = .;
+       *(.note.gnu.build-id)
+       __note_gnu_build_id_end = .;
+#endif
   } :text
 
   .data : {                    /* Data */
diff --git a/xen/common/kernel.c b/xen/common/kernel.c
index a5e3f0e..cd746a9 100644
--- a/xen/common/kernel.c
+++ b/xen/common/kernel.c
@@ -383,6 +383,42 @@  DO(xen_version)(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
             return -EFAULT;
         return 0;
     }
+
+    case XENVER_build_id:
+    {
+        xen_build_id_t build_id;
+        unsigned int sz = 0;
+        int rc = 0;
+        char *p = NULL;
+
+        if ( deny )
+            return -EPERM;
+
+        /* Only return size. */
+        if ( !guest_handle_is_null(arg) )
+        {
+            if ( copy_from_guest(&build_id, arg, 1) )
+                return -EFAULT;
+
+            if ( build_id.len == 0 )
+                return -EINVAL;
+        }
+
+        rc = xen_build_id(&p, &sz);
+        if ( rc )
+            return rc;
+
+        if ( guest_handle_is_null(arg) )
+            return sz;
+
+        if ( sz > build_id.len )
+            return -ENOBUFS;
+
+        if ( copy_to_guest_offset(arg, offsetof(xen_build_id_t, buf), p, sz) )
+            return -EFAULT;
+
+        return sz;
+    }
     }
 
     return -ENOSYS;
diff --git a/xen/common/version.c b/xen/common/version.c
index 786be4e..33c09e5 100644
--- a/xen/common/version.c
+++ b/xen/common/version.c
@@ -1,5 +1,9 @@ 
 #include <xen/compile.h>
+#include <xen/errno.h>
+#include <xen/string.h>
+#include <xen/types.h>
 #include <xen/version.h>
+#include <xen/elf.h>
 
 const char *xen_compile_date(void)
 {
@@ -60,3 +64,47 @@  const char *xen_deny(void)
 {
     return "<denied>";
 }
+#ifdef BUILD_ID
+#define NT_GNU_BUILD_ID 3
+/* Defined in linker script. */
+extern const Elf_Note __note_gnu_build_id_start[], __note_gnu_build_id_end[];
+
+int xen_build_id(char **p, unsigned int *len)
+{
+    const Elf_Note *n = __note_gnu_build_id_start;
+    static bool_t checked = 0;
+
+    if ( checked )
+    {
+        *len = n->descsz;
+        *p = ELFNOTE_DESC(n);
+        return 0;
+    }
+    /* --build-id invoked with wrong parameters. */
+    if ( __note_gnu_build_id_end <= __note_gnu_build_id_start )
+        return -ENODATA;
+
+    /* Check for full Note header. */
+    if ( &n[1] > __note_gnu_build_id_end )
+        return -ENODATA;
+
+    /* Check if we really have a build-id. */
+    if ( NT_GNU_BUILD_ID != n->type )
+        return -ENODATA;
+
+    /* Sanity check, name should be "GNU" for ld-generated build-id. */
+    if ( strncmp(ELFNOTE_NAME(n), "GNU", n->namesz) != 0 )
+        return -ENODATA;
+
+    *len = n->descsz;
+    *p = ELFNOTE_DESC(n);
+
+    checked = 1;
+    return 0;
+}
+#else
+int xen_build_id(char **p, unsigned int *len)
+{
+    return -ENODATA;
+}
+#endif
diff --git a/xen/include/public/version.h b/xen/include/public/version.h
index 44f26b0..adca602 100644
--- a/xen/include/public/version.h
+++ b/xen/include/public/version.h
@@ -30,7 +30,8 @@ 
 
 #include "xen.h"
 
-/* NB. All ops return zero on success, except XENVER_{version,pagesize} */
+/* NB. All ops return zero on success, except
+ * XENVER_{version,pagesize,build_id} */
 
 /* arg == NULL; returns major:minor (16:16). */
 #define XENVER_version      0
@@ -83,6 +84,19 @@  typedef struct xen_feature_info xen_feature_info_t;
 #define XENVER_commandline 9
 typedef char xen_commandline_t[1024];
 
+/* Return value is the number of bytes written, or XEN_Exx on error.
+ * Calling with empty parameter returns the size of build_id. */
+#define XENVER_build_id 10
+struct xen_build_id {
+        uint32_t        len; /* IN: size of buf[]. */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+        unsigned char   buf[];
+#elif defined(__GNUC__)
+        unsigned char   buf[1]; /* OUT: Variable length buffer with build_id. */
+#endif
+};
+typedef struct xen_build_id xen_build_id_t;
+
 #endif /* __XEN_PUBLIC_VERSION_H__ */
 
 /*
diff --git a/xen/include/xen/version.h b/xen/include/xen/version.h
index 2015c0b..466c977 100644
--- a/xen/include/xen/version.h
+++ b/xen/include/xen/version.h
@@ -13,5 +13,6 @@  const char *xen_extra_version(void);
 const char *xen_changeset(void);
 const char *xen_banner(void);
 const char *xen_deny(void);
+int xen_build_id(char **p, unsigned int *len);
 
 #endif /* __XEN_VERSION_H__ */
diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c
index 7e3bcdd..396ee46 100644
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -1664,6 +1664,9 @@  static int flask_version_op (uint32_t op)
     case XENVER_commandline:
         return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
                             VERSION__COMMANDLINE, NULL);
+    case XENVER_build_id:
+        return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
+                            VERSION__BUILD_ID, NULL);
     default:
         return -EPERM;
     }
diff --git a/xen/xsm/flask/policy/access_vectors b/xen/xsm/flask/policy/access_vectors
index 7cb32de..c9cd102 100644
--- a/xen/xsm/flask/policy/access_vectors
+++ b/xen/xsm/flask/policy/access_vectors
@@ -524,4 +524,6 @@  class version
     guest_handle
 # Xen command line.
     commandline
+# Build id
+    build_id
 }