Skip to content

Commit

Permalink
sparc: Fix VDSO build with older binutils.
Browse files Browse the repository at this point in the history
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: 2f6c9bf ("sparc: Improve VDSO instruction patching.")
Reported-by: Meelis Roos <mroos@linux.ee>
Tested-by: Meelis Roos <mroos@linux.ee>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Oct 25, 2018
1 parent 44adbac commit caf539c
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 62 deletions.
2 changes: 0 additions & 2 deletions arch/sparc/include/asm/vdso.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ struct vdso_image {
void *data;
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 */
};

Expand Down
149 changes: 129 additions & 20 deletions arch/sparc/vdso/vclock_gettime.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,33 +90,34 @@ notrace static __always_inline u64 vread_tick(void)
{
u64 ret;

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

notrace static __always_inline u64 vread_tick_stick(void)
{
u64 ret;

__asm__ __volatile__("rd %%asr24, %0" : "=r" (ret));
return ret;
}
#else
notrace static __always_inline u64 vread_tick(void)
{
register unsigned long long ret asm("o4");

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

notrace static __always_inline u64 vread_tick_stick(void)
{
register unsigned long long ret asm("o4");

__asm__ __volatile__("rd %%asr24, %L0\n\t"
"srlx %L0, 32, %H0"
: "=r" (ret));
return ret;
}
Expand All @@ -132,6 +133,16 @@ notrace static __always_inline u64 vgetsns(struct vvar_data *vvar)
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,
struct timespec *ts)
{
Expand All @@ -152,6 +163,26 @@ notrace static __always_inline int do_realtime(struct vvar_data *vvar,
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,
struct timespec *ts)
{
Expand All @@ -172,6 +203,26 @@ notrace static __always_inline int do_monotonic(struct vvar_data *vvar,
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,
struct timespec *ts)
{
Expand Down Expand Up @@ -227,6 +278,31 @@ int
clock_gettime(clockid_t, struct timespec *)
__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
__vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
{
Expand Down Expand Up @@ -262,3 +338,36 @@ __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
int
gettimeofday(struct timeval *, struct timezone *)
__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);
}
3 changes: 0 additions & 3 deletions arch/sparc/vdso/vdso-layout.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ SECTIONS

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

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

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

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

/* 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++) {
ELF(Shdr) *sh = raw_addr + GET_BE(&hdr->e_shoff) +
GET_BE(&hdr->e_shentsize) * i;
if (GET_BE(&sh->sh_type) == SHT_SYMTAB)
symtab_hdr = sh;

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

if (!symtab_hdr)
Expand Down Expand Up @@ -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, "\t.data = raw_data,\n");
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++) {
if (required_syms[i].export && syms[i])
fprintf(outfile, "\t.sym_%s = %" PRIi64 ",\n",
Expand Down
2 changes: 2 additions & 0 deletions arch/sparc/vdso/vdso32/vdso32.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ VERSION {
global:
clock_gettime;
__vdso_clock_gettime;
__vdso_clock_gettime_stick;
gettimeofday;
__vdso_gettimeofday;
__vdso_gettimeofday_stick;
local: *;
};
}
Loading

0 comments on commit caf539c

Please sign in to comment.