Is there STDCALL in Linux?
Asked Answered
E

3

25

I'm trying to port a Windows app to Linux. This appplication marks some functions with the __stdcall attribute. However, I was told by a friend that stdcall is used only on Windows and has no meaning in Linux (but DOES exist in Windows GCC).

Searching Google - some results state that there IS stdcall in Linux.

Is there a stdcall in Linux?

Additionally, GCC indicates that: __attribute__((__stdcall__)) and __attribute__((stdcall)) (without the underscores near stdcall).

Which one is preferred (if applied to Linux at all)?

Euxenite answered 16/6, 2010 at 14:34 Comment(1)
stdcall is just a calling convention, the stdcall certainly can exists in Linux but it is almost never used (perhaps except for Wine programs). It is better to convert your program to follow the local convention. So to answer your question, which one is preferred, is to not use stdcall. stdcall is out of place in a Linux program.Benares
M
17

The simplest solution is to just define __stdcall to nothing conditionally on Linux.

Mouthwatering answered 16/6, 2010 at 14:36 Comment(1)
Thats why I suggested writing a conditional (linux only) macro that expands to "".Mouthwatering
T
12

Here's a link to __stdcall description on MSDN: http://msdn.microsoft.com/en-us/library/zxk0tw93(VS.80).aspx

It's only used to call WinAPI functions. To port such a Windows application to Linux, you need much more than just defining __stdcall to nothing:

#ifndef WIN32 // or something like that...
#define __stdcall
#endif

You would also need to call the Linux-specific API functions instead of Win32 API ones. Depending on the particular part of Win32 API and the size of the application (amount of code), it can be anywhere between moderately difficult and daunting.

Which specific functions are marked by the app as __stdcall?

Indeed, Windows port of GCC has to have __stdcall, because it's supposed to be able to generate conforming code for the Win32 platform. But since under Linux there is only one standard calling convention and it coincides with the default compiler output, this statement is not needed.

The reason your application is not compiling under Linux is almost certainly due to the fact, that it references Win32 API functions that are not defined under Linux -- you need to find appropriate Linux counterparts. Win32 API and Linux GLibc API-s are very much different and cannot be substituted easily.

Probably the easiest way to port your app to Linux would be to use Wine, i.e. modifying the Windows code in such a way, that it runs smoothly under Wine in Linux. This is the way even the most complex applications, like modern computer games, have been made to run under Linux.

Of course, if you really want it to be running natively under Linux, then porting is the only way to go.

Tripletail answered 16/6, 2010 at 15:31 Comment(0)
S
1

stdcall is NOT just a calling convention; in addition to being a calling convention, it allows an isomorphism between C and C++ objects. Here's an example:

#define _CRT_SECURE_NO_WARNINGS // disable marking use of strcpy as error.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class ICdeclGreeter {
public:
    virtual ~ICdeclGreeter(){}
    virtual void setGreeting(const char *greeting) = 0;
    virtual void greet() = 0;
};
class IStdcallGreeter {
public:
    virtual __stdcall ~IStdcallGreeter(){}
    virtual void __stdcall setGreeting(const char *greeting) = 0;
    virtual void __stdcall greet() = 0;
};

class CdeclGreeter : public ICdeclGreeter {
public:
    char *greeting;
    ~CdeclGreeter() {
        if (greeting != nullptr) {
            free(greeting);
            puts("[CdeclGreeter] destroyed");
        }
    }
    void setGreeting(const char *greeting) {
        this->greeting = (char *)malloc(strlen(greeting) + 1);
        strcpy(this->greeting, greeting);
    }
    void greet() {
        puts(greeting);
    }
};
class StdcallGreeter : public IStdcallGreeter {
public:
    char *greeting;
    __stdcall ~StdcallGreeter() {
        if (greeting != nullptr) {
            free(greeting);
            puts("[StdcallGreeter] destroyed");
        }
    }
    void __stdcall setGreeting(const char *greeting) {
        this->greeting = (char *)malloc(strlen(greeting) + 1);
        strcpy(this->greeting, greeting);
    }
    void __stdcall greet() {
        puts(greeting);
    }
};
typedef struct pureC_StdcallGreeter pureC_StdcallGreeter;

typedef struct pureC_StdcallGreeterVtbl {
    void (__stdcall *dtor)(pureC_StdcallGreeter *This);
    void (__stdcall *setGreeting)(pureC_StdcallGreeter *This, const char *greeting);
    void (__stdcall *greet)(pureC_StdcallGreeter *This);
} pureC_IStdcallGreeterVtbl;

struct pureC_StdcallGreeter {
    pureC_IStdcallGreeterVtbl *lpVtbl;
    char *greeting;
    int length;
};

/* naive attempt at porting a c++ class to C; 
   on x86, thiscall passes This via ecx register rather than
   first argument; this register cannot be accessed in C without
   inline assembly or calling a reinterpretation of byte array
   as a function. there is no "This" argument in any of below. */
typedef struct pureC_CdeclGreeter pureC_CdeclGreeter;

typedef struct pureC_CdeclGreeterVtbl {
    void (*dtor)(pureC_CdeclGreeter *This);
    void (*setGreeting)(pureC_CdeclGreeter *This, const char *greeting);
    void (*greet)(pureC_CdeclGreeter *This);
} pureC_CdeclGreeterVtbl;

struct pureC_CdeclGreeter {
    pureC_CdeclGreeterVtbl *lpVtbl;
    char *greeting;
    int length;
};


void test() {
    ICdeclGreeter *g = new CdeclGreeter;
    g->setGreeting("hi");
    g->greet();

    IStdcallGreeter *g2 = new StdcallGreeter;
    g2->setGreeting("hi");
    g2->greet();

    // we can pass pointers to our object to pure C using this interface,
    // and it can still use it without doing anything to it.
    pureC_StdcallGreeter *g3 = (pureC_StdcallGreeter *)g2;
    g3->lpVtbl->setGreeting(g3, "hello, world!");
    g3->lpVtbl->greet(g3);
    g3->lpVtbl->dtor(g3);
    free(g2);

    /*
    // cdecl passes this via ecx in x86, and not as the first argument;
    // this means that this argument cannot be accessed in C without 
    // inline assembly or equivelent. Trying to run code below will cause a runtime error.
    pureC_CdeclGreeter *g4 = (pureC_CdeclGreeter *)g;
    g4->lpVtbl->setGreeting(g4, "hello, world!");
    g4->lpVtbl->greet(g4);

    g4->lpVtbl->dtor(g4);
    free(g);
    */
    delete g;
}

int main(int argc, char **argv)
{
    test();

    system("pause");

    return 0;
}

TLDR; it's not the same as cdecl makes C++ classes not usable from C on platforms using this convention because in order to send "This" to a method, you must set ecx register to address of "This" rather than just pushing it, and likewise if you want to implement a class in C that C++ can recognize, the method will need to get This pointer from ecx register which is not accessible to C without inline assemby or equivelent.

stdcall has this nice property that classes that use stdcall can easily be simultaneously usable from C or C++ without doing anything to them.

So you can only #define __stdcall as long as you don't deal with __thiscall; although there might be some other subtle distinctions.

Salvage answered 23/2, 2018 at 4:3 Comment(2)
It is just a calling convention though, albeit one with the properties you (ab)use in your answer. Moreover this code is platform-dependent and should not be used unless you really really know what you're doing and there's no other easy way.Sedgewake
Explains why COM uses __stdcallGibbet

© 2022 - 2024 — McMap. All rights reserved.