Pull to refresh

Comments 24

правильно ли я понял, что это относится только к инициализированными массивам? и если мы пользуем что-то вроде:
test.h
extern «C» int a[];
int get_a_size();

где-то в библиотеке
test.c
int a[4];
int get_a_size() { return 4; }

в каком-то методе
{ a[0] = 1; a[1] = 2; a[2] = 3; a[3] = 4; }

то ничего плохого при изменении размера не случится?

или для любого внешнего массива?
Надо экспериментировать. Массив в любом случае при загрузке будет перенесён из so-файла в основной модуль. Но будет ли загрузчик копировать область нулей? А если рядом с этим массивом ещё два, но уже инициализированных? Может ли загрузчик скопировать всю область одним разом, или скопирует только инициализированные, а нулевой пропустит? Неизвестно.
UFO just landed and posted this here
Доки на что? Стандарт C не описывает линковку так детально.
Только если в вашем компиляторе есть гарантия, что неинициализированные массивы и инициализированные обязаны располагаться в разных секциях исполняемого файла, то это implementation-defined. В MSVC привязка к секциям настраивается опциями компилятора (впрочем, на windows нет этой проблемы с массивами).
UFO just landed and posted this here
Проблема же не в свойствах, а в способе их достижения.

Неинициализированный массив должен быть заполнен нулями, но как этого достичь — загрузить нули из so-файла, запросить у OS страницу через функцию, гарантированно отдающую чистую страницу, или выполнить код в загрузчике, который обнулит область — выбор из этих вариантов не регламентируется стандартом.
UFO just landed and posted this here
Неинициализированный глобальный массив
Считайте, что в моём комментарии так и написано. Это что-то меняет?
Потому что тут важен результат, а не способ его получения.
Ладно, похоже вы не поняли суть статьи.
UFO just landed and posted this here
Ага, а из этого вы делаете вывод, что инициализированные переменные и не инициализированные по-разному обрабатываются загрузчиком. Ссылаетесь на «доки». Логический вывод непонятен.
Вот только пункт #2 не имеет ничего общего со статьёй: приложение таки запустится и, в некоторых случаях, даже может работать.
Недостаточно. Всё, что говорит стандарт по вопросу, обсуждаемому в статье: если у вас описания переменной в разных исходных файлах отличаются — то может случиться всё, что угодно.

Это описание, как бы это сказать… несколько неполно. Это если матом не хочется ругаться.

UFO just landed and posted this here
при запуске подсовываете другую, с измененным API
Так это, по-вашему, undefined behavior, и может случиться всё, что угодно, или ситуация регулируется стандартом и нам поможет чтение «доки»?
UFO just landed and posted this here
Изначально я написал, что процесс загрузки в данной ситуации не определён и требует экспериментов (с загрузчиком, не компилятором!) для прояснения. К чему были ваши ответы и отсылки к компилятору?
Это справочник. Вся необходимая информация там есть, но в ней слишком легко утонуть. Если хотите понять как устроены разделяемые библиотеки на ELF-системе — тучше почитать вот это.

Тогда станет понятно — что происходит и, главное, почему. Доку на системный ABI после этого можно читать уже разве что для понимания того, что данный ABI является вменяемым и устроен плюс-минус так же, как описано в той статье (там рассматривается только i386 архитектура по достаточно уважительной причине: статья таки 2002 года, а первый процессор AMD64 появился в 2003м).

Маленькое дополнение. Ещё есть ARM. В частности AArch64 примерно после 2011 года появился. Конкретно i386 сейчас уже можно рассматривать как устаревший для большинства задач. Тотже linux дропнул его где-то в 2012 году.

Тотже linux дропнул его где-то в 2012 году.
Линукс дропнул поддержку 80386го процессора, а не i386 ABI. Который и сегодня «живее всех живых».

Да и неважно это для 99% статьи: там i386 в качестве примера используется, а не как что-то уникальное, только там существующее. AAarch64, в общих чертах, так же устроен.
Проблема касается любого внешнего массива и сводится к тому, что бинарник — по умолчанию собирается без опции -fPIC.

Если вы его соберёте с опцией -fPIC — то всё будет работать независимо от размера массива.
если не стрипать ELF, то длину легко достать через libbfd открыв свой же экзешник:

root@Shiva:~$ cat k.c
int arr[4];
root@Shiva:~$ cat k1.c

#include <stdio.h>

extern int arr[];

int main()
{
printf("%p\n", arr);
return 0;
}
root@Shiva:~$ gcc k1.c k.c
root@Shiva:~$ nm --print-size --size-sort --radix=d a.out
0000000002101264 0000000000000001 b completed.7697
0000000000001760 0000000000000002 T __libc_csu_fini
0000000000001776 0000000000000004 R _IO_stdin_used
0000000002101280 0000000000000016 B arr
0000000000001610 0000000000000035 T main
0000000000001344 0000000000000043 T _start
0000000000001648 0000000000000101 T __libc_csu_init
root@Shiva:~$

А если пострипать, то через readelf:

$ readelf --dyn-syms a.out

Symbol table '.dynsym' contains 12 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     5: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)
     6: 0000000000201028     0 NOTYPE  GLOBAL DEFAULT   23 _edata
     7: 0000000000201048     0 NOTYPE  GLOBAL DEFAULT   24 _end
     8: 0000000000201030    20 OBJECT  GLOBAL DEFAULT   24 external_array
     9: 0000000000201028     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    10: 00000000000005c8     0 FUNC    GLOBAL DEFAULT   10 _init
    11: 0000000000000794     0 FUNC    GLOBAL DEFAULT   14 _fini
это смотря как пострипать:

root@Shiva:~$ gcc k1.c k.c
root@Shiva:~$ readelf --dyn-syms -s a.out|grep arr
33: 0000000000200db8 0 OBJECT LOCAL DEFAULT 19 __frame_dummy_init_array_
39: 0000000000200dc0 0 NOTYPE LOCAL DEFAULT 19 __init_array_end
41: 0000000000200db8 0 NOTYPE LOCAL DEFAULT 19 __init_array_start
60: 0000000000201020 16 OBJECT GLOBAL DEFAULT 24 arr
root@Shiva:~$ strip -s a.out
root@Shiva:~$ readelf --dyn-syms -s a.out|grep arr
root@Shiva:~$


и потом я линковал статически
Sign up to leave a comment.

Articles