Я планировал написать данную статью уже давно, но в последние месяцы никак не мог выкроить достаточно времени. Пока я размышлял над статьёй, делал примеры и проверял свои догадки, на Хабре уже обсудили константность — [1][2].
Ради забавы попробуем проделать подобную экономию не со сферическим проектом в вакууме, а с самым что ни на есть живым и грандиозным проектом — с ядром Линукс!
Статья имеет следующую структуру:
По идее статью можно было бы разбить на две части: технические детали про планшет и использование extern const в ядре Линукс. Мне кажется, что тогда полноценных публикаций не получилось бы, поэтому я объединил весь материал в одну статью.
В далёком 2011 году я начитался на хабре обзоров про китайпады — [3][4][5]. Естественно мне захотелось такого чуда и я его заказал.
Немножко поигрался, поглядел кино, поразгадывал маджонг и… И вскоре моё мнение стало альтернативным предыдущему — [6].
После этого было решено изучать планшет с точки зрения программиста, а именно — установить туда обычный Линукс, что-нибудь покомпилировать.
Всё описанное справедливо для китайпада, у которого присутствуют такие опознавательные знаки:
Здесь [8] доступно гик-порно. На фотках отчётливо можно разглядеть выводы JTAG & USART. Также обратите внимание на маркировку.
Для прошивки устройства надо на СД-карточку с файловой системой FAT загрузить файлы с именами firmware2, firmware-discovery, bootloader-discovery; вставить её в разъём и включить планшет. (У меня заработало с флешкой 256МБ и файловой системой W95 FAT32 — идентификатор 0xB в fdisk).
Понадобится рабочая прошивка. Хотя предмет статьи давно устарел, ещё можно найти рабочий вариант здесь — [12]. Я взял версию Axlien.
Предназначение файлов:
Таким образом, если собрать своё собственное рабочее ядро (zImage) для планшета, создать U-Boot образ с теми же параметрами и добавить 192-байтный заголовок, то удастся загрузить свою версию Линукса.
Также имеется возможность поиграть с «официальным» firmware2. Имеются инструменты для распаковки и обратной запаковки файла zImage [9]. Можно распаковать и изучить сценарии обновления устройства, в частности, понять, что обновляет bootloader-discovery и как. С другой стороны, есть возможность отключить упомянутые сценарии и оказаться в оболочке busybox.
В поисках исходников ядра под данный процессор я наткнулся на обсуждение [13], где некто atp_uestc сообщил об открытии исходных кодов ядра для планшета ZT-180.
Сейчас мне уже сложно вспомнить хронологию событий и какое же ядро я пытался грузить сперва: то ли «ванильное» отсюда — [14], то ли модифицированное отсюда — [15]. Запомнился только результат — не заработало, процесс загрузки замирал на «Decompressing kernel...»
Спустя некоторое время в обсуждении появился yuray, который добавлял поддержку данного процессора в третью версию ядра линукс. Именно эту версию ядра мне и удалось запустить на китайпаде.
Скачать исходные тексты ядра версии 3.4.х с kernel.org. На момент написания статьи успешно собиралась версия 3.4.108 и ниже. Загрузить и наложить заплатки для ядра от yuray отсюда rtck.org/zt180/patches/zt180_b0_3.4.patch.xz:
Настроить конфиг. Правильного конфига под этот китайпад я не встречал, поэтому предлагаю взять dot-config отсюда [19]:
Для ядра потребуется initramfs. Я пользователь ОС Slackware GNU/Linux, поэтому решено было взять initrd установщика из неё, чтобы в последствии установить на планшет слаку. Загружаем uinitrd-kirkwood.img [16], там же в файле kernels/README.txt приведены команды для распаковки образа. Из оригинальной слаки образ не подойдёт, т.к. содержит бинарники под x86/amd64, но нам нужны под ARM.
Создаём папку, распаковываем в неё образ:
Указываем в конфиге путь к папке с образом:
#…
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE="/path/to/uinitrd.extracted"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0
#…
Компилируем ядро в два прохода:
Может вылезти ошибка [17][18]. Мне помогло решение, которое и предлагается в тексте ошибки: параметр KALLSYMS_EXTRA_PASS=1.
Потом создаём firmware2:
Для автоматизации сборки был написан сценарий [19]. Использовалась следующая команда:
Загрузил — не работает ЮСБ, точнее, на портах отсутствовало питание. Просил помощи у yuray, на что он ответил, что скорее всего схема моего китайпада имеет другую разводку, отличную от ZT-180.
Потом нашёл эту великолепную статью [11] о том, как дизассемблировать части рабочего ядра. Решил восстанавливать сишный код функций «оригинальной» прошивки, имеющих отношение к USB, и сравнивать их с версиями в исходном коде. К моему разочарованию, функции совпадали с точностью до диграфов. Однако на рабочем ядре имелись функции, которых не было в исходных текстах. Видимо там кроется некоторая магия, которая вселяет жизнь в USB-порты, но её я так и оставил под завесой тайны…
В процессе изучения восстановленного кода ассемблера, я подметил один интересный момент: во многих функциях после их эпилога шли странные инструкции:
Видите 0xF0200000? Странная неопределённая инструкция… да ещё и встречается в нескольких местах. Это некий базовый адрес, подумал я, а значит, что теоретически можно с каждой такой функции сэкономить 4 (четыре!) байта: разместить это значение в одном месте, а все функции будут грузить его по одному адресу — завести константу!
У Мейерса [20] очень хорошо всё расписано. Приведу лишь некоторые пояснения.
Компиляторы языков C и C++ однопроходные, а значит не могут самостоятельно оптимизировать константы. Как минимум требуется помощь со стороны компоновщика, например, параметр /Gw [1].
Эта же однопроходная слабость может сыграть на руку: использовать extern const [1]. Идея заключается в том, чтобы в заголовочном файле описать постоянную следующим образом:
Затем в некотором файле написать строчку:
Для проведения эксперимента потребуется внести следующие правки [19]:
Было решено измерять два показателя:
Сценарий, который подсчитывает размер всех функций содержащих подслово imap, находится здесь [19].
Результаты следующие:
Размер ядра в целом уменьшился на 416, но выигрыш оказался не таким, каким я его ожидал: некоторые функции прибавили в весе.
Возможно более опытный читатель знает причину, но мне на тот момент было не всё так ясно. Рассмотрим исходные тексты ассемблера функции imapx200_timer_ack, размер которой увеличился на 8 байт после модификации:
Первое, что бросается в глаза: сам базовый адрес настолько удачный, что запись оного в регистр умещалась в четыре байта команды mov:
Второе: после модификации компилятору пришлось добавить за эпилогом функции адрес нашей новой постоянной, т.к. его значение менее удобное — четыре байта раз.
И последнее: считывать значения напрямую из памяти невозможно, поэтому сначала записывается адрес постоянной в регистр:
Я думаю, что объективным выводом будет то, что нужно знать о последствиях определения постоянных как в виде макросов, так и с помощью const переменных.
Лично я мечтаю о том, чтобы компилятор (пусть и в связке с компоновщиком) самостоятельно принимал решение о встраивании значения постоянной без сложностей с extern const.
Спасибо за внимание!
Ради забавы попробуем проделать подобную экономию не со сферическим проектом в вакууме, а с самым что ни на есть живым и грандиозным проектом — с ядром Линукс!
Статья имеет следующую структуру:
По идее статью можно было бы разбить на две части: технические детали про планшет и использование extern const в ядре Линукс. Мне кажется, что тогда полноценных публикаций не получилось бы, поэтому я объединил весь материал в одну статью.
Предыстория ^
В далёком 2011 году я начитался на хабре обзоров про китайпады — [3][4][5]. Естественно мне захотелось такого чуда и я его заказал.
Немножко поигрался, поглядел кино, поразгадывал маджонг и… И вскоре моё мнение стало альтернативным предыдущему — [6].
После этого было решено изучать планшет с точки зрения программиста, а именно — установить туда обычный Линукс, что-нибудь покомпилировать.
Планшет Flytouch 2/Superpad III ^
Всё описанное справедливо для китайпада, у которого присутствуют такие опознавательные знаки:
показать
DF-MID10-IX210-V1.1
2010-01-20
XW11070501B512M03101
2010-01-20
XW11070501B512M03101
Здесь [8] доступно гик-порно. На фотках отчётливо можно разглядеть выводы JTAG & USART. Также обратите внимание на маркировку.
Прошивка ^
Для прошивки устройства надо на СД-карточку с файловой системой FAT загрузить файлы с именами firmware2, firmware-discovery, bootloader-discovery; вставить её в разъём и включить планшет. (У меня заработало с флешкой 256МБ и файловой системой W95 FAT32 — идентификатор 0xB в fdisk).
Понадобится рабочая прошивка. Хотя предмет статьи давно устарел, ещё можно найти рабочий вариант здесь — [12]. Я взял версию Axlien.
Предназначение файлов:
- файлы *-discovery — это просто zip-архивы с нужными данными для обновления. firmware-discovery содержит всё, что касается Андроида. Про bootloader ничего сказать не могу — он мне не попадался, но по названию можно догадываться, что это обновление загрузчика — не ясно только, какого именно;
- файл firmware2 представляет наибольший интерес. На тот момент нигде в сети я не нашёл упоминания формата этого файла. Я подумал, ну не могут же китайцы, да ещё и для такого дешёвого барахла, создать мощную систему на основе криптографии… и оказался прав! Первые 192 байта — это просто мусор. Можно взять firmware2 от рабочей прошивки и затереть указанный заголовок нулями — процесс обновления прошивки всё равно запуститься [10]. Далее идёт обычный U-Boot образ, параметры которого можно получить с помощью mkimage.
Таким образом, если собрать своё собственное рабочее ядро (zImage) для планшета, создать U-Boot образ с теми же параметрами и добавить 192-байтный заголовок, то удастся загрузить свою версию Линукса.
Также имеется возможность поиграть с «официальным» firmware2. Имеются инструменты для распаковки и обратной запаковки файла zImage [9]. Можно распаковать и изучить сценарии обновления устройства, в частности, понять, что обновляет bootloader-discovery и как. С другой стороны, есть возможность отключить упомянутые сценарии и оказаться в оболочке busybox.
Сборка ядра ^
В поисках исходников ядра под данный процессор я наткнулся на обсуждение [13], где некто atp_uestc сообщил об открытии исходных кодов ядра для планшета ZT-180.
Сейчас мне уже сложно вспомнить хронологию событий и какое же ядро я пытался грузить сперва: то ли «ванильное» отсюда — [14], то ли модифицированное отсюда — [15]. Запомнился только результат — не заработало, процесс загрузки замирал на «Decompressing kernel...»
Спустя некоторое время в обсуждении появился yuray, который добавлял поддержку данного процессора в третью версию ядра линукс. Именно эту версию ядра мне и удалось запустить на китайпаде.
Скачать исходные тексты ядра версии 3.4.х с kernel.org. На момент написания статьи успешно собиралась версия 3.4.108 и ниже. Загрузить и наложить заплатки для ядра от yuray отсюда rtck.org/zt180/patches/zt180_b0_3.4.patch.xz:
$ cd /path/to/linux-3.4.x
$ patch -p1 -i /path/to/zt180_b0_3.4.patch
Настроить конфиг. Правильного конфига под этот китайпад я не встречал, поэтому предлагаю взять dot-config отсюда [19]:
$ make V=1 ARCH=arm CROSS_COMPILE=/path/to/toolchain/arm-infotm-linux-gnueabi- oldconfig
Для ядра потребуется initramfs. Я пользователь ОС Slackware GNU/Linux, поэтому решено было взять initrd установщика из неё, чтобы в последствии установить на планшет слаку. Загружаем uinitrd-kirkwood.img [16], там же в файле kernels/README.txt приведены команды для распаковки образа. Из оригинальной слаки образ не подойдёт, т.к. содержит бинарники под x86/amd64, но нам нужны под ARM.
Создаём папку, распаковываем в неё образ:
$ mkdir -p /path/to/uinitrd.extracted
$ pushd !$
$ dd if=/path/to/uinitrd-kirkwood.img bs=64 skip=1 | gzip -dc | sudo cpio -div
$ popd
Указываем в конфиге путь к папке с образом:
#…
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE="/path/to/uinitrd.extracted"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0
#…
Компилируем ядро в два прохода:
# clean initrd directory
$ sudo rm -rf /path/to/uinitrd.extracted/lib/{modules, firmware}
# build kernel for the first time
$ make KALLSYMS_EXTRA_PASS=1 ARCH=arm CROSS_COMPILE=/path/to/toolchain/arm-infotm-linux-gnueabi- -j2 zImage
# build modules against the kernel
$ make KALLSYMS_EXTRA_PASS=1 ARCH=arm CROSS_COMPILE=/path/to/toolchain/arm-infotm-linux-gnueabi- -j2 modules
# install modules into initramfs dir
$ sudo make ARCH=arm CROSS_COMPILE=/path/to/toolchain/arm-infotm-linux-gnueabi- -j2 modules_install INSTALL_MOD_PATH=/path/to/uinitrd.extracted/
# install firmware to initramfs folder
$ sudo make ARCH=arm CROSS_COMPILE=/path/to/toolchain/arm-infotm-linux-gnueabi- -j2 firmware_install INSTALL_MOD_PATH=/path/to/uinitrd.extracted/
# remove unnecessary files
$ sudo rm -rf /path/to/uinitrd.extracted/lib/modules/3.4.*/{build,source}
# build the kernel again, with initramfs dir contains modules
$ make KALLSYMS_EXTRA_PASS=1 ARCH=arm CROSS_COMPILE=/path/to/toolchain/arm-infotm-linux-gnueabi- -j2 zImage
Может вылезти ошибка [17][18]. Мне помогло решение, которое и предлагается в тексте ошибки: параметр KALLSYMS_EXTRA_PASS=1.
Потом создаём firmware2:
# make u-boot image
$ /path/to/mkimage -A arm -C none -O linux -T kernel -a 0x40008000 -e 0x40008000 -n linux-3.4 -d arch/arm/boot/zImage arch/arm/boot/uImage
# make firmware2
$ cat /path/to/firmware2.header arch/arm/boot/uImage > firmware2
и загружаем с неё планшет.Для автоматизации сборки был написан сценарий [19]. Использовалась следующая команда:
$ ( time ./my_build.sh ) |& tee `date +%M%H_%F`-build-log.txt
Загрузил — не работает ЮСБ, точнее, на портах отсутствовало питание. Просил помощи у yuray, на что он ответил, что скорее всего схема моего китайпада имеет другую разводку, отличную от ZT-180.
Потом нашёл эту великолепную статью [11] о том, как дизассемблировать части рабочего ядра. Решил восстанавливать сишный код функций «оригинальной» прошивки, имеющих отношение к USB, и сравнивать их с версиями в исходном коде. К моему разочарованию, функции совпадали с точностью до диграфов. Однако на рабочем ядре имелись функции, которых не было в исходных текстах. Видимо там кроется некоторая магия, которая вселяет жизнь в USB-порты, но её я так и оставил под завесой тайны…
В процессе изучения восстановленного кода ассемблера, я подметил один интересный момент: во многих функциях после их эпилога шли странные инструкции:
/* ... */
c039e32c: e59f0030 ldr r0, [pc, #48] ; c039e364 <_binary_0xc039e2e8_imapx200_decode_suspend_start+0x7c>
/* ... */
c039e360: e89da800 ldm sp, {fp, sp, pc}
c039e364: f0200000 undefined instruction 0xf0200000
c039e368: c24b1c9c subgt r1, fp, #39936 ; 0x9c00
Видите 0xF0200000? Странная неопределённая инструкция… да ещё и встречается в нескольких местах. Это некий базовый адрес, подумал я, а значит, что теоретически можно с каждой такой функции сэкономить 4 (четыре!) байта: разместить это значение в одном месте, а все функции будут грузить его по одному адресу — завести константу!
Константность ^
У Мейерса [20] очень хорошо всё расписано. Приведу лишь некоторые пояснения.
Компиляторы языков C и C++ однопроходные, а значит не могут самостоятельно оптимизировать константы. Как минимум требуется помощь со стороны компоновщика, например, параметр /Gw [1].
Эта же однопроходная слабость может сыграть на руку: использовать extern const [1]. Идея заключается в том, чтобы в заголовочном файле описать постоянную следующим образом:
#ifndef MY_CONST_H
#define MY_CONST_H
extern const int my_constant;
#endif /* MY_CONST_H */
Затем в некотором файле написать строчку:
const int my_constant = 42;
это можно разместить в любой единице трансляции. Теперь компилятор не сможет зашить значение постоянной в код, но вынужден будет сделать на неё некоторую ссылку. Компоновщик же получит только один объектный файл, в котором найдётся значение постоянной, после чего подставит конечные адреса в код. Конечно могут быть варианты, но в общем случае компилятор в одиночку беспомощен перед таким трюком и вынужден поступать так, как описано выше. Однако есть маленький нюанс [21].Для проведения эксперимента потребуется внести следующие правки [19]:
показать
diff -ru ./linux-3.4.108/arch/arm/mach-imapx200/Makefile ../linux-3.4.108/arch/arm/mach-imapx200/Makefile
--- ./linux-3.4.108/arch/arm/mach-imapx200/Makefile 2015-09-09 12:17:52.483020878 +0300
+++ ../linux-3.4.108/arch/arm/mach-imapx200/Makefile 2015-09-08 17:11:45.775035963 +0300
@@ -6,7 +6,7 @@
#IMAPX200 support files
-obj-$(CONFIG_CPU_IMAPX200) += irq.o clock.o time.o devices.o pwm.o
+obj-$(CONFIG_CPU_IMAPX200) += irq.o clock.o time.o devices.o pwm.o constants.o
diff -ru ./linux-3.4.108/arch/arm/mach-imapx200/include/mach/imapx_base_reg.h ../linux-3.4.108/arch/arm/mach-imapx200/include/mach/imapx_base_reg.h
--- ./linux-3.4.108/arch/arm/mach-imapx200/include/mach/imapx_base_reg.h 2015-09-09 12:17:52.498020874 +0300
+++ ../linux-3.4.108/arch/arm/mach-imapx200/include/mach/imapx_base_reg.h 2015-09-08 17:11:45.778035706 +0300
@@ -15,13 +15,23 @@
#define IMAPX200_SDRAM_PA (0x40000000)
/************************Virtual address for peripheral*************************/
-#define IMAP_VA_SYSMGR IMAP_ADDR(0x00200000)
-#define IMAP_VA_IRQ IMAP_ADDR(0x00000000)
-#define IMAP_VA_TIMER IMAP_ADDR(0x00300000)
-#define IMAP_VA_WATCHDOG IMAP_ADDR(0x00600000)
-#define IMAP_VA_GPIO IMAP_ADDR(0x00400000)
-#define IMAP_VA_NAND IMAP_ADDR(0x00500000)
-#define IMAP_VA_FB IMAP_ADDR(0x00700000)
+#if defined(IMAP_USE_MACRO_CONSTANTS) || defined(__ASSEMBLY__)
+# define IMAP_VA_SYSMGR IMAP_ADDR(0x00200000)
+# define IMAP_VA_IRQ IMAP_ADDR(0x00000000)
+# define IMAP_VA_TIMER IMAP_ADDR(0x00300000)
+# define IMAP_VA_WATCHDOG IMAP_ADDR(0x00600000)
+# define IMAP_VA_GPIO IMAP_ADDR(0x00400000)
+# define IMAP_VA_NAND IMAP_ADDR(0x00500000)
+# define IMAP_VA_FB IMAP_ADDR(0x00700000)
+#else
+extern const void __iomem __force * const IMAP_VA_SYSMGR;
+extern const void __iomem __force * const IMAP_VA_IRQ;
+extern const void __iomem __force * const IMAP_VA_TIMER;
+extern const void __iomem __force * const IMAP_VA_WATCHDOG;
+extern const void __iomem __force * const IMAP_VA_GPIO;
+extern const void __iomem __force * const IMAP_VA_NAND;
+extern const void __iomem __force * const IMAP_VA_FB;
+#endif /* defined(IMAP_USE_MACRO_CONSTANTS) || defined(__ASSEMBLY__) */
#define PERIPHERAL_BASE_ADDR_PA (0x20C00000)
diff -ru ./linux-3.4.108/arch/arm/plat-imap/cpu.c ../linux-3.4.108/arch/arm/plat-imap/cpu.c
--- ./linux-3.4.108/arch/arm/plat-imap/cpu.c 2015-09-09 12:17:52.607021038 +0300
+++ ../linux-3.4.108/arch/arm/plat-imap/cpu.c 2015-09-08 17:18:30.646035384 +0300
@@ -1,3 +1,5 @@
+#define IMAP_USE_MACRO_CONSTANTS
+
/********************************************************************************
** linux-2.6.28.5/arch/arm/plat-imap/cpu.c
**
diff -ru ./linux-3.4.108/arch/arm/plat-imap/gpio.c ../linux-3.4.108/arch/arm/plat-imap/gpio.c
--- ./linux-3.4.108/arch/arm/plat-imap/gpio.c 2015-09-09 12:17:52.614020935 +0300
+++ ../linux-3.4.108/arch/arm/plat-imap/gpio.c 2015-09-08 17:18:38.566035206 +0300
@@ -1,3 +1,5 @@
+#define IMAP_USE_MACRO_CONSTANTS
+
/* arch/arm/plat-imapx200/gpiolib.c
*
* Copyright 2008 Openmoko, Inc.
diff -ru ./linux-3.4.108/arch/arm/plat-imap/pm_imapx200.c ../linux-3.4.108/arch/arm/plat-imap/pm_imapx200.c
--- ./linux-3.4.108/arch/arm/plat-imap/pm_imapx200.c 2015-09-09 12:17:52.631020886 +0300
+++ ../linux-3.4.108/arch/arm/plat-imap/pm_imapx200.c 2015-09-08 17:18:52.102036874 +0300
@@ -1,3 +1,5 @@
+#define IMAP_USE_MACRO_CONSTANTS
+
#include <linux/init.h>
#include <linux/suspend.h>
#include <linux/serial_core.h>
diff -ru ./linux-3.4.108/drivers/video/infotm/imapfb.c ../linux-3.4.108/drivers/video/infotm/imapfb.c
--- ./linux-3.4.108/drivers/video/infotm/imapfb.c 2015-09-09 12:17:53.350020920 +0300
+++ ../linux-3.4.108/drivers/video/infotm/imapfb.c 2015-09-08 17:34:34.814042727 +0300
@@ -1,3 +1,5 @@
+#define IMAP_USE_MACRO_CONSTANTS
+
/*****************************************************************************
** drivers/video/infotm/imapfb.c
**
diff -ru --new-file ./linux-3.4.108/arch/arm/mach-imapx200/constants.c ../linux-3.4.108/arch/arm/mach-imapx200/constants.c
--- ./linux-3.4.108/arch/arm/mach-imapx200/constants.c 1970-01-01 03:00:00.000000000 +0300
+++ ../linux-3.4.108/arch/arm/mach-imapx200/constants.c 2015-09-09 14:56:53.513487879 +0300
@@ -0,0 +1,11 @@
+#include <linux/compiler.h>
+
+#include <mach/imapx_base_reg.h>
+
+const void __iomem __force * const IMAP_VA_SYSMGR = IMAP_ADDR(0x00200000);
+const void __iomem __force * const IMAP_VA_IRQ = IMAP_ADDR(0x00000000);
+const void __iomem __force * const IMAP_VA_TIMER = IMAP_ADDR(0x00300000);
+const void __iomem __force * const IMAP_VA_WATCHDOG = IMAP_ADDR(0x00600000);
+const void __iomem __force * const IMAP_VA_GPIO = IMAP_ADDR(0x00400000);
+const void __iomem __force * const IMAP_VA_NAND = IMAP_ADDR(0x00500000);
+const void __iomem __force * const IMAP_VA_FB = IMAP_ADDR(0x00700000);
Измерения ^
Было решено измерять два показателя:
- размер файла ядра vmlinux;
- подсчитывать размер функций непосредственно во время работы ОС.
Сценарий, который подсчитывает размер всех функций содержащих подслово imap, находится здесь [19].
Результаты следующие:
показать
# diff -ru 1.log 2.log
--- 1.log 2015-09-11 16:57:28.430158628 +0300
+++ 2.log 2015-09-11 16:57:20.627161803 +0300
@@ -1,4 +1,4 @@
-Data Size: 17844272 Bytes = 17426.05 kB = 17.02 MB
+Data Size: 17843856 Bytes = 17425.64 kB = 17.02 MB
Load Address: 0x40008000
Entry Point: 0x40008000
--- 22012015-rom/sizes.txt 2015-01-28 15:25:51.107945315 +0300
+++ 28012015-rom/sizes.txt 2015-01-28 15:15:48.052949785 +0300
@@ -1,25 +1,25 @@
-imapx200_timer_mask - 44
-imapx200_timer_unmask - 52
-imapx200_timer_ack - 44
+imapx200_timer_mask - 52
+imapx200_timer_unmask - 60
+imapx200_timer_ack - 52
imapx200_irq_add - 24
imapx200_irq_init - 32
imapx200_irq_wake - 44
-imapx200_irq_unmask - 136
-imapx200_irq_mask - 132
-imapx200_irq_ack - 120
+imapx200_irq_unmask - 172
+imapx200_irq_mask - 160
+imapx200_irq_ack - 152
imap_clk_enable - 60
imap_clkcon_enable - 76
-imapx200_gettimeoffset - 64
-imapx200_timer_setup - 164
-imapx200_timer_interrupt - 64
+imapx200_gettimeoffset - 68
+imapx200_timer_setup - 168
+imapx200_timer_interrupt - 76
imap_pwm_suspend - 188
imap_pwm_resume - 184
imap_pwm_start - 164
imap_timer_setup - 404
imap_default_idle - 20
-imapx_poweroff - 56
-imapx_reset - 64
-imapx200_idle - 52
+imapx_poweroff - 88
+imapx_reset - 68
+imapx200_idle - 48
imap_set_board - 112
imapx200_gpio_setpull_updown - 56
imapx200_gpio_getpull_updown - 36
@@ -48,7 +48,7 @@
imapx200_pm_prepare - 24
imapx200_pm_finish - 20
imapx200_pm_do_save - 88
-imapx200_pm_enter - 436
+imapx200_pm_enter - 500
imapx200_pm_configure_extint - 20
imapx200_pm_prepare - 24
imapx200_pm_init - 80
@@ -86,8 +86,8 @@
imapfb_resume - 196
imapfb_suspend - 204
imapfb_backlight_power_supply - 20
-imapfb_set_clk - 44
-imapfb_set_gpio - 88
+imapfb_set_clk - 52
+imapfb_set_gpio - 96
imapfb_set_brightness - 36
imapfb_lcd_power_supply - 32
con_get_unimap - 360
@@ -153,7 +153,7 @@
imap_nand_irq - 92
ehci_imapx200_drv_remove - 80
ehci_imapx200_init - 1148
-ehci_imapx200_drv_probe - 552
+ehci_imapx200_drv_probe - 556
ohci_hcd_imapx200_drv_remove - 80
ohci_imapx200_start - 100
ohci_hcd_imapx200_drv_probe - 512
@@ -196,22 +196,22 @@
imapx200_i2c_probe - 824
imapx200_i2c_irq - 920
imapx200_decode_poll - 184
-imapx200_decode_suspend - 132
+imapx200_decode_suspend - 44
imapx200_decode_resume - 68
-imapx200_decode_release - 192
+imapx200_decode_release - 104
imapx200_decode_open - 124
imapx200_decode_remove - 208
imapx200_decode_ioctl - 384
-imapx200_decode_probe - 908
+imapx200_decode_probe - 816
imapx200_decode_irq_handle - 264
imapx200_encode_poll - 84
-imapx200_encode_suspend - 116
+imapx200_encode_suspend - 28
imapx200_encode_ioctl - 348
imapx200_encode_resume - 68
-imapx200_encode_release - 188
+imapx200_encode_release - 100
imapx200_encode_open - 120
imapx200_encode_remove - 232
-imapx200_encode_probe - 1080
+imapx200_encode_probe - 988
imapx200_encode_irq_handle - 136
sdhci_imap_set_clk_src - 52
sdhci_imap_resume - 36
@@ -220,7 +220,7 @@
sdhci_imap_get_timeout_clk - 40
imapfb_probe - 2952
imapfb_init - 28
-sdhci_imap_probe - 604
+sdhci_imap_probe - 608
sdhci_imap_remove - 20
name_imapx200 - 12
imapfb_a1rgb232_8 - 48
@@ -342,8 +342,8 @@
__kstrtab_imap_get_reservemem_paddr - 26
__kstrtab_con_copy_unimap - 16
__kstrtab_con_set_default_unimap - 23
-imapx200_init_clocks - 1120
-imapx200_timer_init - 120
+imapx200_init_clocks - 1104
+imapx200_timer_init - 124
imapx200_register_device - 56
imap_init_pwm - 308
imapx200_fixup - 36
@@ -542,7 +542,7 @@
imapx200_i2c_driver - 116
imapx200_i2c_driver - 116
imapx200_decode_driver - 80
-imapx200_decode_fops - 144
+imapx200_decode_fops - 148
imapx200_encode_driver - 80
imapx200_encode_fops - 180
sdhci_imap_driver - 80
Размер ядра в целом уменьшился на 416, но выигрыш оказался не таким, каким я его ожидал: некоторые функции прибавили в весе.
Возможно более опытный читатель знает причину, но мне на тот момент было не всё так ясно. Рассмотрим исходные тексты ассемблера функции imapx200_timer_ack, размер которой увеличился на 8 байт после модификации:
показать
--- 0xc0019c40-t-imapx200_timer_ack-2.listing 2015-11-18 22:12:24.196113878 +0300
+++ 0xc0019c50-imapx200_timer_ack-2.listing 2015-11-18 22:12:24.297113880 +0300
@@ -9,10 +9,12 @@
XXXXXXXX: e92dd800 push {fp, ip, lr, pc}
XXXXXXXX: e24cb004 sub fp, ip, #4 ; 0x4
XXXXXXXX: e1a00000 nop (mov r0,r0)
+c0019c60: e59f2018 ldr r2, [pc, #24] ; c0019c80 <_binary_0xc0019c50_imapx200_timer_ack_start+0x30>
XXXXXXXX: e590Y000 ldr rY, [r0]
XXXXXXXX: e3a0X001 mov rX, #1 ; 0x1
-c0019c58: e3a0120f mov r1, #-268435456 ; 0xf0000000
+c0019c6c: e5921000 ldr r1, [r2]
XXXXXXXX: e1a0XX1Y lsl rX, rX, rY
XXXXXXXX: e581X010 str rX, [r1, #16]
XXXXXXXX: e581X000 str rX, [r1]
XXXXXXXX: e89da800 ldm sp, {fp, sp, pc}
+c0019c80: c04e858c subgt r8, lr, ip, lsl #11
Первое, что бросается в глаза: сам базовый адрес настолько удачный, что запись оного в регистр умещалась в четыре байта команды mov:
c0019c58: e3a0120f mov r1, #-268435456 ; 0xf0000000
Второе: после модификации компилятору пришлось добавить за эпилогом функции адрес нашей новой постоянной, т.к. его значение менее удобное — четыре байта раз.
И последнее: считывать значения напрямую из памяти невозможно, поэтому сначала записывается адрес постоянной в регистр:
c0019c60: e59f2018 ldr r2, [pc, #24] ; c0019c80
— четыре байта два.Заключение ^
Я думаю, что объективным выводом будет то, что нужно знать о последствиях определения постоянных как в виде макросов, так и с помощью const переменных.
Лично я мечтаю о том, чтобы компилятор (пусть и в связке с компоновщиком) самостоятельно принимал решение о встраивании значения постоянной без сложностей с extern const.
Спасибо за внимание!
Список источников ^
- ^ 1 2 3 Вычислите длину окружности
- ^ И ещё раз про уникальные константы
- ^ История покупки и опыт использования планшетного ПК Zenithink ZT-180
- ^ Обзор планшета Zenithink Zt-180 10"
- ^ страница уже не доступна, ищите в различных архивах паутины по запросу habrahabr.ru/blogs/iTablet/110714
- ^ страница уже не доступна, ищите в различных архивах паутины по запросу www.good-review.ru/pandawill/2011/02/21/obzor-kitayskogo-plansheta-zenithink-zt-180-10.html
- Переселение души: linux на android планшете
- ^ Фото внутренностей
- ^ Zimage unpack and pack tools
- ^ Decompiling 'firmware2' and 'firmware_discovery'
- ^ Disassembly: Smashing the Android Kernel for Fun and Overclock
- ^ Тема на forum.china-iphone.ru, посвящённая планшету
- ^ Open source project
- ^ github.com/atpboy444/ZT-180
- ^ github.com/dandel/linux-2.6.32.y
- ^ SlackwareARM-14.1
- ^ https://github.com/djwillis/meta-raspberrypi/issues/38
- ^ https://lkml.org/lkml/2012/7/6/260
- ^ 1 2 3 4 github.com/gshep/flytouch2-helper-scripts
- ^ Скотт Мэйерс. Эффективное использование C++. 55 верных советов улучшить структуру и код ваших программ
- ^ Initialization order of globals