原因はまだわかりませんが、コメント欄に収まりきらないアイデアがあります。
まず、C++ ではなく C としてコンパイルし、libpthread にリンクしないことで、テスト プログラムをより単純にすることができます。-lrt
を取得するのに十分なはずですclock_gettime
。また、それをコンパイルすると-static
、動的リンカのスタートアップがそこにないため、トレースが容易になる可能性があります。
静的リンクは、clock_gettime の動作を変更することさえあります。バグを回避できるかどうかを調べるだけでも価値があります。
もう 1 つ見たいのは、この vdso バイパス テスト プログラムの出力です。
#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;
}
の有無にかかわらず-static
、失敗した場合は追加しstrace
ます。
更新 (実際には、これをスキップします。2 番目の更新に進みます)
さらにいくつかの簡単なテストのアイデア:
-m32
gcc コマンドに追加して、Ubuntu ホスト システムで 32 ビット テスト プログラムをコンパイルして実行します。カーネルの 32 ビット互換モードがエラーの原因である可能性があります。その場合、32 ビット バージョンは、リンク先の libc に関係なく失敗します。
- Debian でコンパイルした非静的テスト プログラムを Ubuntu ホスト システムにコピーし、そこで実行してみてください。動作の変更は、原因として libc を指します。
次に、難しいものの時間です。逆アセンブルされたコードを見て、おそらく gdb でそれをシングルステップ実行します。あなた自身でそれを行う代わりに、あなたが実行しているコードのコピーを入手したいと思います. 静的にコンパイルされた失敗したテスト プログラムを、入手できる場所にアップロードしてください。また、カーネルが提供する 32 ビット vdso のコピーも興味深いかもしれません。vdso を抽出するには、次のプログラム (32 ビット chroot でコンパイル) を実行して、というファイルを作成し、vdso.dump
それもアップロードします。
#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;
}
更新 2
etch libc をダウンロードしてこれを再現しました。それは間違いなくglibcの愚かさが原因です。clock_gettime の単純な syscall ラッパーの代わりに、「事前承認されていないクロック ID は使用できません」というプリプロセッサ スパゲッティの大きな塊があります。古い glibc では動作しません。これは、私が聞きたくない質問に私たちを導きます: どうして古いバージョンの Debian を使おうとしているのですか?