Windows에서 __cdecl 또는 __stdcall?


84

현재 DLL로 배포 될 Windows 용 C ++ 라이브러리를 개발 중입니다. 내 목표는 바이너리 상호 운용성을 극대화하는 것입니다. 보다 정확하게는 DLL을 다시 컴파일 할 필요없이 여러 버전의 MSVC ++ 및 MinGW로 컴파일 된 코드에서 DLL의 함수를 사용할 수 있어야합니다. 그러나 어떤 호출 규칙이 가장 좋은지 cdecl또는 stdcall.

때때로 "C 호출 규칙은 컴파일러를 통틀어 동일한 것이 보장되는 유일한 것"과 같은 말을 듣습니다. 이는 " 의 해석 cdecl, 특히 값을 반환하는 방법에 있어 약간의 변형이 있습니다. "와 대조됩니다 . 이것은 특정 라이브러리 개발자 (예 : libsndfile )가 배포하는 DLL에서 눈에 보이는 문제없이 C 호출 규칙을 사용하는 것을 막지는 않는 것 같습니다 .

반면에 stdcall호출 규칙은 잘 정의 된 것 같습니다. 내가 말한 바에 따르면 모든 Windows 컴파일러는 기본적으로 Win32 및 COM에 사용되는 규칙이기 때문에이를 따라야합니다. 이것은 Win32 / COM을 지원하지 않는 Windows 컴파일러가 그다지 유용하지 않다는 가정을 기반으로합니다. 포럼에 게시 된 많은 코드 조각은 기능을 선언 stdcall하지만 이유 를 명확하게 설명하는 단일 게시물을 찾을 수없는 것 같습니다 .

상충되는 정보가 너무 많고 검색 할 때마다 서로 다른 답변을 제공하므로 둘 사이를 결정하는 데 도움이되지 않습니다. 나는 왜 내가 다른 하나를 선택해야하는지 (또는 둘이 동등한 이유)에 대해 명확하고 자세하며 논증적인 설명을 찾고 있습니다.

이 질문은 "클래식"함수뿐만 아니라 가상 멤버 함수 호출에도 적용됩니다. 대부분의 클라이언트 코드는 "인터페이스", 순수 가상 클래스 (예를 들어 여기거기에 설명 된 패턴을 따름)를 통해 내 DLL과 인터페이스하기 때문 입니다.


4
"cdecl의 해석, 특히 값을 반환하는 방법에 약간의 차이가 있습니다."이는 대략 x86에서 cdecl을 사용하는 여러 OS가 서로 다른 cdecl 변형을 사용할 수 있음을 의미합니다. 즉, 둘 다 "cdecl"이라고 부르는 서로 다른 ABI입니다. Windows는 변형을 고정하고 Windows에서 실행되고 Windows의 선택을 존중하지 않는 모든 구현은 Windows cdecl 함수를 호출 할 수 없으므로 stdcall에서 말했듯이 Windows 프로그래밍에는 쓸모가 없으며 일부 표준 C가 있습니다. 구현하기 어려울 것입니다.
Steve Jessop

1
__stdcall 호출 규칙은 Win32 API 함수를 호출하는 데 사용됩니다. docs.microsoft.com/en-us/cpp/cpp/stdcall?view=vs-2017
Zanna

답변:


79

방금 실제 테스트를 수행했습니다 (MSVC ++ 및 MinGW로 DLL 및 응용 프로그램을 컴파일 한 다음 혼합). 나타나는 것처럼 cdecl호출 규칙으로 더 나은 결과를 얻었습니다 .

보다 구체적으로 : 문제 stdcall는 MSVC ++가 .NET Framework를 사용하는 경우에도 DLL 내보내기 테이블의 이름을 엉망으로 만드는 것 extern "C"입니다. 예를 들어 foo이된다 _foo@4. 이것은 __declspec(dllexport)DEF 파일을 사용할 때가 아니라를 사용할 때만 발생 합니다. 그러나 DEF 파일은 제 생각에 유지 관리가 번거롭고 사용하고 싶지 않습니다.

MSVC ++ 이름 맹 글링에는 두 가지 문제가 있습니다.

  • GetProcAddressDLL에서 사용하면 약간 더 복잡해집니다.
  • 기본적으로 MinGW는 데코 레이팅 된 이름 앞에 undescore를 추가하지 않습니다 (예 : MinGW는 foo@4대신 사용 _foo@4). 이는 연결을 복잡하게합니다. 또한 "밑줄 버전"과 호환되지 않는 DLL 및 응용 프로그램의 "밑줄이 아닌 버전"이 갑자기 나타날 위험이 있습니다.

나는 cdecl관례를 시도했다 : MSVC ++와 MinGW 간의 상호 운용성은 완벽하게 작동하고, 즉시 사용 가능하며, 이름은 DLL 내보내기 테이블에 장식되지 않은 상태로 유지됩니다. 가상 방법에서도 작동합니다.

이러한 이유로 cdecl는 저에게 확실한 승자입니다.


특히 64 비트 DLL에는 적용되지 않습니다. __stdcall 및 __cdecl은 일반적으로 모두 무시되므로 내 보낸 C 함수에서 이름 변경이 발생하지 않습니다.
jtbr

@jtbr 내 보낸 C ++ 함수 (예 : no extern "C")는 어떻습니까?
Ela782

@ Ela782 C ++는 함수 오버로딩을 지원하기 위해 항상 이름을 변경합니다. 그러나 이것은 표준화되지 않았으므로 컴파일러마다 다를 수 있습니다.
jtbr

@jtbr 죄송합니다. 이름이 맹 글링이 아닙니다. 내 보낸 C ++ 함수가있는 64 비트 DLL에서도 __stdcall과 __cdecl이 모두 무시되는지 여부를 의미했습니다.
Ela782

1
@ Ela782 예; __stdcall과 __cdecl은 모든 x86-64 컴파일에서 무시됩니다. wikipedia를 참조하십시오 .
jtbr

20

두 호출 규칙의 가장 큰 차이점은 " _ _cdecl"이 호출자에 대한 함수 호출 후 스택의 균형을 조정하는 부담을 주므로 가변적 인 양의 인수를 가진 함수를 허용한다는 것입니다. "__stdcall"규칙은 본질적으로 "단순"하지만 이와 관련하여 유연성이 떨어집니다.

또한 관리 언어는 기본적으로 stdcall 규칙을 사용하므로 P / Invoke를 사용하는 사람은 cdecl을 사용하는 경우 호출 규칙을 명시 적으로 지정해야합니다.

따라서 모든 함수 서명이 정적으로 정의되면 cdecl이 아니라면 stdcall에 의지 할 것입니다.


11

보안 측면에서 __cdecl규칙은 스택 할당을 해제해야하는 호출자이기 때문에 "안전"합니다. __stdcall라이브러리 에서 발생할 수있는 일은 개발자가 스택을 올바르게 할당 해제하는 것을 잊었거나 공격자가 DLL의 스택을 손상시켜 (예 : API 후킹에 의해) 일부 코드를 삽입 할 수 있으며 호출자가 확인하지 않을 수 있습니다. 내 직감이 옳다는 것을 보여주는 CVS 보안 예제가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.