Почему экспортированные функции неправильно названы в DLL?

Я пытаюсь сделать небольшой тестовый плагин, используя NPAPI для Firefox. Это мой код до сих пор:

/*  File: npp_test.cpp
    Copyright (c) 2012 by Niklas Rosenstein

    Testing the NPAPI interface. */

// - Includes & Preprocessors --------------------------------------------------
// - -------- - ------------- --------------------------------------------------

#define DEBUG

#ifdef DEBUG
#   include <iostream>
    using namespace std;
#   include <windows.h>
#endif // DEBUG

#include <stdint.h>

#include <npapi.h>
#include <npfunctions.h>
#include <npruntime.h>

#define DLLEXPORT extern __declspec(dllexport)

// - NPAPI Calls ---------------------------------------------------------------
// - ----- ----- ---------------------------------------------------------------

NPError NP_New(NPMIMEType pluginType, NPP npp, uint16_t mode, int16_t argc,
               char* argn[], char* argv[], NPSavedData* saved);

// - Entrypoints ----------------------
// - ----------- ----------------------

NPError NP_GetEntryPoints(NPPluginFuncs* pFuncs) {
#   ifdef DEBUG
    cout << "NP_GetEntryPoints\n";
#   endif // DEBUG

    // Initialize plugin-functions
    pFuncs->newp = NP_New;

    return NPERR_NO_ERROR;
}

NPError NP_Initialize(NPNetscapeFuncs* npFuncs) {
#   ifdef DEBUG
    cout << "NP_Initialize\n";
    MessageBox(NULL, "NP_Initialize", "Plugin-message", 0);
#   endif // DEBUG
    return NPERR_NO_ERROR;
}

NPError NP_Shutdown() {
#   ifdef DEBUG
    cout << "NP_Shutdown\n";
#   endif // DEBUG
    return NPERR_NO_ERROR;
}

// - Plugin Execution -----------------
// - ------ --------- -----------------

NPError NP_New(NPMIMEType   pluginType,
               NPP          npp,
               uint16_t     mode,
               int16_t      argc,
               char*        argn[],
               char*        argv[],
               NPSavedData* saved) {
#   ifdef DEBUG
    cout << "NP_New\n";
#   endif

    if (!npp)
        return NPERR_INVALID_INSTANCE_ERROR;

    return NPERR_NO_ERROR;
}

Я компилирую код, используя g++ 4.4.1

g++ npp_test.cpp -I"D:\include\xulrunner" -shared -Wall -Wextra -o "npp_test.dll"

Компилируется нормально, но глядя на содержимое DLL с помощью DLL Expat, имена не такие, как ожидалось:

==================================================
Function Name     : _get_output_format
Address           : 0x6889c658
Relative Address  : 0x0001c658
Ordinal           : 5 (0x5)
Filename          : npp_test.dll
Type              : Exported Function
Full Path         : C:\Users\niklas\Desktop\npp_test.dll
==================================================

==================================================
Function Name     : _Z6NP_NewPcP4_NPPtsPS_S2_P12_NPSavedData
Address           : 0x68881270
Relative Address  : 0x00001270
Ordinal           : 4 (0x4)
Filename          : npp_test.dll
Type              : Exported Function
Full Path         : C:\Users\niklas\Desktop\npp_test.dll
==================================================

==================================================
Function Name     : NP_GetEntryPoints@4
Address           : 0x688811d8
Relative Address  : 0x000011d8
Ordinal           : 1 (0x1)
Filename          : npp_test.dll
Type              : Exported Function
Full Path         : C:\Users\niklas\Desktop\npp_test.dll
==================================================

==================================================
Function Name     : NP_Initialize@4
Address           : 0x68881205
Relative Address  : 0x00001205
Ordinal           : 2 (0x2)
Filename          : npp_test.dll
Type              : Exported Function
Full Path         : C:\Users\niklas\Desktop\npp_test.dll
==================================================

==================================================
Function Name     : NP_Shutdown@0
Address           : 0x6888124f
Relative Address  : 0x0000124f
Ordinal           : 3 (0x3)
Filename          : npp_test.dll
Type              : Exported Function
Full Path         : C:\Users\niklas\Desktop\npp_test.dll
==================================================

Разве они не должны называться так, как в источнике? Например, при «расширении» java-dll для Firefox имена в порядке. Использование DLLEXPORT как

#define DLLEXPORT __declspec(dllexport)

тоже не работает. Но это, по крайней мере, "удаляет" функции _get_output_format и _Z6NP_NewPcP4_NPPtsPS_S2_P12_NPSavedData от экспорта, где бы они ни экспортировались, если не используется DLLEXPORT.

Почему имена экспортируемых функций имеют дополнительный суффикс @4/@0? Я предполагаю, что число после @ указывает количество байтов, которые функция принимает в качестве аргументов, но при экспорте это на самом деле не должно содержаться в имени, верно?


person Niklas R    schedule 15.01.2012    source источник
comment
Некоторые имена являются искаженными именами функций C++. @4, вероятно, является номером версии библиотеки.   -  person Jonathan Leffler    schedule 16.01.2012
comment
@4 — это количество байтов, которое функция __stdcall должна удалить из стека после вызова.   -  person Dialecticus    schedule 16.01.2012
comment
Вы определили DLLEXPORT, но никогда его не использовали.   -  person Ben Voigt    schedule 16.01.2012


Ответы (4)


@(stack_size_of_params) — это украшение имени для внешних функций "C" stdcall. Я больше знаком с инструментами Microsoft, но я считаю, что вам нужно будет использовать файл .def для экспорта необработанных имен для функций, использующих stdcall.

Изменить: веб-поиск предлагает параметр командной строки --kill-at для инструментов GNU, который может избежать необходимости в надоедливых файлах .def.

person David Heffernan    schedule 15.01.2012
comment
Спасибо за Ваш ответ. Просто быстро: как я могу указать файл .def для g++, чтобы я мог проверить ваше предложение? Просто не могу найти вариант. **Редактировать: A, просто указав в качестве входного файла. :) - person Niklas R; 16.01.2012
comment
Это работает! Отлично, спасибо. Однако немного неудобно с этим .def-файлом. - person Niklas R; 16.01.2012
comment
Я действительно мало знаю о g++. Веб-поиск предлагает --kill-at удалить в украшении. Это означало бы, что .def не нужен. Или просто включите .def в командную строку для g++ или ld. - person David Heffernan; 16.01.2012
comment
Да, -Wl,--kill-at делает свое дело, используя только --kill-at, заставляет cc1plus.exe сказать, что он не может работать с аргументом -fkill-at. Тай - person Niklas R; 16.01.2012
comment
Очевидно, вы знаете намного больше, чем я знаю. Я просто направил вас в правильном направлении!!! - person David Heffernan; 16.01.2012

Это С++, поэтому имена искажаются, если вы не объявите их также с помощью extern "C"

person CashCow    schedule 15.01.2012
comment
К сожалению, использование #define DLLEXPORT extern "C" __declspec(dllexport) также не решает проблему. - person Niklas R; 16.01.2012
comment
IIRC extern "C" просто меняет тип искажения имени. Вам нужен .def для экспорта вашей функции с любым именем, которое вы предпочитаете. - person Matteo Italia; 16.01.2012

Если вы хотите управлять именами экспортируемых функций, используйте раздел EXPORTS в файле определения компоновщика (*.def), а не __declspec(dllexport).

person Ben Voigt    schedule 15.01.2012
comment
@DavidHeffernan был быстрее, извините. ;) Но спасибо за ваш ответ, это действительно решение головоломки. - person Niklas R; 16.01.2012

С GCC NP_EXPORT уже заботится о видимости символа, например:

extern "C" NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs);

Однако это работает только для Unix, поэтому для GCC в Windows вам придется установить видимость самостоятельно .

В Windows/VC++ вам дополнительно необходимо указать экспорт в файл .def, как уже упоминалось:

NP_GetEntryPoints   @1
NP_Initialize       @2
NP_Shutdown         @3
person Georg Fritzsche    schedule 16.01.2012