x86, vdso: Get rid of the fake section mechanism

Now that we can tolerate extra things dangling off the end of the
vdso image, we can strip the vdso the old fashioned way rather than
using an overcomplicated custom stripping algorithm.

This is a partial reversion of:
    6f121e5 x86, vdso: Reimplement vdso.so preparation in build-time C

Signed-off-by: Andy Lutomirski <luto@amacapital.net>
Link: http://lkml.kernel.org/r/50e01ed6dcc0575d20afd782f9fe98d5ee3e2d8a.1405040914.git.luto@amacapital.net
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
diff --git a/arch/x86/vdso/vdso2c.h b/arch/x86/vdso/vdso2c.h
index 2da32fbc..fd57829 100644
--- a/arch/x86/vdso/vdso2c.h
+++ b/arch/x86/vdso/vdso2c.h
@@ -4,128 +4,14 @@
  * are built for 32-bit userspace.
  */
 
-/*
- * We're writing a section table for a few reasons:
- *
- * The Go runtime had a couple of bugs: it would read the section
- * table to try to figure out how many dynamic symbols there were (it
- * shouldn't have looked at the section table at all) and, if there
- * were no SHT_SYNDYM section table entry, it would use an
- * uninitialized value for the number of symbols.  An empty DYNSYM
- * table would work, but I see no reason not to write a valid one (and
- * keep full performance for old Go programs).  This hack is only
- * needed on x86_64.
- *
- * The bug was introduced on 2012-08-31 by:
- * https://code.google.com/p/go/source/detail?r=56ea40aac72b
- * and was fixed on 2014-06-13 by:
- * https://code.google.com/p/go/source/detail?r=fc1cd5e12595
- *
- * Binutils has issues debugging the vDSO: it reads the section table to
- * find SHT_NOTE; it won't look at PT_NOTE for the in-memory vDSO, which
- * would break build-id if we removed the section table.  Binutils
- * also requires that shstrndx != 0.  See:
- * https://sourceware.org/bugzilla/show_bug.cgi?id=17064
- *
- * elfutils might not look for PT_NOTE if there is a section table at
- * all.  I don't know whether this matters for any practical purpose.
- *
- * For simplicity, rather than hacking up a partial section table, we
- * just write a mostly complete one.  We omit non-dynamic symbols,
- * though, since they're rather large.
- *
- * Once binutils gets fixed, we might be able to drop this for all but
- * the 64-bit vdso, since build-id only works in kernel RPMs, and
- * systems that update to new enough kernel RPMs will likely update
- * binutils in sync.  build-id has never worked for home-built kernel
- * RPMs without manual symlinking, and I suspect that no one ever does
- * that.
- */
-struct BITSFUNC(fake_sections)
-{
-	ELF(Shdr) *table;
-	unsigned long table_offset;
-	int count, max_count;
-
-	int in_shstrndx;
-	unsigned long shstr_offset;
-	const char *shstrtab;
-	size_t shstrtab_len;
-
-	int out_shstrndx;
-};
-
-static unsigned int BITSFUNC(find_shname)(struct BITSFUNC(fake_sections) *out,
-					  const char *name)
-{
-	const char *outname = out->shstrtab;
-	while (outname - out->shstrtab < out->shstrtab_len) {
-		if (!strcmp(name, outname))
-			return (outname - out->shstrtab) + out->shstr_offset;
-		outname += strlen(outname) + 1;
-	}
-
-	if (*name)
-		printf("Warning: could not find output name \"%s\"\n", name);
-	return out->shstr_offset + out->shstrtab_len - 1;  /* Use a null. */
-}
-
-static void BITSFUNC(init_sections)(struct BITSFUNC(fake_sections) *out)
-{
-	if (!out->in_shstrndx)
-		fail("didn't find the fake shstrndx\n");
-
-	memset(out->table, 0, out->max_count * sizeof(ELF(Shdr)));
-
-	if (out->max_count < 1)
-		fail("we need at least two fake output sections\n");
-
-	PUT_LE(&out->table[0].sh_type, SHT_NULL);
-	PUT_LE(&out->table[0].sh_name, BITSFUNC(find_shname)(out, ""));
-
-	out->count = 1;
-}
-
-static void BITSFUNC(copy_section)(struct BITSFUNC(fake_sections) *out,
-				   int in_idx, const ELF(Shdr) *in,
-				   const char *name)
-{
-	uint64_t flags = GET_LE(&in->sh_flags);
-
-	bool copy = flags & SHF_ALLOC &&
-		(GET_LE(&in->sh_size) ||
-		 (GET_LE(&in->sh_type) != SHT_RELA &&
-		  GET_LE(&in->sh_type) != SHT_REL)) &&
-		strcmp(name, ".altinstructions") &&
-		strcmp(name, ".altinstr_replacement");
-
-	if (!copy)
-		return;
-
-	if (out->count >= out->max_count)
-		fail("too many copied sections (max = %d)\n", out->max_count);
-
-	if (in_idx == out->in_shstrndx)
-		out->out_shstrndx = out->count;
-
-	out->table[out->count] = *in;
-	PUT_LE(&out->table[out->count].sh_name,
-	       BITSFUNC(find_shname)(out, name));
-
-	/* elfutils requires that a strtab have the correct type. */
-	if (!strcmp(name, ".fake_shstrtab"))
-		PUT_LE(&out->table[out->count].sh_type, SHT_STRTAB);
-
-	out->count++;
-}
-
-static void BITSFUNC(go)(void *addr, size_t len,
+static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
+			 void *stripped_addr, size_t stripped_len,
 			 FILE *outfile, const char *name)
 {
 	int found_load = 0;
 	unsigned long load_size = -1;  /* Work around bogus warning */
-	unsigned long data_size;
-	ELF(Ehdr) *hdr = (ELF(Ehdr) *)addr;
+	unsigned long mapping_size;
+	ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
 	int i;
 	unsigned long j;
 	ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
@@ -134,9 +20,7 @@
 	const char *secstrings;
 	INT_BITS syms[NSYMS] = {};
 
-	struct BITSFUNC(fake_sections) fake_sections = {};
-
-	ELF(Phdr) *pt = (ELF(Phdr) *)(addr + GET_LE(&hdr->e_phoff));
+	ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_LE(&hdr->e_phoff));
 
 	/* Walk the segment table. */
 	for (i = 0; i < GET_LE(&hdr->e_phnum); i++) {
@@ -154,14 +38,16 @@
 			load_size = GET_LE(&pt[i].p_memsz);
 			found_load = 1;
 		} else if (GET_LE(&pt[i].p_type) == PT_DYNAMIC) {
-			dyn = addr + GET_LE(&pt[i].p_offset);
-			dyn_end = addr + GET_LE(&pt[i].p_offset) +
+			dyn = raw_addr + GET_LE(&pt[i].p_offset);
+			dyn_end = raw_addr + GET_LE(&pt[i].p_offset) +
 				GET_LE(&pt[i].p_memsz);
 		}
 	}
 	if (!found_load)
 		fail("no PT_LOAD seg\n");
-	data_size = (load_size + 4095) / 4096 * 4096;
+
+	if (stripped_len < load_size)
+		fail("stripped input is too short\n");
 
 	/* Walk the dynamic table */
 	for (i = 0; dyn + i < dyn_end &&
@@ -173,11 +59,11 @@
 	}
 
 	/* Walk the section table */
-	secstrings_hdr = addr + GET_LE(&hdr->e_shoff) +
+	secstrings_hdr = raw_addr + GET_LE(&hdr->e_shoff) +
 		GET_LE(&hdr->e_shentsize)*GET_LE(&hdr->e_shstrndx);
-	secstrings = addr + GET_LE(&secstrings_hdr->sh_offset);
+	secstrings = raw_addr + GET_LE(&secstrings_hdr->sh_offset);
 	for (i = 0; i < GET_LE(&hdr->e_shnum); i++) {
-		ELF(Shdr) *sh = addr + GET_LE(&hdr->e_shoff) +
+		ELF(Shdr) *sh = raw_addr + GET_LE(&hdr->e_shoff) +
 			GET_LE(&hdr->e_shentsize) * i;
 		if (GET_LE(&sh->sh_type) == SHT_SYMTAB)
 			symtab_hdr = sh;
@@ -190,7 +76,7 @@
 	if (!symtab_hdr)
 		fail("no symbol table\n");
 
-	strtab_hdr = addr + GET_LE(&hdr->e_shoff) +
+	strtab_hdr = raw_addr + GET_LE(&hdr->e_shoff) +
 		GET_LE(&hdr->e_shentsize) * GET_LE(&symtab_hdr->sh_link);
 
 	/* Walk the symbol table */
@@ -198,9 +84,9 @@
 	     i < GET_LE(&symtab_hdr->sh_size) / GET_LE(&symtab_hdr->sh_entsize);
 	     i++) {
 		int k;
-		ELF(Sym) *sym = addr + GET_LE(&symtab_hdr->sh_offset) +
+		ELF(Sym) *sym = raw_addr + GET_LE(&symtab_hdr->sh_offset) +
 			GET_LE(&symtab_hdr->sh_entsize) * i;
-		const char *name = addr + GET_LE(&strtab_hdr->sh_offset) +
+		const char *name = raw_addr + GET_LE(&strtab_hdr->sh_offset) +
 			GET_LE(&sym->st_name);
 
 		for (k = 0; k < NSYMS; k++) {
@@ -219,49 +105,8 @@
 				syms[k] = GET_LE(&sym->st_value);
 			}
 		}
-
-		if (!strcmp(name, "fake_shstrtab")) {
-			ELF(Shdr) *sh;
-
-			fake_sections.in_shstrndx = GET_LE(&sym->st_shndx);
-			fake_sections.shstrtab = addr + GET_LE(&sym->st_value);
-			fake_sections.shstrtab_len = GET_LE(&sym->st_size);
-			sh = addr + GET_LE(&hdr->e_shoff) +
-				GET_LE(&hdr->e_shentsize) *
-				fake_sections.in_shstrndx;
-			fake_sections.shstr_offset = GET_LE(&sym->st_value) -
-				GET_LE(&sh->sh_addr);
-		}
 	}
 
-	/* Build the output section table. */
-	if (!syms[sym_VDSO_FAKE_SECTION_TABLE_START] ||
-	    !syms[sym_VDSO_FAKE_SECTION_TABLE_END])
-		fail("couldn't find fake section table\n");
-	if ((syms[sym_VDSO_FAKE_SECTION_TABLE_END] -
-	     syms[sym_VDSO_FAKE_SECTION_TABLE_START]) % sizeof(ELF(Shdr)))
-		fail("fake section table size isn't a multiple of sizeof(Shdr)\n");
-	fake_sections.table = addr + syms[sym_VDSO_FAKE_SECTION_TABLE_START];
-	fake_sections.table_offset = syms[sym_VDSO_FAKE_SECTION_TABLE_START];
-	fake_sections.max_count = (syms[sym_VDSO_FAKE_SECTION_TABLE_END] -
-				   syms[sym_VDSO_FAKE_SECTION_TABLE_START]) /
-		sizeof(ELF(Shdr));
-
-	BITSFUNC(init_sections)(&fake_sections);
-	for (i = 0; i < GET_LE(&hdr->e_shnum); i++) {
-		ELF(Shdr) *sh = addr + GET_LE(&hdr->e_shoff) +
-			GET_LE(&hdr->e_shentsize) * i;
-		BITSFUNC(copy_section)(&fake_sections, i, sh,
-				       secstrings + GET_LE(&sh->sh_name));
-	}
-	if (!fake_sections.out_shstrndx)
-		fail("didn't generate shstrndx?!?\n");
-
-	PUT_LE(&hdr->e_shoff, fake_sections.table_offset);
-	PUT_LE(&hdr->e_shentsize, sizeof(ELF(Shdr)));
-	PUT_LE(&hdr->e_shnum, fake_sections.count);
-	PUT_LE(&hdr->e_shstrndx, fake_sections.out_shstrndx);
-
 	/* Validate mapping addresses. */
 	for (i = 0; i < sizeof(special_pages) / sizeof(special_pages[0]); i++) {
 		if (!syms[i])
@@ -281,10 +126,12 @@
 		fail("vvar_begin must be a multiple of 4096\n");
 
 	if (!name) {
-		fwrite(addr, load_size, 1, outfile);
+		fwrite(stripped_addr, stripped_len, 1, outfile);
 		return;
 	}
 
+	mapping_size = (stripped_len + 4095) / 4096 * 4096;
+
 	fprintf(outfile, "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */\n\n");
 	fprintf(outfile, "#include <linux/linkage.h>\n");
 	fprintf(outfile, "#include <asm/page_types.h>\n");
@@ -292,20 +139,21 @@
 	fprintf(outfile, "\n");
 	fprintf(outfile,
 		"static unsigned char raw_data[%lu] __page_aligned_data = {",
-		data_size);
-	for (j = 0; j < load_size; j++) {
+		mapping_size);
+	for (j = 0; j < stripped_len; j++) {
 		if (j % 10 == 0)
 			fprintf(outfile, "\n\t");
-		fprintf(outfile, "0x%02X, ", (int)((unsigned char *)addr)[j]);
+		fprintf(outfile, "0x%02X, ",
+			(int)((unsigned char *)stripped_addr)[j]);
 	}
 	fprintf(outfile, "\n};\n\n");
 
 	fprintf(outfile, "static struct page *pages[%lu];\n\n",
-		data_size / 4096);
+		mapping_size / 4096);
 
 	fprintf(outfile, "const struct vdso_image %s = {\n", name);
 	fprintf(outfile, "\t.data = raw_data,\n");
-	fprintf(outfile, "\t.size = %lu,\n", data_size);
+	fprintf(outfile, "\t.size = %lu,\n", mapping_size);
 	fprintf(outfile, "\t.text_mapping = {\n");
 	fprintf(outfile, "\t\t.name = \"[vdso]\",\n");
 	fprintf(outfile, "\t\t.pages = pages,\n");