Вопрос по gcc, assembly, winapi, c, mingw – Добавление лидирующих подчеркиваний к ассемблерным символам с помощью GCC на Win32?

28

У меня есть кусок кода C, который вызывает функцию, определенную в сборке. В качестве примера, скажем, foo.c содержит:

<code>int bar(int x);  /* returns 2x */
int main(int argc, char *argv[]) { return bar(7); }
</code>

И bar.s содержит реализацию bar () в сборке x86:

<code>.global bar
bar:    movl 4(%esp), %eax
        addl %eax, %eax
        ret
</code>

В Linux я могу легко скомпилировать и связать эти источники с GCC следующим образом:

<code>% gcc -o test foo.c bar.s
% ./test; echo $?
14
</code>

В Windows с MinGW это происходит с ошибкой «неопределенная ссылка на« bar »». Оказывается, причина этого в том, что в Windows все идентификаторы функций с соглашением о вызовах C начинаются с подчеркивания, но так как & quot; bar & quot; определен в сборке, он не получает этот префикс, и связывание не выполняется. (Таким образом, сообщение об ошибке на самом деле жалуется на отсутствие символа _bar, а не bar.)

Подвести итоги:

<code>% 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
</code>

Теперь возникает вопрос: как я могу решить это красиво? Если бы я писал только для Windows, я мог бы просто добавить подчеркивание к идентификатору в bar.s, но тогда код ломался в Linux. Я смотрел на gcc-fleading-underscore а также-fno-leading-underscore параметры, но ни один, кажется, ничего не делает (по крайней мере, в Windows).

Единственная альтернатива, которую я вижу сейчас, - это передача файла сборки через препроцессор C и переопределение всех объявленных символов вручную, если определен WIN32, но это тоже не очень красиво.

У кого-нибудь есть чистое решение для этого? Возможно, вариант компилятора, который я наблюдал? Может быть, ассемблер GNU поддерживает способ указать, что этот конкретный символ относится к функции, использующей соглашение о вызовах C, и должен быть искажен как таковой? Есть еще идеи?

Ваш Ответ

4   ответа
8

Вы можете использовать препроцессор 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__)
5

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

.global bar
.global _bar

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

Да, это тоже работает.
Директива .global только указывает, что этот идентификатор относится к глобальному символу, поэтому его можно было бы заставить работать, если бы я также определил две метки для функции, например: .global bar .global _bar bar: _bar: & lt; etc & gt; Помимо дублирования, я также получаю бесполезную "полоску" символ в Windows и бесполезный знак «_bar» символ в Linux. Я надеялся на что-то более чистое, но это работает, поэтому я благодарю вас за предложение. Maks Verver
4

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

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

27

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

-fleading-underscore

This option and its counterpart, -fno-leading-underscore, forcibly change the way C symbols are represented in the object file. One use is to help link with legacy assembly code.

Warning: the -fleading-underscore switch causes GCC to generate code that is not binary compatible with code generated without that switch. Use it to conform to a non-default application binary interface. Not all targets provide complete support for this switch.

Другой, более безопасный вариант - явно указать GCC имя для использования.

5.39 Controlling Names Used in Assembler Code

You can specify the name to be used in the assembler code for a C function or variable by writing the asm (or __asm__) keyword after the declarator as follows:

     int foo asm ("myfoo") = 2;

This specifies that the name to be used for the variable foo in the assembler code should be ``myfoo' rather than the usual \``_foo'.

On systems where an underscore is normally prepended to the name of a C function or variable, this feature allows you to define names for the linker that do not start with an underscore.

It does not make sense to use this feature with a non-static local variable since such variables do not have assembler names. If you are trying to put the variable in a particular register, see Explicit Reg Vars. GCC presently accepts such code with a warning, but will probably be changed to issue an error, rather than a warning, in the future.

You cannot use asm in this way in a function definition; but you can get the same effect by writing a declaration for the function before its definition and putting asm there, like this:

 extern func () asm ("FUNC");

 func (x, y)
      int x, y;
 /* ... */

It is up to you to make sure that the assembler names you choose do not conflict with any other assembler symbols. Also, you must not use a register name; that would produce completely invalid assembler code. GCC does not as yet have the ability to store static variables in registers. Perhaps that will be added.

В твоем случае,

extern int bar(int x) asm("bar");

следует сообщить GCC, что & quot;bar использует имя asm `` bar` ', даже если это функция ccall'.

Как я уже сказал, параметры -fleading-underscore и -fno-lead-underscore, похоже, ничего не делают (они не удаляют подчеркивания в функциях C и не добавляют их для символов ассемблера); если вы немного погуглите, вы увидите, что у других был такой же опыт, поэтому у меня сложилось впечатление, что эти варианты довольно бесполезны. Предложение asm () хорошее; Я вполне могу в конечном итоге использовать это. Единственным недостатком является то, что сам символ по-прежнему не имеет правильного имени (в Windows это будет _bar), но по крайней мере я могу ссылаться на обеих платформах без дальнейших изменений исходного кода. Maks Verver
В Linux функции, следующие стандартному соглашению о вызовах C (ccall, cdecl, как вы хотите их называть), не оформлены. В Windows стандартным является & quot; по умолчанию & quot; соглашение о вызовах и функции, следующие за чем-либо еще (например, стандартное соглашение о вызовах C), оформлены
Означает ли это, что стандартным поведением GCC в Linux должно быть то, что & quot; C & quot; имена имеют лидирующие подчеркивания? Если так, то есть что-то в среде построения OP, что его отключает?
clang: error: unknown argument: '-no-leading-underscore' clang: error: unknown argument: '-fno-leading-underscore' clang: error: unknown argument: '-fleading-underscore'

Похожие вопросы