Нахождение всего кода в заданном двоичном файле эквивалентно проблеме остановки. Действительно?

Просто читал вопрос, получивший большое количество голосов относительно эмуляторов и заявление

Доказано, что поиск всего кода в заданном двоичном файле эквивалентен проблеме остановки.

Действительно застрял на мне.

Конечно, это не может быть правдой? Разве это не просто большой граф зависимостей?

Был бы очень признателен за дальнейшее понимание этого заявления.


person Maxim Gershkovich    schedule 14.03.2011    source источник
comment
Что вы имеете в виду под поиском кода? Обратный инжиниринг или?   -  person orlp    schedule 14.03.2011
comment
Насколько я понимаю, ОН / ОНА имеет в виду поиск всей цепочки кода, включая зависимости. Найдите строку с этим текстом в выбранном ответе, чтобы увидеть контекст.   -  person Maxim Gershkovich    schedule 14.03.2011
comment
Стоит ли спрашивать об этом на теоретической системе координат?   -  person Graviton    schedule 14.03.2011
comment
Смотрите мой ответ. Найти весь код в программе тривиально, пока все ветви имеют фиксированные целевые адреса. Указатели на функции / вычисленные gotos / самомодифицирующийся код являются возможными осложнениями.   -  person R.. GitHub STOP HELPING ICE    schedule 25.03.2011


Ответы (4)


Я считаю, что имеется в виду «поиск всего кода, который когда-либо выполнялся», то есть поиск покрытия, возможно, в сочетании с динамически сгенерированным кодом. Это действительно можно свести к проблеме остановки.

Скажем, у вас есть идеальный инструмент покрытия, который найдет каждый фрагмент кода в программе, который может когда-либо быть выполнен (так что остальное - мертвый код). При наличии программы P этот инструмент также сможет решить, будет ли расширенная программа (P ; halt) когда-либо выполнять инструкцию halt или часть halt является мертвым кодом. Таким образом, это решит проблему остановки, которая, как мы знаем, неразрешима.

person Fred Foo    schedule 14.03.2011
comment
Потратив некоторое время на обдумывание вашего аргумента. Я не уверен, что убежден. Как предлагается в ответе ниже, мы не пытаемся решить, остановится ли программа (хотя я вижу параллели в этой проблеме). Мы пытаемся найти все зависимости для данной программы. Более того, разве не все зависимости закодированы внутри приложения с помощью метаданных? (Я думаю, не потому, что вы можете загружать их во время выполнения - но тогда зависимость будет сохранена в константе или переменной в какой-то момент) хммммм. Я должен придумать, как превратить это в вики. - person Maxim Gershkovich; 17.03.2011

Я не согласен с ларсманом.

Проблема остановки говорит о том, что не существует P программы, которая могла бы взять любую программу и решить, выполняет ли эта программа инструкцию halt. Позвольте процитировать википедию:

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

С другой стороны, мы не пытаемся создать такую ​​программу / алгоритм, но мы пытаемся найти весь код в этой / этих конкретных программах. Если мы перепроектируем программу и увидим, что она немедленно вызывает exit() (очень оптимистичный пример ситуации), мы доказали, что она вызовет halt, хотя это было невозможно ?!

Если мы пытаемся создать эмулятор, который может запускать любую программу, мы потерпим неудачу, поскольку тогда вы можете (легко) свести это к проблеме остановки. Но обычно вы создаете эмулятор для чего-то вроде Game Boy, который поддерживает ограниченное количество игровых картриджей (программ), и поэтому это возможно.

person orlp    schedule 14.03.2011
comment
Это означает WOOOW или я зря потратил время на этого парня?!? - person orlp; 15.03.2011
comment
Нет, я имел в виду это как комплимент и осознание того, насколько я глуп. :-( ржу не могу - person Maxim Gershkovich; 15.03.2011
comment
Если мы перепроектируем программу и увидим, что она немедленно вызывает exit () (очень оптимистичный пример ситуации), мы доказали, что она вызовет остановку, хотя это было невозможно ?! Проблема остановки не означает, что это невозможно для каждого случая, но что есть некоторые случаи, для которых это невозможно. - person Fred Foo; 17.03.2011
comment
Что касается аргумента о конечном количестве памяти: вы правы, поскольку Gameboy (или даже ПК) формально является конечным автоматом. Количество состояний, однако, равно двум в зависимости от мощности [объема оперативной памяти в машине]. Так что это все еще неразрешимо, как это часто бывает с конечными случаями неразрешимых проблем. - person Fred Foo; 17.03.2011

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

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

person R.. GitHub STOP HELPING ICE    schedule 25.03.2011
comment
Даже без вычисляемых переходов это непростая проблема. Например, условная ветвь всегда может включать или отключать условие. В этом примере 8086: CLC; JC lbl программа никогда не перескакивает на lbl, поэтому анализатор не должен предполагать, что lbl - это код. Точно так же в CLC; JNC lbl; POP CS код всегда перескакивает на lbl, и поэтому POP CS, скорее всего, мертвый код (и это лучше, поскольку эта инструкция была удалена в 80186 из-за своей полной бесполезности). Такие переходы были обычным явлением в коде 6502, поскольку в коде 6502 отсутствовала короткая инструкция относительного безусловного перехода. - person Karol S; 28.08.2018

Я согласен с Ларсманом и считаю, что аргумент можно уточнить. Во-первых, я должен согласиться с тем, что «нахождение всего кода в заданном двоичном файле» означает в этом контексте наличие единственной вычислимой функции, которая определяет, какие байты во входном двоичном файле соответствуют выполняемым инструкциям.

РЕДАКТИРОВАТЬ: если кто-то не понимает, почему здесь проблема, подумайте о запутанном машинном коде. Дизассемблирование такого кода - нетривиальная задача. Если вы начнете разборку в середине многобайтовой инструкции, вы получите неправильную разборку. Это нарушает не только данную инструкцию, но, как правило, несколько других инструкций. и т. д. посмотрите в это. (например, погуглите "обфускация и разборка").

Набросок стратегии, чтобы сделать это точным:

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

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

Тогда любой заданный бит будет либо в начале инструкции, которая выполняется во время выполнения программы, либо не будет. (например, он может быть в середине инструкции, или в данных, или в «мусорном коде» - то есть код, который никогда не будет запущен. Обратите внимание, что я не утверждал, что они являются взаимоисключающими, как, например, инструкция перехода может иметь цель, которая находится в середине конкретной инструкции, тем самым создавая другую инструкцию, которая «на первом проходе» не выглядела как выполненная инструкция.).

Определите ins (i, j) как функцию, которая возвращает 1, если двоичный i имеет инструкцию, начинающуюся с битовой позиции j, которая выполняется в прогоне программы, закодированной i. В противном случае определите ins (i, j) равным 0. предположим, что ins (i, j) вычислим.

Пусть halt_candidate (i) будет следующей программой:

for j = 1 to length(i)
  if(i(j..j+len('halt')-1) == 'halt')
    if(ins(i,j) == 1)
      return 1
return 0

Поскольку мы запрещаем самомодифицирующийся код, приведенный выше, единственный способ остановить программу - это выполнить инструкцию «остановки» в некоторой позиции j. По замыслу halt_candidate (i) вычисляет функцию остановки.

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

В другом направлении нужно закодировать эмулятор формальной машины внутри формальной машины. Затем создайте программу плюс входы i и j, которая имитирует программу i, и когда выполняется инструкция с битовой позицией j, она возвращает 1 и останавливается. Когда выполняется любая другая инструкция, она продолжает выполняться, и если симуляция когда-либо имитирует функцию «остановки» в i, эмулятор переходит в бесконечный цикл. Тогда ins (i, j) эквивалентен halt (emulator (i, j)), и поэтому проблема остановки подразумевает проблему поиска кода.

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

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

РЕДАКТИРОВАТЬ: Я считаю, что доказательством самомодифицирующегося случая будет реализация эмулятора кода системы плюс самомодифицирующийся в статическом коде плюс система данных выше. Затем остановка с помощью самомодифицирующегося кода, разрешенного для программы i с данными x, такая же, как остановка в системе статический код плюс данные выше с двоичным файлом, содержащим эмулятор, i и x как код плюс данные.

person fbg00    schedule 22.05.2014