четверг, 31 января 2008 г.

Баг процессора?

Странную штуку обнаружил. Судя по всему команда pop cs должна быть интерпретирована как двухбайтовая, хотя код имеет однобайтовый.

000 sreg2 111, при sreg2 равном 01, что означает cs, даст код 0x0f, с которого начинаются все двухбайтовые команды.

Хотя с другой стороны эта же команда может быть закодирована через 0x0f 0x89, и скорее всего любой вменяемый ассемблер должен так и поступить. Проверять лень.

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

    jmp KERNEL_CODE_SELECTOR:label
label:

а
    push KERNEL_CODE_SELECTOR
    pop cs

При условии, что eip не изменяется.

PS: В принципе возникноверие этой нестыковки понятно. На 286 было мало команд и мало регистров, двух бит хватало на сегментный регистр, но в 386 появились дополнительные сегментные регистры fs и gs, кроме того 256 кодов стало не хватать, а других свободных кодов не было, и видимо было принято решение использовать для расширенных инструкций код 0x0f, посколько никто никогда не использует короткую форму pop cs. Да и длинная по большому счету не нужна никому.

PPS: B IA32 Software Developer Manual кстати, в описании команды pop, написано
The POP instruction cannot pop a value into the CS register. To load the CS register from the stack, use the RET instruction.
Но ведь закодировать то можно. Прям загадка какая-то. :)

понедельник, 28 января 2008 г.

Разворот стека методом прямой трассировки кода.

Во, как загнул... и это не шутка, метод уже реально работает.

RISC - это наверное рай для программистов на ассемблере. И для программистов компиляторов заодно. Это же счастье, когда любая инструкция занимает ровно 32 бита, и общее количество инструкций порядка 3 дестков...

Но нет, мы на земле. Количество инструкций IA32 не поддается трезвому подсчету. Но мы всетаки пишем и не компилятор, нам достаточно лишь достоверно выяснить, какая функция какую вызывает, на основании имеющегося под рукой стека.

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

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

Можно сказать что это режим разворота без точки выполнения. Мы еще будем к нему возвращаться в случае потери адреса.

    инструкция call (в любой форме)
value <-- адрес возврата


Стоит отметить, что в новой версии я везде проверяю доступность памяти. Первая версия разворачивателя вполне могла бы вызвать PageFault.

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

Кого-то, может быть, пугает слово трассировка, но не стоит пугаться. Нам вовсе не обязательно эмулировать все инструкции. Мы анализируем следующие группы инструкций:
Инструкции, модифицирующие содержимое регистра esp, двигают указатель стека; Инструкции безусловных переходов передвигают указатель трассировки, за исключением случая, когда переход осуществляется на саму инструкцию; Инструкции условных переходов проверяются по обоим ветвям. Что касается различных форм инструкции call - то адрес в стеке обязательно должен соответствовать адресу следующей инструкции, иначе этот call протрассировывается мимо. По разумным соображениям я не включаю в список команд вызова различные прерывания. Да и не все call'ы одинаково полезны, как я уже писал раньше. Я могу точно определять явно заданные адреса, но косвенные формы лишь показывают что вызов есть, но куда было передано управление - остается неизвестным, происходит потеря адреса.

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

Остальные инструкции достаточно просто пропустить, но тут то нас поджидает один неприятный момент, на IA32 инструкции имеют очень переменную длину, и определение длины текущей инструкции - задача весьма нетривиальная. Мне удалось решить ее с помощью двух таблиц, по 256 байт каждая, и небольшой функции. Но таблицы я еще не заполнил до конца.

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

Пока что, без переходов через шлюзы - результат вполне обнадеживающий:

CallStack: Stopped in StubKernelUsePage+122 (0x00102a00)
CallStack: Called by StubPageInitMode+31 (0x00100df9)
CallStack: Called by StubEntry+587 (0x00101428)
CallStack: Called by unknown+0 (0x001000d4)

Последняя точка осталась неизвестной, потому, что для определения символа необходимо отмотать по коду несколько команд назад StubEntryLo=0x001000cc, но это мелочи по большому счету.

пятница, 25 января 2008 г.

.bss в файле???

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

Ага, понятно, все происходит из за стека, который я хочу включить в .bss, но именую словом .stack.

Не знаю, почему в binutils или в gcc или где там, уж не знаю, придают такое значение именам, но
3 .stack 00001000 00000000 00000000 00000298 2**0
CONTENTS, READONLY

В то время как переименованная секция
3 .bss.stack 00001000 00000000 00000000 00000298 2**0
ALLOC


Теперь стало гораздо лучше.

PS: У меня почему-то комплекс, боязнь толстых программ. А еще у меня есть старое ядро, которое полностью занимало 17KiB, оно у меня как некий образец объема. Новое конечно будет больше, но пока стрипнутое занимает 15KiB.

В любом случае, комплекс - не комплекс, но 5 килобайт нулей - это слишком расточительно.

понедельник, 21 января 2008 г.

Что-то великоватый Image...

Собираю новое ядро (2.6.23) для своего LPC2468, и что-то странное происходит с Image...

В то время, как vmlinux вполне корректен на первый взгляд...
$ file vmlinux
vmlinux: ELF 32-bit LSB executable, ARM, version 1, statically linked, not stripped
$ ls -la vmlinux
-rwxr-xr-x 1 dron dron 1919833 Янв 21 10:08 vmlinux


Этого совершенно нельзя сказать про полученный, стандартными средствами, надо сказать, Image.
$ ls -la arch/arm/boot/Image
-rw-r--r-- 1 dron dron 2685758032 Янв 21 10:08 arch/arm/boot/Image
$ file arch/arm/boot/Image
arch/arm/boot/Image: X11 SNF font data, LSB first


Надо сказать что в старом ядре linux.bin собирался кастомной рулей:
linux.bin: vmlinux FORCE
    @$(OBJCOPY) $(OBJCOPYFLAGS) vmlinux $@
    @echo ' Kernel: $@ is ready'


Но лично я не вижу никаких принципиальных отличий от стандартной рули для Image:
$(obj)/Image: vmlinux FORCE
    $(call if_changed,objcopy)
    @echo ' Kernel: $@ is ready'


Которые собственно разворачиваются в
arm-linux-objcopy -O binary -R .note -R .comment -S vmlinux linux.bin
arm-linux-objcopy -O binary -R .note -R .comment -S vmlinux arch/arm/boot/Image

соответственно. Пока даже мыслей нету - почему возникает такая ерунда.

Буду думать.

PS: Ну все ясно. Как говорится - нечего на objcopy пинять, коли vmlinux кривой. в него затесалась секция .note.gnu.build-id, замапленная на 0, в то время как основной код замаплен на 0xa0008000. вот и получается бинарь в 3 гига. Прибил секцию - пришло счастье...

$ ~/arm-linux/bin/arm-linux-objdump -h vmlinux
Sections:
Idx Name Size VMA LMA File off Algn
0 .note.gnu.build-id 00000024 00000000 00000000 00008000 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA, LINK_ONCE_DISCARD
1 .text.head 00000184 a0008000 a0008000 00010000 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE

четверг, 17 января 2008 г.

Массивные изменения с контролем версий.

Обычно изменения исходников достаточно немногочисленны и разобраться с ними вручную не составляет труда. Но иногда возникают сложности...

Я сейчас строю небольшой дистрибутивчик, встраиваемое решение. Состоит он из ядра linux, busybox и одной программочки из util-linux. Минимум. В оригинале система была построена на базе uClinux, но сделать с ним что-то было весьма не просто, старый, ничего не собирается. Поэтому я его распотрошил, выдернул ядро 2.6.11.8 со всякими патчами как было, взял свежий busybox, свежий util-linux, написал Makefile который все быстро билдит почти с учетом всех зависимостей.

Busybox конечно глючная штука. никак с ним не поборюсь, но основные утилиты работают. Самое страшное для меня - это ядро. Надо отметить, что погружал я его в систему контроля версий почастям... сперва ядро 2.6.11.8, потом hsc0 патч, потом погрузил имеющуюся версию и теперь могу отделить патч от разработчика платы в чистом виде. Кроме того на него уже наложен мой патч на шифрование, но там все просто.

От проекта требуется в первую очередь работать с USB, причем не только как хост, но и как девайс. Вот с этим какраз возникают проблемы, которые заключаются в том, что не все флешки одинаково качественны. И не всякое чтение дойдет до середины флешки.

Это была присказка.. или как говорят - преамбула... теперь наступила амбула.

Поскольку в текущем состоянии все работает, имеет смысл перейти на всежее ядро, предполагаю, что в последних ядрах USB стек сильно отличается от ядра 2.6.11.8, которое выпущено в 2005 году. Если мне не изменяет память USB 2.0 тогда только зародилось.

Делаем бранч, но чтобы не раздувать БД не просто меняем одно ядро на другое, а аккуратно патчим с текущего до последнего. При этом необходимые патчи на архитектуру и на шифрование конечно исчезают, но я их потом аккууратненько наложу. С архитектурой всеравно без напильника не обойтись.

Вот после этого и наступила засада... svn status показывает список изменений, необъятного размера, где часть файлов исчезла, часть файлов появилась, и еще много просто модифицировалось. Надо думать как аккуратно зафиксировать все это. Может быть, пришедшее ко мне в голову решение весьма тривиально, но тем не менее оно достойно того, чтобы взять его на вооружение.

$ svn status | grep \! | sed -e 's/\!//g' | xargs svn rm
$ svn status | grep ? | sed -e 's/\?//g' | xargs svn add
$ svn commit

Первая команда удаляет все исчезнувшие файлы, вторая добавляет все новые. И фиксируем.
Такой вот узелок на память.

вторник, 15 января 2008 г.

arm-linux toolchain (рецепт)

Вот наконец оно заработало.

binutils-2.18 не требует патчей

gcc-4.2.2 требует gcc-arm-softfloat.patch. В отличии от остальных - этот патч известный, выдернул из buildroot наверное, не помню точно, хотя и в интернете можно найти. Лечит отсутствие -lfloat.

uClibc-0.9.29 требует uClibc-msync-redeclaration.patch.

linux kernel headers я взял свои текущие (2.6.11.8-hsc0-ea). Не знаю точно, насколько сильно конфигурация ядра влияет на устанавливаемые заголовки, но предпочел взять и ядро и конфигурацию свою. так вернее.

elf2flt-src-20060506.tar.bz2. --disable-got-check не помог elf2flt генерировать файлы правильно, поэтому прищлось вырезать все лишнее. После наложения патча elf2flt-nogot.patch elf2flt будет генерировать исключительно Load-to-ram бинари.

На всякий случай приложу sha1sum исходных архивов.
fdec92e9dfc6c32155869f3910f47041c78e2277 binutils-2.18.tar.bz2
b7046a127b91fd58b5680e1c8fcdab2bd6d9cc5a elf2flt-nogot.patch
61b2133609e44e470d07ee66eb4d7ccfcf33e96e elf2flt-src-20060506.tar.bz2
dcf2139e0f318850d475a6af3dcd5f176f1acb0e gcc-4.2.2.tar.bz2
f2df77f26528fd18466538d0cb2cf5e93d0527f3 gcc-arm-softfloat.patch
1c5a36dc2cfa58b41db413190e45675c44ca4691 uClibc-0.9.29.tar.bz2
7508ee533004dec56070fbe221ccfea1be60389b uClibc-msync-redeclaration.patch


Порядок сборки следующий:
  1. binutils:
    ./configure --prefix=$(PREFIX) --with-sysroot=${PREFIX} --target=arm-linux --disable-nls
    make
    make install

    После сборки исходники не удалять, они еще понадобяться для elf2flt.

  2. gcc (стадия первая):
    ./configure --prefix=$(PREFIX) --target=arm-linux --with-sysroot=${PREFIX} --with-float=soft --with-arch=armv4t --with-tune=arm7tdmi --with-newlib --enable-languages=c --enable-threads=no --disable-shared --disable-nls --disable-multilib --disable-tls --disable-mudflap
    make
    make install

    После сборки и установки binutils в пути (PATH) необходимо добавить $(PREFIX)/bin.

  3. заголовки ядра:
    • Если у вас ядро свежее 2.6.18, то можно сделать так:
      make ARCH=arm CROSS_COMPILE=arm-linux- headers_install INSTALL_HDR_PATH=$(PREFIX)/usr

    • В противном случае (более старое ядро) придется мучаться:
      make ARCH=arm CROSS_COMPILE=arm-linux- prepare
      mkdir -p $(PREFIX)/usr/include
      cp -a ./include/linux $(PREFIX)/usr/include/linux
      cp -a ./include/asm-arm $(PREFIX)/usr/include/asm
      cp -a ./include/asm-generic $(PREFIX)/usr/include/asm-generic


  4. uClibc:
    make CROSS=arm-linux- menuconfig
    make CROSS=arm-linux- KERNEL_HEADERS=$(PREFIX)/usr/include
    make CROSS=arm-linux- install PREFIX=$(PREFIX)/ RUNTIME_PREFIX= DEVEL_PREFIX=usr/ KERNEL_HEADERS=$(PREFIX)/usr/include


  5. gcc (финальная стадия):
    ./configure --prefix=$(PREFIX) --target=arm-linux --with-sysroot=${PREFIX} --with-float=soft --with-arch=armv4t --with-tune=arm7tdmi --enable-languages=c --enable-threads=posix --disable-shared --disable-nls --disable-multilib --disable-mudflap
    make
    make install


  6. elf2flt:
    ./configure --prefix=$(PREFIX) --target=arm-linux --with-binutils-include-dir=../binutils/include/ --with-bfd-include-dir=../binutils/bfd/ --with-libbfd=../binutils/bfd/libbfd.a --disable-got-check
    make
    make install


И на последок самое главное - привожу Makefile, которым все собирается. Я думаю никому не составит труда заточить его под свои нужды или переработать в шелл скрипт. Мне Makefile удобнее.

PS: Я еще не разобрался как заставить работать init из busybox, но это уже вопрос времени. Шеллы всякие в качестве инитов или скрипты у меня прекрасно запускаются. Можно жить.

О, майн GOT...

Мне удалось собрать Hello world... и даже simpleinit из uClinux...

А вот Busybox пока не удалось, и я даже знаю почему.
$ ~/arm-linux/bin/arm-linux-flthdr busybox
busybox
Magic: bFLT
Rev: 4
Build Date: Tue Jan 15 13:48:30 2008
Entry: 0x6c0
Data Start: 0x3f5a0
Data End: 0x46540
BSS End: 0x590c0
Stack Size: 0x4e20
Reloc Start: 0x46540
Reloc Count: 0x12a
Flags: 0x2 ( Has-PIC-GOT )


В то время, как
$ ~/arm-linux/bin/arm-linux-flthdr init
init
Magic: bFLT
Rev: 4
Build Date: Mon Jan 14 17:42:41 2008
Entry: 0x50
Data Start: 0x6980
Data End: 0x7620
BSS End: 0x19070
Stack Size: 0x1000
Reloc Start: 0x7620
Reloc Count: 0xe5
Flags: 0x1 ( Load-to-Ram )


Насколько удалось выяснить, PIC-GOT это такой позиционно независимый бинарник, который может запускаться с любого места памяти. Это как-то связано с ключевым словом XIP (eXecute In Place), которое используется в ядре. У этого бинарника тоже есть релоки, но они какие-то особенные.

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

Но если гора не идет к Магомету, значит надо отучить elf2flt генерировать PIC_GOT. После долгих поисков в интернете обнаружил опцию configure у elf2flt, которую сначала не приметил, наверное потому что не знал что такое got.
elf2flt $ ./configure --help
...
Optional Features:
...
--disable-got-check - disable check for GOT (needed on H8)
...

Ага, это нужно не только H8, но и мне! Посмотрим что из этого выйдет. Но как приятно было увидеть это:
## Starting application at 0xA0008000 ...
Linux version 2.6.11.8-hsc0 (dron@mdf2007) (gcc version 4.2.2) #2 Tue Jan 15 10:14:59 MSK 2008
CPU: Philips-lpc24xx [24000000] (ARMv3)
...
Freeing init memory: 80K
Hello ARM!

понедельник, 14 января 2008 г.

И снова toolchain...

Снапшот от buildroot, как оказалось, кривой. elf2flt, входящая в его состав ругается на отсутствие секции .text, и я не знаю че с этим поделать. Взял elf2flt из состава devkitPro, с ним тулчейн генерирует правильные флэтовые бинари. Но несмотря на успешную сборку тулчейна (в состав которого вошли gcc-4.2.2, binutils-2.18 и uClibc-9.29), и вполне работающее ядро, дальнейших успехов что-то не наблюдается.

## Starting application at 0xA0008000 ...
Linux version 2.6.11.8-hsc0 (dron@mdf2007) (gcc version 4.2.2) #2 Mon Jan 14 17:53:46 MSK 2008

То ли слишком старое ядро имеет несоответствующую текущему положению дел поддержку flat, то ли компилятор генерирует не совсем корректные бинари (хотя я вроде бы указываю arm7tdmi). Обновлять ядро я пока не хочу, это не слишком просто, учитывая тот факт, что на это ядро наложены архитектуры от EmbeddedArtists. Хочу сперва добиться работоспособности.

Но моя проблема еще в том, что даже оригинальный uСlinux (от EmbeddedArtists) на собранном мною тулчейне не работает...

Freeing init memory: 80K
BINFMT_FLAT: reloc outside program 0x14690000 (0 - 0x19034/0x6940), killing init!
BINFMT_FLAT: reloc outside program 0x14690000 (0 - 0x19034/0x6940), killing init!
BINFMT_FLAT: reloc outside program 0xffffffff (0 - 0x4b864/0x2c660), killing sh!

А мои бинари и того хуже...

Freeing init memory: 80K
Unhandled fault: alignment exception (0x5000101) at 0x0000012c
Unhandled fault: alignment exception (0x5000101) at 0x0000012c
Unhandled fault: alignment exception (0x5000101) at 0x0000012c

Когда я разберусь со всеми этими глюками - я обязательно опишу сценарий сборки тулчейна для ARM, ибо на своем опыте убедился насколько востребована эта тема.

пятница, 11 января 2008 г.

arm toolchain

Не так то просто собрать свой тулчейн.

Поскольку стандартная прошивка LPC писалась под старый uclinux, то мне просто необходимо было иметь binutils с elf2flt, потому что там основной формат flat. К тому же мне абсолютно не нужен был glibc.

Долго я с этим возился, и как вижу - я не одинок в этом...
чего мне пока с последними версиями gcc проделать на удалось - под ARM собираться не хочет, binutils собираются, bootstrap compiler тоже, а сборка libc - сваливается... или это сборка полноценного компилера? не помню уже... после десятка безуспешных попыток я это дело отложил на неопределенное время.

Начнем с того, что стандартные средства gentoo (crossdev) хоть и предполагают, но не могут собрать toolchain с uClibc. А поддержка elf2flt видимо когда-то и была, но ныне включается только ковырянием в ебилдах.

Стандартные средства uClinux имеют волосатую дату 14 марта 2003 и включают в себя старый binutils который не в состоянии собрать нынешнее ядро. С неофициальным тулчейном ситуация не намного лучше, ибо он базируется на gcc-3.4.0, в то время как <gcc-3.4.1 имел баг с компиляцией на arm, что также дает полную неюзабельность данных пакетов. А еще я убил бы того, кто придумал закатывать архивы в .sh! Моя система имеет локаль по умолчанию - UTF8. Похоже, что именно это не дает скрипту нормально отрабатывать ибо UTF8 пребразование превращает архив непонятно во что. Пришлось выцеплять архив из .sh файла при помощи dd, после чего их стало возможным раскрутить, но всеравно бестолку.

Есть предположение, что разработчики uClinux забросили эти инструменты в пользу buildroot, который, в отличи от OpenEmbedded, заточен на uClinux. И если повезет, то можно найти снапшот (стабильного релиза так и не нашел), который, после небольшой доработки напильником сможет собрать корректный тулчейн. Так я и поступил, ибо то, что пытаюсь собрать я сам, упорно не хочет работать ругаясь то на отсутствие либ, то на некорректность бинарей.

Вообще я наверное что-то не понимаю, но мне кажется что с кросскомпиляцией GNU'шники что-то намудрили.

PS: А еще почему-то когда я делаю выравнивание вправо у меня сразу меняется межстрочный интервал. глюка какая-то.

суббота, 5 января 2008 г.

mencoder bug???

С сентября меня мучает одна проблема. Мой mencoder почему-то разучился корректно кодировать видео в два прохода. Что я только не делал, пересобирал mplayer с различными USE-флагами (пытался отключать оптимизации всякие), различные опции кодирования менял. Однопроходное кодирование работает корректно, а после второго прохода видео корректно отображает кажется только сам mplayer.

Многочисленные дальнейшие эксперименты показали что кодирование через ffmpeg дает точно такой-же некорректный результат. Вместо 23 минут видео почти все плейеры показывают около 4 часов. Формат звуковой дорожки абсолютно не при чем, менял форматы, но даже один видеопоток отображается некорректно. Так же пытался менять и контейнер (вдруг это проблема avi?), но mpeg точно так же врет.

В порыве отчаяния я пересобрал все до mplayer (emerge -e mplayer), но и это абсолютно ничего не дало. Я что-то не понимаю, неужели никто под gentoo не кодирует видео? Не кодирует в два прохода? Или эта проблема только у меня (руки?). Ведь не может быть такого чтобы за три месяца никто не озадачился проблемой корректности двухпроходного кодирования, ежели она всетаки присутствует.

Где я только не искал отгадку, но пока ее не обнаруживаю. Так и сижу, как дурак, без mencoder'а.