пятница, 4 августа 2006 г.

Патч для spcacat

spcaview умеет подстраивать яркость изображения захватываемого камерой автоматически. А вот spcacat - необходимая для захвата изображения без его просмотра (в batch режиме) почему-то этого не умеет (хотя автор тот же самый). Сделал патчик, который это исправляет (ключ -b). Также добавил ключик -M который указывает, что надо пропустить y кадров, прежде чем "захватить" -N кадров. Это нужно, чтобы дать время на подстройку яркости - она подстраевается либо на основе пропущенного кадра, либо на основе захваченного.

Из-за абсолютно дибильного кода (серьезно, я хуже open source'ного кода не видел никогда) на написание патча ушло 1.5 часа, хотя работы там реально минут на 20-30.

Собственно патч.

суббота, 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

воскресенье, 16 апреля 2006 г.

apt-get install emacs-snapshot

Поставил emacs-snapshot (emacs 22 из cvs). Впечатления.

Первое что заметил сразу:
* Copy'n'past между emacs'ом и прочими программами заработал на все 100%.
* Новый emacs без правки конфигов пустился в терминальном окне (в 21.4 у меня русские буквы не вводились, а разбираться было лень).
* ibuffer-and-update почему-то стало открывать буфер со списком буферов в отдельном окошке, да еще и не делая его активным. Проблема быстро решилась собственной ф-ией my-ibuffer-and-update, так что проблема не критичная. Теперь в ibuffer можно настраивать группы буферов, например для разных проектов, для gnus'а и тд. Настроил пока только gnus (для gnus'овских буферов), custom (для буферов с настройками), fundamental (для прочего
хлама).

Благодрая changelog'у отметил для себя следующие приятные вещи:
* Если редактируемая строка шириной ровно в ширину экрана то дополнительный перевод строки больше не ставится, а если курсор нужно поместить за последний символ такой строки, то он появляется прям на правом 'поле для заметок'. Мне понравилось.
* longlines-mode. Так называемый soft перенос слов в длинных строках. Это когда автоматически происходит виртуальный перенос слов на новую строку, при этом при сохранении в файл или копировании в буфер обмена или другой буфер строка не будет содержать переносов строк. Повесил longlines-mode на f3.

Но самое интересно - ljupdate перестал логинится к серверу lievjournal! Как я с этим (извините меня) натрахался. Часа два убил. Проверил в буфере scratch соответствие между работой функций lj-md5 на одинаковый исходные строки считает одинаковый md5. С помощью ngrep и tcpdump ПОБАЙТНО сверил процесс авторизации ljupdate запущенной в старом emacs и новом, даже на параметры TCP соединений смотрел - но все совпадало, кроме challenge и response. Вместо получения challenge с сервера пробивал константное значение, но ljupdate как и в старом так и в новом emacs'е считал одинаковый response! Что еще можно подумать, если протокол общения верный и совпадает побайтно, кроме значений auth_challenge и auth_response, а auth_response вычисляется соответственно auth_challenge? Все расставила на места документация по функции md5.... Пояснение проблемы на ломаном английском и патч кинул в ljupdate.

среда, 29 марта 2006 г.

JDeveloper

Наконец-то поставил Oracle JDeveloper 10g на ноутбук. Не могу нарадоваться. После подучих и тормозных eclipse'ов прям бальзам на "душу". Хоть месяца три ничего не java и не писал, но все равно приятно..

Себе на будущее, чтобы не искать потом, отмечу, что системный шрифт в нем настраивается через файл ${HOME}/jdevhome/system/oracle.jdeveloper.${JDEV_VERSION}/ide.properties

среда, 22 февраля 2006 г.

Как я правил прошивку своего сотового телефона

В общем решил я тут починить джойстик у своего Siemens CX65 (ношу телефон в кармане джинс вот выпирающая часть джойстика и отломилась еще месяц назад). Взял телефон, снял панель съемную, достал клей моментальный, взял хреновину одну, чтобы приклеить её к остаткам джойстика, намазал джойстик, приставил хреновину, подождал - хреновина прилипла к рукам, а не куда надо. Проделал еще пару попыток, поматерился своей лени на тему зачистить, обезжирить и тд, одел съемную панель обратно, включил телефон и заметил, что джойстик теперь совсем перестал работать, наверное капелька клея скатилась по остаткам джойстика к контактам и изолировала их. Если раньше остатки джойстика можно было как-то ногтем или вспомогательным предметом двигать, чтобы добиться нажатия в нужном направлении, то теперь это стало бесполезно.

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

Первым делом требовалось вытащить эту самую прошивку из телефона, а для этого необходим шнур и програмное обеспечение. За четыре дня до прошлого нового года я купил шнур "телефон<->usb" за 1000 рублей, после чего, буквально в тот же день улетел на родину в Омск (из Москвы) на неделю, а потом в Киев еще недели на две. Когда же я вернулся в Москву и попробовал тот шнур, что купил вскрылись две детали: для телефонов сименс существует множество шнуров различающихся как по моделям телефонов, так и по функциональности. Мой шнур тогда не завелся ни в винде (с родными и не родным дровами), ни в линухе. А гарантия на обмен (15 дней) истекла, пока я мотался Москва->Омск->Москва->Киев->Москва. Но теперь, начитавшись форумов, я твердо решил взять этот шнур и переделать его из нерабочего в рабочий. Подключив его для верности в linux 2.6.15, дабы убедится, что он таки не работает, но по dmesg я увидел обратное:
drivers/usb/serial/usb-serial.c: USB Serial support registered for pl2303
pl2303 3-1:1.0: pl2303 converter detected
usb 3-1: pl2303 converter now attached to ttyUSB0

Поискав драйвера под винду конкретно для pl-2303 я завел его и в винде без всяких передлок. Теперь нужно программное обеспечение... к счастью, с ним никаких проблем, его полно в интернете.

Когда телефон подружился с компьютером, настало время сделать backup этого телефона: рассчитать все коды (HASH, SKEY, IMEI, BKEY, ESN) и записав их в файлик, сделать дампы фулфлэша, буткора (на всякий), лэнгпака (на всякий), еелайта и еефула. Теперь душа спокойна, шансы окончатильно убить тело уменьшились. Для того, чтобы не патчить старую прошивку, я заодно поставил sw50 взамен стоявшей 12 версии (обновил фуллфлэш, обновил файловую систему и мэппинг). На всякий случай снова сделал бэкапы всего подрят.

Бэкапы сделаны, прошивка лежит на винте, самое время поставить IDA, изучить ассемблер ARM710, найти в прошивке нужный код и наконец сделать патч. Для верности и на всякий случай (вдруг пригодится), собрал и поставил на свой debian binutils-arm-linux и компилятор gcc-arm-linux. Изучать набор инструкций ARM было достаточно необычно, интересно и приятно, все-таки набор отличается от x86 в лучшую сторону.

А дальше началось изучение прошивки. Процесс надо сказать неоднозначный.. то ты начинаешь впадать в уныние не знаешь с какой стороны подойти и понять какой вызов был для чего сделан, а то вдруг неожиданно находишь интересные куски кода. Процесс изучения прошивки напоминает следующую ситуацию: вас помещают в произвольное место гиганского лабиринта из которого вам надо найти выход, у вас есть возможность телепортироваться в случайное место лабиринта по желанию, в лабиринте темно, а на полу начерчена замкнутая схема, которую можно использовать на ощупь. Сравнение может быть не очень корректное, но процесс перемещения по функциям немного напоминает.. Радостными моментами в таком деле стали: распознавание стандартных функций типа memset, strcpy, strchr, strlen, printf и т.д., обнаружении функции вызываемой в случае ошибки (не понятно правда куда она ошибку девает, толи в лог, толи еще куда, но это и не важно, а важно, что она вызывается с текстовым описанием ошибки), функции isKeyGood и наконец функции KeyPress!!! А я ведь все время был рядом с этой заветной функцией... всего-то надо было внимательнее смотреть на BFC, хотя наверное эта внимательность пришла как раз в процессе разгребания прочих участков.

Теперь появилось чувство определенности. Самое главное - это найти где править, а это я уже сделал. Дело за малым - найти свободные участки прошвки, озу и написать патч. К сожалению скачать keil arm не получается - траффик на интернет аккаунте кончился, а keil весит под 50м, поэтому решаю покомпилировать с помощью arm-linux-as.

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

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




1, left-up 2, up 3, right-up
4, left 5, click 6, right
7, left-down 8, down 9, right-down

Как нетрудно заметить из функционального описания, программа должна использовать состояние. Выделяем под него 32 бита и находим свободную память для переменной с помощью ArmDebugger'а.

Алгоритм при любом подходе получается немного сложноватый, чтобы тратить время на его реализацию на ассемблере, поэтому я набросал его на C и скомпилировал с помощью arm-linux-gcc в объектный файл. Но не все так просто, как хотелось бы. Замечательный GCC сгенерировал код использующий не хилую таблицу абсолютных адресов (для switch'а), а так как этот код будет располагаться в совершенно другом участке памяти, то надо либо править адреса руками, либо думать как объяснить gcc/as, что я хочу код для определенного участка памяти. Как объяснить GCC/AS я не нашел :(. Но зато разобрался с ld, которым слинковал объектный файл, указав, что хочу секцию .text расположить строго по такому-то адресу. Получилось! Для того, чтобы получить vkp патч на основе elf файла, пишу скрипт на perl'е, на выходе которого получаю заветный патч.

Почти все сделано, напряжение и нетерпение растет! Загружаю патч в IDA, правлю в патче адрес переменной и адрес оригинального обработчика на реальные. Добавляю в патч изменения связанные с изменениями самой прошивки и вот патч готов!

Немного волнуясь, загружаю v_klay, заливаю патч в телефон.

Нажимаю кнопку включения - черный экран и ничего не происходит, что-то внутри опустилось. Нажимаю кнопку включения еще раз! О! Телефон включается - уже хорошо. Робко набираю первую цифру pin кода.. телефон издает характерный звук нажатия и цифра появляется на экране - значит нормальный режим отрабатывает верно. Захожу в меню и пробую режим имитации - работает, я могу бегать по меню.. работает все в том числе диагональные перемещения!

Даже не ожидал, что патч получится с первой попытки! Какой я молодец! :)))

А вот и сам патч.