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; }
то ничего плохого при изменении размера не случится?
или для любого внешнего массива?
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; }
то ничего плохого при изменении размера не случится?
или для любого внешнего массива?
0
Надо экспериментировать. Массив в любом случае при загрузке будет перенесён из so-файла в основной модуль. Но будет ли загрузчик копировать область нулей? А если рядом с этим массивом ещё два, но уже инициализированных? Может ли загрузчик скопировать всю область одним разом, или скопирует только инициализированные, а нулевой пропустит? Неизвестно.
0
UFO just landed and posted this here
Доки на что? Стандарт C не описывает линковку так детально.
Только если в вашем компиляторе есть гарантия, что неинициализированные массивы и инициализированные обязаны располагаться в разных секциях исполняемого файла, то это implementation-defined. В MSVC привязка к секциям настраивается опциями компилятора (впрочем, на windows нет этой проблемы с массивами).
Только если в вашем компиляторе есть гарантия, что неинициализированные массивы и инициализированные обязаны располагаться в разных секциях исполняемого файла, то это implementation-defined. В MSVC привязка к секциям настраивается опциями компилятора (впрочем, на windows нет этой проблемы с массивами).
0
UFO just landed and posted this here
Проблема же не в свойствах, а в способе их достижения.
Неинициализированный массив должен быть заполнен нулями, но как этого достичь — загрузить нули из so-файла, запросить у OS страницу через функцию, гарантированно отдающую чистую страницу, или выполнить код в загрузчике, который обнулит область — выбор из этих вариантов не регламентируется стандартом.
Неинициализированный массив должен быть заполнен нулями, но как этого достичь — загрузить нули из so-файла, запросить у OS страницу через функцию, гарантированно отдающую чистую страницу, или выполнить код в загрузчике, который обнулит область — выбор из этих вариантов не регламентируется стандартом.
0
UFO just landed and posted this here
Неинициализированный глобальный массивСчитайте, что в моём комментарии так и написано. Это что-то меняет?
Потому что тут важен результат, а не способ его получения.Ладно, похоже вы не поняли суть статьи.
0
UFO just landed and posted this here
Ага, а из этого вы делаете вывод, что инициализированные переменные и не инициализированные по-разному обрабатываются загрузчиком. Ссылаетесь на «доки». Логический вывод непонятен.
0
Вот только пункт #2 не имеет ничего общего со статьёй: приложение таки запустится и, в некоторых случаях, даже может работать.
0
Недостаточно. Всё, что говорит стандарт по вопросу, обсуждаемому в статье: если у вас описания переменной в разных исходных файлах отличаются — то может случиться всё, что угодно.
Это описание, как бы это сказать… несколько неполно. Это если матом не хочется ругаться.
Это описание, как бы это сказать… несколько неполно. Это если матом не хочется ругаться.
+2
UFO just landed and posted this here
при запуске подсовываете другую, с измененным APIТак это, по-вашему, undefined behavior, и может случиться всё, что угодно, или ситуация регулируется стандартом и нам поможет чтение «доки»?
0
Это справочник. Вся необходимая информация там есть, но в ней слишком легко утонуть. Если хотите понять как устроены разделяемые библиотеки на ELF-системе — тучше почитать вот это.
Тогда станет понятно — что происходит и, главное, почему. Доку на системный ABI после этого можно читать уже разве что для понимания того, что данный ABI является вменяемым и устроен плюс-минус так же, как описано в той статье (там рассматривается только i386 архитектура по достаточно уважительной причине: статья таки 2002 года, а первый процессор AMD64 появился в 2003м).
Тогда станет понятно — что происходит и, главное, почему. Доку на системный ABI после этого можно читать уже разве что для понимания того, что данный ABI является вменяемым и устроен плюс-минус так же, как описано в той статье (там рассматривается только i386 архитектура по достаточно уважительной причине: статья таки 2002 года, а первый процессор AMD64 появился в 2003м).
+2
Маленькое дополнение. Ещё есть ARM. В частности AArch64 примерно после 2011 года появился. Конкретно i386 сейчас уже можно рассматривать как устаревший для большинства задач. Тотже linux дропнул его где-то в 2012 году.
0
Тотже linux дропнул его где-то в 2012 году.Линукс дропнул поддержку 80386го процессора, а не i386 ABI. Который и сегодня «живее всех живых».
Да и неважно это для 99% статьи: там i386 в качестве примера используется, а не как что-то уникальное, только там существующее. AAarch64, в общих чертах, так же устроен.
+2
Проблема касается любого внешнего массива и сводится к тому, что бинарник — по умолчанию собирается без опции -fPIC.
Если вы его соберёте с опцией -fPIC — то всё будет работать независимо от размера массива.
Если вы его соберёте с опцией -fPIC — то всё будет работать независимо от размера массива.
0
если не стрипать 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:~$
0
А если пострипать, то через 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
+1
это смотря как пострипать:
и потом я линковал статически
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:~$
и потом я линковал статически
0
Sign up to leave a comment.
Как размеры массивов C стали частью двоичного интерфейса библиотеки