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

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

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

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

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: Вау, надо было выложить код! Я вернулся к коду сегодня свежим взглядом и сразу увидел ошибку. См. Мой робкий ответ ниже.


person Casey Rodarmor    schedule 02.04.2009    source источник
comment
Заполнение с помощью nop может исправить проблемы с выравниванием ... или, возможно, вам просто нужно убедиться, что вы делаете чистую сборку?   -  person i_am_jorf    schedule 02.04.2009
comment
Не видя догадки, мы действительно можем только догадываться ...   -  person Uri    schedule 02.04.2009
comment
Мне любопытно, что именно вдохновило вас на попытку вставить nops в оператор if (0)? Какой мыслительный процесс привел вас к мысли, что это исправит вашу ошибку?   -  person Chris Lutz    schedule 02.04.2009
comment
@Chris, я всегда так делаю, когда получаю дампы ядра, на всякий случай, если это их исправит :-)   -  person paxdiablo    schedule 02.04.2009
comment
Можете ли вы дизассемблировать вашу программу и посмотреть, имеют ли nops какой-либо очевидный эффект? Дизассемблирование, вероятно, будет иметь наибольший смысл, если вы скомпилируете свою программу с -O3.   -  person sigjuice    schedule 02.04.2009
comment
@sig, разве O3 не оптимизирует этот оператор if?   -  person paxdiablo    schedule 02.04.2009
comment
@Pax, ты прав. Однако у меня вообще возникают проблемы с отображением nops (даже с -O0) на моем G4 Mac mini. У меня такая же версия gcc (powerpc-apple-darwin9-gcc-4.0.1).   -  person sigjuice    schedule 02.04.2009
comment
@rodarmor, я думаю, нам понадобится код для дальнейшей помощи. Даже с переменными стека, но вы все равно можете перезаписать память. Мы поймем, если вы не хотите, но это ограничивает то, как мы можем вам помочь (и я не думаю, что вашему лектору понравится видеть эти нюансы в коде :-).   -  person paxdiablo    schedule 02.04.2009
comment
Просто из интереса, что вы имеете в виду под самозванцем? Можете ли вы суммировать исполняемый файл и сравнить его с файлом @ sigjuice?   -  person paxdiablo    schedule 02.04.2009
comment
@Pax Я не уверен, но у rodamor G5 может быть другой исполняемый файл. файл / usr / bin / gcc в моей системе говорит (для архитектуры ppc7400). Вот мой md5sum, на всякий случай (56e514baa7dba73b5ded18f9a64c0373 /usr/bin/gcc-4.0)   -  person sigjuice    schedule 02.04.2009
comment
@Chris, это было довольно запутанно ... В принципе, у меня был код, который, я был уверен, ничего не делал, но его удаление сломало все. Поскольку единственное, что он мог делать, это изменять длину кода, я заменил его самым безобидным, что я мог придумать: if (0) nop   -  person Casey Rodarmor    schedule 02.04.2009
comment
@Pax, @sigjuice, я просто пошутил насчет самозванца, но эй, кто знает! Мой md5 другой, но, вероятно, это просто добрые обновления или что-то в этом роде. Получаю: MD5 (/ usr / bin / gcc) = 0ce8a1fa01e8914c0a4825c7c67de6c6   -  person Casey Rodarmor    schedule 02.04.2009
comment
@sigjuice, я просто сравнил файлы .s с nops и без. Мало того, что слишком много различий, чтобы проверить, но и без nops файл .s имеет размер 196 620 байт, а с ним 156 719 байт. (!)   -  person Casey Rodarmor    schedule 02.04.2009
comment
Я отлаживал нечто подобное разочаровывающему. Похоже, вы не знаете, где программа дает сбой ... вам нужно выяснить, где она дает сбой, прежде чем вы сможете приступить к какой-либо отладке.   -  person Trevor Boyd Smith    schedule 02.04.2009
comment
Как и smcameron, предложенный в его ответе ниже, я подозреваю, что проблема со стеком. После того, как мой стек был переполнен, происходили действительно неприятные и удивительные вещи. Вы пользуетесь какой-нибудь рекурсией? Размещать любые массивы в стеке в цикле? Попробуйте разместить в куче что-нибудь большое.   -  person Venesectrix    schedule 02.04.2009
comment
@rodarmor, я время от времени проверяю, но вы накладываете реальные ограничения на помощь, которую мы можем предоставить, пока вы не покажете нам код. Я не думаю, что SO предназначена для игрового шоу :-) Мое предложение - опубликовать код и облегчить всю нашу жизнь (но особенно вашу).   -  person paxdiablo    schedule 03.04.2009
comment
Это не обязательно должен быть код. Просто придумайте минимальный пример, который все еще демонстрирует поведение.   -  person Chuck    schedule 03.04.2009
comment
Мой мир тоже не имеет смысла - stackoverflow.com/questions/688325/   -  person Mark Ransom    schedule 03.04.2009
comment
@rodarmor: изменение размера похоже на то, что GCC решает не встраивать вашу (на несколько байтов) функцию с nops; если истина, сбой при встроенном, вероятно, означает, что вызывающий объект затирает свой собственный стек.   -  person ephemient    schedule 04.04.2009
comment
@pax, хех, ты совершенно прав насчет игрового шоу. Обычно я был бы менее упрямым, но это часть соревнований, которые проводятся каждый год.   -  person Casey Rodarmor    schedule 07.04.2009


Ответы (9)


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

person paxdiablo    schedule 02.04.2009

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

 int a[4];

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

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

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

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

  int a[4];
  a[4]++;
person Joel Spolsky    schedule 02.04.2009
comment
Все инструкции PowerPC 32-битные. Четыре символа nops означают дополнительные 16 байтов. - person sigjuice; 02.04.2009
comment
Да, это хорошие инструкции. NOP - это на самом деле ORI R0, R0,0 (ИЛИ регистр 0 с самим собой и константой 0). - person paxdiablo; 02.04.2009
comment
Учитывая сумасшедшие различия в двух файлах .s (см. Обновление выше), я думаю, что на этот раз повседневное повреждение памяти может быть не таким. - person Casey Rodarmor; 02.04.2009

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

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

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

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

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

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

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

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

person Casey Rodarmor    schedule 07.04.2009
comment
Вы могли заметить это, включив предупреждения компилятора, а именно. test.c: 6: предупреждение: элемент управления достигает конца непустой функции. Всегда неплохо скомпилировать с помощью gcc -Wall - person Hugh Allen; 08.04.2009
comment
@ Хью, с -Wall творится что-то странное. Предупреждает только о выпадении функции, когда у меня отключена оптимизация. -Wall -O0 выдает предупреждение, тогда как -Wall -O {1,2,3,4} тихо. Может быть, дело в решении встроить функцию? - person Casey Rodarmor; 08.04.2009
comment
Похоже, это может быть ошибка. Попробуйте установить последнюю версию gcc, а затем перейдите сюда: gcc.gnu.org/bugs.html - person Hugh Allen; 09.04.2009

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

person ojblass    schedule 02.04.2009

Можете ли вы подтвердить, что действительно получаете разные исполняемые файлы при добавлении 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
person sigjuice    schedule 02.04.2009
comment
Что произойдет, если вы сделаете это, если (1)? Я всегда думал, что -O0 абсолютно не оптимизирует. - person paxdiablo; 02.04.2009
comment
Изменяется ли вывод -O0 при использовании asm volatile (nop); ? (Любопытный.) - person strager; 02.04.2009
comment
Это то, что я думал о if (0). if (1) выдаёт четыре nops, что меня не удивляет. - person sigjuice; 02.04.2009
comment
@strager if (0) {asm volatile (nop) ...} не имеет значения для -O0 или -O3. - person sigjuice; 02.04.2009
comment
@sigjuice, ваш хозяин подземелий все еще разговаривает с вами? Нет, если честно, у вас есть мастерство и понимание, которое я уважаю. - person ojblass; 02.04.2009
comment
Он делает вам комплимент (особенно вызывающе, но тем не менее комплимент :-). - person paxdiablo; 02.04.2009
comment
Да, я попробовал оба варианта, и файлы .s сильно различаются. (см. выше) - person Casey Rodarmor; 02.04.2009

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

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

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

person smcameron    schedule 02.04.2009
comment
Ни в коем случае тупой вопрос! Один раз я редактировал на одной машине, а компилировал / работал на другой. Я был очень удивлен, когда мои изменения не отображались ;-) Однако на этот раз не повезло! - person Casey Rodarmor; 02.04.2009

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

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

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

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

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

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

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

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

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

  9 правил отладки изображения
(источник: google.com)


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

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

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

person Community    schedule 07.04.2009

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

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

person Windows programmer    schedule 03.04.2009