0
- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
// https://github.com/ghc/ghc/blob/e204431e5a5e2fd16da52b04bda2798f16c51344/rts/Interpreter.c#L1184
case bci_PUSH8: {
int off = BCO_NEXT;
Sp_subB(1);
*(StgWord8*)Sp = *(StgWord8*)(Sp_plusB(off+1));
goto nextInsn;
}
case bci_PUSH16: {
int off = BCO_NEXT;
Sp_subB(2);
*(StgWord16*)Sp = *(StgWord16*)(Sp_plusB(off+2));
goto nextInsn;
}
case bci_PUSH32: {
int off = BCO_NEXT;
Sp_subB(4);
*(StgWord32*)Sp = *(StgWord32*)(Sp_plusB(off+4));
goto nextInsn;
}
case bci_PUSH8_W: {
int off = BCO_NEXT;
*(StgWord*)(Sp_minusW(1)) = *(StgWord8*)(Sp_plusB(off));
Sp_subW(1);
goto nextInsn;
}
case bci_PUSH16_W: {
int off = BCO_NEXT;
*(StgWord*)(Sp_minusW(1)) = *(StgWord16*)(Sp_plusB(off));
Sp_subW(1);
goto nextInsn;
}
case bci_PUSH32_W: {
int off = BCO_NEXT;
*(StgWord*)(Sp_minusW(1)) = *(StgWord32*)(Sp_plusB(off));
Sp_subW(1);
goto nextInsn;
}
https://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/Interpreter
> The linker lives in rts/Linker.c and is responsible for handling runtime loading of code into a Haskell process. This is something of a big blob of unpleasant code, and see DynamicGhcPrograms for information about efforts to reduce our dependence on this linker.
Итак, тут у нас стековая машина из хачкеля, которая вродекак отвечает за динамическую загрузку какого-то говна.
Запостил:
j123123,
22 Февраля 2019
Что это за хуйня "*(StgWord8*)Sp" по-вашему? Типы StgWord8 StgWord16 StgWord32 определены в
Что мы видим? Обычные синонимы к херне из uint8_t int8_t uint16_t int16_t и так далее.
А у нас тут есть херня такая, что для bci_PUSH8 мы делаем Sp_subB(1) т.е. уменьшаем стек на 1, потом кастуем: *(StgWord8*)Sp = *(StgWord8*)(Sp_plusB(off+1));
Ну в общем какую-то херь в стек записываем из какого-то говна, но при этом стекпоинтер уменьшаем на 1. Т.е. стек у нас нихера не выровнен. И если потом будет хуйня bci_PUSH16: *(StgWord16*)Sp = *(StgWord16*)(Sp_plusB(off+2));
Которая кастанет невыровненную хуйню в *uint16_t и потом разыменует даже. А это UB
И кто вас блядь учил писать так макросы? Макросы принято писать КАПСОМ!
На фортране и бейсике отлично обходились без этих ваших автоматических переменных.
В «x86» вращаемых банков регистров нет, именно поэтому я за «x86».
Зато в x86 можно циклически сдвигать какой-нибудь регистр, и брать от него кусочек поменьше. В x86-64 например 64-битный регистр rax и его 8-битные кусочки ah и al.
> в асембреле у тебя не было никакой кучи
В виндах и линупсах есть. И ничто не машало использовать билиботеку/написать свой для ДОЧ'а.
В ассемблере есть (точнее, могут быть) недокументированные инструкции, которые хуй знает что делают. Вполне можно считать это UB-ом. А еще, если взять процессор версии X, в котором инструкция A (т.е. некоторая некорректная последовательность байт, которая не может быть декодирована как инструкция) не поддерживается (при попытке ее исполнения будет брошено исключение), то в каком-то более новом процессоре версии X+1 могут додуматься в такой способ кодировать некоторые инструкции, и тогда это уже не будет приводить к броску исключения.
Например
З.Ы. Хотя... вызов недокументированной инструкции на старом проце всё-таки UB. Хуй знает что она там делала. Вроде не гарантируется, что все неизвестные инструкции должны кидать исключение?
В даташите может быть ничего не написано по поводу этой инструкции. Может даже так оказаться, что при попытке выполнения этой инструкции, процессор сгорает нахрен.
Вроде на интеле находили недокументированные опкоды которые что-то делают и не кидают.
The opcodes D6 and F1 are undefined opcodes reserved by the Intel 64 and IA-32 architectures.
Но надо ещё поискать как обстоят дела с недокументированными сочетаниями префиксов.
Other use of the 66H prefix is reserved; such use may cause unpredictable behavior.
The address-size override prefix (67H) allows programs to switch between 16- and 32-bit addressing. <...> Using this prefix and/or other undefined opcodes when operands for the instruction do not reside in memory is reserved; such use may cause unpredictable behavior.
Интел кажется что-то такое тоже изобрел
https://www.opennet.ru/opennews/art.shtml?num=52011
> Суть метода заключается в создании условий для возникновения непредвиденных искажений данных при вычислениях в SGX, от которых не защищает применение шифрования и аутентификации памяти в анклаве. Для внесения искажений, оказалось, можно использовать штатные программные интерфейсы для управления частотой и напряжением, обычно применяемые для снижения энергопотребления во время простоя системы и активации максимальной производительности при выполнении интенсивных работ. Характеристики частоты и напряжения охватывают весь чип, в том числе влияют на выполнение вычислений в изолированном анклаве.
> Изменяя напряжение, можно добиться возникновения условий, при которых заряда оказывается недостаточно для регенерации ячейки памяти внутри CPU, и её значение меняется. Ключевым отличием от атаки RowHammer является то, что RowHammer позволяет изменить содержимое отдельных битов в памяти DRAM, путём цикличного чтения данных из соседних ячеек, в то время как Plundervolt позволяет добиться изменения битов внутри CPU, когда данные уже загружены из памяти для выполнения вычислений. Подобная особенность позволяет обойти применяемые в SGX механизмы контроля целостности и шифрования данных в памяти, так как значения в памяти остаются корректными, но могут исказиться при операциях с ними до того, как результат будет записан в память.
Новая уязвимость у Интел процессоров позволяет управлять напряжением на камне +/- 0.5В, что легко может привести к его выходу из строя
https://www.linux.org.ru/forum/talks/15845897
Компания «Intel» пиздит идеи с говнокода.
Чтобы поведение какой-то конструкции было implementation-defined, оно должно быть задокументировано для всех платформ:
§3.12, [defns.impl.defined]
В результате имеем, что неподдерживаемые инструкции ассемблера — это и не UB, и не ID: с одной стороны, на платформах без поддержки данной инструкции поведение в принципе не определено (т. е. уже не ID), а с другой — на «хороших» платформах — точно задокументировано (не UB).
All new instructions defined for IA-32 processors use binary encodings that were reserved on earlier-generation processors. Attempting to execute a reserved opcode always results in an invalid-opcode (#UD) exception being generated.
6.15 Exception and interrupt reference
Interrupt 6 - Invalid Opcode Exception (#UD)
<...> The opcodes D6 and F1 are undefined opcodes reserved by the Intel 64 and IA-32 architectures. These opcodes, even through undefined, do not generate an invalid opcode exception.
Интел сам себе противоречит... Все опкоды равны, но некоторые равнее других.
На касты там больше похожи movzx и movsx.
Хм, или тупо скриптом Vindicar'а скрывать всех петухов, чтобы невалидное говно вида "вжрукк" не читать.
dwarf
dixed
Шитый код компактный, быстрый, а его интерпретатор реализуется парой инструкций. Вот минимальный адресный интерпретатор прямого шитого кода с примерами подпрограмм:
А в косвенном шитом коде в высокоуровневых определениях только адреса, и они могут хранится в секции с данными, а само ядро маленькое и может целиком поместиться в кешу. А вот пример адресного интерпретатора косвенного шитого кода, он не сложнее и не больше чем у прямого: А в подпрограмном шитом коде адресный интерпретатор не нужен, программа состоит из цепочки call'ов, и не сложно перейти от него к обычному нативному коду.
Теперь ваш ход.
В байкоде только КОП занимает один байт, но у него ещё могут быть аргументы, как в твоём примере у push (и в шитом коде тоже могут быть оргументы). Сам код получается компактный, но для его декодирования нужна как минимум одна таблица как минимум на 256 адресов (можно и меньше, но придётся добавлять условия в интерпретаторе байткола), вот примитивный интерпретатор бацкода: Как видно интерпретатор байткода сложнее чем у прямого или косвенного шитого кода, ему требуется дополнительная память для таблицы. Но его главное преимущество в том, что сам сконпелированный ьайткод лучше переносим чем шитый код.
У меня усё.
Если сильно хочеца генерь образ для gforth или просто бинарник. Вот только деконпелируется он просто, надо брать систему которая умеет в нативный код и оптимизации (типа SP-FORTH).
http://www.complang.tuwien.ac.at/forth/threaded-code.html
там есть примеры переносимых реализаций интерпретаторов, по ним кстати видно какая сишка убогая при своей-то низкоуровневости.
А вот бенчмарк разных техник интерпретации шитого кода:
http://www.complang.tuwien.ac.at/forth/threading/Во многих случаях обычно быстрее прямой. Баткода там, к сожалению нет, но наверное за него можно считать switch threading.
http://govnokod.ru/user/26089/codes
http://govnokod.ru/user/25786/codes
http://govnokod.ru/user/26137/codes
http://govnokod.ru/user/26135/codes
Токены можно легко переносить, а чтоб перенести косвенный шиткод надо генерить объектные файлы, и при загрузке просто подправить нужные адреса. Из форт-систем я знаю, что только GForth умеет делать переносимые образы:
На счёт других языков конпилирующихся в шиткоды я не знаю.
Кстати, прямой шиткод тоже можно переносить как косвенный, надо просто в начале всех определений ещё вставить платформо-специфичный кусочек.
> генерится чисто для скорости, а переносица все равно сырцы?
Тип того, в стандарте нет ничего про перенос сконпелированых программ, в каждой системе может быть по своему, а миожет и вообще не быть. А ещё шиткод может выигрывать по размеру у нативного, в какои-нибудь зачуханом контроллере это может оч важно.
> compile once, run everywhere не полу?
Token threading
З.Ы. Кстати поэтому косвенный jmp обычно в конце каждой примитивной команды накопипащен. А не один на всех.