Как получить правильное значение orig_eax при остановке в системном вызове?

Я присоединяюсь к процессу с помощью ptrace(PTRACE_ATTACH...), пока он находится в системном вызове (например, nanosleep()). Я могу использовать PTRACE_GETREGS для получения содержимого регистра, и eip находится в ожидаемом месте (в __kernel_vsyscall). Однако регистры eax и orig_eax имеют неожиданное содержимое: eax обычно содержит -516, а orig_eax обычно 0.

Это тестовая программа, которую я использовал (взят из http://www.linuxjournal.com/article/6210 и немного изменен):

    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/ptrace.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <unistd.h>
    #include <sys/user.h>

    int main(int argc, char *argv[])
    {
        pid_t traced_process;
        struct user_regs_struct regs;
        long ins;
        if(argc != 2) {
            printf("Usage: %s <pid to be traced>\n",
                   argv[0]);
            exit(1);
        }
        traced_process = atoi(argv[1]);
        ptrace(PTRACE_ATTACH, traced_process,
               NULL, NULL);
        wait(NULL);
        ptrace(PTRACE_GETREGS, traced_process,
               NULL, &regs);
        printf("eax: %lx (%d); orig_eax: %lx\n",
               regs.eax, (int)regs.eax, regs.orig_eax);
        ins = ptrace(PTRACE_PEEKTEXT, traced_process,
                     regs.eip, NULL);
        printf("EIP: %lx Instruction executed: %lx\n",
               regs.eip, ins);
        ptrace(PTRACE_DETACH, traced_process,
               NULL, NULL);
        return 0;
    }

Вывод при подключении к команде "sleep 10000", запущенной в другом терминале:

    eax: fffffdfc (-516); orig_eax: 0
    EIP: b7711424 Instruction executed: c3595a5d

Что означает значение в eax? Почему orig_eax не содержит исходный номер системного вызова (например, 162)? И как мне получить номер системного вызова в этом случае?

Кроме того, почему GDB правильно показывает "162" для "print $ orig_eax"?

Btw. это на Ubuntu 12.04 с ядром 3.2.0:

  • uname -a: "Linux edgebox 3.2.0-24-generic-pae # 37-Ubuntu SMP Ср 25 апреля 10:47:59 UTC 2012 i686 athlon i386 GNU / Linux"
  • / proc / cpuinfo: "Двухъядерный процессор AMD Athlon(tm) II Neo K345"
  • файл which sleep: "/bin/sleep: исполняемый 32-разрядный LSB ELF, Intel 80386, версия 1 (SYSV), динамически связанный (использует общие библиотеки), для GNU/Linux 2.6.24, BuildID[sha1]=0x0965431bde4d183eaa2fa3e3989098ce46b92129, раздетый".

Так что это 32-битное ядро ​​PAE и 32-битная установка Ubuntu на 64-битном процессоре.

3 ответа

Решение

Это на Ubuntu 12.04, с ядром 3.2.0

Это не однозначно идентифицирует вашу систему. Какой процессор?

Мой хрустальный шар говорит мне, что sleep вы вызываете 64-битную программу, а ваша программа трассировки - нет. Или наоборот.

Когда вы подключились к процессу, в процесс был отправлен сигнал, который прервал все выполняющиеся системные вызовы. Большинство системных вызовов просто возвращают -EINTR, если это происходит, и ожидают, что код пользовательского пространства перезапустит их при желании. Существуют и другие системные вызовы, которые могут автоматически перезапускаться, но это большая тема.

В случае некоторых системных вызовов, одним из которых является nanosleep(), процесс уже спал в течение части указанного времени, поэтому вместо перезапуска с самого начала вы хотите спать только оставшееся время. Для достижения этого каждый поток имеет "блок перезапуска" в пространстве ядра. Когда системный вызов прерывается, он заполняет блок перезапуска и возвращает -516 (ERESTART_RESTARTBLOCK). Ядро обрабатывает этот код возврата специально: оно перематывает ПК до системного вызова и изменяет номер системного вызова на "restart_syscall" (0 в вашей архитектуре). Когда процесс перезапускается, он, очевидно, вызовет restart_syscall, который использует блок restart, чтобы выяснить, что делать.

Предполагается, что этот перезапуск обычно невидим для пользователя, но ptrace - один из способов его обнаружения. Однако я не знаю, что GDB удается получить правильное значение для orig_eax.

Когда спящий процесс останавливается ptrace(PTRACE_ATTACH), процесс трассировки может получить информацию о регистрах (user_regs_struct). Системный вызов номер 162, который является sys_nanosleep, eax=-516 указывает возвращаемое значение этого прерванного системного вызова.

Другие вопросы по тегам