Hi,
When working on porting gccgo to gcc-7, I found out that many test failures are
due to a bug in Hurd's implementation of /proc/self/exe (and /proc/<pid>/exe):
The path returned should always be absolute, not relative.
Adding print statements to libbacktrace/posix.c shows the problem:
GNU/Linux:
./build/gotools/go
linkname = /home/srs/Hurd/DEBs/linux_DEBs/gcc-7/gcc-7-7.2.0-4/build/gotools/go
<help text displayed>
$PWD/build/gotools/go
<same as above>
GNU/Hurd:
./build/gotools/go
linkname = ./build/gotools/go
fatal error: libbacktrace could not find executable to open
$PWD/build/gotools/go
linkname = /home/srs/DEBs/gcc-7/gcc-7-7.2.0-3.1/build/gotools/go
<help text displayed>
The following attached programs verifies this issue.
Both Linux programs are tested on amd64 and i386.
1) test_readlink.c:
GNU/Linux:
./test_readlink /proc/self/exe
bufsize = 4096
'/proc/self/exe' points to '/home/srs/Hurd/DEBs/test_cases/test_readlink'
$PWD/test_readlink /proc/self/exe
<same as above>
Here /proc reports a zero st_size from the lstat call. Obviously Hurd does not.
Perhaps not a big deal?
GNU/Hurd:
./test_readlink /proc/self/exe
bufsize = 16
'/proc/self/exe' points to './test_readlink'
$PWD/test_readlink /proc/self/exe
bufsize = 40
'/proc/self/exe' points to '/home/srs/DEBs/test_cases/test_readlink'
2) test_sighandler.c:
GNU/Linux:
./test_sighandler
Got signal 11, faulty address is 0xdeadbeef, from 5597bd471de0
Executable name = '/home/srs/Hurd/DEBs/test_cases/test_sighandler�', len = 47
[bt] Execution path:
[bt] ./test_sighandler(func_b+0x11) [0x5597bd471de0]
[bt] ./test_sighandler(func_b+0x11) [0x5597bd471de0]
[bt] ./test_sighandler(main+0x6a) [0x5597bd471e54]
[bt] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1) [0x7f1cdc8d12b1]
[bt] ./test_sighandler(_start+0x2a) [0x5597bd471ada]
GNU/Hurd:
./test_sighandler
Got signal 4
Executable name = './test_sighandler', len = 21
[bt] Execution path:
[bt] ./test_sighandler(func_b+0x10) [0x8048986]
[bt] ./test_sighandler(main+0x6f) [0x80489ff]
[bt] /lib/i386-gnu/libc.so.0.3(__libc_start_main+0xaa) [0x10b6eea]
This is another bug in the backtrace. hex2dec('deadbeef')/1024^3 = 3.4794 GiB
Even if the address is outside the gnumach range, the program should not fail
with a SIGILL. Or?
Changing 0xdeadbeef to 0xbeadbeef gives:
./test_sighandler
Got signal 11, faulty address is 0xbeadbeef, from 8048986
Executable name = './test_sighandler', len = 21
[bt] Execution path:
[bt] ./test_sighandler(func_b+0x10) [0x8048986]
[bt] ./test_sighandler(main+0x5c) [0x80489ec]
[bt] /lib/i386-gnu/libc.so.0.3(__libc_start_main+0xaa) [0x10b6eea]
/*
gcc -g -Wall -o test_sighandler -rdynamic test_sighandler.c
GNU/Linux:
./test_sighandler
Got signal 11, faulty address is 0xdeadbeef, from 5597bd471de0
Executable name = '/home/srs/Hurd/DEBs/test_cases/test_sighandler�', len = 47
GNU/Hurd:
./test_sighandler
Got signal 4
Executable name = './test_sighandler', len = 21
Changing 0xdeadbeef to 0xbeadbeef gives:
./test_sighandler
Got signal 11, faulty address is 0xbeadbeef, from 8048986
Executable name = './test_sighandler', len = 21
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h>
/* get REG_EIP/REG_RIP from ucontext.h */
#include <ucontext.h>
const char* getExecutableName()
{
char link[1024];
char exe[1024];
//snprintf (link, sizeof link, "/proc/%d/exe", getpid());
snprintf (link, sizeof link, "/proc/self/exe");
if(readlink (link, exe, sizeof link)==-1) {
fprintf(stderr,"ERRORRRRR\n");
exit(1);
}
int len = strlen(exe);
exe[len] = '\0';
printf("Executable name = '%s', len = %d\n", exe, len);
return 0;
}
void bt_sighandler(int sig, siginfo_t *info,
void *secret) {
void *trace[16];
char **messages = (char **)NULL;
int i, trace_size = 0;
ucontext_t *uc = (ucontext_t *)secret;
/* Do something useful with siginfo_t */
if (sig == SIGSEGV)
#ifdef __x86_64__
printf("Got signal %d, faulty address is %p, "
"from %llx\n", sig, info->si_addr,
uc->uc_mcontext.gregs[REG_RIP]);
#else
printf("Got signal %d, faulty address is %p, "
"from %x\n", sig, info->si_addr,
uc->uc_mcontext.gregs[REG_EIP]);
#endif
else
printf("Got signal %d\n", sig);
trace_size = backtrace(trace, 16);
/* overwrite sigaction with caller's address */
#ifdef __x86_64__
trace[1] = (void *) uc->uc_mcontext.gregs[REG_RIP];
#else
trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];
#endif
messages = backtrace_symbols(trace, trace_size);
/* skip first stack frame (points here) */
getExecutableName();
printf("[bt] Execution path:\n");
for (i=1; i<trace_size; ++i)
printf("[bt] %s\n", messages[i]);
exit(0);
}
int func_b(void)
{
char *p = (char *)0xdeadbeef;
//char *p = (char *)0xbeadbeef;
//char *p = (char *)0x0;
*p = 10; /* CRASH here!! */
return 0;
}
int main() {
/* Install our signal handler */
struct sigaction sa;
sa.sa_sigaction = (void *)bt_sighandler;
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
/* ... add any other signal here */
/* Do something */
printf("%d\n", func_b());
}
/*
From readlink(2)
gcc -g -Wall -o test_readlink test_readlink.c
GNU/Linux:
./test_readlink /proc/self/exe
bufsize = 4096
'/proc/self/exe' points to '/home/srs/Hurd/DEBs/test_cases/test_readlink'
GNU/Hurd:
./test_readlink /proc/self/exe
bufsize = 16
'/proc/self/exe' points to './test_readlink'
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
int
main(int argc, char *argv[])
{
struct stat sb;
char *linkname;
ssize_t r, bufsiz;
if (argc != 2) {
fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
exit(EXIT_FAILURE);
}
if (lstat(argv[1], &sb) == -1) {
perror("lstat");
exit(EXIT_FAILURE);
}
bufsiz = sb.st_size + 1;
/* Some magic symlinks under (for example) /proc and /sys
report 'st_size' as zero. In that case, take PATH_MAX as
a "good enough" estimate */
if (sb.st_size == 0)
bufsiz = PATH_MAX;
printf("bufsize = %zd\n", bufsiz);
linkname = malloc(bufsiz);
if (linkname == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
r = readlink(argv[1], linkname, bufsiz);
if (r == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}
linkname[r] = '\0';
printf("'%s' points to '%s'\n", argv[1], linkname);
if (r == bufsiz)
printf("(Returned buffer may have been truncated)\n");
free(linkname);
exit(EXIT_SUCCESS);
}