Commit bfad381c authored by Andy Lutomirski's avatar Andy Lutomirski Committed by H. Peter Anvin
Browse files

x86/vdso: Improve the fake section headers



Fully stripping the vDSO has other unfortunate side effects:

 - binutils is unable to find ELF notes without a SHT_NOTE section.

 - Even elfutils has trouble: it can find ELF notes without a section
   table at all, but if a section table is present, it won't look for
   PT_NOTE.

 - gdb wants section names to match between stripped DSOs and their
   symbols; otherwise it will corrupt symbol addresses.

We're also breaking the rules: section 0 is supposed to be SHT_NULL.

Fix these problems by building a better fake section table.  While
we're at it, we might as well let buggy Go versions keep working well
by giving the SHT_DYNSYM entry the correct size.

This is a bit unfortunate: it adds quite a bit of size to the vdso
image.

If/when binutils improves and the improved versions become widespread,
it would be worth considering dropping most of this.

Signed-off-by: default avatarAndy Lutomirski <luto@amacapital.net>
Link: http://lkml.kernel.org/r/0e546a5eeaafdf1840e6ee654a55c1e727c26663.1403129369.git.luto@amacapital.net


Signed-off-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
parent c1979c37
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -11,7 +11,6 @@ VDSO32-$(CONFIG_COMPAT) := y


# files to link into the vdso
# files to link into the vdso
vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o
vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o
vobjs-nox32 := vdso-fakesections.o


# files to link into kernel
# files to link into kernel
obj-y				+= vma.o
obj-y				+= vma.o
@@ -134,7 +133,7 @@ override obj-dirs = $(dir $(obj)) $(obj)/vdso32/


targets += vdso32/vdso32.lds
targets += vdso32/vdso32.lds
targets += vdso32/note.o vdso32/vclock_gettime.o $(vdso32.so-y:%=vdso32/%.o)
targets += vdso32/note.o vdso32/vclock_gettime.o $(vdso32.so-y:%=vdso32/%.o)
targets += vdso32/vclock_gettime.o
targets += vdso32/vclock_gettime.o vdso32/vdso-fakesections.o


$(obj)/vdso32.o: $(vdso32-images:%=$(obj)/%)
$(obj)/vdso32.o: $(vdso32-images:%=$(obj)/%)


@@ -155,6 +154,7 @@ $(vdso32-images:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
$(vdso32-images:%=$(obj)/%.dbg): $(obj)/vdso32-%.so.dbg: FORCE \
$(vdso32-images:%=$(obj)/%.dbg): $(obj)/vdso32-%.so.dbg: FORCE \
				 $(obj)/vdso32/vdso32.lds \
				 $(obj)/vdso32/vdso32.lds \
				 $(obj)/vdso32/vclock_gettime.o \
				 $(obj)/vdso32/vclock_gettime.o \
				 $(obj)/vdso32/vdso-fakesections.o \
				 $(obj)/vdso32/note.o \
				 $(obj)/vdso32/note.o \
				 $(obj)/vdso32/%.o
				 $(obj)/vdso32/%.o
	$(call if_changed,vdso)
	$(call if_changed,vdso)
+18 −26
Original line number Original line Diff line number Diff line
@@ -2,31 +2,23 @@
 * Copyright 2014 Andy Lutomirski
 * Copyright 2014 Andy Lutomirski
 * Subject to the GNU Public License, v.2
 * Subject to the GNU Public License, v.2
 *
 *
 * Hack to keep broken Go programs working.
 * String table for loadable section headers.  See vdso2c.h for why
 *
 * this exists.
 * 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.  As a
 * workaround, we supply a minimal section table.  vdso2c will adjust the
 * in-memory image so that "vdso_fake_sections" becomes the section table.
 *
 * The bug was introduced by:
 * https://code.google.com/p/go/source/detail?r=56ea40aac72b (2012-08-31)
 * and is being addressed in the Go runtime in this issue:
 * https://code.google.com/p/go/issues/detail?id=8197
 */
 */


#ifndef __x86_64__
const char fake_shstrtab[] __attribute__((section(".fake_shstrtab"))) =
#error This hack is specific to the 64-bit vDSO
	".hash\0"
#endif
	".dynsym\0"

	".dynstr\0"
#include <linux/elf.h>
	".gnu.version\0"

	".gnu.version_d\0"
extern const __visible struct elf64_shdr vdso_fake_sections[];
	".dynamic\0"
const __visible struct elf64_shdr vdso_fake_sections[] = {
	".rodata\0"
	{
	".fake_shstrtab\0"  /* Yay, self-referential code. */
		.sh_type = SHT_DYNSYM,
	".note\0"
		.sh_entsize = sizeof(Elf64_Sym),
	".data\0"
	}
	".altinstructions\0"
};
	".altinstr_replacement\0"
	".eh_frame_hdr\0"
	".eh_frame\0"
	".text";
+32 −8
Original line number Original line Diff line number Diff line
@@ -6,6 +6,16 @@
 * This script controls its layout.
 * This script controls its layout.
 */
 */


#if defined(BUILD_VDSO64)
# define SHDR_SIZE 64
#elif defined(BUILD_VDSO32) || defined(BUILD_VDSOX32)
# define SHDR_SIZE 40
#else
# error unknown VDSO target
#endif

#define NUM_FAKE_SHDRS 16

SECTIONS
SECTIONS
{
{
	. = SIZEOF_HEADERS;
	. = SIZEOF_HEADERS;
@@ -25,7 +35,21 @@ SECTIONS


	.dynamic	: { *(.dynamic) }		:text	:dynamic
	.dynamic	: { *(.dynamic) }		:text	:dynamic


	.rodata		: { *(.rodata*) }		:text
	.rodata		: {
		*(.rodata*)

		/*
		 * Ideally this would live in a C file, but that won't
		 * work cleanly for x32 until we start building the x32
		 * C code using an x32 toolchain.
		 */
		VDSO_FAKE_SECTION_TABLE_START = .;
		. = . + NUM_FAKE_SHDRS * SHDR_SIZE;
		VDSO_FAKE_SECTION_TABLE_END = .;
	}						:text

	.fake_shstrtab	: { *(.fake_shstrtab) }		:text

	.data		: {
	.data		: {
		*(.data*)
		*(.data*)
		*(.sdata*)
		*(.sdata*)
+2 −0
Original line number Original line Diff line number Diff line
@@ -6,6 +6,8 @@
 * the DSO.
 * the DSO.
 */
 */


#define BUILD_VDSO64

#include "vdso-layout.lds.S"
#include "vdso-layout.lds.S"


/*
/*
+22 −9
Original line number Original line Diff line number Diff line
@@ -23,6 +23,8 @@ enum {
	sym_vvar_page,
	sym_vvar_page,
	sym_hpet_page,
	sym_hpet_page,
	sym_end_mapping,
	sym_end_mapping,
	sym_VDSO_FAKE_SECTION_TABLE_START,
	sym_VDSO_FAKE_SECTION_TABLE_END,
};
};


const int special_pages[] = {
const int special_pages[] = {
@@ -30,15 +32,26 @@ const int special_pages[] = {
	sym_hpet_page,
	sym_hpet_page,
};
};


char const * const required_syms[] = {
struct vdso_sym {
	[sym_vvar_page] = "vvar_page",
	const char *name;
	[sym_hpet_page] = "hpet_page",
	bool export;
	[sym_end_mapping] = "end_mapping",
};
	"VDSO32_NOTE_MASK",

	"VDSO32_SYSENTER_RETURN",
struct vdso_sym required_syms[] = {
	"__kernel_vsyscall",
	[sym_vvar_page] = {"vvar_page", true},
	"__kernel_sigreturn",
	[sym_hpet_page] = {"hpet_page", true},
	"__kernel_rt_sigreturn",
	[sym_end_mapping] = {"end_mapping", true},
	[sym_VDSO_FAKE_SECTION_TABLE_START] = {
		"VDSO_FAKE_SECTION_TABLE_START", false
	},
	[sym_VDSO_FAKE_SECTION_TABLE_END] = {
		"VDSO_FAKE_SECTION_TABLE_END", false
	},
	{"VDSO32_NOTE_MASK", true},
	{"VDSO32_SYSENTER_RETURN", true},
	{"__kernel_vsyscall", true},
	{"__kernel_sigreturn", true},
	{"__kernel_rt_sigreturn", true},
};
};


__attribute__((format(printf, 1, 2))) __attribute__((noreturn))
__attribute__((format(printf, 1, 2))) __attribute__((noreturn))
Loading