mirror of
https://github.com/RGBCube/serenity
synced 2025-05-20 11:55:08 +00:00
Kernel: Add 'ptrace' syscall
This commit adds a basic implementation of the ptrace syscall, which allows one process (the tracer) to control another process (the tracee). While a process is being traced, it is stopped whenever a signal is received (other than SIGCONT). The tracer can start tracing another thread with PT_ATTACH, which causes the tracee to stop. From there, the tracer can use PT_CONTINUE to continue the execution of the tracee, or use other request codes (which haven't been implemented yet) to modify the state of the tracee. Additional request codes are PT_SYSCALL, which causes the tracee to continue exection but stop at the next entry or exit from a syscall, and PT_GETREGS which fethces the last saved register set of the tracee (can be used to inspect syscall arguments and return value). A special request code is PT_TRACE_ME, which is issued by the tracee and causes it to stop when it calls execve and wait for the tracer to attach.
This commit is contained in:
parent
c9396be83f
commit
6b74d38aab
13 changed files with 300 additions and 102 deletions
|
@ -33,6 +33,7 @@
|
|||
#include <Kernel/Profiling.h>
|
||||
#include <Kernel/Scheduler.h>
|
||||
#include <Kernel/Thread.h>
|
||||
#include <Kernel/ThreadTracer.h>
|
||||
#include <Kernel/VM/MemoryManager.h>
|
||||
#include <Kernel/VM/PageDirectory.h>
|
||||
#include <Kernel/VM/ProcessPagingScope.h>
|
||||
|
@ -508,6 +509,29 @@ ShouldUnblockThread Thread::dispatch_signal(u8 signal)
|
|||
ASSERT(m_stop_state != State::Invalid);
|
||||
set_state(m_stop_state);
|
||||
m_stop_state = State::Invalid;
|
||||
// make sure SemiPermanentBlocker is unblocked
|
||||
if (m_state != Thread::Runnable && m_state != Thread::Running
|
||||
&& m_blocker && m_blocker->is_reason_signal())
|
||||
unblock();
|
||||
}
|
||||
|
||||
else {
|
||||
auto* thread_tracer = tracer();
|
||||
if (thread_tracer != nullptr) {
|
||||
// when a thread is traced, it should be stopped whenever it receives a signal
|
||||
// the tracer is notified of this by using waitpid()
|
||||
// only "pending signals" from the tracer are sent to the tracee
|
||||
if (!thread_tracer->has_pending_signal(signal)) {
|
||||
m_stop_signal = signal;
|
||||
// make sure SemiPermanentBlocker is unblocked
|
||||
if (m_blocker && m_blocker->is_reason_signal())
|
||||
unblock();
|
||||
m_stop_state = m_state;
|
||||
set_state(Stopped);
|
||||
return ShouldUnblockThread::No;
|
||||
}
|
||||
thread_tracer->unset_signal(signal);
|
||||
}
|
||||
}
|
||||
|
||||
auto handler_vaddr = action.handler_or_sigaction;
|
||||
|
@ -900,4 +924,21 @@ void Thread::reset_fpu_state()
|
|||
memcpy(m_fpu_state, &s_clean_fpu_state, sizeof(FPUState));
|
||||
}
|
||||
|
||||
void Thread::start_tracing_from(pid_t tracer)
|
||||
{
|
||||
m_tracer = ThreadTracer::create(tracer);
|
||||
}
|
||||
|
||||
void Thread::stop_tracing()
|
||||
{
|
||||
m_tracer = nullptr;
|
||||
}
|
||||
|
||||
void Thread::tracer_trap(const RegisterState& regs)
|
||||
{
|
||||
ASSERT(m_tracer.ptr());
|
||||
m_tracer->set_regs(regs);
|
||||
send_urgent_signal_to_self(SIGTRAP);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue