Из пустого в порожнее
2024-12-20 12:25Взялся я поисследовать Си-шный рантайм. Глянуть, что делается в программе до main() и после. Беру пустую программу:
У меня иногда мелькает вопрос, чем занимаются компьютеры, когда мы на них не глядим. Какие-то бесполезные биты на байты множат. 😀
Компилирую, причём линкую статически, чтобы избежать сложностей динамического загрузчика:$ cat empty.c
int main()
{
// empty
}
Глянем размер бинарника. На процессоре x86_64 под Ubuntu получается 667 килобайт:$ cc -static -Os empty.c -o empty
На процессоре arm64 выходит чуть получше, 497 килобайт:$ size empty
text data bss dec hex filename
667574 23384 22440 713398 ae2b6 empty
Такие объёмы настораживают. Хорошо, запустим и посчитаем количество выполненных машинных команд. На процессоре x86_64:$ size empty
text data bss dec hex filename
497904 22180 21616 541700 84404 empty
На процессоре arm64:$ bintrace -o x86.trace ./empty
$ grep '^0x' x86.trace | wc
170979 1371361 10303769
Ну офигеть просто. Больше ста тысяч команд чтобы просто стартонуть Си-шный код. Неладно что-то в Датском королевстве.$ bintrace -o arm64.trace ./empty
$ grep '^0x' arm64.trace | wc
108719 565684 5186654
У меня иногда мелькает вопрос, чем занимаются компьютеры, когда мы на них не глядим. Какие-то бесполезные биты на байты множат. 😀

no subject
Date: 2024-12-20 20:48 (UTC)no subject
Date: 2024-12-20 20:59 (UTC)На процессоре x86_64 под Ubuntu получается 667 килобайт
Date: 2024-12-20 21:35 (UTC)gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
no subject
Date: 2024-12-20 21:47 (UTC)no subject
Date: 2024-12-20 21:52 (UTC)$ perf record -e instructions hyperfine --shell=none ./empty $ perf report --dsos=empty | head -n50 [...] 6.63% empty [.] classify_object_over_fdes 4.42% empty [.] read_encoded_value_with_base 3.29% empty [.] __tunables_init 0.40% empty [.] intel_check_word.constprop.0 0.24% empty [.] __strncmp_avx2 0.17% empty [.] getenv 0.09% empty [.] _dl_non_dynamic_init 0.08% empty [.] _int_malloc 0.08% empty [.] _dl_lookup_symbol_x [...]Если правильно понимаю, проценты маленькие, потому что еще выполнялась куча кода в ядре и в hyperfine.
Выходит, что большая часть инструкций занимается обработкой .eh_frame и glibc tunables.
no subject
Date: 2024-12-20 22:02 (UTC)no subject
Date: 2024-12-20 22:03 (UTC)Попробуй добавить в начало
#include <unistd.h> void exit(int status) { _exit(status); }no subject
Date: 2024-12-20 22:56 (UTC)no subject
Date: 2024-12-20 23:24 (UTC)no subject
Date: 2024-12-21 00:43 (UTC)no subject
Date: 2024-12-21 00:46 (UTC)И в первом случае тоже так ругается, с _exit.
no subject
Date: 2024-12-21 01:28 (UTC)no subject
Date: 2024-12-21 01:31 (UTC)no subject
Date: 2024-12-21 01:42 (UTC)$ cat empty.c int main() {} #include <unistd.h> void exit(int status) { _exit(status); } $ cc -static empty.c -v ... /usr/libexec/gcc/aarch64-linux-gnu/13/collect2 -plugin /usr/libexec/gcc/aarch64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/aarch64-linux-gnu/13/lto-wrapper -plugin-opt=-fresolution=/tmp/ccDzVNsd.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --build-id --hash-style=gnu --as-needed -Bstatic -X -EL -maarch64linux --fix-cortex-a53-843419 -z relro /usr/lib/gcc/aarch64-linux-gnu/13/../../../aarch64-linux-gnu/crt1.o /usr/lib/gcc/aarch64-linux-gnu/13/../../../aarch64-linux-gnu/crti.o /usr/lib/gcc/aarch64-linux-gnu/13/crtbeginT.o -L/usr/lib/gcc/aarch64-linux-gnu/13 -L/usr/lib/gcc/aarch64-linux-gnu/13/../../../aarch64-linux-gnu -L/usr/lib/gcc/aarch64-linux-gnu/13/../../../../lib -L/lib/aarch64-linux-gnu -L/lib/../lib -L/usr/lib/aarch64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/aarch64-linux-gnu/13/../../.. /tmp/cc1QUvYS.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/aarch64-linux-gnu/13/crtend.o /usr/lib/gcc/aarch64-linux-gnu/13/../../../aarch64-linux-gnu/crtn.o /usr/bin/ld: /usr/lib/gcc/aarch64-linux-gnu/13/../../../aarch64-linux-gnu/libc.a(exit.o): in function `exit': (.text+0x230): multiple definition of `exit'; /tmp/cc1QUvYS.o:empty.c:(.text+0x8): first defined here collect2: error: ld returned 1 exit statusno subject
Date: 2024-12-21 01:50 (UTC)int main() { // empty } // Tell the compiler incoming stack alignment is not RSP%16==8 or ESP%16==12 __attribute__((force_align_arg_pointer)) void _start() { /* main body of program: call main(), etc */ /* exit system call */ asm("movl $1,%eax;" "xorl %ebx,%ebx;" "int $0x80" ); main(); __builtin_unreachable(); // tell the compiler to make sure side effects are done before the asm statement }_start(), правда, срисован с 32-битной версии, но это можно потом поправить.
gcc -static -nostdlib -Os empty.c -o empty
полученый бинарник "работает"
no subject
Date: 2024-12-21 03:33 (UTC)весь Си-шный рантайм отрезан
Date: 2024-12-21 08:45 (UTC)P.S. отвечаю не чтобы поспорить, а потому, что в примере вызов main() не там стоит. Позорник ... :)
no subject
Date: 2024-12-21 08:48 (UTC)no subject
Date: 2024-12-21 08:56 (UTC)no subject
Date: 2024-12-21 12:58 (UTC)no subject
Date: 2024-12-21 16:43 (UTC)no subject
Date: 2024-12-21 17:02 (UTC)Второй год пытаюсь донести до коллег, что библиотека объектных модулей это библиотека.
Из нее должно браться при линковке нужное и то, что нужно нужному. Рекурсивно.
Как в книжной библиотеке: дают запрошенные книги (тут без рекурсии, да), а не отгружают 20 тонн всех запасов.
Разучились строить архитектуры и классов и модулей без использования кулёчного принципа: "а вдруг и это понадобится? Прибьём гвоздями на всякий случай!"
no subject
Date: 2024-12-21 17:04 (UTC)no subject
Date: 2024-12-22 00:11 (UTC)$ cat empty.c #include <unistd.h> void _start() { extern int main(); _exit(main()); } int main() { return 42; } $ cat Makefile empty: empty.o; ld -static -e _start -o $@ $^ /lib64/libc.a $ make cc -c -o empty.o empty.c ld -static -e _start -o empty empty.o /lib64/libc.a $ ./empty $ echo $? 42 $ stat -c %s empty 9704 $ size empty text data bss dec hex filename 232 0 4 236 ec empty $no subject
Date: 2024-12-22 00:22 (UTC)no subject
Date: 2024-12-22 01:47 (UTC)no subject
Date: 2024-12-22 04:25 (UTC)no subject
Date: 2024-12-26 06:53 (UTC)