Question

I have setup a chrooted Debian Etch (32bit) under Ubuntu 12.04 (64bit), and it appears that clock_gettime() works with CLOCK_MONOTONIC, but fails with both CLOCK_PROCESS_CPUTIME_ID and CLOCK_THREAD_CPUTIME_ID. The errno is set to EINVAL, which according to the man page means that "The clk_id specified is not supported on this system."

All three clocks work fine outside the chrooted Debian and in 64bit chrooted Debian etch.

Can someone explains to me why this is the case and how to fix it?

Much appreciated.

Était-ce utile?

La solution

I don't know the cause yet, but I have ideas that won't fit in the comment box.

First, you can make the test program simpler by compiling it as C instead of C++ and not linking it to libpthread. -lrt should be good enough to get clock_gettime. Also, compiling it with -static could make tracing easier since the dynamic linker startup stuff won't be there.

Static linking might even change the behavior of clock_gettime. It's worth trying just to find out whether it works around the bug.

Another thing I'd like to see is the output of this vdso-bypassing test program:

#define _GNU_SOURCE
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/syscall.h>

int main(void)
{
    struct timespec ts;
    if(syscall(SYS_clock_gettime, CLOCK_PROCESS_CPUTIME_ID, &ts)) {
        perror("clock_gettime");
        return 1;
    }
    printf("CLOCK_PROCESS_CPUTIME_ID: %lu.%09ld\n",
           (unsigned long)ts.tv_sec, ts.tv_nsec);
    return 0;
}

with and without -static, and if it fails, add strace.

Update (actually, skip this. go to the second update)

A couple more simple test ideas:

  1. compile and run a 32-bit test program in the Ubuntu host system, by adding -m32 to the gcc command. It's possible that the kernel's 32-bit compatibility mode is causing the error. If that's the case, then the 32-bit version will fail no matter which libc it gets linked to.
  2. take the non-static test programs you compiled under Debian, copy them to the Ubuntu host system and try to run them there. Change in behavior will point to libc as the cause.

Then it's time for the hard stuff. Looking at disassembled code and maybe single-stepping it in gdb. Instead of having you do that on your own, I'd like to get a copy of the code you're running. Upload a a static-compiled failing test program somewhere I can get it. Also a copy of the 32-bit vdso provided by your kernel might be interesting. To extract the vdso, run the following program (compiled in the 32-bit chroot) which will create a file called vdso.dump, and upload that too.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int getvseg(const char *which, const char *outfn)
{
  FILE *maps, *outfile;
  char buf[1024];
  void *start, *end;
  size_t sz;
  void *copy;
  int ret;
  char search[strlen(which)+4];

  maps = fopen("/proc/self/maps", "r");
  if(!maps) {
    perror("/proc/self/maps");
    return 1;
  }
  outfile = fopen(outfn, "w");
  if(!outfile) {
    perror(outfn);
    fclose(maps);
    return 1;
  }

  sprintf(search, "[%s]\n", which);
  while(fgets(buf, sizeof buf, maps)) {
    if(strlen(buf)<strlen(search) ||
       strcmp(buf+strlen(buf)-strlen(search),search))
      continue;
    if(sscanf(buf, "%p-%p", &start, &end)!=2) {
      fprintf(stderr, "weird line in /proc/self/maps: %s", buf);
      continue;
    }
    sz = (char *)end - (char *)start;
    /* copy because I got an EFAULT trying to write directly from vsyscall */
    copy = malloc(sz);
    if(!copy) {
      perror("malloc");
      goto fail;
    }
    memcpy(copy, start, sz);
    if(fwrite(copy, 1, sz, outfile)!=sz) {
      if(ferror(outfile))
        perror(outfn);
      else
        fprintf(stderr, "%s: short write", outfn);
      free(copy);
      goto fail;
    }
    free(copy);
    goto success;
  }
  fprintf(stderr, "%s not found\n", which);

fail:
  ret = 1;
  goto out;
success:
  ret = 0;
out:
  fclose(maps);
  fclose(outfile);
  return ret;
}

int main(void)
{
  int ret = 1;
  if(!getvseg("vdso", "vdso.dump")) {
    printf("vdso dumped to vdso.dump\n");
    ret = 0;
  }
  if(!getvseg("vsyscall", "vsyscall.dump")) {
    printf("vsyscall dumped to vsyscall.dump\n");
    ret = 0;
  }
  return ret;
}

Update 2

I reproduced this by downloading an etch libc. It's definitely caused be glibc stupidity. Instead of a simple syscall wrapper for clock_gettime it has a big wad of preprocessor spaghetti culminating in "you can't use clockid's that we didn't pre-approve". You're not going to get it to work with that old glibc. Which brings us to the question I didn't want to ask: why are you trying to use an obsolete version of Debian anyway?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top