I plan to create a logging/tracing mechanism, which writes the address (const char*
) of string literals to a ring-buffer. These strings are in the read-only data-segment and are created by the preprocessor with __function__
or __file__
.
The Question: Is it possible, to analyze this ring-buffer content after a Segfault, if all pointers are valid? With "valid" I mean that they point to a mapped memory area开发者_如何学编程 and dereferencing won't cause a segmentation fault.
I'm working with Linux 2.6.3x and GCC 4.4.x.
Best regards,
Charly
I think the approach you are looking for is to handle a SIGSEGV
signal via sigaction
.
void handler(int, siginfo_t *info, ucontext_t *uap)
{
/* Peek at parameters here... I'm not sure exactly what you want to do. */
}
/* Set up the signal handler... */
struct sigaction sa, old_sa;
memset(&sa, 0 sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &sa, &old_sa))
{
/* TODO: handle error */
}
Note however that catching SIGSEGV
on your own process is kind of weird. The process is likely in a bad state that can't be recovered from. The actions you'll be able to do in response to it may be limited, and it's most likely the process being killed is a good thing.
If you want it to be a bit more stable, there is the sigaltstack
call which lets you specify an alternate stack buffer, so that if you've completely hosed your stack you can still handle SIGSEGV
. To use this you need to set SA_ONSTACK
in sa.sa_flags
above.
If you want to respond to SEGV
from the safety of another process (thereby isolating yourself from the poorly behaving segfaulting code and making it so that you won't crash while inspecting it), you can use ptrace
. This interface is complex, has many non-portable parts, and is mainly used to write debuggers. But you can do great things with it, like read and write the process's memory and registers, and alter its execution.
The usual way to check if dereferencing a memory region will cause a segfault is to use read()
or write()
. Eg to check if the first 128 bytes pointed to by ptr
are safely readable:
int fd[2];
if (pipe(fd) >= 0) {
if (write(fd[1], ptr, 128) > 0)
/* OK */
else
/* not OK */
close(fd[0]);
close(fd[1]);
}
(write()
will return EFAULT
rather than raising a signal if the region isn't readable).
If you want to test more than PIPE_BUF
bytes at a time, you'll need to read and discard from the reading side of the pipe.
Of course if the stack or other memory that you rely upon has been corrupted then there could be problems, but that is true for any code.
Assuming that that there is no problem with the stack or other memory that you rely upon, and assuming that you do not call any functions like malloc()
that are not async-signal safe, and assuming that you do not attempt to return from your signal handler, then there should be no problem reading or writing your buffer from within your signal handler.
If you are trying to test whether a particular address is valid, you could use a system call such as mincore()
and check for an error result.
Once you've received a segfault, all bets are off. The pointers may be valid or they may have been corrupted. You just don't know. You may be able to compare them with valid values or the pointer to the ring buffer itself may have been corrupted. In which case, you'll probably get garbage.
精彩评论