Добавление лидирующих подчеркиваний к ассемблерным символам с помощью 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, и должен быть искажен как таковой? Есть еще идеи?

23.06.2009 20:05:23
4 ОТВЕТА

Вы можете объявить это дважды?

.global bar
.global _bar

Некоторое время я не писал ассемблер, но действительно ли идентификатор .global действует как ярлык?

5
23.06.2009 20:45:09
Директива .global только указывает, что этот идентификатор относится к глобальному символу, поэтому его можно было бы заставить работать, если бы я также определил две метки для функции, например: .global bar .global _bar bar: _bar: <etc> Помимо Дублирование, я также получаю бесполезный символ "bar" в Windows и бесполезный символ "_bar" в Linux. Я надеялся на что-то более чистое, но это работает, поэтому я благодарю вас за предложение.
Maks Verver 24.06.2009 14:11:22

Один из вариантов, хотя и опасный, состоит в том, чтобы убедить GCC опустить обязательное подчеркивание ABI.

  • -fleading-underscore

    Эта опция и ее аналог -fno-leading-underscoreпринудительно изменяют способ представления символов C в объектном файле. Одним из способов является помощь в связывании с устаревшим кодом сборки.

    Предупреждение:-fleading-underscore переключатель вызывает НКУ для генерации кода , который не совместим с двоичным кодом , генерируемым без этого переключателя. Используйте его для соответствия бинарному интерфейсу приложения не по умолчанию. Не все цели обеспечивают полную поддержку этого переключателя.

Другой, более безопасный вариант - явно указать 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".

30
24.06.2009 00:23:20
Означает ли это, что стандартное поведение GCC в Linux должно заключаться в том, что имена "C" имеют начальное подчеркивание? Если так, то есть что-то в среде сборки OP, что отключает его?
Michael Burr 24.06.2009 00:35:41
В Linux функции, следующие стандартному соглашению о вызовах C (ccall, cdecl, как вы хотите это называть), не оформлены. В Windows stdcall является соглашением о вызовах по умолчанию, а функции, следующие чему-либо еще (например, стандартное соглашение о вызовах C), оформлены.
ephemient 24.06.2009 00:46:26
Как я уже говорил, опции -fleading-underscore и -fno-lead-underscore, похоже, ничего не делают (они не удаляют подчеркивания в функциях C и не добавляют их для символов ассемблера); если вы немного погуглите, то увидите, что у других был такой же опыт, поэтому у меня сложилось впечатление, что эти варианты довольно бесполезны. Предложение asm () хорошее; Я вполне могу использовать это. Единственным недостатком является то, что сам символ по-прежнему не имеет правильного имени (которое будет _bar в Windows), но по крайней мере я могу ссылаться на обеих платформах без дальнейших изменений исходного кода.
Maks Verver 24.06.2009 14:15:50
clang: error: unknown argument: '-no-leading-underscore' clang: error: unknown argument: '-fno-leading-underscore' clang: error: unknown argument: '-fleading-underscore'
anon58192932 19.10.2016 13:45:40
Вам не нужно externв прототипе. godbolt.org/z/kpFShC показывает, что void start(void) asm("_mystart");работает. Кроме того, вы, вероятно, хотите использовать имя типа в вашем примере вместо использования устаревшего C default- int.
Peter Cordes 20.02.2020 03:39:38

Компиляторы для цели ELF по умолчанию не добавляют начальные подчеркивания. Вы можете добавить -fleading-underscoreпри компиляции в формат ELF (под Linux). Используйте условный файл в make-файле.

Ссылка: http://opencores.org/openrisc,gnu_toolchain (выполните поиск на странице «оставить глобальные имена без изменений»)

4
19.10.2011 20:09:19

Вы можете использовать препроцессор 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__)
8
13.04.2012 17:19:58