Actualmente estoy desarrollando una biblioteca C ++ para Windows que se distribuirá como una DLL. Mi objetivo es maximizar la interoperabilidad binaria; más precisamente, las funciones en mi DLL deben poder usarse a partir de código compilado con múltiples versiones de MSVC ++ y MinGW sin tener que recompilar la DLL. Sin embargo, estoy confundido acerca de qué convención de llamadas es mejor cdecl
o stdcall
.
A veces escucho declaraciones como "la convención de llamada de C es la única garantizada para ser la misma en todos los compiladores", lo que contrasta con declaraciones como " Hay algunas variaciones en la interpretación de cdecl
, particularmente en cómo devolver valores ". Esto no parece impedir que ciertos desarrolladores de bibliotecas (como libsndfile ) utilicen la convención de llamadas de C en las DLL que distribuyen, sin ningún problema visible.
Por otro lado, la stdcall
convención de llamada parece estar bien definida. Por lo que me han dicho, básicamente se requiere que todos los compiladores de Windows lo sigan porque es la convención utilizada para Win32 y COM. Esto se basa en la suposición de que un compilador de Windows sin compatibilidad con Win32 / COM no sería muy útil. Muchos fragmentos de código publicados en foros declaran funciones como, stdcall
pero parece que no puedo encontrar una sola publicación que explique claramente por qué .
Hay demasiada información contradictoria y cada búsqueda que hago me da diferentes respuestas, lo que realmente no me ayuda a decidir entre los dos. Estoy buscando una explicación clara, detallada y argumentada de por qué debería elegir uno sobre el otro (o por qué los dos son equivalentes).
Tenga en cuenta que esta pregunta no solo se aplica a las funciones "clásicas", sino también a las llamadas de funciones de miembros virtuales, ya que la mayoría del código de cliente interactuará con mi DLL a través de "interfaces", clases virtuales puras (siguiendo los patrones descritos, por ejemplo, aquí y allá ).
fuente
Respuestas:
Acabo de hacer algunas pruebas en el mundo real (compilando DLL y aplicaciones con MSVC ++ y MinGW, y luego mezclándolos). Como parece, obtuve mejores resultados con la
cdecl
convención de llamadas.Más específicamente: el problema
stdcall
es que MSVC ++ modifica los nombres en la tabla de exportación de DLL, incluso cuando se usaextern "C"
. Por ejemplo sefoo
convierte_foo@4
. Esto solo ocurre cuando se usa__declspec(dllexport)
, no cuando se usa un archivo DEF; sin embargo, los archivos DEF son una molestia de mantenimiento en mi opinión, y no quiero usarlos.La alteración de nombres de MSVC ++ plantea dos problemas:
GetProcAddress
en la DLL se vuelve un poco más complicado;foo@4
lugar de_foo@4
), lo que complica el enlace. Además, introduce el riesgo de que aparezcan "versiones sin subrayado" de DLL y aplicaciones que son incompatibles con las "versiones de subrayado".Probé la
cdecl
convención: la interoperabilidad entre MSVC ++ y MinGW funciona perfectamente, lista para usar y los nombres permanecen sin decorar en la tabla de exportación de DLL. Incluso funciona con métodos virtuales.Por estos motivos,
cdecl
es un claro ganador para mí.fuente
extern "C"
)?La mayor diferencia entre las dos convenciones de llamada es que " _ _cdecl" coloca la carga de equilibrar la pila después de una llamada de función en el llamador, lo que permite funciones con cantidades variables de argumentos. La convención "__stdcall" es de naturaleza "más simple", aunque menos flexible en este sentido.
Además, creo que los lenguajes administrados usan la convención stdcall de forma predeterminada, por lo que cualquiera que use P / Invoke tendría que indicar explícitamente la convención de llamada si va con cdecl.
Por lo tanto, si todas las firmas de sus funciones se van a definir estáticamente, probablemente me inclinaría por stdcall, si no cdecl.
fuente
En términos de seguridad, la
__cdecl
convención es "más segura" porque es la persona que llama la que necesita desasignar la pila. Lo que puede suceder en una__stdcall
biblioteca es que el desarrollador podría haber olvidado desasignar la pila correctamente, o un atacante podría inyectar algún código corrompiendo la pila de la DLL (por ejemplo, mediante el enlace de API) que luego no es verificado por la persona que llama. No tengo ningún ejemplo de seguridad CVS que muestre que mi intuición es correcta.fuente