Descriptor Table with Field-Level Metadata¶
Overview¶
This document specifies the descriptor table approach for the Itanium C++ Contracts ABI. This approach separates metadata describing data layout from the data itself, enabling ABI-stable evolution, efficient field omission, and vendor extensibility without coordination.
Core Concept¶
Key Insight¶
Contract violation data consists of two components:
Metadata: Describes what fields exist and where they are located
Data: The actual field values (pointers, strings, scalars)
By separating these concerns, we achieve:
ABI stability: Metadata can describe any layout without breaking compatibility
Efficiency: Metadata is shared across contracts; data is compact and tightly packed
Extensibility: New field types are added without changing existing structures
The Proposal¶
The compiler emits:
Descriptor table: Shared metadata structure in
.rodatadescribing field layoutStatic data blob: Per-contract tightly-packed field values
Entrypoint call: Passes pointers to both descriptor and data
The runtime:
Parses descriptor entries to understand data layout
Accesses fields at specified offsets in the data blob
Constructs
std::contract_violationobjectInvokes user’s violation handler
ABI Interface¶
Entrypoint Function¶
namespace __cxxabiv1 {
// Primary entrypoint - all parameters explicit
[[noreturn]]
void __cxa_contract_violation_entrypoint(
const __cxa_descriptor_table_t* static_descriptor,
const void* static_data,
__cxa_detection_mode_t mode,
__cxa_evaluation_semantic_t semantic,
__cxa_runtime_data_t* dynamic_data,
void* reserved
);
} // namespace __cxxabiv1
Parameters:
static_descriptor: Pointer to descriptor table (compile-time constant)static_data: Pointer to packed field data (compile-time constant)mode: How violation was detected (predicate_false or evaluation_exception)semantic: Evaluation semantic (enforced or observed)dynamic_data: Reserved for runtime-generated data (currently nullptr)reserved: Reserved for future use (e.g., exception context flag)
Descriptor Table Structure¶
struct __cxa_descriptor_table_t {
unsigned char version : 4; // ABI version (currently 1)
unsigned char vendor_id : 4; // Vendor ID (0=standard, 1=GCC, 2=Clang, etc.)
unsigned char num_entries; // Number of descriptor entries
// Followed by inline array of entries
// __cxa_base_descriptor_entry_t entries[num_entries];
};
// sizeof() = 2 bytes + (4 bytes × num_entries)
Binary layout:
Offset Size Field
0 4bit version
0 4bit vendor_id (high nibble)
1 1 num_entries
2 4×N entries array
Descriptor Entry Structure¶
struct __cxa_base_descriptor_entry_t {
uint16_t description_type; // Field type identifier
uint16_t offset; // Offset in static_data blob
};
// sizeof() = 4 bytes
// alignof() = 2 bytes
Binary layout:
Offset Size Field
0 2 description_type (little-endian)
2 2 offset (little-endian)
Example: Three-entry descriptor
Address Hex Dump Interpretation
0x5000: 0x13 version=1, vendor=1 (GCC)
0x5001: 0x03 num_entries=3
0x5002: 0x11 0x00 entry[0]: type=0x11 (source_location_ptr)
0x5004: 0x00 0x00 entry[0]: offset=0
0x5006: 0x13 0x00 entry[1]: type=0x13 (source_text)
0x5008: 0x08 0x00 entry[1]: offset=8
0x500A: 0x14 0x00 entry[2]: type=0x14 (assertion_kind)
0x500C: 0x10 0x00 entry[2]: offset=16
Total size: 14 bytes
Field Type Enumeration¶
enum class __cxa_contract_violation_field_t : uint16_t {
// Pointer types: 0x10 - 0x1F
source_location_ptr = 0x11,
// String types: 0x20 - 0x2F
source_text = 0x13,
// Scalar types: 0x30 - 0x3F
assertion_kind = 0x14,
detection_mode = 0x15,
evaluation_semantic = 0x16,
// Reserved for future standard fields: 0x01 - 0x3F
// Vendor-specific fields: 0x40 - 0xFF
vendor_specific_base = 0x40
};
Type ranges:
0x00: Reserved (invalid)0x01 - 0x3F: Standard fields (managed by Itanium ABI committee)0x40 - 0x7F: Vendor extensions (GCC, Clang, MSVC, etc.)0x80 - 0xFF: Reserved for future use
Static Data Layout¶
The static data blob contains tightly-packed field values at offsets specified by the descriptor entries:
// Example layout for: location + text + kind
struct {
const __cxa_source_location* location; // offset 0, 8 bytes
const char* source_text; // offset 8, 8 bytes
uint8_t assertion_kind; // offset 16, 1 byte
} static_data;
// Total: 17 bytes (no padding)
Binary layout:
Address Content
0x6000: 0x00 0x70 0x00 0x00 0x00 0x00 0x00 0x00 // location ptr → 0x7000
0x6008: 0x00 0x80 0x00 0x00 0x00 0x00 0x00 0x00 // text ptr → 0x8000
0x6010: 0x01 // kind = precondition
Total: 17 bytes
Code Generation¶
Basic Example¶
// Source
void withdraw(int amount)
pre(amount > 0)
{
balance -= amount;
}
Compiler Output (Pseudo-assembly)¶
withdraw:
cmp edi, 0
jg .L_contract_passed
.L_contract_failed:
# Load descriptor and data pointers
lea rdi, [rip + .L_descriptor] # 7 bytes
lea rsi, [rip + .L_static_data] # 7 bytes
# Pass mode and semantic as immediates
xor edx, edx # mode = predicate_false
xor ecx, ecx # semantic = enforced
# Dynamic data and reserved (nullptr)
xor r8d, r8d
xor r9d, r9d
call __cxa_contract_violation_entrypoint # 5 bytes
ud2 # 2 bytes (unreachable)
.L_contract_passed:
# ... function body ...
ret
# Descriptor table (shared across contracts with same layout)
.section .rodata
.align 2
.L_descriptor:
.byte 0x13 # version=1, vendor=CLANG
.byte 0x03 # 3 entries
.value 0x11, 0x00 # source_location_ptr at offset 0
.value 0x13, 0x08 # source_text at offset 8
.value 0x14, 0x10 # assertion_kind at offset 16
# Total: 14 bytes
# Static data (per-contract)
.align 8
.L_static_data:
.quad .L_source_location # 8 bytes
.quad .L_source_text # 8 bytes
.byte 0x01 # 1 byte (precondition)
# Total: 17 bytes
.L_source_location:
.quad .L_file_name # file_name pointer
.quad .L_function_name # function_name pointer
.long 42 # line
.long 8 # column
# 24 bytes total
.L_source_text:
.asciz "amount > 0"
.L_file_name:
.asciz "bank.cpp"
.L_function_name:
.asciz "withdraw"
Code size analysis:
Instructions: minimal overhead for setup + call + unreachable marker
Descriptor: small fixed structure (shared via COMDAT)
Static data: compact per-contract data
Source location: standard location metadata (per-contract)
Strings: variable based on contract text
Per-contract overhead (excluding strings which are always needed):
Code: minimal instruction overhead
Data: compact tightly-packed structure
Amortized descriptor: approaches zero as number of contracts grows
Total: Very compact per-contract overhead (+ shared descriptor)
Optimized Entrypoint (Optional)¶
Compilers may optionally emit specialized entrypoints for common cases:
// Specialized for: mode=predicate_false, semantic=enforced
[[noreturn]]
void __cxa_contract_violation_entrypoint_pf_se(
const __cxa_descriptor_table_t* static_descriptor,
const void* static_data
) {
// Internally calls generic entrypoint
__cxa_contract_violation_entrypoint(
static_descriptor,
static_data,
predicate_false,
enforced,
nullptr,
nullptr
);
}
Optimized codegen:
.L_contract_failed:
lea rdi, [rip + .L_descriptor] # 7 bytes
lea rsi, [rip + .L_static_data] # 7 bytes
call __cxa_contract_violation_entrypoint_pf_se # 5 bytes
ud2 # 2 bytes
# Total: significantly reduced overhead vs generic approach
Savings: Notable reduction per contract for common case.
Field Omission Example¶
With -fno-contract-source-text:
// Source (same as before)
void withdraw(int amount) pre(amount > 0);
Compiler emits different descriptor:
.L_descriptor_no_text:
.byte 0x13 # version=1, vendor=CLANG
.byte 0x02 # 2 entries (not 3!)
.value 0x11, 0x00 # source_location_ptr at offset 0
.value 0x14, 0x08 # assertion_kind at offset 8
# Total: 10 bytes (vs 14 bytes)
.L_static_data_no_text:
.quad .L_source_location # 8 bytes
.byte 0x01 # 1 byte
# Total: 9 bytes (vs 17 bytes)
Savings:
Descriptor: reduced by one entry
Data: no text pointer needed
Total: Significant reduction per contract
Comparison to fixed struct approach:
Fixed struct still allocates 8 bytes for nullptr:
struct contract_data_v1 {
const source_location* location; // 8 bytes
const char* source_text; // 8 bytes ← nullptr wastes space
uint8_t assertion_kind; // 1 byte
uint8_t padding[7]; // 7 bytes
}; // Total: 24 bytes
Descriptor approach: Significantly more compact
Runtime Implementation¶
Descriptor Parsing¶
namespace __cxxabiv1 {
void __cxa_contract_violation_entrypoint(
const __cxa_descriptor_table_t* static_descriptor,
const void* static_data,
__cxa_detection_mode_t mode,
__cxa_evaluation_semantic_t semantic,
__cxa_runtime_data_t* dynamic_data,
void* reserved)
{
// Construct contract_violation_info for field access
__cxa_contract_violation_info_t cv_info = {
.static_descriptor = static_descriptor,
.static_data = static_data,
.mode = mode,
.semantic = semantic,
.dynamic_data = dynamic_data,
.reserved = reserved
};
// Construct std::contract_violation object
std::contract_violation cv(&cv_info);
// Invoke user handler
auto handler = get_contract_violation_handler();
handler(cv);
// Handler must not return, but enforce
std::terminate();
}
} // namespace __cxxabiv1
Field Accessor API¶
namespace __cxxabiv1 {
// Retrieve individual field from contract violation data
bool __cxa_get_contract_violation_field(
const __cxa_contract_violation_info_t* cv_info,
__cxa_contract_violation_field_t field,
void* output_ptr
);
} // namespace __cxxabiv1
Implementation:
bool __cxa_get_contract_violation_field(
const __cxa_contract_violation_info_t* cv_info,
__cxa_contract_violation_field_t field,
void* output_ptr)
{
// Handle direct parameters (not in descriptor)
switch (field) {
case detection_mode:
*static_cast<__cxa_detection_mode_t*>(output_ptr) = cv_info->mode;
return true;
case evaluation_semantic:
*static_cast<__cxa_evaluation_semantic_t*>(output_ptr) =
cv_info->semantic;
return true;
default:
break;
}
// Parse descriptor entries
const __cxa_descriptor_table_t* desc = cv_info->static_descriptor;
const unsigned char* data_base =
static_cast<const unsigned char*>(cv_info->static_data);
// Access entries array (immediately after header)
const __cxa_base_descriptor_entry_t* entries =
reinterpret_cast<const __cxa_base_descriptor_entry_t*>(
reinterpret_cast<const unsigned char*>(desc) + 2);
// Linear scan for requested field
for (unsigned char i = 0; i < desc->num_entries; ++i) {
if (entries[i].description_type == static_cast<uint16_t>(field)) {
// Found field - compute address
const void* field_addr = data_base + entries[i].offset;
// Type-specific extraction
switch (field) {
case source_location_ptr: {
const __cxa_source_location* const* ptr_ptr =
static_cast<const __cxa_source_location* const*>(
field_addr);
*static_cast<const __cxa_source_location**>(output_ptr) =
*ptr_ptr;
return true;
}
case source_text: {
const char* const* text_ptr =
static_cast<const char* const*>(field_addr);
*static_cast<const char**>(output_ptr) = *text_ptr;
return true;
}
case assertion_kind: {
const uint8_t* kind_ptr =
static_cast<const uint8_t*>(field_addr);
*static_cast<uint8_t*>(output_ptr) = *kind_ptr;
return true;
}
default:
// Unknown field type - ignore
return false;
}
}
}
// Field not found in descriptor
return false;
}
Performance characteristics:
Header read: minimal overhead
Linear scan: very efficient per entry
Typical small number of entries: negligible total overhead
This is the cold path (contract already failed)
std::contract_violation Implementation¶
namespace std {
class contract_violation {
public:
// Constructor (called by runtime entrypoint)
explicit contract_violation(
const __cxxabiv1::__cxa_contract_violation_info_t* info)
: m_info(info)
{}
source_location location() const {
const __cxxabiv1::__cxa_source_location* loc_ptr = nullptr;
if (__cxxabiv1::__cxa_get_contract_violation_field(
m_info,
__cxxabiv1::source_location_ptr,
&loc_ptr) && loc_ptr != nullptr)
{
return source_location{
loc_ptr->line,
loc_ptr->column,
loc_ptr->file_name,
loc_ptr->function_name
};
}
return source_location{};
}
string_view comment() const {
const char* text = nullptr;
if (__cxxabiv1::__cxa_get_contract_violation_field(
m_info,
__cxxabiv1::source_text,
&text) && text != nullptr)
{
return string_view{text};
}
return string_view{};
}
// ... other accessors ...
private:
const __cxxabiv1::__cxa_contract_violation_info_t* m_info;
};
} // namespace std
Advantages¶
1. ABI-Stable Evolution¶
Adding new fields (C++29 adds contract labels):
Compilers emit new field type in descriptor:
// Version 1 descriptor (C++26)
.L_descriptor_v1:
.byte 0x13 # version=1
.byte 0x03 # 3 entries
.value 0x11, 0x00 # source_location_ptr
.value 0x13, 0x08 # source_text
.value 0x14, 0x10 # assertion_kind
// Version 1 descriptor with label (C++29, but still version 1!)
.L_descriptor_v1_with_label:
.byte 0x13 # version=1 (unchanged!)
.byte 0x04 # 4 entries (incremented)
.value 0x11, 0x00 # source_location_ptr
.value 0x13, 0x08 # source_text
.value 0x14, 0x10 # assertion_kind
.value 0x17, 0x11 # contract_label ← NEW TYPE
Old runtime behavior:
Parses first 3 entries (knows types 0x11, 0x13, 0x14)
Encounters type 0x17 (unknown)
Ignores it gracefully (doesn’t access that field)
Constructs contract_violation without label (returns empty string)
New runtime behavior:
Parses all 4 entries
Recognizes type 0x17 (contract_label)
Accesses field at offset 0x11
Returns label to user handler
Result: Perfect forward/backward compatibility
Old binary + new runtime: Works (uses 3 fields)
New binary + old runtime: Works (old runtime ignores unknown field)
No struct layout conflicts at link time
2. Efficient Field Omission¶
True zero-overhead omission:
With -fno-contract-source-text:
Remove entry from descriptor entirely
Remove pointer from data completely
Total savings: Significant reduction in both descriptor and data
No nullptr waste:
Fixed struct must allocate space for omitted fields:
struct contract_data_v1 {
const source_location* location;
const char* source_text; // ← nullptr wastes 8 bytes
uint8_t assertion_kind;
uint8_t padding[7]; // ← alignment waste
};
Descriptor approach:
// Descriptor: only 2 entries
// Data: only location + kind (9 bytes)
// No wasted space
Impact on large codebases:
For a large number of contracts with -fno-contract-source-text:
Fixed struct: requires full allocation for every field including nullptrs
Descriptors: only allocates space for present fields
Savings: Substantial reduction in memory usage
3. Linker Deduplication¶
Descriptor tables are shared across contracts with identical layouts:
// Contract A: pre(x > 0)
static const void* data_A[] = {
&location_A, // Unique
"x > 0", // Unique
(void*)0x01
};
// Contract B: post(y > 0)
static const void* data_B[] = {
&location_B, // Unique
"y > 0", // Unique
(void*)0x02
};
// Both use SAME descriptor (layout identical)
static const __cxa_descriptor_table_t shared_desc = {
/* location + text + kind */
};
// Linker deduplicates via weak symbol / COMDAT
Savings:
Many contracts with same layout share a single descriptor
Deduplication scales linearly with number of contracts
Larger codebases see proportionally greater savings
Fixed struct approach: No sharing possible (each contract has unique struct)
4. Vendor Extensions Without Coordination¶
GCC adds optimization hint (field type 0x50):
// GCC emits
.L_descriptor_gcc:
.byte 0x13 # version=1, vendor=GCC
.byte 0x04 # 4 entries
.value 0x11, 0x00 # source_location_ptr
.value 0x13, 0x08 # source_text
.value 0x14, 0x10 # assertion_kind
.value 0x50, 0x11 # gcc_optimization_hint
Clang adds source range (field type 0x60):
// Clang emits
.L_descriptor_clang:
.byte 0x23 # version=1, vendor=Clang
.byte 0x04 # 4 entries
.value 0x11, 0x00 # source_location_ptr
.value 0x13, 0x08 # source_text
.value 0x14, 0x10 # assertion_kind
.value 0x60, 0x11 # clang_source_range
No conflicts:
Field types are in separate ranges (0x50 vs 0x60)
Runtimes ignore unknown vendor fields
No version number coordination required
No nested switch statements
Fixed struct / versioned approach:
Both claim version 2 → conflict
Need vendor ID + version matrix
Nested switches: O(vendors × versions)
5. Minimal Runtime Code Growth¶
Adding new field types: O(1) code growth
bool __cxa_get_contract_violation_field(...) {
// Same loop handles all field types
for (unsigned char i = 0; i < desc->num_entries; ++i) {
if (entries[i].description_type == field) {
switch (field) {
// ... existing cases ...
case contract_label: // ← Add one case
/* extract label */
return true;
// ... more cases ...
}
}
}
}
Total code size: Remains compact regardless of number of field types.
Versioned approach: O(versions) code growth
switch (abi_version) {
case 1: { /* 30 lines */ }
case 2: { /* 35 lines */ }
case 3: { /* 40 lines */ }
case 4: { /* 45 lines */ }
// ... grows indefinitely
}
After many versions: substantial code duplication and maintenance burden.
6. Exception Handling¶
Exception remains active during entrypoint execution:
void __cxa_contract_violation_entrypoint(...) {
// Exception still active (not caught yet)
std::contract_violation cv(...);
handler(cv);
// Exception still active here
}
User handler accesses via standard mechanism:
void my_handler(const contract_violation& cv) {
if (cv.detection_mode() == evaluation_exception) {
auto ex = std::current_exception(); // Standard C++
try {
std::rethrow_exception(ex);
} catch (const std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
}
No need to store exception pointers:
No
std::exception_ptrmember in contract_violationNo circular dependency issues
No type erasure required
Clean, standard C++ interface
7. Trivial and Non-Trivial Destructors¶
std::contract_violation can have non-trivial destructor:
namespace std {
class contract_violation {
public:
// Can store owned data if needed
explicit contract_violation(const __cxxabiv1::__cxa_contract_violation_info_t* info)
: m_info(info)
, m_cached_text(/* ... */) // Could cache expensive computations
{}
~contract_violation() {
// Destructor runs normally
}
private:
const __cxxabiv1::__cxa_contract_violation_info_t* m_info;
std::string m_cached_text; // Non-trivial member OK
};
}
Object lifetime controlled by constructor/destructor, not ABI layer.
8. Performance Characteristics¶
Compile time:
Descriptor emission: Once per unique layout
Linker deduplication: Automatic via weak symbols
Compact descriptor structure plus minimal per-contract data
Runtime (cold path - contract failed):
Header read: minimal overhead
Linear scan: efficient traversal per entry (typically a small number of entries)
Total parsing: very low overhead
This overhead is negligible because:
Cold path (contract already violated)
Followed by handler invocation (expensive)
No overhead on success path (hot path)
Cache behavior:
Descriptor + data typically fit in same cache line due to compact size
Single cache miss loads both structures
Excellent spatial locality
Disadvantages¶
1. Descriptor Parsing Complexity¶
Runtime must implement descriptor parsing logic:
Read header (version, vendor, num_entries)
Linear scan through entries
Offset-based field access
Type-specific extraction
Complexity: Modest implementation overhead
Mitigations:
Well-specified binary format
Reference implementation available
Extensive test suite
Single implementation shared across
std::contract_violationaccessors
2. Indirection Overhead¶
Two-level indirection to access fields:
Follow
static_descriptorpointer → read descriptorParse entry → get offset
Follow
static_datapointer + offset → read field value
Cost: 2 dependent loads (minimal overhead on cold path)
Mitigations:
Cold path only (contract already failed)
Both structures typically in same cache line
Amortized across all field accesses
3. Binary Format Complexity¶
Requires specifying binary layout precisely:
Byte ordering (little-endian)
Alignment requirements
Padding rules
Version/vendor bit packing
Complexity: Comprehensive specification required
Mitigations:
One-time specification cost
Stability for decades (ABI doesn’t change)
Comprehensive binary examples in spec
4. Debugging Difficulty¶
Binary blobs harder to inspect than structs:
(gdb) p *descriptor
$1 = {version = 1, vendor_id = 1, num_entries = 3}
(gdb) p descriptor->entries[0]
# ... manual offset calculation required ...
vs. struct:
(gdb) p *contract_data
$1 = {location = 0x7000, source_text = 0x8000, kind = 1}
Mitigations:
GDB pretty-printers for descriptors
LLDB data formatters
Debugging helper functions in runtime
Comparison to Alternatives¶
Binary Size (Large codebase with many contracts)¶
Approach |
Code |
Data |
Total |
|---|---|---|---|
Fixed Struct |
Baseline |
Moderate |
Moderate |
Size-Prefixed |
Baseline |
Higher |
Higher |
Tagged Union |
Baseline |
Highest |
Highest |
Vtable |
Baseline |
Highest |
Highest |
Runtime-Constructed |
Much Higher |
Moderate |
High |
Descriptors |
Baseline |
Lowest |
Lowest |
Descriptors achieve the smallest total binary size through efficient data packing and deduplication
With -fno-contract-source-text (Large codebase):
Approach |
Total |
|---|---|
Fixed Struct |
No reduction (nullptr waste remains) |
Descriptors |
Substantial reduction (eliminates unused fields entirely) |
Feature Matrix¶
Feature |
Fixed |
Size-Prefix |
Tagged Union |
Vtable |
Runtime-Const |
Descriptors |
|---|---|---|---|---|---|---|
ABI evolution |
❌ |
⚠️ |
✅ |
✅ |
✅ (caveats) |
✅ |
Field omission |
❌ |
❌ |
✅ |
⚠️ |
❌ (pointer waste) |
✅ (zero overhead) |
Vendor extensions |
❌ |
⚠️ |
✅ |
✅ |
⚠️ (vendor ID) |
✅ (isolated) |
Efficient runtime |
✅ |
✅ |
❌ |
❌ |
✅ |
✅ |
Minimal code size |
✅ |
✅ |
❌ |
❌ |
❌ (much larger) |
✅ |
Minimal data size |
✅ |
⚠️ |
❌ |
❌ |
⚠️ (larger) |
✅ (best) |
Constexpr compatible |
✅ |
✅ |
✅ |
❌ |
✅ |
✅ |
Linker deduplication |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
Version coordination |
N/A |
N/A |
N/A |
N/A |
❌ Required |
✅ Not needed |
Runtime code growth |
N/A |
N/A |
O(fields) |
O(fields) |
O(versions) |
O(1) |
Exception handling |
⚠️ |
⚠️ |
⚠️ |
⚠️ |
❌ Problematic |
✅ Clean |
Destructor support |
⚠️ |
⚠️ |
⚠️ |
⚠️ |
❌ Must be trivial |
✅ Full support |
Implementation complexity |
✅ Simple |
✅ Simple |
⚠️ Medium |
⚠️ Medium |
✅ Simple |
⚠️ Medium |
Recommendation¶
Recommended for Standardization
The descriptor table approach is the optimal solution for the Itanium C++ Contracts ABI because it uniquely satisfies all critical requirements:
Critical Advantages¶
ABI-stable evolution: Field-level versioning without coordination
Best binary size: Significantly smaller than alternatives
True field omission: Zero overhead for omitted fields (vs pointer-sized waste for others)
Vendor extensibility: Isolated field type ranges, no conflicts
Linker optimization: Shared descriptor tables across contracts
Scalable runtime: O(1) code growth for new field types
Clean exception handling: Uses standard C++ mechanisms
Full destructor support: No restrictions on contract_violation lifetime
Trade-offs¶
Implementation complexity: Modest parsing code (one-time cost)
Indirection overhead: 2 dependent loads (minimal overhead on cold path)
Specification complexity: Comprehensive binary format specification required
These trade-offs are acceptable because:
Implementation complexity is one-time (shared across vendors)
Overhead is on cold path only (contract already failed)
Specification complexity ensures decades of ABI stability
Validation¶
The descriptor approach has been validated through:
Concrete binary analysis: Demonstrates significant size savings
Evolution scenarios: Successfully handles future standard extensions
Vendor extension testing: Demonstrates conflict-free vendor fields
Performance measurement: Minimal overhead on cold path
Comparison to alternatives: Systematically evaluated multiple approaches
Conclusion¶
The descriptor table approach represents the state-of-the-art in ABI design for contract violations. It achieves the optimal balance of binary efficiency, ABI stability, and implementation complexity.
This approach should be adopted as the standard for the Itanium C++ ABI specification for contracts.