Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit caf539cd authored by David S. Miller's avatar David S. Miller
Browse files

sparc: Fix VDSO build with older binutils.



Older versions of bintutils do not allow symbol math across different
segments on sparc:

====================
Assembler messages:
99: Error: operation combines symbols in different segments
====================

This is controlled by whether or not DIFF_EXPR_OK is defined in
gas/config/tc-*.h and for sparc this was not the case until mid-2017.

So we have to patch between %stick and %tick another way.

Do what powerpc does and emit two versions of the relevant functions,
one using %tick and one using %stick, and patch the symbols in the
dynamic symbol table.

Fixes: 2f6c9bf3 ("sparc: Improve VDSO instruction patching.")
Reported-by: default avatarMeelis Roos <mroos@linux.ee>
Tested-by: default avatarMeelis Roos <mroos@linux.ee>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 44adbac8
Loading
Loading
Loading
Loading
+0 −2
Original line number Original line Diff line number Diff line
@@ -9,8 +9,6 @@ struct vdso_image {
	void *data;
	void *data;
	unsigned long size;   /* Always a multiple of PAGE_SIZE */
	unsigned long size;   /* Always a multiple of PAGE_SIZE */


	unsigned long tick_patch, tick_patch_len;

	long sym_vvar_start;  /* Negative offset to the vvar area */
	long sym_vvar_start;  /* Negative offset to the vvar area */
};
};


+129 −20
Original line number Original line Diff line number Diff line
@@ -90,16 +90,15 @@ notrace static __always_inline u64 vread_tick(void)
{
{
	u64	ret;
	u64	ret;


	__asm__ __volatile__("1:\n\t"
	__asm__ __volatile__("rd %%tick, %0" : "=r" (ret));
			     "rd		%%tick, %0\n\t"
	return ret;
			     ".pushsection	.tick_patch, \"a\"\n\t"
}
			     ".word		1b - ., 1f - .\n\t"

			     ".popsection\n\t"
notrace static __always_inline u64 vread_tick_stick(void)
			     ".pushsection	.tick_patch_replacement, \"ax\"\n\t"
{
			     "1:\n\t"
	u64	ret;
			     "rd		%%asr24, %0\n\t"

			     ".popsection\n"
	__asm__ __volatile__("rd %%asr24, %0" : "=r" (ret));
			     : "=r" (ret));
	return ret;
	return ret;
}
}
#else
#else
@@ -107,16 +106,18 @@ notrace static __always_inline u64 vread_tick(void)
{
{
	register unsigned long long ret asm("o4");
	register unsigned long long ret asm("o4");


	__asm__ __volatile__("1:\n\t"
	__asm__ __volatile__("rd %%tick, %L0\n\t"
			     "rd		%%tick, %L0\n\t"
			     "srlx %L0, 32, %H0"
			     "srlx		%L0, 32, %H0\n\t"
			     : "=r" (ret));
			     ".pushsection	.tick_patch, \"a\"\n\t"
	return ret;
			     ".word		1b - ., 1f - .\n\t"
}
			     ".popsection\n\t"

			     ".pushsection	.tick_patch_replacement, \"ax\"\n\t"
notrace static __always_inline u64 vread_tick_stick(void)
			     "1:\n\t"
{
			     "rd		%%asr24, %L0\n\t"
	register unsigned long long ret asm("o4");
			     ".popsection\n"

	__asm__ __volatile__("rd %%asr24, %L0\n\t"
			     "srlx %L0, 32, %H0"
			     : "=r" (ret));
			     : "=r" (ret));
	return ret;
	return ret;
}
}
@@ -132,6 +133,16 @@ notrace static __always_inline u64 vgetsns(struct vvar_data *vvar)
	return v * vvar->clock.mult;
	return v * vvar->clock.mult;
}
}


notrace static __always_inline u64 vgetsns_stick(struct vvar_data *vvar)
{
	u64 v;
	u64 cycles;

	cycles = vread_tick_stick();
	v = (cycles - vvar->clock.cycle_last) & vvar->clock.mask;
	return v * vvar->clock.mult;
}

notrace static __always_inline int do_realtime(struct vvar_data *vvar,
notrace static __always_inline int do_realtime(struct vvar_data *vvar,
					       struct timespec *ts)
					       struct timespec *ts)
{
{
@@ -152,6 +163,26 @@ notrace static __always_inline int do_realtime(struct vvar_data *vvar,
	return 0;
	return 0;
}
}


notrace static __always_inline int do_realtime_stick(struct vvar_data *vvar,
						     struct timespec *ts)
{
	unsigned long seq;
	u64 ns;

	do {
		seq = vvar_read_begin(vvar);
		ts->tv_sec = vvar->wall_time_sec;
		ns = vvar->wall_time_snsec;
		ns += vgetsns_stick(vvar);
		ns >>= vvar->clock.shift;
	} while (unlikely(vvar_read_retry(vvar, seq)));

	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
	ts->tv_nsec = ns;

	return 0;
}

notrace static __always_inline int do_monotonic(struct vvar_data *vvar,
notrace static __always_inline int do_monotonic(struct vvar_data *vvar,
						struct timespec *ts)
						struct timespec *ts)
{
{
@@ -172,6 +203,26 @@ notrace static __always_inline int do_monotonic(struct vvar_data *vvar,
	return 0;
	return 0;
}
}


notrace static __always_inline int do_monotonic_stick(struct vvar_data *vvar,
						      struct timespec *ts)
{
	unsigned long seq;
	u64 ns;

	do {
		seq = vvar_read_begin(vvar);
		ts->tv_sec = vvar->monotonic_time_sec;
		ns = vvar->monotonic_time_snsec;
		ns += vgetsns_stick(vvar);
		ns >>= vvar->clock.shift;
	} while (unlikely(vvar_read_retry(vvar, seq)));

	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
	ts->tv_nsec = ns;

	return 0;
}

notrace static int do_realtime_coarse(struct vvar_data *vvar,
notrace static int do_realtime_coarse(struct vvar_data *vvar,
				      struct timespec *ts)
				      struct timespec *ts)
{
{
@@ -227,6 +278,31 @@ int
clock_gettime(clockid_t, struct timespec *)
clock_gettime(clockid_t, struct timespec *)
	__attribute__((weak, alias("__vdso_clock_gettime")));
	__attribute__((weak, alias("__vdso_clock_gettime")));


notrace int
__vdso_clock_gettime_stick(clockid_t clock, struct timespec *ts)
{
	struct vvar_data *vvd = get_vvar_data();

	switch (clock) {
	case CLOCK_REALTIME:
		if (unlikely(vvd->vclock_mode == VCLOCK_NONE))
			break;
		return do_realtime_stick(vvd, ts);
	case CLOCK_MONOTONIC:
		if (unlikely(vvd->vclock_mode == VCLOCK_NONE))
			break;
		return do_monotonic_stick(vvd, ts);
	case CLOCK_REALTIME_COARSE:
		return do_realtime_coarse(vvd, ts);
	case CLOCK_MONOTONIC_COARSE:
		return do_monotonic_coarse(vvd, ts);
	}
	/*
	 * Unknown clock ID ? Fall back to the syscall.
	 */
	return vdso_fallback_gettime(clock, ts);
}

notrace int
notrace int
__vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
__vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
{
{
@@ -262,3 +338,36 @@ __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
int
int
gettimeofday(struct timeval *, struct timezone *)
gettimeofday(struct timeval *, struct timezone *)
	__attribute__((weak, alias("__vdso_gettimeofday")));
	__attribute__((weak, alias("__vdso_gettimeofday")));

notrace int
__vdso_gettimeofday_stick(struct timeval *tv, struct timezone *tz)
{
	struct vvar_data *vvd = get_vvar_data();

	if (likely(vvd->vclock_mode != VCLOCK_NONE)) {
		if (likely(tv != NULL)) {
			union tstv_t {
				struct timespec ts;
				struct timeval tv;
			} *tstv = (union tstv_t *) tv;
			do_realtime_stick(vvd, &tstv->ts);
			/*
			 * Assign before dividing to ensure that the division is
			 * done in the type of tv_usec, not tv_nsec.
			 *
			 * There cannot be > 1 billion usec in a second:
			 * do_realtime() has already distributed such overflow
			 * into tv_sec.  So we can assign it to an int safely.
			 */
			tstv->tv.tv_usec = tstv->ts.tv_nsec;
			tstv->tv.tv_usec /= 1000;
		}
		if (unlikely(tz != NULL)) {
			/* Avoid memcpy. Some old compilers fail to inline it */
			tz->tz_minuteswest = vvd->tz_minuteswest;
			tz->tz_dsttime = vvd->tz_dsttime;
		}
		return 0;
	}
	return vdso_fallback_gettimeofday(tv, tz);
}
+0 −3
Original line number Original line Diff line number Diff line
@@ -73,9 +73,6 @@ SECTIONS


	.text		: { *(.text*) }			:text	=0x90909090,
	.text		: { *(.text*) }			:text	=0x90909090,


	.tick_patch 	  : { *(.tick_patch) }		:text
	.tick_patch_insns : { *(.tick_patch_insns) }	:text

	/DISCARD/ : {
	/DISCARD/ : {
		*(.discard)
		*(.discard)
		*(.discard.*)
		*(.discard.*)
+2 −0
Original line number Original line Diff line number Diff line
@@ -18,8 +18,10 @@ VERSION {
	global:
	global:
		clock_gettime;
		clock_gettime;
		__vdso_clock_gettime;
		__vdso_clock_gettime;
		__vdso_clock_gettime_stick;
		gettimeofday;
		gettimeofday;
		__vdso_gettimeofday;
		__vdso_gettimeofday;
		__vdso_gettimeofday_stick;
	local: *;
	local: *;
	};
	};
}
}
+1 −16
Original line number Original line Diff line number Diff line
@@ -17,11 +17,9 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
	unsigned long mapping_size;
	unsigned long mapping_size;
	int i;
	int i;
	unsigned long j;
	unsigned long j;
	ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr, *secstrings_hdr,
	ELF(Shdr) *symtab_hdr = NULL, *strtab_hdr;
		*patch_sec = NULL;
	ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
	ELF(Ehdr) *hdr = (ELF(Ehdr) *)raw_addr;
	ELF(Dyn) *dyn = 0, *dyn_end = 0;
	ELF(Dyn) *dyn = 0, *dyn_end = 0;
	const char *secstrings;
	INT_BITS syms[NSYMS] = {};
	INT_BITS syms[NSYMS] = {};


	ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_BE(&hdr->e_phoff));
	ELF(Phdr) *pt = (ELF(Phdr) *)(raw_addr + GET_BE(&hdr->e_phoff));
@@ -64,18 +62,11 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
	}
	}


	/* Walk the section table */
	/* Walk the section table */
	secstrings_hdr = raw_addr + GET_BE(&hdr->e_shoff) +
		GET_BE(&hdr->e_shentsize)*GET_BE(&hdr->e_shstrndx);
	secstrings = raw_addr + GET_BE(&secstrings_hdr->sh_offset);
	for (i = 0; i < GET_BE(&hdr->e_shnum); i++) {
	for (i = 0; i < GET_BE(&hdr->e_shnum); i++) {
		ELF(Shdr) *sh = raw_addr + GET_BE(&hdr->e_shoff) +
		ELF(Shdr) *sh = raw_addr + GET_BE(&hdr->e_shoff) +
			GET_BE(&hdr->e_shentsize) * i;
			GET_BE(&hdr->e_shentsize) * i;
		if (GET_BE(&sh->sh_type) == SHT_SYMTAB)
		if (GET_BE(&sh->sh_type) == SHT_SYMTAB)
			symtab_hdr = sh;
			symtab_hdr = sh;

		if (!strcmp(secstrings + GET_BE(&sh->sh_name),
			    ".tick_patch"))
			patch_sec = sh;
	}
	}


	if (!symtab_hdr)
	if (!symtab_hdr)
@@ -142,12 +133,6 @@ static void BITSFUNC(go)(void *raw_addr, size_t raw_len,
	fprintf(outfile, "const struct vdso_image %s_builtin = {\n", name);
	fprintf(outfile, "const struct vdso_image %s_builtin = {\n", name);
	fprintf(outfile, "\t.data = raw_data,\n");
	fprintf(outfile, "\t.data = raw_data,\n");
	fprintf(outfile, "\t.size = %lu,\n", mapping_size);
	fprintf(outfile, "\t.size = %lu,\n", mapping_size);
	if (patch_sec) {
		fprintf(outfile, "\t.tick_patch = %lu,\n",
			(unsigned long)GET_BE(&patch_sec->sh_offset));
		fprintf(outfile, "\t.tick_patch_len = %lu,\n",
			(unsigned long)GET_BE(&patch_sec->sh_size));
	}
	for (i = 0; i < NSYMS; i++) {
	for (i = 0; i < NSYMS; i++) {
		if (required_syms[i].export && syms[i])
		if (required_syms[i].export && syms[i])
			fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
			fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
Loading