Добавление начальных подчеркиваний в символы ассемблера с GCC на Win32?
У меня есть кусок кода C, который вызывает функцию, определенную в сборке. В качестве примера, скажем, foo.c содержит:
int bar(int x); /* returns 2x */
int main(int argc, char *argv[]) { return bar(7); }
И bar.s содержит реализацию bar() в сборке x86:
.global bar
bar: movl 4(%esp), %eax
addl %eax, %eax
ret
В Linux я могу легко скомпилировать и связать эти источники с GCC следующим образом:
% gcc -o test foo.c bar.s
% ./test; echo $?
14
В Windows с MinGW это происходит с ошибкой "неопределенная ссылка на" bar "". Оказывается, причина этого в том, что в Windows все идентификаторы функций с соглашением о вызовах языка C имеют префикс подчеркивания, но, так как "bar" определен в сборке, он не получает этот префикс, и связывание не выполняется. (Таким образом, сообщение об ошибке на самом деле жалуется на отсутствие символа _bar, а не bar.)
Подвести итоги:
% gcc -c foo.c bar.s
% nm foo.o bar.o
foo.o:
00000000 b .bss
00000000 d .data
00000000 t .text
U ___main
U _bar
00000000 T _main
bar.o:
00000000 b .bss
00000000 d .data
00000000 t .text
00000000 T bar
Теперь возникает вопрос: как я могу решить это красиво? Если бы я писал только для Windows, я мог бы просто добавить подчеркивание к идентификатору в bar.s, но тогда код ломался в Linux. Я смотрел на GCC -fleading-underscore
а также -fno-leading-underscore
параметры, но ни один из них ничего не делает (по крайней мере, в Windows).
Единственная альтернатива, которую я вижу сейчас, - это передать файл сборки через препроцессор C и переопределить все объявленные символы вручную, если определен WIN32, но это тоже не очень красиво.
У кого-нибудь есть чистое решение для этого? Возможно, вариант компилятора, который я наблюдал? Может быть, ассемблер GNU поддерживает способ указать, что этот конкретный символ относится к функции, использующей соглашение о вызовах C, и должен быть искажен как таковой? Есть другие идеи?
5 ответов
Один из вариантов, хотя и опасный, состоит в том, чтобы убедить GCC опустить обязательное подчеркивание ABI.
-fleading-underscore
Этот вариант и его аналог,
-fno-leading-underscore
, принудительно изменить способ представления символов C в объектном файле. Одним из способов является помощь в связывании с устаревшим кодом сборки.Предупреждение:
-fleading-underscore
Переключатель заставляет GCC генерировать код, который не является двоичным, совместимым с кодом, созданным без этого переключателя. Используйте его для соответствия бинарному интерфейсу приложения не по умолчанию. Не все цели обеспечивают полную поддержку этого переключателя.
Другой, более безопасный вариант - явно указать GCC имя для использования.
5.39 Управляющие имена, используемые в коде ассемблера
Вы можете указать имя, которое будет использоваться в коде ассемблера для функции или переменной C, написав
asm
(или же__asm__
) ключевое слово после объявления следующим образом:int foo asm ("myfoo") = 2;
Это указывает, что имя будет использоваться для переменной
foo
в ассемблере код должен быть ``myfoo' rather than the usual \``_foo
".В системах, где подчеркивание обычно предшествует имени функции или переменной C, эта функция позволяет определять имена для компоновщика, которые не начинаются с подчеркивания.
Не имеет смысла использовать эту функцию с нестатической локальной переменной, поскольку такие переменные не имеют имен ассемблера. Если вы пытаетесь поместить переменную в конкретный регистр, см. Explicit Reg Vars. GCC в настоящее время принимает такой код с предупреждением, но, вероятно, будет изменен для выдачи ошибки, а не предупреждения, в будущем.
Вы не можете использовать
asm
таким образом, в определении функции; но вы можете получить тот же эффект, написав объявление для функции до ее определения и поместивasm
там вот так:extern func () asm ("FUNC"); func (x, y) int x, y; /* ... */
Вы должны убедиться, что выбранные вами имена ассемблера не конфликтуют с любыми другими символами ассемблера. Кроме того, вы не должны использовать регистрационное имя; это привело бы к совершенно неверному ассемблерному коду. GCC пока не имеет возможности хранить статические переменные в регистрах. Возможно, это будет добавлено.
В твоем случае,
extern int bar(int x) asm("bar");
должен сказать GCC, что " bar
использует имя asm ``bar`', даже если это функция ccall ".
Вы можете использовать препроцессор C для предварительной обработки сборки и использовать макрос для добавления недостающих подчеркиваний в Windows. Во-первых, вам нужно переименовать файл сборки с bar.s на bar.S (заглавная 'S'). Это говорит gcc использовать cpp для предварительной обработки файла.
Чтобы добавить недостающие подчеркивания, вы можете определить макрос "cdecl", например так:
#if defined(__WIN32__)
# define cdecl(s) _##s
#else
# define cdecl(s) s
#endif
Тогда используйте это так:
.global cdecl(bar)
cdecl(bar):
movl 4(%esp), %eax
addl %eax, %eax
ret
Обратите внимание, что Mac OSX также требует начальных подчеркиваний, поэтому вы можете обновить первую строку макроса следующим образом:
#if defined(__WIN32__) || defined(__APPLE__)
Вы можете объявить это дважды?
.global bar
.global _bar
Некоторое время я не писал ассемблер, но действительно ли идентификатор.global действует как ярлык?
Компиляторы для цели ELF по умолчанию не добавляют начальные подчеркивания. Вы могли бы добавить -fleading-underscore
при компиляции в формат ELF (под Linux). Используйте условный файл в make-файле.
Ссылка: http://opencores.org/openrisc,gnu_toolchain (выполните поиск на странице "оставить глобальные имена без изменений")
Была эта проблема с JNI, где, например, Java Runtime 1.5/32Bit ожидает ведущие символы подчеркивания для функций extern-C. "-fleading-underscore" не работает!
Решение здесь состояло в том, чтобы проанализировать заголовки-заглушки из Java-компилятора для объявленных функций и получить определения подстановки для них с шаблоном «#define bar _bar».