Есть ли какой-либо способ сократить этот машинный код Hello World в сборке AArch64?

#assembly #optimization #arm64 #code-size

Вопрос:

Я работаю над скомпилированным файлом сборки AArch64 программы «Hello World» для Linux.

Я уже успешно сократил его с 504 байт до 124 байт.Единственная более «оптимизация», о которой я могу думать, — это найти несколько инструкций, которые выполняют две или более задач в одной инструкции.

В настоящее время машинный код в файле (представленный в asm) выглядит следующим образом:

   mov x8, 64     // __NR_write
  adr x1, hello  //the string, I know the exact address
  mov x2, 10     //string length (actually only "HelloWorld")

j:
  mov x0, 0      // write to stdin happens to work
  svc 0
  mov x8, 93     // __NR_exit
  b j    //the branching saves me one instruction to exit with status = 0
 

Есть ли здесь какая-нибудь инструкция по сокращению чего-либо?

Комментарии:

1. Это для Linux, верно? Встраивание в исполняемый файл ELF? Вы упаковываете этот код в заголовок ELF или что-то в этом роде, как в muppetlabs.com /~breadbox/software/tiny/teensy.html ?

2. Да, есть много перекрытий и записи в заголовки

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

4. Ну, я не создаю его, я записываю необработанные байты в файл из созданного вручную шестнадцатеричного дампа

5. Это звучит неудобно по сравнению с созданием его с as помощью (из исходного кода, включая некоторые .byte .word директивы и) и использованием objcopy для получения раздела .text в файл самостоятельно. Как в том учебнике / статье x86, который используется с NASM, который может напрямую собираться в плоский двоичный файл.

Ответ №1:

Это может сработать для ldp x0, x2, [sp], #16 того, чтобы вставить два верхних слова из стека, argc и argv[0] , в x0 и x2, если вы не возражаете написать кучу двоичных байтов (или даже другого мусора) после вашей строки.

В среде запуска процесса Linux есть указатель стека, указывающий на argc , а над argv[] ним — значения массива. (Не указатель на argv, как main gets; первое двойное слово в нем есть argv[0] . Выше argv[] есть env[] .)

  • argc будет равно 1, так что это работает для стандартного вывода fd, если нормально запускаться из оболочки без аргументов.
  • argv является указателем на стековую память, поэтому является огромным целым числом, намного превышающим 10, поэтому write() будет считывать байты до тех пор, пока не попадет на неотмеченную страницу.
    (Linux write фактически копирует предыдущие байты в fd, не возвращая -EFAULT , если ненулевое количество байтов может быть записано до возникновения ошибки. Кажется, он проверяет читаемость только более поздних страниц, когда доходит до них. Это деталь реализации, которая не документирована, но это то, что на самом деле делает текущий Linux, по крайней мере, на x86-64.)

Это может даже завершиться со статусом 0, предполагая, что он выполняется без аргументов. Адресация после инкремента приведет к ldp загрузке следующей итерации x0 = argv[1] = NULL. (И env[0] в x2; мы знаем, что мы не будем по ошибке читать дальше верхней части области стека, потому что env[] находится там.)

Но нет необходимости exit(0) печатать текст; может работать любой статус выхода. (Если вы не возражаете против шума из оболочки, вы могли бы даже организовать свою программу так, чтобы она выполняла сегментацию вместо выполнения системного вызова exit, сохраняя все инструкции после первого svc 0 !)


Если вы запустили программу без аргументов с помощью ручного execve, so argv[0] = 0 , она вызовет write(0, hello, 0) и, следовательно, ничего не напечатает.

Но если вы запустите его с одним аргументом (не считая argv[0], который оболочки передают неявно), он будет печатать в stderr . При использовании 2 или более аргументов он попытается выполнить запись в не открытый fd, и запись вернется -EBADF , как вы могли видеть ниже strace .

Комментарии:

1. идея хорошая, но, 1) Я сохраняю строку в заголовке файла, и больше нет места для байтов, 2) Я должен выйти с кодом состояния 0, это было условие, которое я указал для себя

2. @gXLg: Ах, тогда вы застряли бы с конечным мусором, если только вы не можете поместить его в сегмент, ФАЙЛ которого заканчивается в конце строки, поэтому остальная часть страницы заполнена нулями, как и BSS. Но, по-видимому, у вас есть только одна запись в заголовке вашей программы ELF, которая охватывает этот и исполняемый код, а другая запись увеличит общий размер.

3. Да, это так, только один заголовок программы, никаких заголовков разделов и 16 байт, перекрывающихся между заголовком программы и файла. И все же спасибо вам за время, которое вы потратили на это.