OSDev

для всех
Текущее время: 26 дек 2024, 15:35

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 56 ]  На страницу Пред.  1, 2, 3, 4, 5, 6
Автор Сообщение
СообщениеДобавлено: 03 июн 2014, 13:39 

Зарегистрирован: 15 апр 2014, 14:13
Сообщения: 127
Bargest писал(а):
Если компилировать код на Си без использования оптимизации вообще, то декомпиляция будет относительно вменяемой назад в Си.
"относительно вменяемым" будет и текст на голом ассемблере. Вопрос только в приемлемости такой вменяемости.
Bargest писал(а):
Если бы было возможно включить хорошую оптимизацию при транслировании кода на Java в байт-код, то восстановить это было бы не сильно проще, чем Си из ассемблера. Только разница в том, что ни один JAVA-компилятор (до байт-кода) не производит никакой толковой оптимизации (а многие компиляторы - никакой вообще). В первую очередь поэтому JAVA неплохо декомпилируется.

Компиляторы неспроста не производят существенную оптимизацию при компиляции в байткод. И совсем неспроста в байткод введены возможности хранить названия функций, перменных и прочего. Всё это есть тот самый высокий уровень байткода, против которого первым стал возражать Yoda. И при машино-независимом байткоде это реально единственный правильный путь, ведь если бы компилятор выкидывал много лишней по его мнению информации из байткода, то следующий обязательный этап JIT компиляции на разных платформах страдал бы от отсутствия нужной информации, а разработчики не получали бы понятной отладочной информации.
Bargest писал(а):
Никаких специфических конструкций, помогающих в декомпиляции, в байткоде попросту нет.

Речь не о специфических инструкциях. Хотя в байткоде они есть (в виде кучи данных о названиях, например). Но дело началось с того, что Yoda решил назвать байткод низкоуровневым представлением, доказывая несостоятельность моих доводов о преимуществах Java. Ну и я в ответ показал высокоуровневые возможности байткода на примере компиляции-декомпиляции. И при этом ни про какие специфические инструкции не упоминал.
Bargest писал(а):
Имена полей-методов - не аргумент в пользу декомпиляции, т.к. это не логика кода, это просто, так сказать, обязательная дебаг-информация

Это не только информация для отладки. Есть куча применений этих самых имён под общим названием introspection. То есть данные о именах после их введения оказались очень даже нужными и многими активно используются в чистой воды логике кода. И далее на этой основе в язык введены аннотации, что ещё более повысило важность всяческих имён. Так что имена - очень даже важный аргумент, правда непонятно в каком споре, ведь что вы собственно пытаетесь доказать ? Что ассемблер более понятен чем байткод ?
Bargest писал(а):
В то же время я видел java-программы, которые сдекомпилировать было сложнее, чем многие сишные.

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

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 03 июн 2014, 17:12 

Зарегистрирован: 31 окт 2011, 18:20
Сообщения: 230
Цитата:
Можно пример ? Вы сейчас по сути заявили, что байткод содержит неверный логический код, не соответствующий ожиданиям программиста.

Нет, я всего лишь заявил, что генераторы байт-кода зачастую создают такой код, который очевидным образом не декомпилируется. Конкретный файл привести не могу, ибо NDA, однако таких файлов множество. Зачастую декомпиляторы выдают код, к примеру, следующего вида:
Код:
if (a > b)
;
while(true)
{
 return;
  b = b + 5;
}

И это без всякой обфускации. Угадайте, что это было в начале. А что иногда (не часто) начинается при сложных условиях, особенно для циклов, с учетом ленивости вычислений - смотреть страшно.
Цитата:
Это не только информация для отладки. Есть куча применений этих самых имён под общим названием introspection. То есть данные о именах после их введения оказались очень даже нужными и многими активно используются в чистой воды логике кода. И далее на этой основе в язык введены аннотации, что ещё более повысило важность всяческих имён.

Хорошо. Возьмем все имена, заменим на численные идентификаторы. Код будет продолжать работать. Многие обфускаторы меняют, и все работает.
Теперь. Возьмем строки чисел и заменим реально на числа. Все то же самое. А чем это отличается от связывания по адресам? Да, по сути своей, ничем; при динамической линковке в DLL адреса точно также можно вычислять с использованием индексов функций и подставлять, все продолжает работать. Единственное - буквы читать удобнее, чем какие-то чиселки и идентификаторы.
А также вы в курсе, что спецификация, к примеру, Dalvik, косвенно рекомендует писать код так, чтобы аннотации не значили по сути ничего, кроме той же самой удобной при отладке информации? Вернее сказать, что там написано так: "аннотации описаны в таком-то формате для того, чтобы файлы для более поздних версий DVM всегда работали на более старых". Да, аннотации можно просто вырезать, и программа будет работать. Я так делал на работе сотни раз (один мой инструмент для простоты игнорирует аннотации при обработке файла и, как следствие, выносит их при сохранении).
Цитата:
И при машино-независимом байткоде это реально единственный правильный путь, ведь если бы компилятор выкидывал много лишней по его мнению информации из байткода, то следующий обязательный этап JIT компиляции на разных платформах страдал бы от отсутствия нужной информации

Я уже описал, что эта информация почти бесполезна компилятору, имеет смысл она только для разработчика. Высокоуровневостью же в байткоде жабы не пахнет и близко. Просто потому, что тут нет никаких конструкций, кардинально отличающих его от любых ассемблеров. Те же самые push/pop/add/mov/jmp, к которым добавили еще пару вариаций new. Вызов метода по имени, как я уже сказал, запросто мог бы быть заменен на идексы/адреса, и все бы работало так же; виртуальные функции делаются с помощью массивов индексов/адресов. В то время как высокоуровневый язык отличается в первую очередь тем, что имеет логические конструкции, а не просто массив команд с переходами по нему как заблагорассудится. То есть байткод был бы высокоуровневым, если бы, к примеру, строился только в виде дерева кода и в таком же виде и хранился. А так, как он есть - ассемблер ассемблером. Так что компилятор как раз-таки выкидывает огромное количество информации о логике кода, переводя его в низкуровневый язык, являющийся тупым массивом команд. Компилятор из байткода в машинный код уже не знает, где цикл, где условие. И нет, goto на адрес раньше текущего еще циклом не является.
Высокоуровневость присутствует в плане построения структуры дерева классов, т.е. архитектуры самой ВМ, и небольшой поддержки этого в байткоде. Однако это не очень сильный аргумент в пользу качественной компиляции, т.к. именно к переводу одного набора низкоуровневых команд в другой не имеет почти никакого отношения.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 04 июн 2014, 16:29 

Зарегистрирован: 15 апр 2014, 14:13
Сообщения: 127
Bargest писал(а):
Зачастую декомпиляторы выдают код, к примеру, следующего вида:
Код:
if (a > b)
;
while(true)
{
 return;
  b = b + 5;
}

И это без всякой обфускации. Угадайте, что это было в начале.

Теряюсь в догадках. Особенно "вставляет" наличие двух "мёртвых" фрагментов кода и отсутствие вообще чего-либо живого (то есть хоть что-то делающего с данными или осуществляющего переходы).

Если некий "декомпилятор" называет себя именно декомпилятором и выдаёт такое, то у меня есть сильное подозрение о грамотности авторов либо о неком дополнительном усложнении задачи вроде кривого перевода из далвиковского кода в стандартный Java-байткод. Гуглы они вообще те ещё "друзья программистов", от них вполне можно ожидать кастрации всего полезного в нормальном байткоде и превращение этого месива в "андроид".
Bargest писал(а):
Возьмем все имена, заменим на численные идентификаторы. Код будет продолжать работать.

Продолжать работать будет лишь в большинстве случаев. То есть тривиальное использование длины строки с названием метода уже всё портит. Хотя да, это редкость, потому и прокатывает замена обфускаторами, но до поры до времени. То есть опять имеем пример декомпилятора от начинающих студентов - да нафиг париться про какие-то там редкие варианты, ведь меня пока устраивает ...
Bargest писал(а):
Возьмем строки чисел и заменим реально на числа.

Здесь вообще не понял - вы же уже заменили строки на числа выше. Зачем тогда введение никому ненужной сущности "строка чисел" ?
Bargest писал(а):
А чем это отличается от связывания по адресам?

Зависит от того, что вы понимаете под связыванием по адресам. Если вы про адреса функций/методов, то в случае с JVM это никаким местом не относится к названиям методов. То есть вы просто путаете сладкое с длинным.
Bargest писал(а):
А также вы в курсе, что спецификация, к примеру, Dalvik, косвенно рекомендует писать код так, чтобы аннотации не значили по сути ничего, кроме той же самой удобной при отладке информации?

Вы же сами написали про обратную совместимость, о которой гугло-кодеры не захотели думать изначально, а теперь нагибают всех остальных думать за них. То есть вы привели пример убожества и предлагаете на его основе делать далеко идущие выводы. Вы поймите - если вы имели дело только с андроидом, вы совсем не стали знатоком Java, но лишь познакомились с кастрированной гуглами версией изначально гораздо более глубоко продуманной системы.
Bargest писал(а):
Цитата:
И при машино-независимом байткоде это реально единственный правильный путь, ведь если бы компилятор выкидывал много лишней по его мнению информации из байткода, то следующий обязательный этап JIT компиляции на разных платформах страдал бы от отсутствия нужной информации

Я уже описал, что эта информация почти бесполезна компилятору

Ну дело ваше, но для моего компилятора информация очень даже нужна.
Bargest писал(а):
Высокоуровневостью же в байткоде жабы не пахнет и близко. Просто потому, что тут нет никаких конструкций, кардинально отличающих его от любых ассемблеров. Те же самые push/pop/add/mov/jmp, к которым добавили еще пару вариаций new.

Ну так вы возможно и С с Java называете низкоуровневыми языками ? Те же mov/add/jс к которым добавили new. В чём вы видите признаки высокоуровневости ?
Bargest писал(а):
высокоуровневый язык отличается в первую очередь тем, что имеет логические конструкции, а не просто массив команд с переходами по нему как заблагорассудится.

И как по вашему, можно назвать оператор switch в Си или Java логической конструкцией ? Если нет - то что же тогда есть логическая конструкция ? А если да, то вы должны признать байткод высокоуровневым по вашему собственному определению. Как бы похоже, что вы зарапотровались маленько :)
Bargest писал(а):
байткод был бы высокоуровневым, если бы, к примеру, строился только в виде дерева кода и в таком же виде и хранился.

Ну опять заявление из серии "я не подумал". Или вы готовы привести пример на фортране с указанием развесистых деревьев и такой же формы хранения обычного текста в кодировке ASCII ?

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 04 июн 2014, 22:25 

Зарегистрирован: 31 окт 2011, 18:20
Сообщения: 230
На все отвечать не буду, ввиду того, что читаете вы не очень внимательно и пытаетесь перевести стрелки на якобы мою некомпетентность, поэтому только ключевые моменты.
Цитата:
И как по вашему, можно назвать оператор switch в Си или Java логической конструкцией ? Если нет - то что же тогда есть логическая конструкция ? А если да, то вы должны признать байткод высокоуровневым по вашему собственному определению. Как бы похоже, что вы зарапотровались маленько :)

if, while - это и есть самые основные логические конструкции. Switch тоже, отчасти (в pascal - да, а вот в C/Java его принцип кривоват, основана на обычном goto, где case - это метки). И как раз по моему определению байткод жавы не является высокоуровневым.
Проблема в том, что в байткоде циклов нет вообще, условия только на уровне if + goto (сравните с jz/jnz в асм), а switch сводится к куче джампов (точнее, к одному джампу по переменному адресу, но не суть), благодаря чему код опять же становится беспорядочным месивом команд и переходов по ним.
Возвращаясь к моему примеру в самом начале, этот код на самом деле до компиляции выглядел так:
Код:
if (a > b)
   b = b + 5;
return;

Неожиданно, да? И дело не в убогости разработчиков декомпилятора. Вот так это выглядит в, так сказать, псевдо-байткоде (недодекомпиляция :) для понятности):
Код:
    if (a > b) goto label1
label2:
    return
label1:
    b = b + 5
    goto label2

Распишите a > b на push+push+if, а b = b + 5 в push push add pop (мне привычней в терминах обычной асмы), и получите реальный байткод метода. Такое я вижу очень часто. Java любит так делать.
А теперь финт ушами. Как, по вашему, будет выглядеть условие
Код:
if ((a > func1(b) || func2(a) < func1(b)) && (h > z * z + k))

? А как оно будет выглядеть, если это условие цикла? А если это условие для break/continue? Вот тут-то и всплывает главное отличие низкоуровневого кода от высокоуровневого. В низкоуровневом (байткоде жавы, асме и т.д.) это превратиться в raw-массив команд, осуществить качественный анализ которого и выделить логические блоки из беспорядочного лабиринта прыжков затруднительно. Во всяком случае из 5 опробованных мной декомпиляторов (jd, jad, fernflower, еще один какой-то и мой самопальный:) последний можно не учитывать) все периодически выдают бредовый код.

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


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 05 июн 2014, 12:10 

Зарегистрирован: 15 апр 2014, 14:13
Сообщения: 127
Bargest писал(а):
if, while - это и есть самые основные логические конструкции. Switch тоже, отчасти (в pascal - да, а вот в C/Java его принцип кривоват, основана на обычном goto, где case - это метки). И как раз по моему определению байткод жавы не является высокоуровневым.

Ну я вас совсем не понимаю - если switch тоже, то почему "как раз по моему определению байткод жавы не является высокоуровневым" ? Если некий принцип "кривоват" в С, то почему он вдруг уже совсем не кривоват в паскале ?

Ну и если if по вашему мнению является логической конструкцией без оговорок, то вот вам тот же if из байткода:
Код:
12  iload_1 [b]
16 if_icmpgt 30

Сравните эту запись с бэйсиком:
Код:
16 if b>0 then goto 30

Ну да, нету слов then и goto, но для рускоговорящего начинающего программиста что then, что goto, что if_icmpgt - всё едино.
Bargest писал(а):
Проблема в том, что в байткоде циклов нет вообще, условия только на уровне if + goto (сравните с jz/jnz в асм), а switch сводится к куче джампов (точнее, к одному джампу по переменному адресу, но не суть), благодаря чему код опять же становится беспорядочным месивом команд и переходов по ним.

Ещё раз предлагаю сравнить с кучей языков высокого уровня (а ля бэйсик или фортран), где все эти goto цветут во всей красе.
Bargest писал(а):
Возвращаясь к моему примеру в самом начале, этот код на самом деле до компиляции выглядел так:
Код:
if (a > b)
   b = b + 5;
return;

Неожиданно, да? И дело не в убогости разработчиков декомпилятора. Вот так это выглядит в, так сказать, псевдо-байткоде (недодекомпиляция :) для понятности):
Код:
    if (a > b) goto label1
label2:
    return
label1:
    b = b + 5
    goto label2

То есть декомпилятор проигнорировал goto label1 и тем самым полностью извратил смысл программы. Разве можно назвать не кривым декомпилятор, который тупо удаляет часть инструкций из программы ?
Bargest писал(а):
А теперь финт ушами. Как, по вашему, будет выглядеть условие
Код:
if ((a > func1(b) || func2(a) < func1(b)) && (h > z * z + k))
?

Будет выглядеть вот так:
Код:
 11  iload_0 [a]
 12  iload_1 [b]
 13  invokestatic test.Test.func1(int) : int [19]
 16  if_icmpgt 30
 19  iload_0 [a]
 20  invokestatic test.Test.func2(int) : int [23]
 23  iload_1 [b]
 24  invokestatic test.Test.func1(int) : int [19]
 27  if_icmpge 40
 30  iload_2 [h]
 31  iload_3 [z]
 32  iload_3 [z]
 33  imul
 34  iload 4 [k]
 36  iadd
 37  if_icmple 40
 40  return

После декомпиляции jad-ом будет выглядеть вот так:
Код:
        if(a > func1(b) || func2(a) < func1(b))
            if(h <= z * z + k);

Сравните с начальным вариантом:
Код:
if ((a > func1(b) || func2(a) < func1(b)) && (h > z * z + k));

Очередной раз убеждаюсь, что всё прекрасно в мире нормальной Java и нормальных декомпиляторов.

Я, конечно, понимаю ваш внутренний бунт против "непонятности" байткода с первого взгляда. Но точно так же вам будет непонятен любой язык высокого уровня, где привычные if then goto заменены на что-то менее привычное. Например есть такой язычок от конторы 1С, он очень похож на бэйсик, но там все ключевые слова переведены на русский язык, так вот все кто сталкивается с этим язычком первым делом начинают возмущаться - ну что за уроды это придумали, ну что за убожество, ну и т.д. А всё потому, что просто в нём привычные ключевые слова заменены на непривычные.
Bargest писал(а):
из 5 опробованных мной декомпиляторов (jd, jad, fernflower, еще один какой-то и мой самопальный:)

Рад, что вы не на пустом месте заявления делаете, но даже свой декомпилятор создали. :) Ни чуть не ёрничаю.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: 05 июн 2014, 17:12 

Зарегистрирован: 31 окт 2011, 18:20
Сообщения: 230
Мда. Sii и Yoda явно были правы. Жаль.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 56 ]  На страницу Пред.  1, 2, 3, 4, 5, 6

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Создано на основе phpBB® Forum Software © phpBB Group
Русская поддержка phpBB