Как написать обработчик ошибок сегментации, чтобы ошибочная инструкция не перезапускалась?(C и Linux) - PullRequest
2 голосов
/ 08 августа 2011

Я написал обработчик ошибок сегментации, но проблема в том, что команда, в которой происходит ошибка, перезапускается после перехода к обработчику, и это заставляет обработчик переходить в бесконечный цикл.

Я хочу, чтобы обработчик работал так, чтобы после достижения обработчика инструкция, следующая за ошибочной инструкцией, выполнялась так, чтобы она не переходила в бесконечный цикл. Может кто-нибудь помочь мне с фрагментом кода?

Я использую C и Linux.

Ответы [ 3 ]

6 голосов
/ 08 августа 2011

Предупреждение: я не рекомендую делать это.Послушайте комментарии, в которых вам предлагается найти другой способ решения вашей проблемы

Я также хотел бы еще раз повторить предупреждение Хеннинга Махолмса о том, что оно будет чрезвычайно специфичным для архитектуры и непереносимым.Это будет адское обслуживание, и вам придется вручную обрабатывать множество различных инструкций, если только это не одна конкретная последовательность команд, которую вы ищете (как в приведенном ниже примере).

С учетом сказанного, если вы все еще хотите это сделать, это можно сделать следующим образом:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>

#define __USE_GNU
#include <signal.h>

void action(int sig, siginfo_t* siginfo, void* context)
{
    sig=sig; siginfo=siginfo;// ignore warning

    // get execution context
    mcontext_t* mcontext = &((ucontext_t*)context)->uc_mcontext;

    // find out what instruction faulted
#if defined(__x86_64)
    uint8_t* code = (uint8_t*)mcontext->gregs[REG_RIP];
    if (code[0] == 0x88 && code[1] == 0x10) { // mov %dl,(%rax)
        mcontext->gregs[REG_RIP] += 2; // skip it!
        return;
    }
#elif defined(__i386)
    uint8_t* code = (uint8_t*)mcontext->gregs[REG_EIP];
    if (code[0] == 0x88 && code[1] == 0x10) { // mov %dl,(%eax)
        mcontext->gregs[REG_EIP] += 2; // skip it!
        return;
    }
#else
#error "Unsupported system"
#endif
    // unknown/unhandled instruction failed...

    // only for debugging, shouldn't print stuff in a signal handler
    int i = 0; 
    for (i = 0; i < 16; i++) {
        fprintf(stderr, "%2.2X ", code[i]);
    }
    fprintf(stderr, "\n");
    exit(1);
}

int main(void)
{
    // install SIGSEGV handler
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_sigaction = action;
    act.sa_flags = SA_SIGINFO;
    if (sigaction(SIGSEGV, &act, NULL) < 0) {
        perror("sigaction");
        return 1;
    }

    // cause fault
    int i;
    for (i = 0; i < 10; i++) {
        ((unsigned char*)0)[i] = i;
    }
    return EXIT_SUCCESS;
}

Здесь я обработал только одну конкретную последовательность инструкцийдля 32- и 64-разрядных x86, хотя поддерживать дополнительные архитектуры и инструкции должно быть тривиально (если утомительно).

Обновление: вы (сейчас) упоминаете, что находитесь на компьютере ARM.На самом деле это должно облегчить задачу, поскольку инструкции всегда 32-битные (за исключением режима большого пальца), если я не ошибаюсь.У меня нет машины ARM, чтобы протестировать этот, поэтому вам придется копаться в sys/ucontext.h, чтобы проверить, правильно ли я назвал имена.Конечно, вы также должны проверять ошибочную инструкцию аналогичным образом.Мое лучшее предположение относительно того, как это происходит для ARM, заключается в следующем (расположенном рядом с другими #if defined(...) утверждениями:

    #elif defined(__arm) // or use what your GCC defines, also check for 32-bit arm mode or whatever...
    uint8_t* code = (uint8_t*)mcontext->arm_pc;
    if (*(uint32_t*)code == /*some instruction*/) {
        mcontext->arm_pc += 4; // skip it!
        return;
    }
1 голос
/ 08 августа 2011

Простой пропуск ошибочной инструкции звучит как рецепт для чрезвычайно трудных для отслеживания ошибок. Однако, если вы действительно этого хотите, вы можете переписать IP в структуре ucontext, которую обработчик получает как второй или третий (я забыл какой, но не пытайтесь ничего из этого без тщательного прочтения manpages). Вам нужно будет разобрать ошибочную инструкцию для себя, чтобы узнать, как долго она работает - это хорошо, потому что вы не должны пропускать инструкции, которые вы не понимаете.

Что бы вы ни делали, результат будет чрезвычайно специфичным для архитектуры и непереносимым.

0 голосов
/ 08 августа 2011

Я нашел альтернативный и, возможно, более простой способ написания обработчика ошибок сегментации.Хотя user78863 ответ и усилия заслуживают похвалы, но, учитывая сложность кода и сложность переноса, я думаю, что мое решение лучше.Поэтому я приму мой ответ.

Вот ссылка на код: Можем ли мы сбросить sigsetjmp, чтобы снова вернуть «0» (Сброс sigsetjmp)?

...