", а не ядро линукс и тем более не какое окружение там настроено..." - мною было где то сказано относительно генерации чего ли бо ядром linux? где мной сказано про ядро linux...??? и поэтому бредишь похоже ты сам... --- Сообщение объединено, Feb 23, 2025 --- Первое - нужно мне... цель - разобраться в изложении материала книги. Второе - ответ услышан... ты не пуп земли... Третье - добиться доказательство ответа: это точно ELF? и в заключении ответ на вопрос по теме поста - связка Си и ассемблера.
Не зависит. Только от версии gcc и библиотек в системе, используемых твоей программой. Соответственно библиотекам ставятся и заголовки к ним, которые могут содержать инструкции gcc для генерации той или иной конструкции в конечном файле. После создания .s файла вы получаете уже готовый к производству бинарного файла набор инструкций и директив ассемблера, который подключается к вашему проекту как он есть без изменения (только те флаги, на которые реагирует ассемблер). Вообще процесс компиляции можно разбить на два этапа, которые gcc выполняет подряд или останавливается после превого. Когда вы даете gcc файл .c или .cpp или .s (или любой другой исходник), тогда вы заставляете gcc перенаправить команду в компилятор. Компилятор создает из этого исходника бинарный файл для линкера .o или .obj. Но если вы говорите создать .s файл, тогда на выходе получаете .s файл исходника и уже линке его прожевать не сможет (процесс останавливается после первого этапа). После сборки всех исходных текстов в объектный (бинарный) формат запускается линкер, который оперирует блоками данных из этих объектных файлов. Он собирает весь код в одну секцию, все данные в одну секцию, а так же приправляет все это исправлениями для доступа из одного блока кода к его блоку переменных, а не в рандомное место. Вот на этом процессе и возникают проблемы с адресацией дальше 2 Гб, когда в коде уже заложена арифметика указателей не оперирующая смещениями больше 32 бит. Линкеру надо подставить в код новое смещение, а оно требует для размещения не 32-х, а большего числа бит (64-х). Вот и вся ваша проблема. А в чем проблема у вас с совмещением тоже не понятно. К тому же в теме вы пишите про MASM, но так и не использовали его (все больше используете as - gnu assembler). Еще для обсуждения ваших вопросов не плохо было бы показать версии вашего компилятора, чтобы спрашивать о коде, который он создает для своих нужд. --- Сообщение объединено, Feb 23, 2025 --- Т.е. о MSYS2 или MinGW вы не слышали. --- Сообщение объединено, Feb 23, 2025 --- И про WSL тоже не слышали --- Сообщение объединено, Feb 23, 2025 --- Не знаете - записали. --- Сообщение объединено, Feb 23, 2025 --- Для ответа на эти вопросы вам надо искать все в исходниках к соответствующим версиям компиляторов. Т.к. вы спрашиваете о служебном коде, который генерирует конкретная версия для работоспособности программы.
Если ты увидел эту ошибку, то всё, ты дальше долбишься в закрытую дверь - у тебя компилятор не может делать ELF с сегментами более 2Гб несмотря на 64-битность. Эта дверь закрыта, переходи к следующему вопросу из книги, тем более что это явно вопрос не из книги. На винде безусловный отлуп - сами 64-битные PE+ просто забиты 32-битными полями и тупо не могут быть больше 2Гб. Нужна Windows 15+ чтобы ситуация изменилась. На линуксе интереснее - судя по гуглу там таки есть REL64_64, т.е. 64-битные релокации, но компилятор должен быть собран с поддержкой этого добра и более того - libc с crt тоже должны быть собраны в режиме -mcmodel=large, что уже другая даже плоскость вопроса, но тоже необходимая. И судя по всему по дефолту из-за тех накладных расходов что large model накладывает этим никто не хочет заморачиваться даже. Поэтому задачка нетривиальная. Дойдут руки посмотрю как в моей бубунте дела обстоят.
На это ответили еще на 4 странице - ограничение формата исполняемого файла, но оно связано с ограничениями в инструкциях процессора. --- Сообщение объединено, Feb 23, 2025 --- Если книга подает материал так не качественно, тогда стоит взять книгу по проще, а после вернуться к изучению этой. Иначе у вас и так каша в голове совсем убежит.
Исправляюсь - проблема в линуксе не в этом, тут у меня самого в голове смешалось то, что происходит в Windows. В линуксе же пока что единственное что я вижу по гуглу сам не попробовав - это то, что используются несмотря на -mcmodel=large 32-битные релокации (REL64_32) в таблицах релокации и поэтому линковщик пасует. Но это пока еще не значит что проблема в размерах сегментов. Более того - проблема возможно вообще только в том, что crt и libc скомилированы без опции -mcmodel=large и поэтому именно линковка с ними вызывает ошибку, а если бы мы писали чисто собственный код да еще на асме, то проблемы бы тоже не было. Дойдут руки попробую проверить.
С этим головняком позволят развлечься Gentoo или Arch. Нужен -mcmodel=large у libc - просто собери сам.
Кстати да, версия, что в линуксе подвох только в том, что libc скомпилирован без -mcmodel=large получает всё больше подтверждений в обсуждении на других ресурсах. Опыт простейший надо сделать, скомпилировать следующую программу с двумя вариантами ключей: Code (Text): #include <iostream> #include <cstring> const long long strSize = 1500000000; // 1.5 Гб char message1[strSize]; // этот массивчик пойдёт в секцию data PE const char message2[strSize] = { 1 }; // а этот в секцию rdata int main(int argc, char *argv[]) { memset(message1, 1, strSize); message1[strSize - 1] = 0; std::cout << strlen(message1) << " " << message2 << "\n"; return 0; } С -mcmodel=small она обязана не скомпилироваться. А вот далее компилировать надо с -mcmodel=medium и -mcmodel=large. Скорее всего на любой системе первое скомпилируется - потому что -mcmodel погружает сегменты с кодом и маленькие данные в первые первые 2Гб и даже если libc скомпилирован в small он просто должен туда попасть и не будет порождать ошибок. А вот если второе при этом не скомпилируется, то это просто подтвердит эту же версию. Но сам пока никак не могу добраться до линукса, хотя уже почти уверен что так дела и обстоят.
aa_dav, не очень понимаю нахрена городить статики столько, кода на 2 ГБ это как вообще. Память вызываем динамически... Попытался твой код скомпилировать на UASM64, быстро его перевёл, на малых данных компилируется, но с 1'500'000'000, сначала на долго задумывается, а потом сообщает о ошибке памяти. Code (ASM): .x64 option casemap:none option frame:auto option LITERALS:ON IF @Platform EQ 1 APP_WIN64 EQU 1 ENDIF include windows.inc include msvcrt.inc includelib msvcrt.lib include \assemblers\include\macros.asm strSize = 1500000000 ;// 1.5 Гб .data message1 byte strSize dup (?) ;// этот массивчик пойдёт в секцию data PE .const message2 byte strSize dup (1) ;// а этот в секцию rdata .code main proc frame argc:sdword, argv:ptr ptr, envp:ptr ptr ;argc:ecx, argv:rdx, envp:r8 memset(&message1, 1, strSize) mov message1[strSize - 1], 0 printf$("%lld %s\n", strlen(&message1), &message2) xor eax, eax ret main endp main_startup3_END UASM рулит, но тут вероятно тоже надо какие-то опции включать. Ватник. Code ( (Unknown Language)): @echo off set _name_="test4gb" set _path_="\assemblers" %_path_%\bin\uasm64 -win64 /c /I%_path_%\include\x64 %_name_%.asm %_path_%\bin\link /SUBSYSTEM:CONSOLE /machine:x64 /LARGEADDRESSAWARE:NO /LIBPATH:%_path_%\lib\x64 %_name_%.obj if exist %_name_%.obj del %_name_%.obj pause --- Сообщение объединено, Feb 26, 2025 --- Апд: Ассемблер скомпилировал код, работал 303 сек. А вот линкер подавился, ошибка LNK1248. Надо линкер 64 битный... --- Сообщение объединено, Feb 26, 2025 --- Не, получается РЕ не поддерживает такие файлы, а линукса у меня нет, и использовать его не планирую.
Да это просто проверка способен ли компилятор и ОСь на 64-bit ultra super force unleashed или есть какие то затыки, т.к. создатели думают, что не время еще всю собаку с цепи спустить. Ну да, в винде получается даже формат выполнимого файла еще тянет ограничения 32-бит. А в линуксе вроде как сделали, но так, что надо еще немного (или много) постараться.
Code (Bash): xchgeaxeax@home-kde ~/testDir/2gbTest $ cat ./main.cpp #include <iostream> #include <cstring> const long long strSize = 1500000000; // 1.5 Гб const char message1[strSize] = { 0 }; // этот массивчик пойдёт в секцию rdata const char message2[strSize] = { '1' }; // а этот в секцию rdata int main(int argc, char *argv[]) { std::cout << strlen(message1) << " " << message2 << "\n"; return 0; } xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=small -o ./2gbtest_small ./main.cpp [B]многа нецензурной лексики в мой адрес[/B] collect2: ошибка: выполнение ld завершилось с кодом возврата 1 xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=medium -o ./2gbtest_medium ./main.cpp xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=large -o ./2gbtest_large ./main.cpp xchgeaxeax@home-kde ~/testDir/2gbTest $ ./2gbtest_medium 0 1 xchgeaxeax@home-kde ~/testDir/2gbTest $ ./2gbtest_large 0 1 xchgeaxeax@home-kde ~/testDir/2gbTest $ ls -la итого 5859412 drwxr-xr-x 1 xchgeaxeax xchgeaxeax 70 фев 26 15:52 . drwxr-xr-x 1 xchgeaxeax xchgeaxeax 3340 фев 26 15:40 .. -rwxr-xr-x 1 xchgeaxeax xchgeaxeax 3000016112 фев 26 15:52 2gbtest_large -rwxr-xr-x 1 xchgeaxeax xchgeaxeax 3000016376 фев 26 15:52 2gbtest_medium -rw-r--r-- 1 xchgeaxeax xchgeaxeax 381 фев 26 15:51 main.cpp Я не стал городить огородов и сделал так. Получились эльфы по 3 Гб каждый и даже нормально выполнились. А вот -mcmodel=small как раз из-за размеров и не смогла собраться. Линкер сказал, что так нельзя.
Ну вот, дошли руки до бубунты моей и всё так и есть как я парой коммов выше писал - по умолчанию либси/стдлиб скомпилированы с -mcmodel=small и не переживают линковку с -mcmodel=large если сегменты реально вышли за 2Гб. Однако есть срединный путь - mcmodel=medium когда, как я понял, стандартные сегменты типа .text или .data специально пихаются в начало образа чтобы уместиться в 2Гб, но если мы заводим свои данные (и, видимо, и код) размером больше определенного размера помещается в особые "гигантские сегменты" и вот они отпихиваются в конец образа и таким образом можно успешно скомилировать пример и напихать в него хоть 10 Гб глобальных массивов. Таким образом действительно, линукс оказался на коне, хотя и не без подводных камней, и действительно в каком нибудь генту скорее всего можно всё закомпилить в -mcmodel=large и быть во всеоружии.
Ну если что, то я у себя на Calculate Linux пробовал. А это производная от Gentoo. У меня как раз medium и large сработали
А, да, тут нюансик есть - чтобы выбило ошибку на large надо вот это: const long long strSize = 1500000000; // 1.5 Гб поменять на: const long long strSize = 3000000000; // 3 Гб Тогда у меня с large выбивает в убунте, я не совсем уверенно это могу объяснить - видимо секция .bss из libc никак не используется и если она попадает на конец образа, то опять таки коду либси становится нестрашно. Что-то такое.
Даже если поменять Code (Bash): xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=small -o ./2gbtest_small ./main1.cpp многа нецензурного в мой адрес collect2: ошибка: выполнение ld завершилось с кодом возврата 1 xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=medium -o ./2gbtest_medium ./main1.cpp xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=large -o ./2gbtest_large ./main1.cpp xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=small -o ./2gbtest_small2 ./main2.cpp xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=medium -o ./2gbtest_medium2 ./main2.cpp xchgeaxeax@home-kde ~/testDir/2gbTest $ g++ -mcmodel=large -o ./2gbtest_large2 ./main2.cpp xchgeaxeax@home-kde ~/testDir/2gbTest $ cat ./main1.cpp #include <iostream> #include <cstring> const long long strSize = 3000000000; // 1.5 Гб char message1[strSize]; // этот массивчик пойдёт в секцию data PE const char message2[strSize] = { 1 }; // а этот в секцию rdata int main(int argc, char *argv[]) { memset(message1, 1, strSize); message1[strSize - 1] = 0; std::cout << strlen(message1) << " " << message2 << "\n"; return 0; } xchgeaxeax@home-kde ~/testDir/2gbTest $ cat ./main2.cpp #include <iostream> #include <cstring> const long long strSize = 1500000000; // 1.5 Гб char message1[strSize]; // этот массивчик пойдёт в секцию data PE const char message2[strSize] = { 1 }; // а этот в секцию rdata int main(int argc, char *argv[]) { memset(message1, 1, strSize); message1[strSize - 1] = 0; std::cout << strlen(message1) << " " << message2 << "\n"; return 0; } xchgeaxeax@home-kde ~/testDir/2gbTest $ ./2gbtest_small bash: ./2gbtest_small: Нет такого файла или каталога xchgeaxeax@home-kde ~/testDir/2gbTest $ ./2gbtest_medium 2999999999 xchgeaxeax@home-kde ~/testDir/2gbTest $ ./2gbtest_large 2999999999 xchgeaxeax@home-kde ~/testDir/2gbTest $ ./2gbtest_small2 1499999999 xchgeaxeax@home-kde ~/testDir/2gbTest $ ./2gbtest_medium2 1499999999 xchgeaxeax@home-kde ~/testDir/2gbTest $ ./2gbtest_large2 1499999999 xchgeaxeax@home-kde ~/testDir/2gbTest $ ls -la итого 10254012 drwxr-xr-x 1 xchgeaxeax xchgeaxeax 192 фев 27 16:26 . drwxr-xr-x 1 xchgeaxeax xchgeaxeax 3340 фев 26 16:10 .. -rwxr-xr-x 1 xchgeaxeax xchgeaxeax 3000019848 фев 27 16:25 2gbtest_large -rwxr-xr-x 1 xchgeaxeax xchgeaxeax 1500020104 фев 27 16:26 2gbtest_large2 -rwxr-xr-x 1 xchgeaxeax xchgeaxeax 3000020112 фев 27 16:25 2gbtest_medium -rwxr-xr-x 1 xchgeaxeax xchgeaxeax 1500016272 фев 27 16:26 2gbtest_medium2 -rwxr-xr-x 1 xchgeaxeax xchgeaxeax 1500016304 фев 27 16:26 2gbtest_small2 -rw-r--r-- 1 xchgeaxeax xchgeaxeax 428 фев 27 16:23 main1.cpp -rw-r--r-- 1 xchgeaxeax xchgeaxeax 428 фев 27 16:24 main2.cpp -rw-r--r-- 1 xchgeaxeax xchgeaxeax 432 фев 26 16:16 main.cpp У меня не собрался опять только -mcmodel=small для константы 3гб
Хм, странно, чего то я тогда еще недопонимаю в целой картине. У меня стабильно large не компилировался там где medium компилировался когда даже самый первый сегмент данных вылезал за 2Гб. Не знаю даже чем объяснить такую разницу. А, надо еще попробовать обновиться и на свежайшем дистре попробовать, может там какие то стратегии распределения сегментов в линкере поменяли или типа того.