Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Исправлена ​​ошибка с четырьмя nops в if (0), world больше не имеет смысла

Автор вопроса: Casey RodarmorОтветов: 9

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

Я отменил некоторые изменения, переделал их, а затем продолжил возиться в течение следующих двух часов, пока не довел это до абсурда.

Следующее, вставленное где угодно в теле функции, но нигде больше в программе, не исправляет:

if(0) {
    __asm__("nop\n");
    __asm__("nop\n");
    __asm__("nop\n");
    __asm__("nop\n");
}

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

Пожалуйста, помогите мне разобраться в этом мире! Я не хочу списывать это на GCC, поскольку первое правило отладки - не винить компилятор. Но, черт возьми, я собираюсь. Я запускаю Mac OS 10.5 на башне G5, и рассматриваемый компилятор идентифицирует себя как "powerpc-apple-darwin9-gcc-4.0.1", но я думаю, что это мог быть самозванец ...

ОБНОВЛЕНИЕ: Все любопытнее и любопытнее ... Я сравнил файлы .s с nops и без них. Мало того, что слишком много различий, чтобы проверить, но и без nops файл .s имеет размер 196 620 байт, а с ним 156 719 байт. (!)

ОБНОВЛЕНИЕ 2: Вау, надо было выложить код! Я вернулся к коду сегодня свежим взглядом и сразу увидел ошибку. См. Мой робкий ответ ниже.

  • Заполнение с помощью nop может исправить проблемы с выравниванием ... или, возможно, вам просто нужно убедиться, что вы делаете чистую сборку? i_am_jorf 02.04.2009
  • Не видя догадки, мы действительно можем только догадываться ... Uri 02.04.2009
  • Мне любопытно, что именно вдохновило вас на попытку вставить nops в оператор if (0)? Какой мыслительный процесс привел вас к мысли, что это исправит вашу ошибку? Chris Lutz 02.04.2009
  • @Chris, я всегда так делаю, когда получаю дампы ядра, на всякий случай, если это их исправит :-) paxdiablo 02.04.2009
  • Можете ли вы дизассемблировать вашу программу и посмотреть, имеют ли nops какой-либо очевидный эффект? Дизассемблирование, вероятно, будет иметь наибольший смысл, если вы скомпилируете свою программу с -O3. sigjuice 02.04.2009
  • @sig, разве O3 не оптимизирует этот оператор if? paxdiablo 02.04.2009

macosgcccdebugging

Ответы

Ответ 1 · рейтинг 15 · paxdiablo

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

Ответ 2 · рейтинг 13 · Joel Spolsky

Это неправильная арифметика указателя, либо напрямую (через указатель), либо косвенно (проходя за конец массива). Проверьте все свои массивы. Не забывайте, что если ваш массив

 int a[4];

тогда [4] не существует.

Вы случайно что-то перезаписываете в стеке. Стек содержит как локальные переменные, так и параметры, и адрес возврата из вашей функции. Возможно, вы повредили обратный адрес таким образом, чтобы его вылечили лишние петли.

Например, если у вас есть код, который что-то добавляет к адресу возврата, вставка этих дополнительных 16 байтов noops решит проблему, потому что вместо возврата после следующей строки кода вы возвращаетесь в середину некоторых noops.

Один из способов добавить что-то к адресу возврата - пройти за конец локального массива или параметра, например

  int a[4];
  a[4]++;

Ответ 3 · рейтинг 6 · Casey Rodarmor

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

Основная проблема заключалась в том, что я пропустил операторы возврата в рекурсивной функции. Я имел:

bool function() {
    /* lots of code */
    function()
}

Когда это должно было быть:

bool function() {
    /* lots of code */
    return function()
}

Это сработало, потому что благодаря магии оптимизации нужное значение оказалось в нужном регистре в нужное время и попало в нужное место.

Изначально ошибка возникла, когда я разбил первый вызов на отдельную функцию. И на тот момент дополнительные nops были разницей между этим первым случаем, встроенным непосредственно в общую рекурсивную функцию.

Затем, по причинам, которые я не полностью понимаю, встраивание этого первого случая привело к тому, что правильное значение не оказалось в нужном месте в нужное время, и функция вернула мусор.

Ответ 4 · рейтинг 3 · ojblass

Происходит ли это при сборке в режиме отладки и выпуска (с символами и без)? В отладчике он ведет себя так же? Код искажен? Вы компилируете с оптимизацией? Вы можете попробовать другую машину?

Ответ 5 · рейтинг 3 · sigjuice

Можете ли вы подтвердить, что действительно получаете разные исполняемые файлы при добавлении if (0) {nops}? В моей системе нет сообщений об ошибках.

$ gcc --version
powerpc-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5490)

$ cat nop.c
void foo()
{
    if (0) {
        __asm__("nop");
        __asm__("nop");
        __asm__("nop");
        __asm__("nop");
    }
}

$ gcc nop.c -S -O0 -o -
    .
    .
_foo:
    stmw r30,-8(r1)
    stwu r1,-48(r1)
    mr r30,r1
    lwz r1,0(r1)
    lmw r30,-8(r1)
    blr

$ gcc nop.c -S -O3 -o -
    .
    .
_foo:
    blr

Ответ 6 · рейтинг 2 · smcameron

Я предполагаю, что это повреждение стека - хотя gcc должен оптимизировать что-либо внутри if (0), я бы подумал.

Вы можете попробовать вставить большой массив в стек в своей функции и посмотреть, исправит ли это это - это также повлечет за собой повреждение стека.

Вы уверены, что выполняете то, что думаете? (глупый вопрос, но такое бывает.)

Ответ 7 · рейтинг 2 · Trevor Boyd Smith

Похоже, вам нужно будет немного поработать и смазать локти

Ваша проблема звучит похоже на то, что я отлаживал в прошлом, когда мое приложение работало регулярно ... когда из ниоткуда оно перескочило в другую часть приложения, и стек вызовов полностью испортился (однако это было встроенное программирование)!

Похоже, вы проводите свое время, "думая" о том, "что должно происходить" ... тогда как вы должны "смотреть" на "то, что на самом деле происходит". Часто самые серьезные ошибки - это вещи, о которых вы даже не подумаете, что они "должны произойти".

Я бы подошел к проблеме так:

  1. Выбери свой любимый отладчик
  2. Начните пошагово выполнять свой код, наблюдайте за стеком вызовов и локальными переменными и ищите подозрительную активность.
  3. Сделайте сбой системы
  4. Сосредоточьтесь на том, где система дает сбой

Сосредоточьтесь на повторении изменений кода:

  1. внесение изменений в код, которые "приведут к сбою системы"
  2. запуск / отладка и просмотр
  3. Если он работает нормально, вы ищете / пытаетесь не то, и вам нужно попробовать что-то еще. Если вы допустили ошибку, значит, вы добились прогресса в поиске ошибки.
  4. Если вы не знаете, где и как выходит из строя система, вы не сможете решить проблему.

Это будет хорошей возможностью развить свои навыки отладки. Для получения дополнительной помощи по развитию навыков отладки прочтите книгу " 9 правил отладки ".

Вот плакат из книги:


(источник: google.com)


Конкретные предложения

  1. Если вы думаете, что это компилятор, запустите другую платформу / ОС / компилятор.
  2. После того, как вы исключили платформу / ОС / компилятор, попробуйте реструктурировать код. Найдите "умные" части кода и посмотрите, действительно ли они делают то, что код должен делать ... может быть, умное решение на самом деле не было умным и делает что-то еще.

Ответ 8 · рейтинг 1 · Community

Я являюсь автором "Отладки", на которую так любезно упомянул выше Тревор Бойд Смит. Он прав - ключевые правила здесь №2 "Сделай неудачу" (с которым, кажется, у тебя все в порядке) и №3 "Перестань думать и смотри". Приведенные выше предположения очень хороши (демонстрируя владение правилом № 1 - понимание системы - в данном случае то, как размер кода может изменить ошибку). Но на самом деле наблюдение за его отказом с помощью отладчика покажет вам, что на самом деле происходит, без каких-либо догадок.

Ответ 9 · Windows programmer

Выделите эту функцию в отдельный файл .c (или .cpp или что-то еще). Скомпилируйте только этот один файл с nops и без них в файлы .s и сравните их.

Попробуйте старую версию gcc. Вернитесь на 5 или 10 лет назад и посмотрите, не станет ли ситуация более странной.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы