Jump to content
xbelanch

Building extensions on mingw

Recommended Posts

Hi everyone,

 

After reading the BIS documentation of how to create extensions (https://community.bistudio.com/wiki/Extensions) and see examples from KK's blog I tried to make the same sample but on Cygwin (MSYS-2) but It's doesn't work. I do appreciate some help from anyone with more experience than me. I share the code and the compilation command lines:

 

Source code (my1st.cpp):

// Windows Header Files:
#include <Windows.h>

#define DLLEXPORT  __declspec(dllexport)

extern "C" {
	DLLEXPORT void __stdcall RVExtension(char *output, int outputSize, const char *function);
}
 

void __stdcall RVExtension(char *output, int outputSize, const char *function)
{
	strncpy_s(output, outputSize, "IT WORKS!", _TRUNCATE);
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
    )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

Command line compilation:

g++ -c -o my1st.o my1st.cpp

g++ -o my1st.dll my1st.o -s -shared -Wl,--subsystem,windows

Share this post


Link to post
Share on other sites

Hey there,

 

under windows either install the VS2013 compiler kit for VS2015 or just install the enitre VS2013. Then make a new dll, without any predefined code. You won't need anything that VS thinks you would need there.

 

Use code from Killzonekids blog, or maybe have a look at my extension (see signature), to have a basic structure.

 

then compile for x86, make sure to use vs2013 compiler, as arma players have that Redistributable Package installed, and do your testing.

 

 

To compile under linux, I would suggest looking into my mentioned project, as a how to compile and the commands are included as well as the src for that. 

 

 

Regards Arkensor

Share this post


Link to post
Share on other sites
Thanks for your answer @Arkensor. I have installed VS2010 and it works perfectly to build the "Hello world" sample from KK's blog. I'm used to work with cygwin for developing my own projects and that was the reason I'm finding a way to build arma 3 extensions properly with that environment. Also I took a look on your notes of how to compile on Linux. I found it very helpful but at the moment my focus is on see if I can find a workaround before I give it up ;) and move finally to VS.

Share this post


Link to post
Share on other sites

First i would find out if your DLL exports the interface function. One way to do this is using DUMPBIN. To do this you must open the Visual Studio developer command prompt, and execute DUMPBIN like so.

dumpbin /EXPORTS "my/path/myextension.dll"

If all goes well you should see output similar to this.

Dump of file myextension.dll

File Type: DLL

  Section contains the following exports for myextension.dll

    00000000 characteristics
    578F8D42 time date stamp Wed Jul 20 17:40:02 2016
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 00014F5F _RVExtension@12 = @ILT+3930(_RVExtension@12)

  Summary

        1000 .00cfg
        1000 .data
        1000 .gfids
        2000 .idata
        7000 .rdata
        2000 .reloc
        1000 .rsrc
       2B000 .text
       13000 .textbss
        1000 .tls

Note the export _RVExtension@12. This is what Arma looks for when you use callExtension.

Share this post


Link to post
Share on other sites

Hi Foxyy. 

 

Yep. It does, but it doesn't work. Here's the output after compiling with next  g++ parameters:

g++ -shared -fPIC -m32 -std=c++11 test.cpp -o test.dll -Wl,--subsystem -Wl,windows -Wl,--out-implib=libtest.dll.a -Wl,--export-all-symbols -Wl,--enable-auto-import

If I test the result with file test.dll it results:

 

test.dll: PE32 executable (DLL) (GUI) Intel 80386, for MS Windows
 
and here's the result after execute dumpbin /exports test.dll
Dump of file test.dll


File Type: DLL


  Section contains the following exports for test.dll


    00000000 characteristics
    579E0DA9 time date stamp Sun Jul 31 16:39:37 2016
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names


    ordinal hint RVA      name


          1    0 00001570 RVExtension@12


  Summary


        1000 .CRT
        1000 .bss
        1000 .data
        2000 .debug_abbrev
        1000 .debug_aranges
        7000 .debug_info
        2000 .debug_line
        1000 .debug_loc
        1000 .debug_ranges
        1000 .debug_str
        1000 .edata
        1000 .eh_frame
        1000 .idata
        1000 .rdata
        1000 .reloc
        2000 .text
        1000 .tls

 

 

I don't have a big experience with .dlls but it seems that .dlls built with mingw doesn't like to Arma 3 ;) It'd be great if it works... hope you have more luck and skills than me!

 

 

PS: the same file compiled by  cl /LD test.cpp  /link  /out:test.dll

 

File Type: DLL


  Section contains the following exports for testMSVC.dll


    00000000 characteristics
    579E0EA6 time date stamp Sun Jul 31 16:43:50 2016
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names


    ordinal hint RVA      name


          1    0 00001000 _RVExtension@12


  Summary


        2000 .data
        2000 .rdata
        1000 .reloc
        5000 .text

 

Share this post


Link to post
Share on other sites
          1    0 00001570 RVExtension@12

 

You've exported the function incorrectly. The name should be _RVExtension@12 . Note the underscore at the beginning.

Share this post


Link to post
Share on other sites

Ouch! alright... but that's the compiler do :-/ it seems to be closed to this thread: http://stackoverflow.com/questions/35701657/how-to-implement-the-rvextension-function-for-an-arma-3-dll-in-rust 

 

UPDATE:

This http://stackoverflow.com/questions/2804893/c-dll-export-decorated-mangled-names, this http://stackoverflow.com/questions/2810118/how-to-tell-the-mingw-linker-not-to-export-all-symbols and this http://www.transmissionzero.co.uk/computing/advanced-mingw-dll-topics/

helped me a lot to understand much better the challenge. After spending several hours dealing with it I achieved the same symbol name that exports Visual Studio but still doesn't work uu

Edited by xbelanch

Share this post


Link to post
Share on other sites

Alright, if the function is exported correctly now, next you might try rigging up a little test case to call the function yourself.

#include <iostream>
#include <Windows.h>

int main()
{
	HMODULE module = LoadLibrary("myextension.dll");
	
	if (!module)
	{
		std::cout << "!module" << std::endl;
		return;
	}
	
	FARPROC farproc = GetProcAddress(module, "_RVExtension@12");
	
	if (!farproc)
	{
		std::cout << "!farproc" << std::endl;
		return;
	}
	
	auto* function = reinterpret_cast<void (__stdcall*)(char*, size_t, const char*)>(farproc);
	
	char buffer[10240];
	buffer[0] = 0;
	
	function(buffer, sizeof(buffer), "my extension arguments");
	
	std::cout << "result:" << buffer << std::endl;
	
	return 0;
}

Something like this ought to do. Then you can fire up your favourite debugger and get debugging.

Share this post


Link to post
Share on other sites

test.cpp source code:

#include <Windows.h>
#include <string.h>


extern "C" __declspec(dllexport) void __stdcall  RVExtension(char *output, int outputSize, const char *function)
{


    outputSize -= 1;
    if (!strcmp(function,"version"))
    {
        strncpy(output,"1.0",outputSize);
    }
    else if (strcmp(function, "function") == 0)
    {
        strncpy(output, "return of a function", outputSize);
    }
    else
    {
        strncpy(output,function,outputSize);
    }
}




// Normal Windows DLL junk...
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
    )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

compilation command lines:

gcc -W -c -o test.o test.cpp && dllwrap -s -o test.dll --def test.def test.o -Wl,--subsystem -Wl,windows -Wl,--enable-stdcall-fixup

where test.def is:

EXPORTS
    _RVExtension@12 = RVExtension
 
if I execute your helpful testing code it works:
$ ./foxyy.exe
result:1.0
same source code compiled with VS CL I obtain the same result:

cl /LD test.cpp  /link  /out:test2.dll
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.


test.cpp
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.


/out:test.dll
/dll
/implib:test.lib
/out:test2.dll
test.obj
   Creating library test.lib and object test.exp

Dumpbin /exports of the binary generated with g++:

File Type: DLL


  Section contains the following exports for test.dll


    00000000 characteristics
    579F5C22 time date stamp Mon Aug 01 16:26:42 2016
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names


    ordinal hint RVA      name


          1    0 00001570 _RVExtension@12


  Summary


        1000 .CRT
        1000 .bss
        1000 .data
        1000 .edata
        1000 .eh_fram
        1000 .idata
        1000 .rdata
        1000 .reloc
        1000 .rsrc
        2000 .text
        1000 .tls
 
 
 
 
 

 

 

Share this post


Link to post
Share on other sites

Clearly your library works. Does Arma load it? To find out you should first call it using callExtension. Next there are a variety of ways to find out which modules are loaded by a process. You could do it programmatically, for example using the .NET Process class and its Modules property. You could also attach a debugger such as the one integrated in Visual Studio. Personally i tend to use the more lightweight Cheat Engine for such simple tasks. Or you could use the Windows Resource Monitor (Vista and later), which lists the modules loaded by a process in the CPU tab under associated modules. Of course in all cases you should disable BattlEye, as the rootkit it installs makes peering this information a bit trickier.

 

If your module is indeed loaded, but still you get the wrong results in game, i suggest you attach a debugger to the game process and breakpoint your code to step through it and see what is going on.

Share this post


Link to post
Share on other sites

No... Arma doesn't load it. I checked it with Process Explorer. Arma loads the dlls (as you said) dinamically every time you use callExtension (and that works if I check it with a dll built with VS). Maybe I must open a ticket at https://feedback.bistudio.com/?

Share this post


Link to post
Share on other sites

Arma loads the dll on first call, and keeps it until the arma procrss ends. Its a static library, it wont be loaded every time sou call it.

To find out if the dll is loaded, do a output back to arma and log it into rpt or try to move the dll. If arma made love with it, you wont be able to move the dll, as it is in use.

Share this post


Link to post
Share on other sites

Yep, alright... "dinamically" wasn't correct. I understand (and sorry in advance if I'm wrong) when you say "loads the dll on first" it refers at the time that any sqf calls the function callExtension. Before that Arma doesn't load any third-party dlls (and this makes sense for safety reasons I guess) and following that logic you can put a new dll while Arma is running and load it (always at your own risk  xD)

Share this post


Link to post
Share on other sites

Are you running Arma with BattlEye? Because unless it is whitelisted with BE it will not load.

Share this post


Link to post
Share on other sites

Are you running Arma with BattlEye? Because unless it is whitelisted with BE it will not load.

 

I believe OP mentioned that when building with MSVC, everything worked as expected so i assume this is not the problem. Also last time i tried it BattlEye did not block loading extensions in singleplayer in Arma 3, like it does in Arma 2, though this could since have changed.

Share this post


Link to post
Share on other sites

I believe OP mentioned that when building with MSVC, everything worked as expected so i assume this is not the problem. Also last time i tried it BattlEye did not block loading extensions in singleplayer in Arma 3, like it does in Arma 2, though this could since have changed.

I had many comments on the blog about extensions not working only to find out it was due to BE blocking them. I never asked how and when people were using them.

Share this post


Link to post
Share on other sites

Are you running Arma with BattlEye? Because unless it is whitelisted with BE it will not load.

 

Yep. MSVC built is fully working without no problem with BE... I started to think that the problem with Mingw dll is related to .CRT (http://stackoverflow.com/questions/15822048/using-mingw-to-build-a-windows-dll-that-depends-on-visual-studio-crt-msvcr110-d)

Share this post


Link to post
Share on other sites

If you're sure your library is not getting loaded by the game, next i would breakpoint LoadLibraryExA and GetProcAddress in kernelbase.dll and check the last error after each call to figure out why one of them is failing.

Share this post


Link to post
Share on other sites

How about you run arma 3 without BE to prove that's not the cause. It has definitely blocked my client side dll before.

Share this post


Link to post
Share on other sites

giphy.gif

 


Color me embarassed. Well... what can I say? Good news? It works. Bad news? It was because BE :( My apologies... I've lost the general picture shrinking my mind on the dll compilation output. Anyway... very thanks for helping me. :) Now we can say that Mingw can build extensions for Arma 3

Share this post


Link to post
Share on other sites

Of course it can, like any other C++ compiler.

 

You said before that the library built with MSVC worked. Did you also use BE then, and did that not get blocked, while the library built with MinGW did? It seems very odd to me that BE would act differently depending on the compiler used to build the library.

Share this post


Link to post
Share on other sites

My bad. I was launching Arma the whole time directly from the exe not from the launcher... lesson learned and sorry for wasting your time :(

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×