질문 님이 쓰신 글 :
:
: void __fastcall TForm1::Button1Click(TObject *Sender)
: {
: int k = 100;
: int t = 0;
: int r = 0;
:
: try {
: r = k / t;
: }
: catch(...)
: {
: Caption = "divide zero...";
: }
: }
:
:
: 32비트로 컴파일 하면 응용프로그램에서 캐치가 되고, 캡션에 출력도 잘되는데
: 64비트로 컴파일 하면 응용 프로그램에서 예외 캐치를 못합니다.
: 버그인가요?
:
:
:
:
이건 좀 기술적인 부분인데 컴파일러의 버그라기 보다는
본질적으로 엠바가 clang 오픈소스 컴파일러를 베끼면서 Exception 필터링 기능을 구현해 놓지 못했기 때문임.
C++ 컴파일러에서 Exception을 구현하는 방식은 벤더마다 다르고,
clang 오픈소스 컴파일러의 경우에는 Itanium ABI를 따르는 GNU 컴파일러의 setjmp/longjmp 방식이나
dwarf 형식 두가지 방식을 지원하도록 되어있는데, 이 방식의 경우 윈도우즈 64비트 OS 에서 native 로 제공하는
Exception 처리 방식과 다르기 때문에 특히 Callback 함수에서 문제가 있게되어서
현재 GNU 컴파일러는 기존의 방식을 사용하지 않고 Windows 64bit OS가 native 하게 지원하는 64bit SEH 방식으로
컴파일러 툴체인이 컴파일 될수 있도록 Configuration 이 설정되어 있음.
Windows 32 bit OS에서는 프레임 방식으로 Exception을 처리해서 예외가 발생하지 않더라도 스택 Unwind를 위한
에필로그 코드가 무조건 실행되는 오버헤드를 피할수 없었지만, Windows 64bit OS에서는 예외가 발생하지 않으면
전혀 그런 오버헤드 없이 프로그램이 실행될수 있도록 Exception 방식을 Table Driven 방식으로 근본적으로 바꿨음.
명시적으로 throw 에 의한 동기적인 예외가 아닌, CPU에 의해서 하드웨어적으로 캐치되는 비동기적 Exception을 처리하기 위해선
void __fastcall TForm3::Button1Click(TObject *Sender)
{
int value = 100;
int divider = 0;
int r = 0;
try{
r = value / divider;
}
__except(GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO ?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
Caption = L"Divide zero";
}
}
위와 같은 코드패턴을 이용해야 함.
32비트 컴파일러로 컴파일해서 디버거에서 실행하지 않고 직접 실행하면, 캡션에 문자가 뜰 것임.
여기서 GetExceptionCode() 는 함수 처럼 보이지만, 실제는 함수가 아니고 컴파일러가 인식하는
내부 컴파일 디렉티브임.
32비트 컴파일러에서는 저런 Exception 필터링 기능을 지원하지만
엠바가 clang 오픈소스 컴파일러를 열심히 베껴서 만든 64bit 컴파일러는 Exception 필터링 기능을 아직
구현해 놓고 있지 못하고 있음.
그리고 또한가지 C++ 빌더의 근본적인 문제는 델파이 Exception 처리 방식을 따라야 하기 때문에
예외가 발생할시 로컬스코프의 객체만 자동으로 소멸시켜줄 뿐, 힙에 생성된 객체 까지 자동으로
소멸시켜주지 못함. 델파이로 만들어진 VCL을 뒷치닥거리 하기 위해서 이런 짓을 ㅋ
예외처리는 델파이 컴파일러 보다 오히려 FPC 컴파일러가 근본적으로 더 잘 만들어져 있음.
RTL도 델파이는 Early 타임 Exception을 제대로 캐치하지 못하게 엉터리로 구현되어 있고.
사족으로 엠바카데로가 64 bit CRT 런타임 라이브러리에 대한 소스코드를 제공하고 있지 않은데
그래서 소스레벨 디버깅도 안되고.
그건 clang 에서 사용하는 로우레벨의 컴파일러 지원 코드들이 이를테면 예외처리를 위한 로우레벨 부분
컴파일러 지원을 위한 빌트인 함수등의 코드가 GNU 컴파일러 랭귀지 확장셋을 써서 구현되어 있고
엠바카데로 애들도 CRT 런타임 코드 컴파일을 위해서 GNU 컴파일러를 썼기 때문임.
clang 은 디버깅 포맷으로 GNU 컴파일러 툴 체인이 사용하는 dwarf 포맷을 사용하는데,
엠바가 그놈에 파스칼 랭귀지를 사용하는 델파이 뒷치닥거리 하기 위해서 지들 편한대로
디버깅 포맷을 변형해서 사용하기 때문에 GNU 컴파일러로 컴파일한 모듈에 대한 디버깅도 안되고 ㅋ