diff --git a/Config.mk b/Config.mk index 7b2fe7f579..0213498bc5 100644 --- a/Config.mk +++ b/Config.mk @@ -135,6 +135,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 build-id && echo n || echo y) + +export XEN_HAS_BUILD_ID ?= n +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/xen/arch/arm/Makefile b/xen/arch/arm/Makefile index f77f8db351..ead0cc0aa7 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 990959592c..1f010bd32e 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 { @@ -57,10 +60,18 @@ SECTIONS *(.lockprofile.data) __lock_profile_end = .; #endif - - _erodata = .; /* End of read-only data */ } :text +#if defined(BUILD_ID) + . = ALIGN(4); + .note.gnu.build-id : { + __note_gnu_build_id_start = .; + *(.note.gnu.build-id) + __note_gnu_build_id_end = .; + } :note :text +#endif + _erodata = .; /* End of read-only data */ + .data : { /* Data */ . = ALIGN(PAGE_SIZE); *(.data.page_aligned) diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile index bd7ba9f346..4665a6888c 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -74,6 +74,12 @@ efi-y := $(shell if [ ! -r $(BASEDIR)/include/xen/compile.h -o \ -O $(BASEDIR)/include/xen/compile.h ]; then \ echo '$(TARGET).efi'; fi) +ifneq ($(build_id_linker),) +notes_phdrs = --notes +else +notes_phdrs = +endif + ifdef CONFIG_XSPLICE all_symbols = --all-symbols ifdef CONFIG_FAST_SYMBOL_LOOKUP @@ -84,7 +90,7 @@ all_symbols = endif $(TARGET): $(TARGET)-syms $(efi-y) boot/mkelf32 - ./boot/mkelf32 $(TARGET)-syms $(TARGET) 0x100000 \ + ./boot/mkelf32 $(notes_phdrs) $(TARGET)-syms $(TARGET) 0x100000 \ `$(NM) -nr $(TARGET)-syms | head -n 1 | sed -e 's/^\([^ ]*\).*/0x\1/'` .PHONY: tests @@ -119,22 +125,28 @@ $(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 $(build_id_linker) \ $(BASEDIR)/common/symbols-dummy.o -o $(@D)/.$(@F).0 $(NM) -pa --format=sysv $(@D)/.$(@F).0 \ | $(BASEDIR)/tools/symbols $(all_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 $(build_id_linker) \ $(@D)/.$(@F).0.o -o $(@D)/.$(@F).1 $(NM) -pa --format=sysv $(@D)/.$(@F).1 \ | $(BASEDIR)/tools/symbols $(all_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]* +note.o: $(TARGET)-syms + $(OBJCOPY) -O binary --only-section=.note.gnu.build-id $(BASEDIR)/xen-syms $@.bin + $(OBJCOPY) -I binary -O elf64-x86-64 -B i386:x86-64 \ + --rename-section=.data=.note.gnu.build-id -S $@.bin $@ + rm -f $@.bin + 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 @@ -147,6 +159,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),:) +ifneq ($(build_id_linker),) +$(TARGET).efi: note.o +note_file := note.o +else +note_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 \ @@ -163,7 +182,7 @@ $(TARGET).efi: prelink-efi.o efi.lds efi/relocs-dummy.o $(BASEDIR)/common/symbol | $(guard) $(BASEDIR)/tools/symbols $(all_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 $(note_file) -o $@ if $(guard) false; then rm -f $@; echo 'EFI support disabled'; fi rm -f $(@D)/.$(@F).[0-9]* @@ -195,4 +214,5 @@ clean:: rm -f $(BASEDIR)/.xen-syms.[0-9]* boot/.*.d rm -f $(BASEDIR)/.xen.efi.[0-9]* efi/*.o efi/.*.d efi/*.efi efi/disabled efi/mkreloc rm -f boot/reloc.S boot/reloc.lnk boot/reloc.bin + rm -f note.o $(MAKE) -f $(BASEDIR)/Rules.mk -C test clean diff --git a/xen/arch/x86/boot/mkelf32.c b/xen/arch/x86/boot/mkelf32.c index 993a7eeeda..8c51990325 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_note = { + 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,28 +257,34 @@ 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; char buffer[1024]; - int bytes, todo, i; + int bytes, todo, i = 1; + int num_phdrs = 1; Elf32_Ehdr in32_ehdr; Elf64_Ehdr in64_ehdr; Elf64_Phdr in64_phdr; - if ( argc != 5 ) + if ( argc < 5 ) { - fprintf(stderr, "Usage: mkelf32 " + fprintf(stderr, "Usage: mkelf32 [--notes] " " \n"); return 1; } - inimage = argv[1]; - outimage = argv[2]; - loadbase = strtoul(argv[3], NULL, 16); - final_exec_addr = strtoull(argv[4], NULL, 16); + if ( !strcmp(argv[1], "--notes") ) + { + i = 2; + num_phdrs = 2; + } + inimage = argv[i++]; + outimage = argv[i++]; + loadbase = strtoul(argv[i++], NULL, 16); + final_exec_addr = strtoull(argv[i++], NULL, 16); infd = open(inimage, O_RDONLY); if ( infd == -1 ) @@ -285,11 +320,10 @@ int main(int argc, char **argv) (int)in64_ehdr.e_phentsize, (int)sizeof(in64_phdr)); return 1; } - - 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); return 1; } @@ -304,6 +338,32 @@ int main(int argc, char **argv) /*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); + return 1; + } + /* 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_note); + + /* Fill out the .note section. */ + out_shdr_note.sh_size = note_sz; + out_shdr_note.sh_addr = note_base; + out_shdr_note.sh_offset = RAW_OFFSET + offset; + } + outfd = open(outimage, O_WRONLY|O_CREAT|O_TRUNC, 0775); if ( outfd == -1 ) { @@ -335,8 +420,14 @@ 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(¬e_phdr); + do_write(outfd, ¬e_phdr, sizeof(note_phdr)); + } + + if ( (bytes = RAW_OFFSET - sizeof(out_ehdr) - (num_phdrs * sizeof(out_phdr)) ) < 0 ) { fprintf(stderr, "Header overflow.\n"); return 1; @@ -355,9 +446,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_note); + /* Append the .note section. */ + do_write(outfd, &out_shdr_note, sizeof(out_shdr_note)); + /* 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)); + } close(infd); close(outfd); diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S index 5eb825e57c..b14bcd2eed 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 { @@ -79,6 +82,16 @@ SECTIONS *(.rodata) *(.rodata.*) +#if defined(BUILD_ID) && defined(EFI) +/* + * No mechanism to put an PT_NOTE in the EFI file - so put + * it in .rodata section. (notes.o supplies us with .note.gnu.build-id). + */ + . = ALIGN(4); + __note_gnu_build_id_start = .; + *(.note.gnu.build-id) + __note_gnu_build_id_end = .; +#endif . = ALIGN(8); /* Exception table */ __start___ex_table = .; @@ -96,9 +109,24 @@ SECTIONS *(.lockprofile.data) __lock_profile_end = .; #endif - _erodata = .; } :text +#if defined(BUILD_ID) && !defined(EFI) +/* + * What a strange section name. The reason is that on ELF builds this section + * is extracted to notes.o (which then is ingested in the EFI file). But the + * compiler may want to inject other things in the .note which we don't care + * about - hence this unique name. + */ + . = ALIGN(4); + .note.gnu.build-id : { + __note_gnu_build_id_start = .; + *(.note.gnu.build-id) + __note_gnu_build_id_end = .; + } :note :text +#endif + _erodata = .; + #ifdef EFI . = ALIGN(MB(2)); #else diff --git a/xen/common/version.c b/xen/common/version.c index fc9bf422a5..30578a6c76 100644 --- a/xen/common/version.c +++ b/xen/common/version.c @@ -1,6 +1,13 @@ #include +#include +#include +#include +#include +#include #include +#include + const char *xen_compile_date(void) { return XEN_COMPILE_DATE; @@ -61,6 +68,51 @@ const char *xen_deny(void) return ""; } +static const void *build_id_p __read_mostly; +static unsigned int build_id_len __read_mostly; + +int xen_build_id(const void **p, unsigned int *len) +{ + if ( !build_id_len ) + return -ENODATA; + + *len = build_id_len; + *p = build_id_p; + + return 0; +} + +#ifdef BUILD_ID +/* Defined in linker script. */ +extern const Elf_Note __note_gnu_build_id_start[], __note_gnu_build_id_end[]; + +static int __init xen_build_init(void) +{ + const Elf_Note *n = __note_gnu_build_id_start; + + /* --build-id invoked with wrong parameters. */ + if ( __note_gnu_build_id_end <= &n[0] ) + 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; + + build_id_len = n->descsz; + build_id_p = ELFNOTE_DESC(n); + + return 0; +} +__initcall(xen_build_init); +#endif /* * Local variables: * mode: C diff --git a/xen/include/xen/elfstructs.h b/xen/include/xen/elfstructs.h index ab9b1ea5fb..615eb0696e 100644 --- a/xen/include/xen/elfstructs.h +++ b/xen/include/xen/elfstructs.h @@ -40,6 +40,9 @@ typedef uint32_t Elf64_Word; typedef int64_t Elf64_Sxword; typedef uint64_t Elf64_Xword; +/* Unique build id string format when using --build-id. */ +#define NT_GNU_BUILD_ID 3 + /* * e_ident[] identification indexes * See http://www.caldera.com/developers/gabi/2000-07-17/ch4.eheader.html diff --git a/xen/include/xen/version.h b/xen/include/xen/version.h index 2015c0be34..400160fa52 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(const void **p, unsigned int *len); #endif /* __XEN_VERSION_H__ */