![]() |
Добро пожаловать, гость ( Вход | Регистрация )
![]() ![]() |
![]() |
-=CHE@TER=- |
![]()
Сообщение
#1
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
В общем, в последнее время часто приходится работать с программами на Си / C++.
В связи с этим хотелось бы нормальный компилятор. Сейчас пользуюсь DEV-CPP 4.9.8.0 и MSVC++ 6.0. Чем меня не устраивают оба этих компилятора - на примере adx2wave с моего сайта. DEV-CPP всем хорош, но использует msvcrt.dll, что меня несколько напрягает, размер .EXE - 14,336 байт. MSVC++ 6.0 - при включении опции оптимизации по размеру тоже начинает, гад, использовать msvcrt.dll, если её не включить - всё хорошо, использует только .DLL от ядра системы, но, увы, размером похвастаться не может - 40,960 байт. Поэтому хотелось бы спросить - существует ли нормальный компилятор (даже, скорее, линкер - т.к. скомпилировать, чтобы ошибок не было, могу и под другим компилятором), который: а) Не гадит в получаемый исходный файл лишней информацией (как MSVC). б) Не использует левые библиотеки, кроме .DLL ядра системы (как DEV-CPP). в) Генерирует небольшие по размеру исполняемые файлы. г) Ещё, было бы здорово, если бы он был Freeware'ный. Кто-нибудь знает - такое в природе существует? P.S. Блин, написал бы уже давно собственный компилятор для Delphi и C++, чем тем что есть пользоваться, но, увы, у меня нет такого дикого количества времени и сил... |
Grom PE |
![]()
Сообщение
#2
|
![]() Advanced Member ![]() ![]() ![]() Группа: CTPAX-X Сообщений: 84 Регистрация: 7-February 08 Из: i@grompe.org.ru Пользователь №: 3,120 Спасибо сказали: 95 раз(а) ![]() |
А чем тебе не нравится msvcrt.dll? Она везде есть, начиная с Win98.
Тут волшебства не бывает, нет такого компилятора, который бы взял и сделал код меньше. Надо дать понять компилятору, что ты от него хочешь. С DEV-CPP (MingW) я толком не работал, а для MSVC знаю несколько ключей и директив. В командной строке: call cl /nologo /O1igab1 /G6Fy /Zp1 /QIfist <исходники> В главном исходнике: CODE #pragma optimize("gsy", on) #pragma comment(linker,"/section:.text,CERW /merge:.rdata=.text /ignore:4078") // Соединить секции, тут надо экспериментировать, при соединении некоторых секций EXE-шник раздувается #pragma comment(linker,"/opt:nowin98") // Использовать выравнивание секций в 512 байт, а не 4096 #pragma comment(linker,"/entry:main") // Обход внутренней стартовой функции, если программа не использует сишные функции, а только WinAPI #pragma comment(linker,"/subsystem:windows") // Или /subsystem:console, иногда без явного указания ругается #pragma comment(linker,"/nodefaultlib:libc") // Отключить стандартную сишную библиотеку #pragma comment(lib,"kernel32") // Подключить нужные библиотеки для WinAPI #pragma comment(lib,"user32") #pragma comment(lib,"gdi32") #define WIN32_LEAN_AND_MEAN // Иногда помогает уменьшить размер для WinAPI'шных прог. Хорошо еще пропатчить линкер, чтоб не пихал сигнатуру "Rich" в начале EXE-шника. Для версии link.exe 6.00.8447, размер 462901 байт: 00045826: 03 -> 90 00045827: C8 -> 90 Для других версий - находим такой код и меняем там 03C8 на 9090: CODE 03C8 add ecx,eax 898DE4010000 mov [ebp][000001E4],ecx FF1514114000 call _tzset;MSVCRT Если поставили "/entry:main", а программа использует командную строку в сишном стиле, то можно отдельно ее инициализировать: CODE __declspec(dllimport) void __getmainargs(int *,char ***,int *,int,int *); int main(int argc,char *argv[]) { ... { int tmp; __getmainargs(&argc,&argv,&tmp,0,&tmp); } ... } Спасибо сказали:
|
-=CHE@TER=- |
![]()
Сообщение
#3
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Grom PE!
Не помогло - всё-равно 40,960 байт... Надо что-то делать. (*улыбается*) |
-=CHE@TER=- |
![]()
Сообщение
#4
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Нашёл такую замечатульную вещицу: Tiny C Compiler
GCC плачет в обнимку с MS VC++ 6.0 глядя на итоговый размер. Но проблема та же - msvcrt.dll У программы есть ключик -static, который позволяет линковать программу статически. Например, при компиляции файла "test.c": QUOTE void main(void){} через TCC вот таким способом:QUOTE tcc.exe -static test.c получаем кучу ошибок:QUOTE tcc: undefined symbol '_controlfp' В справке написано, что можно линковать библиотеки. При попытке слинковать с msvcrt.lib от VC++ 6.0 получаю ошибку:tcc: undefined symbol '__set_app_type' tcc: undefined symbol '__getmainargs' tcc: undefined symbol 'exit' QUOTE >tcc.exe -static test.c -lMSVCRT.LIB Чтобы оно работало, нужно взять файл "MSVCRT.LIB", переименовать его в "libMSVCRT.a" и поместить в каталог "lib" - только тогда сработает (чтобы узнать это пришлось в исходниках рыться - libtcc.c:2083):tcc: cannot find -lMSVCRT.LIB QUOTE tcc.exe -static test.c -lMSVCRT Однако же, хоть оно и находит файлы библиотеки (кстати, ей ещё некоторые понадобятся - см. через FileMon, к каким она будет обращаться - libKERNEL32.a и ещё чего-то), но компилироваться статически отказывается - пишет всё те же ошибки. Кто-нибудь знает, как можно TCC подружить с либами от студии, чтобы итоговый .EXE файл ничего кроме kernel32.dll не использовал?.. |
-=CHE@TER=- |
![]()
Сообщение
#5
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Look at this!!! It's awesome!!!
"compile.bat" CODE @echo off C:\Dev-Cpp\bin\gcc.exe -c -Wall -pedantic -mwindows "test.c" C:\Dev-Cpp\bin\ld test.o --subsystem windows -o test.exe C:\Dev-Cpp\lib\CRT_noglob.o -L C:\Dev-Cpp\lib -l kernel32 -l user32 -nostdlib --exclude-libs msvcrt.a -e_WinMain@16 C:\Dev-Cpp\bin\strip.exe "test.exe" >nul 2>&1 (если нужно консольное приложение - убираем флаг "--subsystem windows") "test.c" CODE #include <windows.h> static TCHAR s[] = TEXT("Hello world!\n"); static DWORD dw; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { /* WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), s, lstrlen(s), &dw, NULL); ReadFile(GetStdHandle(STD_INPUT_HANDLE), s, 1, &dw, NULL);*/ MessageBox(0, s, NULL, 0); ExitProcess(0); } Всего 2 Кб и никаких "msvcrt.dll"!!! Параметры WinMain(), конечно, придётся инициализировать вручную... но остальное работает!!! |
-=CHE@TER=- |
![]()
Сообщение
#6
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Мне вчера было откровение, которым спешу поделиться, но обо всём по порядку.
Во-первых, я отказался от TCC, потому что, несмотря на маленький размер (при желании такого размера и через GCC собрать можно), почему-то все программы с использованием этого компилятора убивает после компиляции AVG (да, он у меня всё ещё стоит, хотя уже бесит). Помнится точно такая же нелюбовь была у долбанного Dr.Web к программам на Delphi... Во-вторых, про откровение. Если кто-нибудь компилировал программы под GCC, то наверняка обращал внимание, что строки там идут прямо в теле секции кода (в примере ниже точки - непечатаемые символы): QUOTE ...........INPUT FILE MISSING.РРРРРРРРРРРРР.........INVALID EXT!.РРР где Р - символ с кодом 0x90 (команда NOP) для выравнивания строки до границы в 16 байт.В результате чего код выглядит как куча навоза. MSVC++ 6.0, к примеру, сгребает все строки в конец файла в секцию с данными. Но уродский GCC это делать не хочет. По идее, это сделано чтобы оптимизировать скорость выполнения программы: где строчка используется там её и пихать, чтобы указателем по памяти туда-сюда не прыгать. Однако, на деле прирост производительности от этого трюка весьма сомнительный, а вот прирост размера кода весьма существенный. Я читал кучу документаций по ключам компилятора и линковщика и, единственный выход который я нашёл, объявлять все строки статическими. Т.е. вместо кода: CODE printf("Hello world!"); писать: CODE static char st[] = "Hello world!"; printf(st); Тогда строчки будут где надо. Но писать так для каждой строки, и тем более для каких-нибудь простых параметров, типа "%s\%s", это же удавиться просто можно. И вот, вчера я натолкнулся на то что так долго искал... А теперь (барабанная дробь) строчка командной строки компилирующая исходный код как надо: QUOTE C:\Dev-Cpp\bin\gcc.exe -s -fwritable-strings -Os -Wall -ansi -pedantic "!.!" -o "!.exe" C:\Dev-Cpp\lib\CRT_noglob.o Это строчка для меню по F2 в FAR Manager, так что "!.!" - это имя файла на котором стоит курсор, а "!.exe" оно же с расширением .EXE (ключ -o задаёт имя выходного файла). Теперь о ключах. -s Не добавлять информацию для отладки. Этот ключ избавляет от запуска утилиты strip.exe на получившейся программе, чтобы она похудела, и компиляция проходит быстрее. Хочу отметить, что информация для отладки может занимать очень много места, иногда даже больше чем вся программа без неё. -fwritable-strings То самое откровение - пихает все строчки в секцию данных, а вернее делает их изменяемыми. Единственный минус - из-за этого каждая строчка считается уникальной и три вызова printf("%s\%s", ....); создадут 3 строки "%s\%s" в секции с данными... Впрочем, для небольших программ на сайт, где строчки не повторяются это не критично. -Os Optimize for Size - оптимизирует код сначала по быстродействию, затем по размеру. -Wall -ansi -pedantic Warnings=all, стандарт ANSI'89, pedantic - скрупулёзно сообщать о любых огрехах, даже самых незначительных. C:\Dev-Cpp\lib\CRT_noglob.o Этот файл нужен, чтобы при запуске программы с параметром "*.dat" в каталоге где лежат файлы "file1.dat", "file2.dat" и "file3.dat" получить на входе один параметр в виде "*.dat", а не три параметра в виде упомянутых файлов. Это иксовая фишка разворачивающая маску в имена файлов, которая включена по умолчанию и её нужно вручную вырубать. Скомпилированная подобным образом программа по прежнему будет зависеть от гадской "msvcrt.dll" (надо будет написать модуль для её замены, типа HELPERS2.INC для DCC32HACK), но внутри уже будет выглядеть так, как и должна. К сожалению, у MSVC++ 6.0 при включении всех упомянутых Grom PE выше оптимизаций всё равно размер получается меньше, но ещё сильнее уменьшить размер в GCC я не смог (если кто знает как - обязательно пишите). На последок хочу сказать, что я теперь в некотором роде наСИльник, потому что в Си, в отличие от Delphi, очень просто (без написания лишнего кода и кучи преобразований) можно напрямую работать с памятью, что экономит время написания программы и силы. Delphi я не бросаю, но если что-то мне удобнее писать на Си, то буду писать на нём. |
-=CHE@TER=- |
![]()
Сообщение
#7
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Ничего люди нормально сделать не могут!
Руки у них непонятно откуда! Подключаю к программе сишный файл, ни одной функции ещё оттуда не использую, но они все (ВСЕ!) включаются в выходной .EXE файл! Начал гуглить как можно избавиться от unused functions в GCC - везде предлагают только такой вариант: CFLAGS += -fdata-sections -ffunction-sections LDFLAGS += --gc-sections Пробую - нифига. Потом ещё полазил по Интернету и привет - эти флаги специфичные для платформ и под Windows не пашут. У меня GCC 3.2 - там есть уже упомянутый: -fwritable-strings В новом этот ключ убрали, зато есть какой-то -dead_чего-то-там, который, вроде бы, должен что-то такое делать - нифига не работает. С горя нашёл даже DeadStrip и всё было бы просто замечательно, но он импорт не перестраивает! Т.е. если есть какая-то функция, использующая WinAPI'шные функции, то они будут в импорте выходного файла, хотя НИГДЕ не используются! Итого, в программе, где ничего кроме ExitProcess(0) нет, импорт занимает чуть ли не 99% кода... Кто-нибудь знает как можно в GCC (именно этой версии от DEV-CPP) выкинуть неиспользуемые функции (вместе с импортом)? Сразу оговорюсь, что варианты комментировать функции или вручную включать-отключать их через define'ы - абсолютно неинтересны. Спасибо сказали:
|
useretail |
![]()
Сообщение
#8
|
Member ![]() ![]() Группа: Authorized Сообщений: 19 Регистрация: 3-May 08 Пользователь №: 6,696 Спасибо сказали: 8 раз(а) ![]() |
> именно этой версии от DEV-CPP
не понял. можно ссылку > эти флаги специфичные для платформ и под Windows не пашут ну это смотря в какой среде. прежде чем что-то советовать хотелось-бы ответ на первый вопрос Если речь о Dev-C++ (http://www.bloodshed.net/dev/), то там используется gcc c MinGW. Среда MinGW хоть и минималистична, но всегда страдала отсутствием некоторых непортируемых ф-й. Если вам критично использование gcc (а бегло прочитав тред я понял что нет), то качестве замены могу посоветовать Cygwin, но тогда прийдется таскать с собой чемодан в виде cygwin1.dll. То что вам требуется точно есть в MSVC. Ставьте Visual Studio и будет вам счастье ![]() Спасибо сказали:
|
-=CHE@TER=- |
![]()
Сообщение
#9
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Welcome back! Что-то давно на форуме видно не было, но мы всегда рады старым хорошим знакомым.
Кстати, а почему на "вы" или это было обращение ко всем? (*улыбается*) > именно этой версии от DEV-CPP не понял. можно ссылку > эти флаги специфичные для платформ и под Windows не пашут ну это смотря в какой среде. прежде чем что-то советовать хотелось-бы ответ на первый вопрос Если речь о Dev-C++ (http://www.bloodshed.net/dev/), то там используется gcc c MinGW. Среда MinGW хоть и минималистична, но всегда страдала отсутствием некоторых непортируемых ф-й. Если вам критично использование gcc (а бегло прочитав тред я понял что нет), то качестве замены могу посоветовать Cygwin, но тогда прийдется таскать с собой чемодан в виде cygwin1.dll. То что вам требуется точно есть в MSVC. Ставьте Visual Studio и будет вам счастье ![]() Да, именно этот Dev-C++. Да, там используется MinGW. Критично именно использование GCC, потому что: 1) Он бесплатный, а также его лицензия позволяет писать программы на нём и продавать - MSVC по этой причине не подходит (его покупать нужно). 2) MSVC отпадает ещё и по той причине, что единственная более или менее нормальная версия - это 6.0, все остальные геренируют код который выглядит как кусок не буду говорить чего, плюс не будет работать под Windows 98. 3) Как я уже сказал в GCC той версии что я пользуюсь есть флаг -fwritable-strings, в более поздних версиях которого его нет. 4) Я уже достаточно серьёзно разобрался с GCC и знаю все (или большинство) его подводных камней, так что мне бы, по хорошему, тут немного дожать и там немного дожать - и будет очень хорошо. 5) Cygwin не устраивает именно что из-за "cygwin1.dll". Я пишу под Windows (ну, кроме распаковщиков для игр и специфичных программ для нескольких платформ) и мне нужно чтобы программа работала "из коробки", а тот же пресловутый msvcrt.dll отсутствует в чистом Windows 98 SE. Короче, я хочу, чтобы сишный компилятор компилировал используя только библиотеки системы (kernel32.dll, user32.dll, ...) и ничего более. Если нужны какие-то отсутствующие там функции, то пусть они будут статичным кодом внутри .EXE файла - меня это полностью устраивает. Пользуясь случаем расскажу ещё об одном подводном камне GCC. Итак, у меня не собирается программа, выплёвывая в консоль ошибку: QUOTE library.o(.text+0x123):library.c: undefined reference to `memset' C:\Dev-Cpp\bin\mingw32-make: *** [Program.exe] Error 1 Сравнил makefile с тем (у другой программы), который компилировался - всё точно также. Стал комментарить все функции и выяснил, что если в программе используется функция: ZeroMemory(buff, size); то всё нормально, но стоит хоть где-то вызвать: FillMemory(buff, size, filler); Всё - получаем такую ошибку. Насильное указание ключа -fbuiltin, а также: #define memset __builtin_memset Нифига не помогают. Обе эти функции (ZeroMemory() и FillMemory()) в заголовочных файлах переопределяются через memset(), но почему-то зануление заменяется inline-кодом, а заполнение - нет (и, более того, ещё и отключает встроенное зануление). В общем, полная задница - походу, все стандартные функции придётся реализовывать вручную, а с синтаксисом GAS'а (GNU Assembler) - это ад адский. Думаю сделать на FASM'е, потом просто в либу собрать и подключать её при компиляции... Спасибо сказали:
|
useretail |
![]()
Сообщение
#10
|
Member ![]() ![]() Группа: Authorized Сообщений: 19 Регистрация: 3-May 08 Пользователь №: 6,696 Спасибо сказали: 8 раз(а) ![]() |
QUOTE Кстати, а почему на "вы" Даже не знаю.. наверное из-за уважения. Сам я работу делаю в линуксе, а если что-то нужно для винды, то просто кросскомпилю тем-же mingw. Хотя от винды тоже не отказываюсь (даже сейчас). Считаю что код нужно писать так что-бы он собирался на любых платформах. Попробуй(те) ![]() Спасибо сказали:
|
-=CHE@TER=- |
![]()
Сообщение
#11
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Как вы, наверное, помните, я пишу сишные программы на:
QUOTE gcc.exe (GCC) 3.2 (mingw special 20020817-1) Это тот GCC, который из DEV-CPP. Писал тут одну программу DOS/Windows совместимую. Всё было нормально, пока не проверил программу под DOS в Borland Turbo C++ 3.0. Поменял кое-чего, сохранил и... она перестала копилироваться под Windows. Получаю какую-то странную ошибку: QUOTE C:\Temp/ccmYeaaa.o(.eh_frame+0x11):TESTFILE.C: undefined reference to `__gxx_personality_v0' Погуглил - говорят, надо какими-то ключами из командной строки что-то отключать. Но у меня же всё работало! Начинаю по одной строчке и блоками возвращать программу из резервной копии, которая комилировалась. Возвращал, возвращал... всё вернул! Обе программы идентичны, даже через "fc /b old_file.c TESTFILE.C" проверил - нет различий! Что за черт?! Осталось только имя поменять - сменил, всё заработало! Итого: если файл имеет расширение "*.C" (C - заглавная!) и компилируется через "GCC.EXE", то включается какой-то зело параноидальный режим, в котором нельзя вызывать функции отсутствующие в стандарте ANSI C89. В частности у меня вызывалась функция GetLastError() из WinAPI. При этом отсутствие или наличие ключа "-ansi" ни на что не влияет. Спасибо сказали:
|
-=CHE@TER=- |
![]()
Сообщение
#12
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Представьте себе такую программу:
CODE #include <stdio.h> #include <stdlib.h> #include <malloc.h> #include <string.h> static char strx[] = TEXT("HELLO WORLD!"); /* 3984 => 0x0F90 */ void func(void) { char buf[3984]; strcpy(buf, strx); printf("%s\n", buf); } int main(int argc, char *argv[]) { func(); return(0); } Всё компилируется и работает на отлично. А теперь если поставить вместо 3984 число 3985 или больше, то получим что-то очень странное: QUOTE C:\Temp/ccsfeaaa.o(.text+0x352):test.c: undefined reference to `_alloca' После долгих гуглений и битья головой об стол нашёл вот такое обсуждение: QUOTE Danny Smith To disable stack probing, add this switch -mno-stack-arg-probe. Randall R Schulz Man! I scanned through the GCC man page for anything that would control this action, and couldn't find anything. I don't see "-mno-stack-arg-probe" listed there at all, nor is any option that includes the word "probe." Danny Smith Yeah, no documentation to speak of. The only reason I know about it because I've been chasing the _alloca bug that Fish reported. It is still present in GCC trunk (3.4). I have submitted a simple patch that fixes, but ... time for a ping in the New Year. Вдумайтесь в это - в документации к GCC нет ни слова об этом ключе, а единственный человек, который о нём знает, узнал о его существовании исправляя баг в компиляторе. Словами не передать как я фрустрирую! Спасибо сказали:
|
-=CHE@TER=- |
![]()
Сообщение
#13
|
Walter Sullivan ![]() ![]() ![]() Группа: Root Admin Сообщений: 1,360 Регистрация: 4-February 08 Пользователь №: 3 Спасибо сказали: 313 раз(а) ![]() |
Фига себе, я в эту тему год назад писал. Как быстро время летит...
Итак, ещё один косяк GCC. Если вы делаете .DLL и у вас нет импортируемых функций (т.е., например, ваша библиотека не вызывает ни одну функцию Windows), то GCC всё равно создаёт файл с секцией импорта в 256 байт (.idata) которая состоит из одних нулей, плюс в хвосте секции кода (.text) у вас будет 16+байт выравнивание на 32 байтную границу (почему не просто на 16???), а затем ещё 16 байт из четырёх DWORD: -1, 0, -1, 0 - это конец таблицы для указателей на функции импорта. Но, т.к. импорта нет, то и этого маркера не должно быть, однако ж, GCC такой GCC... |
![]() ![]() |
Упрощённая версия | Сейчас: 8th September 2024 - 20:31 |