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

У меня иногда мелькает вопрос, чем занимаются компьютеры, когда мы на них не глядим. Какие-то бесполезные биты на байты множат. 😀

Date: 2024-12-20 20:48 (UTC)
juan_gandhi: (Default)
From: [personal profile] juan_gandhi
Обеспечивают безопасность, небось.

Date: 2024-12-20 20:59 (UTC)
madef: (Default)
From: [personal profile] madef
Во второй половине девяностых довелось мне принимать участие в одном проектике с embedded software. Тогда оно всё целиком сочинялось на ассемблере. Не удивлюсь, если и сейчас так.
From: [personal profile] h1uke
это у вас Ubuntu новая и компилятор получше. А на 22.04.5 LTS, если из коробки, еще больше:

$ size empty
 text     data    bss    dec      hex   filename
 781207   23240   23016  827463   ca047 empty

gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)

Date: 2024-12-20 21:47 (UTC)
From: [personal profile] dedekha
Ничего удивительного много чего надо подготовить, а для любителей приключений полно способов пользоваться своим собственным. И по нынешним временам это вполне дешево. И на множестве embedded system едет вполне полный bsd или линукс.




Date: 2024-12-20 21:52 (UTC)
mephi42: (Default)
From: [personal profile] mephi42
Попробовал посмотреть в профайлере:

$ 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.

Date: 2024-12-20 22:02 (UTC)
vlad_m: (Default)
From: [personal profile] vlad_m
Glibc?

Date: 2024-12-21 17:02 (UTC)
vlad_m: (Default)
From: [personal profile] vlad_m
Разучились люди библиотеки писать.

Второй год пытаюсь донести до коллег, что библиотека объектных модулей это библиотека.
Из нее должно браться при линковке нужное и то, что нужно нужному. Рекурсивно.

Как в книжной библиотеке: дают запрошенные книги (тут без рекурсии, да), а не отгружают 20 тонн всех запасов.

Разучились строить архитектуры и классов и модулей без использования кулёчного принципа: "а вдруг и это понадобится? Прибьём гвоздями на всякий случай!"

Date: 2024-12-20 22:03 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Там же exit(), который всё stdio за собой тащит.

Попробуй добавить в начало
#include <unistd.h>
void exit(int status) { _exit(status); }


Edited Date: 2024-12-20 22:16 (UTC)

Date: 2024-12-21 01:28 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Это какое-то безобразие. _exit() - это системный вызов, который должен приводить к безусловному завершению процесса. Интересно, в чём там проблема.

Date: 2024-12-21 00:43 (UTC)
archaicos: Шарж (Default)
From: [personal profile] archaicos
А если _Exit() из stdlib.h?

Date: 2024-12-21 01:31 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Это проблемы статической линковки. Если libc.a упомянута явно, то это multiple definition, а если -lc, то вроде не должно.

Date: 2024-12-21 08:48 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Выходит, писать на "голом Си" (алгоритмический язык), вызывая лишь те внешние функции/системные вызовы, которые хочется, и отказаться от всего остального библиотечного скарба, больше нельзя? Досадно.

Date: 2024-12-21 12:58 (UTC)
dmarck: (Default)
From: [personal profile] dmarck
-nostdinc -nostdlib ещё существуют, но это сущий адЪ

Date: 2024-12-21 16:43 (UTC)
spamsink: (Default)
From: [personal profile] spamsink
Они, собственно, и не для этого. Хочется-то stdlib оставить, а только от stdio избавиться.

Date: 2024-12-21 01:50 (UTC)
From: [personal profile] h1uke
вот так получается:


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

$ size empty
   text    data     bss     dec     hex filename
   152     0        0       152     98  empty


полученый бинарник "работает"

From: [personal profile] h1uke
ну, не "весь", а "стандартный". Очень часто именно это и нужно.

P.S. отвечаю не чтобы поспорить, а потому, что в примере вызов main() не там стоит. Позорник ... :)

Date: 2024-12-21 17:04 (UTC)
vlad_m: (Default)
From: [personal profile] vlad_m
А я помню староглиняные времена, когда main(argc, argv, envp) вызывалась непосредственно ядром при exec...(). 😁

Date: 2024-12-22 00:11 (UTC)
henry_flower: A melancholy wolf (Default)
From: [personal profile] henry_flower
на федорі 41 (вимбачте, не втримавсь):

$ 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
$ 

Date: 2024-12-22 00:22 (UTC)
henry_flower: A melancholy wolf (Default)
From: [personal profile] henry_flower
до речі, а шо то за bintrace такий? гоогл дає кілька варіянтів