glibc/sysdeps/unix/sysv/linux/powerpc/get_timebase_freq.c
Stan Shebs 335c1007bf powerpc: Fix static-linked version of __ppc_get_timebase_freq [BZ #24640]
__ppc_get_timebase_freq() always return 0 when using static linked
glibc.

This is a minimal example.c to reproduce:

    /******************************/
    #include <inttypes.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <sys/platform/ppc.h>

    int main() {
        uint64_t freq = __ppc_get_timebase_freq();
        printf("Time Base frequency = %"PRIu64" Hz\n", freq);
        if (freq == 0)
            return -1;
        return 0;
    }
    /******************************/

Compile command: gcc -static example.c

This bug has been reproduced, fixed and tested on all powerpc platforms
(ppc32, ppc64 and ppc64le).

The underlying code of __ppc_get_timebase_freq uses __get_timebase_freq
that has a different implementation for shared and static version of
glibc.  In the static version, there is an incorrect sense in the if
check for the fd returned when opening /proc/cpuinfo.

This solution is mostly a cherry-pick from:

  commit 4791e4f773d060c1a37b27aac5b03cdfa9327afc
  Author: Stan Shebs <stanshebs@google.com>
  Date:   Fri May 17 12:25:19 2019 -0700
  Subject: Fix sense of a test in the static-linking version of ppc get_clockfreq

That is in branch glibc/google/grte/v5-2.27/master and was mentioned for
inclusion on master here:

  https://www.sourceware.org/ml/libc-alpha/2019-05/msg00409.html

Adapted from original fix for get_clockfreq. That code was moved to
get_timebase_freq.

Also added a static-build testcase for __ppc_get_timebase_freq since the
underlying function has different implementations for shared and static
build.

	[BZ #24640]
	* sysdeps/unix/sysv/linux/powerpc/get_timebase_freq.c
	[!SHARED] (__get_timebase_freq): Fix sense of a test in the
	static-linking version.
	* sysdeps/unix/sysv/linux/powerpc/Makefile
	(tests-static): Add test-gettimebasefreq-static.
	(tests): Likewise.
	* sysdeps/unix/sysv/linux/powerpc/test-gettimebasefreq-static.c:
	New file.
2019-06-19 19:11:06 -03:00

107 lines
3.3 KiB
C

/* Get the frequency of the time base.
Copyright (C) 2012-2019 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#include <stdint.h>
#include <string.h>
#include <libc-internal.h>
#include <not-cancel.h>
#include <libc-vdso.h>
uint64_t
__get_timebase_freq (void)
{
hp_timing_t result = 0L;
#ifdef SHARED
/* The vDSO does not return an error (it clear cr0.so on returning). */
INTERNAL_SYSCALL_DECL (err);
result =
INTERNAL_VSYSCALL_NO_SYSCALL_FALLBACK (get_tbfreq, err, uint64_t, 0);
#else
/* We read the information from the /proc filesystem. /proc/cpuinfo
contains at least one line like:
timebase : 33333333
We search for this line and convert the number into an integer. */
int fd = __open_nocancel ("/proc/cpuinfo", O_RDONLY);
if (__glibc_unlikely (fd == -1))
return result;
/* The timebase will be in the 1st 1024 bytes for systems with up
to 8 processors. If the first read returns less then 1024
bytes read, we have the whole cpuinfo and can start the scan.
Otherwise we will have to read more to insure we have the
timebase value in the scan. */
char buf[1024];
ssize_t n;
n = __read_nocancel (fd, buf, sizeof (buf));
if (n == sizeof (buf))
{
/* We are here because the 1st read returned exactly sizeof
(buf) bytes. This implies that we are not at EOF and may
not have read the timebase value yet. So we need to read
more bytes until we know we have EOF. We copy the lower
half of buf to the upper half and read sizeof (buf)/2
bytes into the lower half of buf and repeat until we
reach EOF. We can assume that the timebase will be in
the last 512 bytes of cpuinfo, so two 512 byte half_bufs
will be sufficient to contain the timebase and will
handle the case where the timebase spans the half_buf
boundry. */
const ssize_t half_buf = sizeof (buf) / 2;
while (n >= half_buf)
{
memcpy (buf, buf + half_buf, half_buf);
n = __read_nocancel (fd, buf + half_buf, half_buf);
}
if (n >= 0)
n += half_buf;
}
__close_nocancel (fd);
if (__glibc_likely (n > 0))
{
char *mhz = memmem (buf, n, "timebase", 7);
if (__glibc_likely (mhz != NULL))
{
char *endp = buf + n;
/* Search for the beginning of the string. */
while (mhz < endp && (*mhz < '0' || *mhz > '9') && *mhz != '\n')
++mhz;
while (mhz < endp && *mhz != '\n')
{
if (*mhz >= '0' && *mhz <= '9')
{
result *= 10;
result += *mhz - '0';
}
++mhz;
}
}
}
#endif
return result;
}
weak_alias (__get_timebase_freq, __ppc_get_timebase_freq)