суббота, 10 июня 2006 г.

Joyemu v3. Пишим сложный патч для телефона на C. Информация должна быть свободной.

Еще одной записью в TODO сегодня стало меньше, после новой версии патча, выпустить которую у меня наконец-то дошли руки. Что изменилось:
1. Теперь при включении режима эмуляции джойстика светодиоды в верхней части телефона горят и горят до выхода из эмуляции. Благодаря этому всегда понятно в каком мы сейчас режиме.
2. Нажатие на софт кнопки ничего не эмулирует. Иногда их надо нажимать, не выходя из эмуляции.
3. Блокировка клавиатуры эмуляцию выключает.

Новая версия патча в формате vkp.

Вопреки сложившейся традиции с форума сайта http://allsiemens.com я выложу и исходники патча, и все функции прошивки Siemens CX65 sw50, которые мне удалось найти (более 400), расскажу как писать патчи на C. Я не придерживаюсь мнения, что каждый новичок должен найти все эти функции сам - этот подход мне кажется бесполезной тратой времени, которое человек мог бы направить на что-то полезное, а не изобретать велосипед. Я также не сторонник подхода, когда создается элитарный клуб, между людьми которого передается информация - мне это несколько противно. Информация должна быть свободной.

Тем не менее, эта информация не освобождает от необходимости знать Assembler ARM.

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

Первое, что делает joyemu - перехватывает нажатие на клавишу. Для этого мы ищем в прошивке тот участок кода, который обрабатывает это событие. Нашли, допустим, по адресу ORIGINAL_KEY_HANDLER. Теперь надо найти свободное место в прошивке под наш код, который будет обрабатывать это нажатие (ищем в прошивке длинную последовательность FF). Пускай наш обработчик должен быть по адресу OUR_KEY_HANDLER. Передать управление на наш обработчик при нажатии на клавишу можно двумя способами. Найти все места из которых происходит переход в ORIGINAL_KEY_HANDLER. И заменить на переходы на OUR_KEY_HANDLER. Так обычно никто никогда не делает, ибо мест таких может быть много. Мы же вставляем по адресу ORIGINAL_KEY_HANDLER инструкцию для загрузки в регистр PC адрес OUR_KEY_HANDLER. То, что мы затерли этими инструкциями запоминаем, оно нам пригодится, когда мы захотим вызвать оригинальный обработчик. А код, который находится за нашей инструкцией перехода на OUR_KEY_HANDLER, будет иметь адрес ORIGINAL_KEY_HANDLER_CHUNK.
Получается вот что:

ROM:A121D4BC ORIGINAL_KEY_HANDLER:
ROM:A121D4BC 04 F0 1F E5 LDR PC, =OUR_KEY_HANDLER
ROM:A121D4C0 10 F3 65 A1 DCD OUR_KEY_HANDLER
ROM:A121D4C4 ORIGINAL_KEY_HANDLER_CHUNK:


А так как в алгоритме эмуляции нам понадобится вызов оригинального обработчика нажатия, то для этого надо где-то разместить тот самый затертый код, после которого вставить переход к ORIGINAL_KEY_HANDLER_CHUNK. Разместим этот код сразу перед
OUR_KEY_HANDLER по адресу MOVED_ORIGINAL_KEY_HANDLER. Выглядит это вот так (сначала идут 8 оригинальных байт, потом 4 байта инструкции перехода, после 8 байт данных для инструкций):

ROM:A165F2FC MOVED_ORIGINAL_KEY_HANDLER:
ROM:A165F2FC 70 40 2D E9 STMFD SP!, {R4-R6,LR}
ROM:A165F300 04 10 9F E5 LDR R1, =off_A8663120
ROM:A165F304 04 F0 1F E5 LDR PC, =ORIGINAL_KEY_HANDLER_CHUNK
ROM:A165F308 C4 D4 21 A1 DCD ORIGINAL_KEY_HANDLER_CHUNK
ROM:A165F30C 20 31 66 A8 DCD off_A8663120
ROM:A165F310 OUR_KEY_HANDLER:


Сохраняем изменения как vkp patch. Для этого в интернете валяется IDC скрипт для IDA, а также инструкции.

Из всех адресов, дальше нам понадобятся два:
MOVED_ORIGINAL_KEY_HANDLER - это та точка входа, куда мы должны сделать переход, если хотим вызывать родной обработчик нажатия.
OUR_KEY_HANDLER - здесь будет наш обработчик.

Теперь осталось только наш обработчик написать. Работа с ассемблером на этом заканчивается, на сцену выходит C. Для компиляции я использовал пакет gcc-4.1-arm для Debian. Начну с описания опций необходимых для компиляции под телефон:
-c -- делает объектный файл.
-mcpu=arm926ej-s -- компилировать под указанный процессор. Именно эти процы стоят в Siemens x65.
-mthumb-interwork -- компилировать инструкции дальнего перехода с учетом бита отвечающего за ARM/THUMB режим работы процессора. Короче вместо B и BL будет BX и BLX.
-fomit-frame-pointer -- а нафик он нужен?
-fno-unit-at-a-time -- это чтобы откомпилированные функции в объектном файле были в том же порядке, в котором они были в исходниках.
-mlong-calls -- ну длинные переходы типа.
Прочие опции типа -Wall -Os или -O3 - это на усмотрение.

Допустим мы написали C файл. Скомпилировали его с вышеперечисленными опциями в объектный файл. Теперь его надо слинковать в исполняемый файл elf. Это маленькое извращение нужно для того, чтобы линкер привел все relocation'ы в норму и разместил все по тем адресам, которые мы ему скажем. Нет, конечно можно при компиляции указать -fPIC и получить оверхеадный код, но мы пойдем другим путем. Опции линкера:
-s -- удаляет всю символьную информацию. А нафик она?
-nostdlib -- Не прилинковывать никаких левых библиотек.
-Bstatic -- Эта опция говорит о том, что все релокейшены будут жестко забиты. Никаких динамически подгружаемых библиотек. Статическая компиляция.
-X -- Забыл уже зачем добавил. Но наверное надо. Пусть будет.
-entry 0 -- стартовый адрес. 0 - адрес первой функции первого объектного файла.
-R local.symbols -- Файл, откуда будут подгружаться определения локальных символов.
-R ../CX65sw50dev/firmware.symbols -- определение символов прошивки.
--section-start .text=A165F310 -- адрес, по которому размещать код. Как раз тот самый адрес OUR_KEY_HANDLER.
--section-start .data=A80C8FFC -- адрес, по которому размещать данные (переменные).

Среди опций мелькнули два файла. Первый файл local.symbols:
my_original_press_key = 0xA165F2FC;
-- тут сказано, что C'ная функция с именем 'my_original_press_key' расположена по адресу '0xA165F2FC', а это тот самый адрес MOVED_ORIGINAL_KEY_HANDLER. Тоесть, чтобы вызвать из C оригинальный обработчик, я должен воспользоваться функцией void my_original_press_key (int keycode);

Файл ../CX65sw50dev/firmware.symbols, аналогичен, сюда я внес немного функций и переменных, которые всегда есть в прошивке sw50 у cx65, например кусок:

[...]
switch_phone_off = 0xA1240EF0;
dynamic_light_mode = 0xA863E6E0;

set_dynamic_light_mode = 0xA0B1ECDD;
locked = 0xA8656060;
[...]

Замечу, что если функция прошивки написана THUMB инструкциями, то к адресу нужно прибавить единичку, то есть адрес должен быть нечетным (младший бит включен).
Также напомню, что если мы хотим использовать внешние функции, то их прототипы где-то надо описать. Локальную в рамках патча функцию my_original_press_key я описал прям в .c файле, а функции и переменные прошивки в файле cx65sw50.h:

[...]
void switch_phone_off ();
void set_dynamic_light_mode (unsigned mode);

extern unsigned dynamic_light_mode;
extern unsigned locked;
[...]


Теперь все вспомогательные вещи описаны. Можно приступать к написанию самого патча.
Нужно помнить, что первая функция в объектном файле будет той функцией, что ляжет по адресу OUR_KEY_HANDLER. Еще нужно указывать глобальным переменным секцию .data, чтобы они вдруг не оказались в секции кода .text (как любит делать последний gcc), нужно это из-за того, что в телефоне есть флэш память (под код), а есть RAM (под переменные). Если попытаться изменить с помощью STR что-то, что лежит во флэше, то телефон пискнет и выключится.

К нашим баранам. Первой функцией будет pressKeyChunk, которая и будет эмулировать нажатия на джойстик путем хранения состояния, вызова к оригинальному коду и т.д. Все, что нужно этой функции объявлено перед ней прототипами. Чтобы отслеживать изменение состояния светодиодов и реагировать на блокировку клавиатуры, при первом нажатии на клавишу, будет запущен таймер с частотой вызова обработчика ~0.4с. Как это все сделано можно увидеть скачав исходный код (5kb).
Для компиляции патча необходим мой Набор-Разработчика-Патчей-Для-CX65-SW50 (6.5kb). Этот архив включает в себя:
Makefile.inc в котором прописаны правила сборки,
cx65sw50.h прототипы некоторых функций прошивки,
firmware.symbols адреса некоторых функций прошивки,
fw50.map адреса всех функций, что мне удалось найти в прошивке, пока не надоело,
mk_patch.pl скрипт для создания vkp патча на основе elf файла, использует в свой работе программу elfsh.

Конечный vkp файл создается на основе elf файла скриптом mk_patch.pl и добавлением заголовка - файла, который мы сделали из IDA с помощью IDC скрипта.

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

Немного слов о функциях прошивки которые я нашел (из них более 250 nucleus os).
Как я уже говорил, адреса этих функций находятся в файле fw50.map архива (6.5kb). Если первой цифрой адреса не является 'A', то значит это смещение функции внутри секции и надо перед использованием к адресу прибавить A0000000. Какое-то количество этих функций удалось насобирать по форуму. Немного - из других патчей. Из туториалов. Что-то нашел сам. Небольшое число функций nucleus os удалось получить по сигнатурам, остальные - мой кропотливый труд.

Перед использованием какой-то функции ее желательно проверить. Не гарантирую, что идентифицировал правильно и правильно интерпретировал.

Для использования функций операционной системы Nucleus PLUS достаточно посмотреть на nucleus.h который есть в демоверсии этого продукта, а также можно поискать документацию по этим функциям. Самое полное, что удалось найти - это pdf под названием 'Nucleus Plus Internals'.

P.S. Спасибо авторам программ IDA, ArmDebugger и V_klay, а также другим, кто писал софт для работы с Siemens x65. Без этих программ телефон скорее всего пришлось бы править паяльником или покупать новый.
P.S. Все это является художественным вымыслом. Совпадение с реальностью случайны. Слышал, что обезьяны в состоянии случайно Войну и Мир написать, чем я хуже?

четверг, 8 июня 2006 г.

Labtec Webcam Pro. Driver bugfix. Пример фиксинга драйвера.

Купил я тут вебкамеру. Выбирал такую, чтобы под линухом работала, была дешевой и качество имела получше. В выборе помогла вот эта страничка, на которой представлен перечень вебкамер поддерживаемых драйвером spca5xx с указанием качества картинки. Купил. Принес домой. Покдлючил. Скачал драйвера. Откомпилировал. Запустил xawtv, ужаснулся изображению. Повел камеру всторону и заметил, что компьютер "замер" и ни на что больше не реагирует. Оказалось, линух вис при резкой смене освещения.

Поискал в интернете - может кто уже сталкивался? Множество пользователей вебкамер с драйвером spca5xx сталкивались с этой проблемой. Кто-то советует не дергать камеру. Кто-то винист усб мост, хост или еще что-то. Автор драйвера вообще заявил, что драйвер написал для работы на мощьных компьютерах и собственно все у нас ok. Ближе всех к истине оказался вот этот пост. Но предложенное им решение (править код ядра линуха) это скорее лечение симптомов, а не устранение причины.
В чем суть проблемы. Есть у драйвера spca5xx такслет - это такая задача, которая шедулится из обработчика прерывания на этап окончания работы прерывания, тоесть прерывание побыстрому отработало, и запускается тасклет который может что-то делать продолжительное время (при этом другие прерывания не задисаблены). У каждого тасклета есть флаг состояния TASKLET_STATE_SCHED - если этот флаг включен, то считается, что тасклет еще не выполнялся, а только планируется к выполнению. Проблема возникла в том коде ядра, который активирует выполнение зашедуленного тасклета. Перед выполнением, ядро проверяет состояние этого флага, и если флаг включен, то сбрасывает его и выполняет код задачи. А вот если вдруг этот флаг выключен, но почему-то этот тасклет в очереди, то ядро выполняет функцию BUG() - тоесть распечатывает содержимое стека и вешается. Собственно, автор того поста так и предложил сделать - убрать вызов ф-ии BUG(). Если задуматься, почему к моменту выполнения тасклет оказывает без флага TASKLET_STATE_SCHED, можно предположить:
1. Бага в ядре - маловероятно.
2. Кто-то помимо ядра этот флаг сбрасывает. Проверка показала, что в драйверах spca5xx строка TASKLET_STATE_SCHED вообще отсутствует.
3. Память под тасклет выделяется один раз. Во время работы тасклета возникает прерывание и оно реинциализирует структуру тасклета (решедулит тасклет). После прерывания происходит возврат к тасклету, он оканчивает работу и сбрасывает флаг. А так как тасклет зашедулен в очереди, то к моменту его выполнения флаг оказывается сброшенным. Проверка показала, что это не так. Да и флаг сбрасывается до работы такслета, а не после.
4. Надо смотреть код.

В драйверах spca5xx есть всего одно место где шедулится тасклет. И сразу перед вызовом функции tasklet_schedule стоит функция tasklet_init. Все это обернуто циклом по пакетами полученным от usb драйвера и условием при котором шедулится тасклет: если найден конец фрейма (оноже начало следующего). Получается, что если обнаружен фрейм, то структура тасклета инициализируется и шедулится. А если обнаружено два фрейма? Вот она и бага. Если обнаружено два фрейма, то в ядре в очереди тасклетов окажется две записи которые ссылаются на один и тот же участок памяти.
Как пофиксить:
1. Поставить проверку перед инитом. Если уже зашедулено, то игнорируем и не шедулим больше. Минусы - фрейм теряется. Плюсы - просто.
2. Выделять память под тасклет динамически, в тасклет параметром передавать указатель на структуру в которой указатель на структуру тасклета и фрейм для декодирования. После декодирования память такслета очищать. Минусы - потеря производительности, муторно вообще. Плюсы - фреймы не теряются, правильное решение.
3. Замутить какую-нибудь очередь или пулл.. лениво.
Вобщем выбрал я первый вариант. И так сойдет %). Написал письмо в рассылку с патчем и обьяснениями на ломоном английском. Кому надо сам доделает нормально (open source модель такая вот).

А люблю я все-таки причины ошибок искать. Докапываться до сути... Такой процесс интересный. Расследование.

пятница, 2 июня 2006 г.

Один из проектов о кончине которого я жалею

Сегодня вдруг накатила тоска по одному из моих проектов, который я забросил в связи увлечением Haskell'ом. Разочаровавшись в кафедральной модели разработки O'Caml (невозможность вносить изменения сторонник разработчиков) я решил написать свой ML.. лучше, открытие и гибче. И вот лежит это недописанное чудо на винчестере в архивах, и уже с трудом-то компилируется (в Ocaml'евом camlp4 теперь не loc, а _loc называется, интерфейсы поменяли и тд). Но собрать понастальгировать таки удалось. Оставлю-ка я себе на память процесс работы с компилятором.

Компиляция, как это выглядело

Исходный файл.

---=====================================================================------
-- DESC: Just a test.
-- RUN: %(tdir)s/nnl-%(ckind)s %(src)s -O0 -o %(src)s.exec; %(src)s.exec
-- EXIT: 13
-- RM: %(src)s.exec
---=====================================================================------

let pairer = \x -> 3, x
and add = \(x, y) -> (ffi_call "builtin:int:+" (x:int) (y:int) : int)
and apply = \(f, arg) -> f arg in
apply (add, pairer 10)

./tools/nnl-llvm --show-all --keep-all ./test/00100-Base/0058000-Test.nn -o a.exec

АСТ дерево.

impl_file:
8.0 - 11.26: VE_let:
B_simple:
8.4 - 8.10: VP_ident: pairer
8.13 - 8.23: VE_fun:
8.14 - 8.15: VP_ident: x
8.19 - 8.23: VE_tuple:
8.19 - 8.20: VE_int: 3
8.22 - 8.23: VE_long_ident: x
B_simple:
9.4 - 9.7: VP_ident: add
9.10 - 9.69: VE_fun:
9.11 - 9.17: VP_paren:
9.12 - 9.16: VP_tuple:
9.12 - 9.13: VP_ident: x
9.15 - 9.16: VP_ident: y
9.21 - 9.69: VE_type_constraint:
9.22 - 9.62: VE_ffi_call: "builtin:int:+"
9.47 - 9.54: VE_type_constraint:
9.48 - 9.49: VE_long_ident: x
9.50 - 9.53: TE_long_ident: int
9.55 - 9.62: VE_type_constraint:
9.56 - 9.57: VE_long_ident: y
9.58 - 9.61: TE_long_ident: int
9.65 - 9.68: TE_long_ident: int
B_simple:
10.4 - 10.9: VP_ident: apply
10.12 - 10.30: VE_fun:
10.13 - 10.21: VP_paren:
10.14 - 10.20: VP_tuple:
10.14 - 10.15: VP_ident: f
10.17 - 10.20: VP_ident: arg
10.25 - 10.30: VE_flat:
10.25 - 10.26: VE_long_ident: f
10.27 - 10.30: VE_op:
10.27 - 10.30: VE_long_ident: arg
in_expr:
11.4 - 11.26: VE_flat:
11.4 - 11.9: VE_long_ident: apply
11.10 - 11.26: VE_op:
11.10 - 11.26: VE_paren:
11.11 - 11.25: VE_tuple:
11.11 - 11.14: VE_long_ident: add
11.16 - 11.25: VE_flat:
11.16 - 11.22: VE_long_ident: pairer
11.23 - 11.25: VE_op:
11.23 - 11.25: VE_int: 10
Типизированное АСТ дерево.

impl_file:
8.0 - 11.26: 13>35>int: VE_let:
B_simple:
8.4 - 8.10: 14>(15>int -> 16>(17>int * 18>15>int)): VP_ident: pairer/290
8.13 - 8.23: 14>(15>int -> 16>(17>int * 18>15>int)): VE_fun:
8.14 - 8.15: 15>int: VP_ident: x/300
8.19 - 8.23: 16>(17>int * 18>15>int): VE_tuple:
8.19 - 8.20: 17>int: VE_int: 3
8.22 - 8.23: 18>15>int: VE_long_ident: x/300
B_simple:
9.4 - 9.7: 21>(22>(24>int * 25>int) -> 23>int): VP_ident: add/314
9.10 - 9.69: 21>(22>(24>int * 25>int) -> 23>int): VE_fun:
9.11 - 9.17: 22>(24>int * 25>int): VP_paren:
9.12 - 9.16: 22>(24>int * 25>int): VP_tuple:
9.12 - 9.13: 24>int: VP_ident: x/322
9.15 - 9.16: 25>int: VP_ident: y/325
9.21 - 9.69: 23>int: VE_type_constraint:
9.22 - 9.62: 27>int: VE_ffi_call: "builtin:int:+"
9.47 - 9.54: 28>int: VE_type_constraint:
9.48 - 9.49: 29>int: VE_long_ident: x/322
9.50 - 9.53: 29>int: TE_long_ident: int
9.55 - 9.62: 30>int: VE_type_constraint:
9.56 - 9.57: 31>int: VE_long_ident: y/325
9.58 - 9.61: 31>int: TE_long_ident: int
9.65 - 9.68: 27>int: TE_long_ident: int
B_simple:
10.4 - 10.9: 33>(34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 35>int): VP_ident: apply/384
10.12 - 10.30: 33>(34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 35>int): VE_fun:
10.13 - 10.21: 34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)): VP_paren:
10.14 - 10.20: 34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)): VP_tuple:
10.14 - 10.15: 36>(39>37>(24>int * 25>int) -> 35>int): VP_ident: f/394
10.17 - 10.20: 37>(24>int * 25>int): VP_ident: arg/397
10.25 - 10.30: 35>int: VE_apply:
10.25 - 10.26: 39>37>(24>int * 25>int) -> 35>int: VE_long_ident: f/394
10.27 - 10.30: 39>37>(24>int * 25>int): VE_long_ident: arg/397
in_expr:
11.4 - 11.26: 13>35>int: VE_apply:
11.4 - 11.9: 42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 13>35>int: VE_long_ident: apply/384
11.10 - 11.26: 42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)): VE_paren:
11.11 - 11.25: 42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)): VE_tuple:
11.11 - 11.14: 44>(22>(24>int * 25>int) -> 23>int): VE_long_ident: add/314
11.16 - 11.25: 45>(17>int * 18>15>int): VE_apply:
11.16 - 11.22: 46>15>int -> 45>(17>int * 18>15>int): VE_long_ident: pairer/290
11.23 - 11.25: 46>15>int: VE_int: 10

Лямбда выражение.

(let "13>35>int" pairer/290
(fun "14>(15>int -> 16>(17>int * 18>15>int))" arg.2/-1
(let "16>(17>int * 18>15>int)" x/300
(var "15>int" "arg.2/-1") in
(block "16>(17>int * 18>15>int)"
(int "17>int" 3)
(var "18>15>int" "x/300")))) in
(let "13>35>int" add/314
(fun "21>(22>(24>int * 25>int) -> 23>int)" arg.1/-1
(let "27>int" tuple.2/-1
(var "22>(24>int * 25>int)" "arg.1/-1") in
(let "27>int" y/325
(field "25>int" (var "22>(24>int * 25>int)" "tuple.2/-1") 1) in
(let "27>int" x/322
(field "24>int" (var "22>(24>int * 25>int)" "tuple.2/-1") 0) in
(ffi_call "27>int" "builtin:int:+"
(var "29>int" "x/322")
(var "31>int" "y/325")))))) in
(let "13>35>int" apply/384
(fun "33>(34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 35>int)" arg.0/-1
(let "35>int" tuple.1/-1
(var "34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int))" "arg.0/-1") in
(let "35>int" arg/397
(field "37>(24>int * 25>int)"
(var "34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int))" "tuple.1/-1")
1) in
(let "35>int" f/394
(field "36>(39>37>(24>int * 25>int) -> 35>int)"
(var "34>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int))" "tuple.1/-1")
0) in
(apply "35>int"
(var "39>37>(24>int * 25>int) -> 35>int" "f/394")
(var "39>37>(24>int * 25>int)" "arg/397")))))) in
(apply "13>35>int"
(var "42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int)) -> 13>35>int" "apply/384")
(block "42>(36>(39>37>(24>int * 25>int) -> 35>int) * 37>(24>int * 25>int))"
(var "44>(22>(24>int * 25>int) -> 23>int)" "add/314")
(apply "45>(17>int * 18>15>int)"
(var "46>15>int -> 45>(17>int * 18>15>int)" "pairer/290")
(int "46>15>int" 10)))))))
Откомпилированный в LLVM код (llvm assember):

declare int "builtin:int:+" (int, int)

{int, int}* "fun:39" (int "var:arg.2/-1") {
"temp:34" = cast int "var:arg.2/-1" to int
"var:x/300" = cast int "temp:34" to int
"temp:37" = malloc {int, int}
"temp:35" = add int 0, 3
"temp:37_f0" = getelementptr {int, int}* "temp:37", int 0, uint 0
store int "temp:35", int* "temp:37_f0"
"temp:36" = cast int "var:x/300" to int
"temp:37_f1" = getelementptr {int, int}* "temp:37", int 0, uint 1
store int "temp:36", int* "temp:37_f1"
"temp:38" = cast {int, int}* "temp:37" to {int, int}*
ret {int, int}* "temp:38"
}

int "fun:32" ({int, int}* "var:arg.1/-1") {
"temp:21" = cast {int, int}* "var:arg.1/-1" to {int, int}*
"var:tuple.2/-1" = cast {int, int}* "temp:21" to {int, int}*
"temp:28" = cast {int, int}* "var:tuple.2/-1" to {int, int}*
"temp:29_addr" = getelementptr {int, int}* "temp:28", int 0, uint 1
"temp:29" = load int* "temp:29_addr"
"var:y/325" = cast int "temp:29" to int
"temp:25" = cast {int, int}* "var:tuple.2/-1" to {int, int}*
"temp:26_addr" = getelementptr {int, int}* "temp:25", int 0, uint 0
"temp:26" = load int* "temp:26_addr"
"var:x/322" = cast int "temp:26" to int
"temp:22" = cast int "var:x/322" to int
"temp:23" = cast int "var:y/325" to int
"temp:24" = call int "builtin:int:+" (int "temp:22", int "temp:23")
"temp:27" = cast int "temp:24" to int
"temp:30" = cast int "temp:27" to int
"temp:31" = cast int "temp:30" to int
ret int "temp:31"
}
int "fun:19" ({{int ({int, int}*)*}*, {int, int}*}* "var:arg.0/-1") {
"temp:8" = cast {{int ({int, int}*)*}*, {int, int}*}* "var:arg.0/-1" to {{int ({int, int}*)*}*, {int, int}*}*
"var:tuple.1/-1" = cast {{int ({int, int}*)*}*, {int, int}*}* "temp:8" to {{int ({int, int}*)*}*, {int, int}*}*
"temp:15" = cast {{int ({int, int}*)*}*, {int, int}*}* "var:tuple.1/-1" to {{int ({int, int}*)*}*, {int, int}*}*
"temp:16_addr" = getelementptr {{int ({int, int}*)*}*, {int, int}*}* "temp:15", int 0, uint 1
"temp:16" = load {int, int}** "temp:16_addr"
"var:arg/397" = cast {int, int}* "temp:16" to {int, int}*
"temp:12" = cast {{int ({int, int}*)*}*, {int, int}*}* "var:tuple.1/-1" to {{int ({int, int}*)*}*, {int, int}*}*
"temp:13_addr" = getelementptr {{int ({int, int}*)*}*, {int, int}*}* "temp:12", int 0, uint 0
"temp:13" = load {int ({int, int}*)*}** "temp:13_addr"
"var:f/394" = cast {int ({int, int}*)*}* "temp:13" to {int ({int, int}*)*}*
"temp:10" = cast {int ({int, int}*)*}* "var:f/394" to {int ({int, int}*)*}*
"temp:9" = cast {int, int}* "var:arg/397" to {int, int}*
"temp:11_fptr" = getelementptr {int ({int, int}*)*}* "temp:10", int 0, uint 0
"temp:11_f" = load int ({int, int}*)** "temp:11_fptr"
"temp:11" = call int "temp:11_f" ({int, int}* "temp:9")
"temp:14" = cast int "temp:11" to int
"temp:17" = cast int "temp:14" to int
"temp:18" = cast int "temp:17" to int
ret int "temp:18"
}

int %main () {
"temp:39" = malloc {{int, int}* (int)*}
"temp:39_fptr" = getelementptr {{int, int}* (int)*}* "temp:39", int 0, uint 0
store {int, int}* (int)* "fun:39", {int, int}* (int)** "temp:39_fptr"
"var:pairer/290" = cast {{int, int}* (int)*}* "temp:39" to {{int, int}* (int)*}*
"temp:32" = malloc {int ({int, int}*)*}
"temp:32_fptr" = getelementptr {int ({int, int}*)*}* "temp:32", int 0, uint 0
store int ({int, int}*)* "fun:32", int ({int, int}*)** "temp:32_fptr"
"var:add/314" = cast {int ({int, int}*)*}* "temp:32" to {int ({int, int}*)*}*
"temp:19" = malloc {int ({{int ({int, int}*)*}*, {int, int}*}*)*}
"temp:19_fptr" = getelementptr {int ({{int ({int, int}*)*}*, {int, int}*}*)*}* "temp:19", int 0, uint 0
store int ({{int ({int, int}*)*}*, {int, int}*}*)* "fun:19", int ({{int ({int, int}*)*}*, {int, in
t}*}*)** "temp:19_fptr"
"var:apply/384" = cast {int ({{int ({int, int}*)*}*, {int, int}*}*)*}* "temp:19" to {int ({{int ({int, int}*)*}*, {int, int}*}*)*}*
"temp:6" = cast {int ({{int ({int, int}*)*}*, {int, int}*}*)*}* "var:apply/384" to {int ({{int ({int, int}*)*}*, {int, int}*}*)*}*
"temp:5" = malloc {{int ({int, int}*)*}*, {int, int}*}
"temp:1" = cast {int ({int, int}*)*}* "var:add/314" to {int ({int, int}*)*}*
"temp:5_f0" = getelementptr {{int ({int, int}*)*}*, {int, int}*}* "temp:5", int 0, uint 0
store {int ({int, int}*)*}* "temp:1", {int ({int, int}*)*}** "temp:5_f0"
"temp:3" = cast {{int, int}* (int)*}* "var:pairer/290" to {{int, int}* (int)*}*
"temp:2" = add int 0, 10
"temp:4_fptr" = getelementptr {{int, int}* (int)*}* "temp:3", int 0, uint 0
"temp:4_f" = load {int, int}* (int)** "temp:4_fptr"
"temp:4" = call {int, int}* "temp:4_f" (int "temp:2")
"temp:5_f1" = getelementptr {{int ({int, int}*)*}*, {int, int}*}* "temp:5", int 0, uint 1
store {int, int}* "temp:4", {int, int}** "temp:5_f1"
"temp:7_fptr" = getelementptr {int ({{int ({int, int}*)*}*, {int, int}*}*)*}* "temp:6", int 0, uint 0
"temp:7_f" = load int ({{int ({int, int}*)*}*, {int, int}*}*)** "temp:7_fptr"
"temp:7" = call int "temp:7_f" ({{int ({int, int}*)*}*, {int, int}*}* "temp:5")
"temp:20" = cast int "temp:7" to int
"temp:33" = cast int "temp:20" to int
"temp:40" = cast int "temp:33" to int
ret int "temp:40"
}
Запуск LLVM оптимизации.

=====
Alias Analysis Evaluator Report =====
385 Total Alias Queries Performed
179 no alias responses (46.4%)
137 may alias responses (35.5%)
69 must alias responses (17.9%)
Alias Analysis Evaluator Pointer Alias Summary: 46%/35%/17%
68 Total ModRef Queries Performed
20 no mod/ref responses (29.4%)
0 mod responses (0.0%)
0 ref responses (0.0%)
48 mod & ref responses (70.5%)
Alias Analysis Evaluator Mod/Ref Summary: 29%/0%/0%/70%
*** Function unreachable from main: builtin:int:-
*** Function unreachable from main: builtin:int:/
*** Function unreachable from main: builtin:int:*
*** Function unreachable from main: builtin:int:%
*** Function unreachable from main: builtin:int:=
*** Function unreachable from main: builtin:int:<>
*** Function unreachable from main: builtin:int:<
*** Function unreachable from main: builtin:int:>
*** Function unreachable from main: builtin:int:<=
*** Function unreachable from main: builtin:int:>=
===== Alias Analysis Evaluator Report =====
48 Total Alias Queries Performed
17 no alias responses (35.4%)
25 may alias responses (52.0%)
6 must alias responses (12.5%)
Alias Analysis Evaluator Pointer Alias Summary: 35%/52%/12%
21 Total ModRef Queries Performed
0 no mod/ref responses (0.0%)
0 mod responses (0.0%)
0 ref responses (0.0%)
21 mod & ref responses (100.0%)
Alias Analysis Evaluator Mod/Ref Summary: 0%/0%/0%/100%
===== Alias Analysis Evaluator Report =====
6 Total Alias Queries Performed
2 no alias responses (33.3%)
2 may alias responses (33.3%)
2 must alias responses (33.3%)
Alias Analysis Evaluator Pointer Alias Summary: 33%/33%/33%
3 Total ModRef Queries Performed
0 no mod/ref responses (0.0%)
0 mod responses (0.0%)
0 ref responses (0.0%)
3 mod & ref responses (100.0%)
Alias Analysis Evaluator Mod/Ref Summary: 0%/0%/0%/100%
Результат оптимизации (llvm assember):

int %main() {
ret int 13
}
Целевой ассебмлер, AT&T нотация:

.text
.align 16
.globl main
.type main, @function
main:
subl $2, %esp
fnstcw (%esp)
movb $2, 1(%esp)
fldcw (%esp)
movl $13, %eax
addl $2, %esp
ret

Результат работы:
akshaal@akshaal:~/mnt/oldprojects/nnl-boot% ./a.exec
**** Exit code: 13

let f as (++++) = \_ -> \x -> (ffi_call "builtin:int:+" x 10 : int) in
f 6 (10 ++++ 3)

==================================================================
Исходный файл:

---=====================================================================------
-- DESC: Just a test.
-- RUN: %(tdir)s/nnl-%(ckind)s %(src)s -O0 -o %(src)s.exec; %(src)s.exec
-- EXIT: 23
-- RM: %(src)s.exec
---=====================================================================------

let f as (++++) = \_ -> \x -> (ffi_call "builtin:int:+" x 10 : int) in
f 6 (10 ++++ 3)

Результат:

int %main() {
ret int 23
}

....


make test

--- TEST RESULTS -------------------------------------------------------------

00100-Base.0001000-Empty.nn : PASS

00100-Base.0002000-GoodComment.nn : PASS

00100-Base.0003000-GoodDashComment.nn : PASS

00100-Base.0004000-GoodSolidComment.nn : PASS

00100-Base.0005000-GoodComment2.nn : PASS

00100-Base.0009000-HelpOption.nn : PASS

00100-Base.000A000-ShowTypedAstOption.nn : PASS

00100-Base.000B000-ShowAstOption.nn : PASS

00100-Base.000C000-StopAfterOption.nn : PASS

00100-Base.000D000-ImplSuffixOption.nn : PASS

00100-Base.000E000-ImplSuffix.nn : PASS

00100-Base.000F000-WithoutImplSuffix.nn : PASS

00100-Base.0010000-Struct.nn : PASS

00100-Base.0011000-GarbageAfterStruct.nn : PASS

00100-Base.0012000-StructWithoutEnd.nn : PASS

00100-Base.0013000-GraphOp.nn : PASS

00100-Base.0014000-IdentOp.nn : PASS

00100-Base.0015000-LongGraphOp.nn : PASS

00100-Base.0016000-LongIdentOp.nn : PASS

00100-Base.0016800-TypeConstraint.nn : PASS

00100-Base.0016880-Fun.nn : PASS

00100-Base.0017000-ShowIntAST.nn : PASS

00100-Base.0018000-ShowIdentAST.nn : PASS

00100-Base.0019000-ShowLongIdentAst.nn : PASS

00100-Base.001A000-ShowOpAst.nn : PASS

00100-Base.001B000-ShowLongOpAst.nn : PASS

00100-Base.001C000-ShowIdentOpAst.nn : PASS

00100-Base.001D000-ShowLongIdentOpAst.nn : PASS

00100-Base.001E000-ShowParenAst.nn : PASS

00100-Base.001F000-ShowFloatAst.nn : PASS

00100-Base.001F800-ShowStringAST.nn : PASS

00100-Base.0020000-ShowFlatAst.nn : PASS

00100-Base.0021000-ShowIfThenElseAst.nn : PASS

00100-Base.0022000-ShowLetAst.nn : PASS

00100-Base.0023000-ShowApplyAst.nn : PASS

00100-Base.0023800-ShowFFICallAst.nn : PASS

00100-Base.0023A00-ShowFFICall0Ast.nn : PASS

00100-Base.0024000-ShowStructAst.nn : PASS

00100-Base.0025000-ShowValueAst.nn : PASS

00100-Base.0026000-ShowImplAst.nn : PASS

00100-Base.0027000-ShowValuePatAst.nn : PASS

00100-Base.0028000-ShowOpValuePatAst.nn : PASS

00100-Base.0028800-ShowTypeConstraintAst.nn : PASS

00100-Base.0029000-ShowIntTypedAST.nn : PASS

00100-Base.002A000-ShowFloatTypedAST.nn : PASS

00100-Base.002B000-ShowParenIntTypeAST.nn : PASS

00100-Base.0030000-ShowNotTypedAST.nn : PASS

00100-Base.0032000-ShowPlusTypedAST.nn : PASS

00100-Base.0033000-ShowPlusUseTypedAST.nn : PASS

00100-Base.0034000-ShowPlusUseAsOpTypedAST.nn : PASS

00100-Base.0034800-ShowTypeConstraintTypedAst.nn: PASS

00100-Base.0034880-ShowFloatTypeConstraintTypedAst.nn: PASS

00100-Base.0035000-ShowLetInTypedAST.nn : PASS

00100-Base.0035800-ShowFFICallTypedAST.nn : PASS

00100-Base.0035900-ShowFFICall0TypedAST.nn : PASS

00100-Base.0037000-StopAfterLambda.nn : PASS

00100-Base.0038000-ShowLambdaOption.nn : PASS

00100-Base.0039000-ShowIntLambda.nn : PASS

00100-Base.003A000-ShowFloatLambda.nn : PASS

00100-Base.003B000-ShowNotLambda.nn : PASS

00100-Base.003C000-ShowApplyLambda.nn : PASS

00100-Base.003D000-ShowIfThenElseLambda.nn : PASS

00100-Base.003E000-ShowOpLambda.nn : PASS

00100-Base.003E800-ShowFFICallLambda.nn : PASS

00100-Base.003E900-ShowFFICall0Lambda.nn : PASS

00100-Base.003E980-ShowLetInLambda.nn : PASS

00100-Base.003F000-EqPrecedence.nn : PASS

00100-Base.0040000-StopAfterCodegen.nn : PASS

00100-Base.0041000-ShowCodeOption.nn : PASS

00100-Base.0042000-OOption.nn : PASS

00100-Base.0042800-O0Option.nn : PASS

00100-Base.0043000-ReturnInt.nn : PASS

00100-Base.0044000-Return123Int.nn : PASS

00100-Base.0045000-LetIn.nn : PASS

00100-Base.0046000-LetInWithRebinding.nn : PASS

00100-Base.0047000-FFIAndLet.nn : PASS

00100-Base.0048000-ShowAllOption.nn : PASS

00100-Base.0049000-KeepAllOption.nn : PASS

00100-Base.004A000-ShowFunAst.nn : PASS

00100-Base.004B000-ShowFunTypedAst.nn : PASS

00100-Base.004C000-ShowFunLambda.nn : PASS

00100-Base.004D000-FunApply.nn : PASS

00100-Base.004E000-ShowTupleAst.nn : PASS

00100-Base.004F000-ShowTupleTypedAst.nn : PASS

00100-Base.0050000-ShowTupleLambda.nn : PASS

00100-Base.0051000-ShowTuplePatternAst.nn : PASS

00100-Base.0052000-ShowTuplePatternTypedAst.nn: PASS

00100-Base.0053000-ShowTuplePatternLambda.nn : PASS

00100-Base.0054000-Tuple.nn : PASS

00100-Base.0055000-Tuple2.nn : PASS

00100-Base.0055800-ShowParenPatternAst.nn : PASS

00100-Base.0055900-ShowParenPatternTypedAst.nn: PASS

00100-Base.0056000-Tuple3.nn : PASS

00100-Base.0057000-CommFunPreced.nn : PASS

00100-Base.0058000-Test.nn : PASS

00100-Base.0059000-ShowAnyPatternAst.nn : PASS

00100-Base.005A000-ShowAnyPatternTypedAst.nn : PASS

00100-Base.005A800-ShowAnyPatternLambda.nn : PASS

00100-Base.005B000-AnyPattern.nn : PASS

00100-Base.005C000-Test.nn : PASS

00100-Base.005D000-Test.nn : PASS

00100-Base.005E000-Test.nn : PASS

00100-Base.005F000-Test.nn : PASS

00100-Base.0060000-ShowAsPatternAst.nn : PASS

00100-Base.0061000-ShowAsPatternTypedAst.nn : PASS

00100-Base.0062000-ShowAsPatternLambda.nn : PASS

00100-Base.0063000-AsPattern.nn : PASS

00100-Base.0064000-Test.nn : PASS

00100-Base.0065000-ShowTypeConstraintPatternAst.nn: PASS

00100-Base.0066000-ShowTypeConstraintPatternTypedAst.nn: PASS

00100-Base.0067000-Test.nn : PASS

00100-Base.0068000-ShowGraphLetBindingAst.nn : PASS

00100-Base.0068800-ShowGraphLetBindingAst2.nn : PASS

00100-Base.0069000-ShowGraphLetBindingTypedAst.nn: PASS

00100-Base.006A000-ShowGraphLetBindingLambda.nn: FAIL
Test doesn't exit with 0. Regexp '^.*\\(let "\\d+>\\(\\d+>int \\* \\d+>int\\)" y/\\d+.*$' not found in stdout.

TestCompiler.description:
Show lambda of graph operator binding in let statement.

TestCompiler.exit:
2

TestCompiler.run:
./tools/nnl-llvm --stop-after lambda --show-lambda
/home/akshaal/mnt/oldprojects/nnl-boot/test/00100-Base/006A000-ShowGraphLetBindingLambda.nn

TestCompiler.stderr:
Fatal error: exception
Match_failure("lib/NNL/Lambda/NNLLambdaTranslate.ml", 146, 7)

TestCompiler.stdout:

qmtest.end_time:
2006-07-02T11:23:48Z

qmtest.start_time:
2006-07-02T11:23:48Z

qmtest.target:
local

00200-Backend.00100-LLVM.00006000-KeepCodeOption.nn: PASS

00200-Backend.00100-LLVM.00007000-KeepBytecodeOption.nn: PASS

00200-Backend.00100-LLVM.00008000-KeepLinkedOption.nn: PASS

00200-Backend.00100-LLVM.00009000-KeepOptimizedOption.nn: PASS

00200-Backend.00100-LLVM.0000A000-KeepAsmOption.nn: PASS

00300-Runtime.00100-BuiltinIntEq.nn : PASS

00300-Runtime.00200-BuiltinIntEq2.nn : PASS

00300-Runtime.00300-BuiltinIntPlus.nn : PASS

00300-Runtime.00380-BuiltinIntPlus2.nn : PASS

00300-Runtime.00400-BuiltinIntMinus.nn : PASS

00300-Runtime.00480-BuiltinIntMinus2.nn : PASS

00300-Runtime.00500-BuiltinIntMult.nn : PASS

00300-Runtime.00580-BuiltinIntMult2.nn : PASS

00300-Runtime.00600-BuiltinIntDiv.nn : PASS

00300-Runtime.00680-BuiltinIntDiv2.nn : PASS

00300-Runtime.00700-BuiltinIntMod.nn : PASS

00300-Runtime.00780-BuiltinIntMod2.nn : PASS

00300-Runtime.00800-BuiltinIntNe.nn : PASS

00300-Runtime.00880-BuiltinIntNe2.nn : PASS

00300-Runtime.00900-BuiltinIntLt.nn : PASS

00300-Runtime.00980-BuiltinIntLt2.nn : PASS

00300-Runtime.00990-BuiltinIntLt3.nn : PASS

00300-Runtime.00A00-BuiltinIntGt.nn : PASS

00300-Runtime.00A80-BuiltinIntGt2.nn : PASS

00300-Runtime.00A90-BuiltinIntGt3.nn : PASS

00300-Runtime.00B00-BuiltinIntLe.nn : PASS

00300-Runtime.00B80-BuiltinIntLe2.nn : PASS

00300-Runtime.00B90-BuiltinIntLe3.nn : PASS

00300-Runtime.00C00-BuiltinIntGe.nn : PASS

00300-Runtime.00C80-BuiltinIntGe2.nn : PASS

00300-Runtime.00C90-BuiltinIntGe3.nn : PASS

--- TESTS THAT DID NOT PASS --------------------------------------------------

00100-Base.006A000-ShowGraphLetBindingLambda.nn: FAIL
Test doesn't exit with 0. Regexp '^.*\\(let "\\d+>\\(\\d+>int \\* \\d+>int\\)" y/\\d+.*$' not found in stdout.


--- STATISTICS ---------------------------------------------------------------

146 tests total
1 ( 1%) tests FAIL
145 ( 99%) tests PASS