C++ Exception Basics

TL;DR

  • C++ μ˜ˆμ™Έ μ²˜λ¦¬λŠ” Itanium C++ ABI의 κ·œμΉ™μ„ λ”°λ₯Έλ‹€.

  • 정상 κ²½λ‘œλŠ” λΉ„μš©μ΄ μ—†κ³ (=zero-cost), μ˜ˆμ™Έκ°€ 던져질 λ•Œλ§Œ μŠ€νƒ 언와인딩이 μˆ˜ν–‰λœλ‹€.

  • 언와인딩은 DWARF μ–Έμ™€μΈλ“œ 정보(.eh_frame) λ₯Ό μ΄μš©ν•˜μ—¬ μŠ€νƒ ν”„λ ˆμž„μ„ ν•œ 단계씩 되돌리며,
    ν”„λ ˆμž„λ§ˆλ‹€ personality ν•¨μˆ˜κ°€ β€œcatch λŒ€μƒμΈμ§€/μ •λ¦¬λ§Œ 할지”λ₯Ό νŒλ‹¨ν•œλ‹€.

  • μ˜ˆμ™Έ κ°μ²΄λŠ” heap 에 λ§Œλ“€μ–΄μ§€λ©° reference count 둜 생λͺ…μ£ΌκΈ°λ₯Ό κ΄€λ¦¬ν•œλ‹€.

  • 언와인딩 κ³Όμ •μ—μ„œ callee-saved λ ˆμ§€μŠ€ν„°λ§Œ λ³΅μ›λ˜κ³ , caller-saved λ ˆμ§€μŠ€ν„°λŠ” ν•„μš” μ‹œ landing pad μ½”λ“œκ°€ μž¬κ΅¬μ„±ν•œλ‹€.

  • μ„±λŠ₯ 병λͺ©μ€ 동적 라이브러리 λͺ©λ‘μ„ λ³΄ν˜Έν•˜λŠ” μ „μ—­ λ‘œλ” 락과 .eh_frame 탐색 λΉ„μš©μ΄λ©°,
    언와인더 κ΅¬ν˜„μ²΄(libgcc_s, LLVM libunwind λ“±)λŠ” 이λ₯Ό 캐싱 μ „λž΅μœΌλ‘œ μ΅œμ ν™”ν•œλ‹€.


Overview

1) μ˜ˆμ™Έ λ°œμƒ μ‹œ 전체 흐름

  1. throw β†’ μ»΄νŒŒμΌλŸ¬κ°€ μƒμ„±ν•œ μ½”λ“œκ°€ μ˜ˆμ™Έ 객체λ₯Ό heap에 ν• λ‹Ήν•˜κ³  __cxa_throw 호좜

  2. __cxa_throw β†’ Itanium ABI의 _Unwind_RaiseException 호좜

  3. 언와인더가 ν˜„μž¬ μŠ€νƒ ν”„λ ˆμž„ μœ„μΉ˜μ—μ„œ DWARF CFI(unwind info) λ₯Ό μ½λŠ”λ‹€

  4. 1단계(Search phase)

    • 각 ν”„λ ˆμž„λ§ˆλ‹€ personality ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•΄
      β€œμ΄ ν”„λ ˆμž„μ΄ 이 μ˜ˆμ™Έλ₯Ό μž‘μ„ 수 μžˆλŠ”κ°€?” 검사
  5. 2단계(Cleanup phase)

    • λ‹€μ‹œ μ²˜μŒλΆ€ν„° ν”„λ ˆμž„μ„ μˆœνšŒν•˜λ©°

      • destructor 호좜

      • catch λŒ€μƒ ν”„λ ˆμž„κΉŒμ§€ μŠ€νƒ λ‘€λ°±

    • λ„μ°©ν•˜λ©΄ landing pad둜 점프

  6. landing padμ—μ„œ __cxa_begin_catch μ‹€ν–‰ β†’ μ˜ˆμ™Έ 객체 μ°Έμ‘° νšλ“

  7. catch 블둝 μ’…λ£Œ μ‹œ __cxa_end_catch 호좜 β†’ refcount κ°μ†Œ 및 ν•„μš” μ‹œ μ‚­μ œ


2) Itanium C++ ABIκ°€ 보μž₯ν•˜λŠ” 것

  • μ˜ˆμ™Έ 객체 λ©”λͺ¨λ¦¬ λ ˆμ΄μ•„μ›ƒ

  • __cxa_throw, __cxa_begin_catch, __cxa_end_catch, __gxx_personality_v0 λ“±μ˜ μΈν„°νŽ˜μ΄μŠ€

  • personality ν•¨μˆ˜κ°€ μˆ˜ν–‰ν•΄μ•Ό ν•˜λŠ” λ™μž‘κ³Ό νŒλ‹¨ κ·œμΉ™

  • DWARF 기반 μŠ€νƒ 언와인딩 절차
    β†’ 컴파일러(gcc/clang)κ°€ 달라도 μ˜ˆμ™Έ λͺ¨λΈμ€ ν˜Έν™˜λ¨


3) DWARF μ–Έμ™€μΈλ“œ 정보(.eh_frame)

  • 각 ν•¨μˆ˜μ˜ μŠ€νƒ λ ˆμ΄μ•„μ›ƒ λ³€ν™”, ν”„λ‘€λ‘œκ·Έ/μ—ν•„λ‘œκ·Έμ—μ„œ μ €μž₯된 λ ˆμ§€μŠ€ν„° μœ„μΉ˜ 등을 κΈ°λ‘ν•œ metadata

  • μ–Έμ™€μΈλ”λŠ” 이λ₯Ό ν•΄μ„ν•˜μ—¬ β€œμ΄μ „ ν”„λ ˆμž„μ˜ SP/IP/FP/μ €μž₯ λ ˆμ§€μŠ€ν„°β€λ₯Ό 볡원할 수 μžˆλ‹€

  • μ˜ˆμ™Έ 처리 μ „μš©μ΄λ©°, 디버그 정보(.debug_info)μ™€λŠ” λ³„κ°œ


4) personality ν•¨μˆ˜

ν”„λ ˆμž„λ§ˆλ‹€ ν˜ΈμΆœλ˜λŠ” β€œμ–Έμ–΄λ³„ 처리 엔진” μ—­ν• .

  • C++: __gxx_personality_v0

  • μ±…μž„

    1. 이 ν”„λ ˆμž„μ΄ ν•΄λ‹Ή μ˜ˆμ™Έ νƒ€μž…μ„ catchν•  수 μžˆλŠ”μ§€ νŒλ‹¨

    2. cleanup-only μ˜μ—­μΈμ§€ νŒλ‹¨

    3. landing pad의 μœ„μΉ˜(IP)λ₯Ό 언와인더에 μ•Œλ €μ€Œ


5) λ ˆμ§€μŠ€ν„° 볡원 κ·œμΉ™

  • callee-saved λ ˆμ§€μŠ€ν„°λ§Œ DWARFλ₯Ό 톡해 λ°˜λ“œμ‹œ 볡원

    • (예: x86-64: RBX, RBP, R12~15 λ“±)
  • caller-savedλŠ” landing pad μ½”λ“œκ°€ 직접 ν•„μš” μ‹œ λ‹€μ‹œ λ‘œλ”©

  • SP, IP(PC), FP 같은 ν”„λ ˆμž„ κ΄€λ ¨ λ ˆμ§€μŠ€ν„°λŠ” 항상 언와인더가 볡원


6) μ„±λŠ₯ 병λͺ©κ³Ό μ΅œμ ν™”

  • 동적 라이브러리 λͺ©λ‘μ€ λ‘œλ”μ˜ μ „μ—­ 락(dl loader lock) 으둜 보호됨
    β†’ .eh_frame μœ„μΉ˜ 탐색 μ‹œ 락 νšλ“ ν•„μš”

  • λŒ€κ·œλͺ¨ ν”„λ‘œκ·Έλž¨μ—μ„œ μ˜ˆμ™Έ λΉ„μš©μ΄ κΈ‰μ¦ν•˜λŠ” 이유

    • ν”„λ ˆμž„λ§ˆλ‹€ DWARFλ₯Ό νŒŒμ‹±

    • μž¦μ€ μ „μ—­ 락 νšλ“

  • LLVM libunwind, libgcc_s 등은

    • IP β†’ CFI 캐싱

    • ν•¨μˆ˜ λ‹¨μœ„ λ˜λŠ” 호좜 지점 λ‹¨μœ„ 캐싱

    • backtrace fast path μ΅œμ ν™”
      등을 μ‚¬μš©ν•΄ μ„±λŠ₯ κ°œμ„ 


Example (κ°œλ… 흐름 μ˜ˆμ‹œ)

μ˜ˆμ™Έ λ°œμƒ β†’ __cxa_throw
β†’ _Unwind_RaiseException
β†’ Frame N personality 검사(뢀적합)
β†’ Frame N–1 personality 검사(뢀적합)
β†’ Frame N–2 personality 검사(catch κ°€λŠ₯ β†’ search phase μ’…λ£Œ)
β†’ cleanup phase μ‹œμž‘(μœ„ ν”„λ ˆμž„λ“€ destructor μ‹€ν–‰)
β†’ landing pad IP둜 점프
β†’ __cxa_begin_catch
β†’ catch 블둝 μ‹€ν–‰
β†’ __cxa_end_catch β†’ refcount 0 β†’ μ˜ˆμ™Έ 객체 delete


Takeaways

  • C++ μ˜ˆμ™Έ μ²˜λ¦¬λŠ” zero-cost λͺ¨λΈμ΄λ©°, μ‹€ν–‰ 쀑 μ˜ˆμ™Έκ°€ 없을 λ•Œ λΉ„μš©μ΄ 사싀상 μ—†λ‹€.

  • 언와인딩은 DWARF + Itanium ABI + personality ν•¨μˆ˜μ˜ μ‘°ν•©μœΌλ‘œ λ™μž‘ν•œλ‹€.

  • μ˜ˆμ™Έ κ°μ²΄λŠ” heap에 있으며 refcount둜 생λͺ…μ£ΌκΈ° κ΄€λ¦¬λœλ‹€.

  • caller-saved / callee-saved ꡬ뢄은 언와인딩 λΉ„μš© μ΅œμ ν™”λ₯Ό μœ„ν•΄ μ€‘μš”ν•˜λ‹€.

  • 동적 라이브러리 ν™˜κ²½μ—μ„œλŠ” λ‘œλ” 락 경쟁이 μ˜ˆμ™Έ λΉ„μš©μ„ μ•…ν™”μ‹œν‚¬ 수 있으며,
    ν˜„λŒ€ μ–Έμ™€μΈλ”λŠ” 이λ₯Ό 캐싱 μ „λž΅μœΌλ‘œ μ™„ν™”ν•œλ‹€.

  • GCC/Clang λ“± μ„œλ‘œ λ‹€λ₯Έ C++ λŸ°νƒ€μž„μ΄λΌλ„ Itanium ABIλ₯Ό μ€€μˆ˜ν•˜λŠ” ν•œ μƒν˜Έ 운용 κ°€λŠ₯ν•˜λ‹€.

References

CppCon 2017: Dave Watson β€œC++ Exceptions and Stack Unwinding” C++ Exceptions for Smaller Firmware - Khalil Estell - CppCon 2024