The Mess We Are In
This is a sort of a tribute to Joe Armstrong.
#include <iostream> int main() { std::cout << "Hello, World!" << std::endl; }
clang++ -m64 -march=native -std=c++20 -stdlib=libc++ -O3 a.cpp
ldd ./a.out
linux-vdso.so.1 (0x00007ffc46ed5000) libc++.so.1 => /usr/lib64/libc++.so.1 (0x00007fa8c2449000) libc++abi.so.1 => /usr/lib64/libc++abi.so.1 (0x00007fa8c2405000) libunwind.so.8 => /usr/lib64/libunwind.so.8 (0x00007fa8c23e7000) libm.so.6 => /lib64/libm.so.6 (0x00007fa8c230d000) libc.so.6 => /lib64/libc.so.6 (0x00007fa8c2136000) /lib64/ld-linux-x86-64.so.2 (0x00007fa8c257e000) liblzma.so.5 => /lib64/liblzma.so.5 (0x00007fa8c20ff000)
Gentoo uses the wrong libunwind
, but it does not matter.
objdump -d a.out
a.out: file format elf64-x86-64 Disassembly of section .text: 0000000000001ed0 <_start>: 1ed0: f3 0f 1e fa endbr64 1ed4: 31 ed xorl %ebp, %ebp 1ed6: 49 89 d1 movq %rdx, %r9 1ed9: 5e popq %rsi 1eda: 48 89 e2 movq %rsp, %rdx 1edd: 48 83 e4 f0 andq $-16, %rsp 1ee1: 50 pushq %rax 1ee2: 54 pushq %rsp 1ee3: 45 31 c0 xorl %r8d, %r8d 1ee6: 31 c9 xorl %ecx, %ecx 1ee8: 48 8d 3d 81 00 00 00 leaq 129(%rip), %rdi # 0x1f70 <main> 1eef: ff 15 db 17 00 00 callq *6107(%rip) # 0x36d0 <memset+0x36d0> 1ef5: f4 hlt 1ef6: cc int3 1ef7: cc int3 1ef8: cc int3 1ef9: cc int3 1efa: cc int3 1efb: cc int3 1efc: cc int3 1efd: cc int3 1efe: cc int3 1eff: cc int3 1f00: 80 3d d1 28 00 00 00 cmpb $0, 10449(%rip) # 0x47d8 <memset+0x47d8> 1f07: 75 24 jne 0x1f2d <_start+0x5d> 1f09: c6 05 c8 28 00 00 01 movb $1, 10440(%rip) # 0x47d8 <memset+0x47d8> 1f10: 48 83 3d c8 17 00 00 00 cmpq $0, 6088(%rip) # 0x36e0 <memset+0x36e0> 1f18: 74 13 je 0x1f2d <_start+0x5d> 1f1a: 48 8d 3d 3f ee ff ff leaq -4545(%rip), %rdi # 0xd60 <__EH_FRAME_LIST_END__> 1f21: 48 8d 35 b8 28 00 00 leaq 10424(%rip), %rsi # 0x47e0 <memset+0x47e0> 1f28: e9 63 04 00 00 jmp 0x2390 <__register_frame_info@plt> 1f2d: c3 retq 1f2e: 66 90 nop 1f30: 50 pushq %rax 1f31: 80 3d e8 28 00 00 00 cmpb $0, 10472(%rip) # 0x4820 <memset+0x4820> 1f38: 75 34 jne 0x1f6e <_start+0x9e> 1f3a: c6 05 df 28 00 00 01 movb $1, 10463(%rip) # 0x4820 <memset+0x4820> 1f41: 48 83 3d 9f 17 00 00 00 cmpq $0, 6047(%rip) # 0x36e8 <memset+0x36e8> 1f49: 74 0c je 0x1f57 <_start+0x87> 1f4b: 48 8b 3d be 27 00 00 movq 10174(%rip), %rdi # 0x4710 <__dso_handle> 1f52: e8 49 04 00 00 callq 0x23a0 <__cxa_finalize@plt> 1f57: 48 83 3d 91 17 00 00 00 cmpq $0, 6033(%rip) # 0x36f0 <memset+0x36f0> 1f5f: 74 0d je 0x1f6e <_start+0x9e> 1f61: 48 8d 3d f8 ed ff ff leaq -4616(%rip), %rdi # 0xd60 <__EH_FRAME_LIST_END__> 1f68: 58 popq %rax 1f69: e9 42 04 00 00 jmp 0x23b0 <__deregister_frame_info@plt> 1f6e: 58 popq %rax 1f6f: c3 retq 0000000000001f70 <main>: 1f70: 55 pushq %rbp 1f71: 53 pushq %rbx 1f72: 48 83 ec 18 subq $24, %rsp 1f76: 64 48 8b 04 25 28 00 00 00 movq %fs:40, %rax 1f7f: 48 89 44 24 10 movq %rax, 16(%rsp) 1f84: 48 8b 3d 6d 17 00 00 movq 5997(%rip), %rdi # 0x36f8 <memset+0x36f8> 1f8b: 48 8d 35 12 ed ff ff leaq -4846(%rip), %rsi # 0xca4 <_IO_stdin_used+0x4> 1f92: ba 0d 00 00 00 movl $13, %edx 1f97: e8 94 00 00 00 callq 0x2030 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m> 1f9c: 48 89 c3 movq %rax, %rbx 1f9f: 48 8b 00 movq (%rax), %rax 1fa2: 48 8b 70 e8 movq -24(%rax), %rsi 1fa6: 48 01 de addq %rbx, %rsi 1fa9: 48 8d 6c 24 08 leaq 8(%rsp), %rbp 1fae: 48 89 ef movq %rbp, %rdi 1fb1: e8 0a 04 00 00 callq 0x23c0 <_ZNKSt3__18ios_base6getlocEv@plt> 1fb6: 48 8b 35 43 17 00 00 movq 5955(%rip), %rsi # 0x3700 <memset+0x3700> 1fbd: 48 89 ef movq %rbp, %rdi 1fc0: e8 0b 04 00 00 callq 0x23d0 <_ZNKSt3__16locale9use_facetERNS0_2idE@plt> 1fc5: 48 8b 08 movq (%rax), %rcx 1fc8: 48 89 c7 movq %rax, %rdi 1fcb: be 0a 00 00 00 movl $10, %esi 1fd0: ff 51 38 callq *56(%rcx) 1fd3: 89 c5 movl %eax, %ebp 1fd5: 48 8d 7c 24 08 leaq 8(%rsp), %rdi 1fda: e8 01 04 00 00 callq 0x23e0 <_ZNSt3__16localeD1Ev@plt> 1fdf: 40 0f be f5 movsbl %bpl, %esi 1fe3: 48 89 df movq %rbx, %rdi 1fe6: e8 05 04 00 00 callq 0x23f0 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc@plt> 1feb: 48 89 df movq %rbx, %rdi 1fee: e8 0d 04 00 00 callq 0x2400 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv@plt> 1ff3: 64 48 8b 04 25 28 00 00 00 movq %fs:40, %rax 1ffc: 48 3b 44 24 10 cmpq 16(%rsp), %rax 2001: 75 09 jne 0x200c <main+0x9c> 2003: 31 c0 xorl %eax, %eax 2005: 48 83 c4 18 addq $24, %rsp 2009: 5b popq %rbx 200a: 5d popq %rbp 200b: c3 retq 200c: e8 ff 03 00 00 callq 0x2410 <__stack_chk_fail@plt> 2011: 48 89 c3 movq %rax, %rbx 2014: 48 8d 7c 24 08 leaq 8(%rsp), %rdi 2019: e8 c2 03 00 00 callq 0x23e0 <_ZNSt3__16localeD1Ev@plt> 201e: 48 89 df movq %rbx, %rdi 2021: e8 fa 03 00 00 callq 0x2420 <_Unwind_Resume@plt> 2026: cc int3 2027: cc int3 2028: cc int3 2029: cc int3 202a: cc int3 202b: cc int3 202c: cc int3 202d: cc int3 202e: cc int3 202f: cc int3 0000000000002030 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m>: 2030: 55 pushq %rbp 2031: 41 57 pushq %r15 2033: 41 56 pushq %r14 2035: 41 55 pushq %r13 2037: 41 54 pushq %r12 2039: 53 pushq %rbx 203a: 48 83 ec 28 subq $40, %rsp 203e: 49 89 d6 movq %rdx, %r14 2041: 49 89 f7 movq %rsi, %r15 2044: 48 89 fb movq %rdi, %rbx 2047: 64 48 8b 04 25 28 00 00 00 movq %fs:40, %rax 2050: 48 89 44 24 20 movq %rax, 32(%rsp) 2055: 48 8d 7c 24 08 leaq 8(%rsp), %rdi 205a: 48 89 de movq %rbx, %rsi 205d: e8 ce 03 00 00 callq 0x2430 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_@plt> 2062: 80 7c 24 08 00 cmpb $0, 8(%rsp) 2067: 0f 84 ab 00 00 00 je 0x2118 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m+0xe8> 206d: 48 8b 03 movq (%rbx), %rax 2070: 48 8b 40 e8 movq -24(%rax), %rax 2074: 4c 8d 24 03 leaq (%rbx,%rax), %r12 2078: 4c 8b 6c 03 28 movq 40(%rbx,%rax), %r13 207d: 8b 6c 03 08 movl 8(%rbx,%rax), %ebp 2081: 8b 84 03 90 00 00 00 movl 144(%rbx,%rax), %eax 2088: 83 f8 ff cmpl $-1, %eax 208b: 75 47 jne 0x20d4 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m+0xa4> 208d: 48 8d 7c 24 18 leaq 24(%rsp), %rdi 2092: 4c 89 e6 movq %r12, %rsi 2095: e8 26 03 00 00 callq 0x23c0 <_ZNKSt3__18ios_base6getlocEv@plt> 209a: 48 8b 35 5f 16 00 00 movq 5727(%rip), %rsi # 0x3700 <memset+0x3700> 20a1: 48 8d 7c 24 18 leaq 24(%rsp), %rdi 20a6: e8 25 03 00 00 callq 0x23d0 <_ZNKSt3__16locale9use_facetERNS0_2idE@plt> 20ab: 48 8b 08 movq (%rax), %rcx 20ae: 48 89 c7 movq %rax, %rdi 20b1: be 20 00 00 00 movl $32, %esi 20b6: ff 51 38 callq *56(%rcx) 20b9: 88 44 24 07 movb %al, 7(%rsp) 20bd: 48 8d 7c 24 18 leaq 24(%rsp), %rdi 20c2: e8 19 03 00 00 callq 0x23e0 <_ZNSt3__16localeD1Ev@plt> 20c7: 0f be 44 24 07 movsbl 7(%rsp), %eax 20cc: 41 89 84 24 90 00 00 00 movl %eax, 144(%r12) 20d4: 81 e5 b0 00 00 00 andl $176, %ebp 20da: 4d 01 fe addq %r15, %r14 20dd: 83 fd 20 cmpl $32, %ebp 20e0: 4c 89 fa movq %r15, %rdx 20e3: 49 0f 44 d6 cmoveq %r14, %rdx 20e7: 44 0f be c8 movsbl %al, %r9d 20eb: 4c 89 ef movq %r13, %rdi 20ee: 4c 89 fe movq %r15, %rsi 20f1: 4c 89 f1 movq %r14, %rcx 20f4: 4d 89 e0 movq %r12, %r8 20f7: e8 b4 00 00 00 callq 0x21b0 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_> 20fc: 48 85 c0 testq %rax, %rax 20ff: 75 17 jne 0x2118 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m+0xe8> 2101: 48 8b 03 movq (%rbx), %rax 2104: 48 8b 40 e8 movq -24(%rax), %rax 2108: 48 8d 3c 03 leaq (%rbx,%rax), %rdi 210c: 8b 74 03 20 movl 32(%rbx,%rax), %esi 2110: 83 ce 05 orl $5, %esi 2113: e8 28 03 00 00 callq 0x2440 <_ZNSt3__18ios_base5clearEj@plt> 2118: 48 8d 7c 24 08 leaq 8(%rsp), %rdi 211d: e8 2e 03 00 00 callq 0x2450 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev@plt> 2122: 64 48 8b 04 25 28 00 00 00 movq %fs:40, %rax 212b: 48 3b 44 24 20 cmpq 32(%rsp), %rax 2130: 75 61 jne 0x2193 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m+0x163> 2132: 48 89 d8 movq %rbx, %rax 2135: 48 83 c4 28 addq $40, %rsp 2139: 5b popq %rbx 213a: 41 5c popq %r12 213c: 41 5d popq %r13 213e: 41 5e popq %r14 2140: 41 5f popq %r15 2142: 5d popq %rbp 2143: c3 retq 2144: eb 0f jmp 0x2155 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m+0x125> 2146: 49 89 c6 movq %rax, %r14 2149: 48 8d 7c 24 18 leaq 24(%rsp), %rdi 214e: e8 8d 02 00 00 callq 0x23e0 <_ZNSt3__16localeD1Ev@plt> 2153: eb 03 jmp 0x2158 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m+0x128> 2155: 49 89 c6 movq %rax, %r14 2158: 48 8d 7c 24 08 leaq 8(%rsp), %rdi 215d: e8 ee 02 00 00 callq 0x2450 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev@plt> 2162: eb 03 jmp 0x2167 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m+0x137> 2164: 49 89 c6 movq %rax, %r14 2167: 4c 89 f7 movq %r14, %rdi 216a: e8 f1 02 00 00 callq 0x2460 <__cxa_begin_catch@plt> 216f: 48 8b 03 movq (%rbx), %rax 2172: 48 8b 78 e8 movq -24(%rax), %rdi 2176: 48 01 df addq %rbx, %rdi 2179: e8 f2 02 00 00 callq 0x2470 <_ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv@plt> 217e: e8 fd 02 00 00 callq 0x2480 <__cxa_end_catch@plt> 2183: 64 48 8b 04 25 28 00 00 00 movq %fs:40, %rax 218c: 48 3b 44 24 20 cmpq 32(%rsp), %rax 2191: 74 9f je 0x2132 <_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m+0x102> 2193: e8 78 02 00 00 callq 0x2410 <__stack_chk_fail@plt> 2198: 48 89 c3 movq %rax, %rbx 219b: e8 e0 02 00 00 callq 0x2480 <__cxa_end_catch@plt> 21a0: 48 89 df movq %rbx, %rdi 21a3: e8 78 02 00 00 callq 0x2420 <_Unwind_Resume@plt> 21a8: 48 89 c7 movq %rax, %rdi 21ab: e8 90 01 00 00 callq 0x2340 <__clang_call_terminate> 00000000000021b0 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_>: 21b0: 55 pushq %rbp 21b1: 41 57 pushq %r15 21b3: 41 56 pushq %r14 21b5: 41 55 pushq %r13 21b7: 41 54 pushq %r12 21b9: 53 pushq %rbx 21ba: 48 83 ec 38 subq $56, %rsp 21be: 64 48 8b 04 25 28 00 00 00 movq %fs:40, %rax 21c7: 48 89 44 24 30 movq %rax, 48(%rsp) 21cc: 48 85 ff testq %rdi, %rdi 21cf: 0f 84 1c 01 00 00 je 0x22f1 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x141> 21d5: 4d 89 c6 movq %r8, %r14 21d8: 49 89 cf movq %rcx, %r15 21db: 49 89 d4 movq %rdx, %r12 21de: 49 89 fd movq %rdi, %r13 21e1: 44 89 4c 24 0c movl %r9d, 12(%rsp) 21e6: 48 89 c8 movq %rcx, %rax 21e9: 48 29 f0 subq %rsi, %rax 21ec: 49 8b 48 18 movq 24(%r8), %rcx 21f0: 31 db xorl %ebx, %ebx 21f2: 48 29 c1 subq %rax, %rcx 21f5: 48 0f 4f d9 cmovgq %rcx, %rbx 21f9: 48 89 d5 movq %rdx, %rbp 21fc: 48 29 f5 subq %rsi, %rbp 21ff: 48 85 ed testq %rbp, %rbp 2202: 7e 16 jle 0x221a <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x6a> 2204: 49 8b 45 00 movq (%r13), %rax 2208: 4c 89 ef movq %r13, %rdi 220b: 48 89 ea movq %rbp, %rdx 220e: ff 50 60 callq *96(%rax) 2211: 48 39 e8 cmpq %rbp, %rax 2214: 0f 85 d7 00 00 00 jne 0x22f1 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x141> 221a: 48 85 db testq %rbx, %rbx 221d: 0f 8e 94 00 00 00 jle 0x22b7 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x107> 2223: 4c 89 74 24 10 movq %r14, 16(%rsp) 2228: 48 83 fb 17 cmpq $23, %rbx 222c: 73 0e jae 0x223c <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x8c> 222e: 8d 04 1b leal (%rbx,%rbx), %eax 2231: 88 44 24 18 movb %al, 24(%rsp) 2235: 4c 8d 74 24 19 leaq 25(%rsp), %r14 223a: eb 26 jmp 0x2262 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0xb2> 223c: 48 8d 6b 10 leaq 16(%rbx), %rbp 2240: 48 83 e5 f0 andq $-16, %rbp 2244: 48 89 ef movq %rbp, %rdi 2247: e8 44 02 00 00 callq 0x2490 <_Znwm@plt> 224c: 49 89 c6 movq %rax, %r14 224f: 48 89 44 24 28 movq %rax, 40(%rsp) 2254: 48 83 cd 01 orq $1, %rbp 2258: 48 89 6c 24 18 movq %rbp, 24(%rsp) 225d: 48 89 5c 24 20 movq %rbx, 32(%rsp) 2262: 8b 44 24 0c movl 12(%rsp), %eax 2266: 0f b6 f0 movzbl %al, %esi 2269: 4c 89 f7 movq %r14, %rdi 226c: 48 89 da movq %rbx, %rdx 226f: e8 2c 02 00 00 callq 0x24a0 <memset@plt> 2274: 41 c6 04 1e 00 movb $0, (%r14,%rbx) 2279: f6 44 24 18 01 testb $1, 24(%rsp) 227e: 74 07 je 0x2287 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0xd7> 2280: 48 8b 74 24 28 movq 40(%rsp), %rsi 2285: eb 05 jmp 0x228c <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0xdc> 2287: 48 8d 74 24 19 leaq 25(%rsp), %rsi 228c: 4c 8b 74 24 10 movq 16(%rsp), %r14 2291: 49 8b 45 00 movq (%r13), %rax 2295: 4c 89 ef movq %r13, %rdi 2298: 48 89 da movq %rbx, %rdx 229b: ff 50 60 callq *96(%rax) 229e: 48 89 c5 movq %rax, %rbp 22a1: f6 44 24 18 01 testb $1, 24(%rsp) 22a6: 74 0a je 0x22b2 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x102> 22a8: 48 8b 7c 24 28 movq 40(%rsp), %rdi 22ad: e8 fe 01 00 00 callq 0x24b0 <_ZdlPv@plt> 22b2: 48 39 dd cmpq %rbx, %rbp 22b5: 75 3a jne 0x22f1 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x141> 22b7: 4d 29 e7 subq %r12, %r15 22ba: 4d 85 ff testq %r15, %r15 22bd: 7e 15 jle 0x22d4 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x124> 22bf: 49 8b 45 00 movq (%r13), %rax 22c3: 4c 89 ef movq %r13, %rdi 22c6: 4c 89 e6 movq %r12, %rsi 22c9: 4c 89 fa movq %r15, %rdx 22cc: ff 50 60 callq *96(%rax) 22cf: 4c 39 f8 cmpq %r15, %rax 22d2: 75 1d jne 0x22f1 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x141> 22d4: 49 c7 46 18 00 00 00 00 movq $0, 24(%r14) 22dc: 64 48 8b 04 25 28 00 00 00 movq %fs:40, %rax 22e5: 48 3b 44 24 30 cmpq 48(%rsp), %rax 22ea: 74 18 je 0x2304 <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x154> 22ec: e8 1f 01 00 00 callq 0x2410 <__stack_chk_fail@plt> 22f1: 45 31 ed xorl %r13d, %r13d 22f4: 64 48 8b 04 25 28 00 00 00 movq %fs:40, %rax 22fd: 48 3b 44 24 30 cmpq 48(%rsp), %rax 2302: 75 e8 jne 0x22ec <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x13c> 2304: 4c 89 e8 movq %r13, %rax 2307: 48 83 c4 38 addq $56, %rsp 230b: 5b popq %rbx 230c: 41 5c popq %r12 230e: 41 5d popq %r13 2310: 41 5e popq %r14 2312: 41 5f popq %r15 2314: 5d popq %rbp 2315: c3 retq 2316: 48 89 c3 movq %rax, %rbx 2319: f6 44 24 18 01 testb $1, 24(%rsp) 231e: 74 0a je 0x232a <_ZNSt3__116__pad_and_outputIcNS_11char_traitsIcEEEENS_19ostreambuf_iteratorIT_T0_EES6_PKS4_S8_S8_RNS_8ios_baseES4_+0x17a> 2320: 48 8b 7c 24 28 movq 40(%rsp), %rdi 2325: e8 86 01 00 00 callq 0x24b0 <_ZdlPv@plt> 232a: 48 89 df movq %rbx, %rdi 232d: e8 ee 00 00 00 callq 0x2420 <_Unwind_Resume@plt> 2332: cc int3 2333: cc int3 2334: cc int3 2335: cc int3 2336: cc int3 2337: cc int3 2338: cc int3 2339: cc int3 233a: cc int3 233b: cc int3 233c: cc int3 233d: cc int3 233e: cc int3 233f: cc int3 0000000000002340 <__clang_call_terminate>: 2340: 50 pushq %rax 2341: e8 1a 01 00 00 callq 0x2460 <__cxa_begin_catch@plt> 2346: e8 75 01 00 00 callq 0x24c0 <_ZSt9terminatev@plt> 234b: cc int3 Disassembly of section .init: 000000000000234c <_init>: 234c: f3 0f 1e fa endbr64 2350: 48 83 ec 08 subq $8, %rsp 2354: 48 8b 05 7d 13 00 00 movq 4989(%rip), %rax # 0x36d8 <memset+0x36d8> 235b: 48 85 c0 testq %rax, %rax 235e: 74 02 je 0x2362 <_init+0x16> 2360: ff d0 callq *%rax 2362: 48 83 c4 08 addq $8, %rsp 2366: c3 retq Disassembly of section .fini: 0000000000002368 <_fini>: 2368: f3 0f 1e fa endbr64 236c: 48 83 ec 08 subq $8, %rsp 2370: 48 83 c4 08 addq $8, %rsp 2374: c3 retq Disassembly of section .plt: 0000000000002380 <.plt>: 2380: ff 35 a2 23 00 00 pushq 9122(%rip) # 0x4728 <memset+0x4728> 2386: ff 25 a4 23 00 00 jmpq *9124(%rip) # 0x4730 <memset+0x4730> 238c: 0f 1f 40 00 nopl (%rax) 0000000000002390 <__register_frame_info@plt>: 2390: ff 25 a2 23 00 00 jmpq *9122(%rip) # 0x4738 <memset+0x4738> 2396: 68 00 00 00 00 pushq $0 239b: e9 e0 ff ff ff jmp 0x2380 <.plt> 00000000000023a0 <__cxa_finalize@plt>: 23a0: ff 25 9a 23 00 00 jmpq *9114(%rip) # 0x4740 <memset+0x4740> 23a6: 68 01 00 00 00 pushq $1 23ab: e9 d0 ff ff ff jmp 0x2380 <.plt> 00000000000023b0 <__deregister_frame_info@plt>: 23b0: ff 25 92 23 00 00 jmpq *9106(%rip) # 0x4748 <memset+0x4748> 23b6: 68 02 00 00 00 pushq $2 23bb: e9 c0 ff ff ff jmp 0x2380 <.plt> 00000000000023c0 <_ZNKSt3__18ios_base6getlocEv@plt>: 23c0: ff 25 8a 23 00 00 jmpq *9098(%rip) # 0x4750 <memset+0x4750> 23c6: 68 03 00 00 00 pushq $3 23cb: e9 b0 ff ff ff jmp 0x2380 <.plt> 00000000000023d0 <_ZNKSt3__16locale9use_facetERNS0_2idE@plt>: 23d0: ff 25 82 23 00 00 jmpq *9090(%rip) # 0x4758 <memset+0x4758> 23d6: 68 04 00 00 00 pushq $4 23db: e9 a0 ff ff ff jmp 0x2380 <.plt> 00000000000023e0 <_ZNSt3__16localeD1Ev@plt>: 23e0: ff 25 7a 23 00 00 jmpq *9082(%rip) # 0x4760 <memset+0x4760> 23e6: 68 05 00 00 00 pushq $5 23eb: e9 90 ff ff ff jmp 0x2380 <.plt> 00000000000023f0 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE3putEc@plt>: 23f0: ff 25 72 23 00 00 jmpq *9074(%rip) # 0x4768 <memset+0x4768> 23f6: 68 06 00 00 00 pushq $6 23fb: e9 80 ff ff ff jmp 0x2380 <.plt> 0000000000002400 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE5flushEv@plt>: 2400: ff 25 6a 23 00 00 jmpq *9066(%rip) # 0x4770 <memset+0x4770> 2406: 68 07 00 00 00 pushq $7 240b: e9 70 ff ff ff jmp 0x2380 <.plt> 0000000000002410 <__stack_chk_fail@plt>: 2410: ff 25 62 23 00 00 jmpq *9058(%rip) # 0x4778 <memset+0x4778> 2416: 68 08 00 00 00 pushq $8 241b: e9 60 ff ff ff jmp 0x2380 <.plt> 0000000000002420 <_Unwind_Resume@plt>: 2420: ff 25 5a 23 00 00 jmpq *9050(%rip) # 0x4780 <memset+0x4780> 2426: 68 09 00 00 00 pushq $9 242b: e9 50 ff ff ff jmp 0x2380 <.plt> 0000000000002430 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryC1ERS3_@plt>: 2430: ff 25 52 23 00 00 jmpq *9042(%rip) # 0x4788 <memset+0x4788> 2436: 68 0a 00 00 00 pushq $10 243b: e9 40 ff ff ff jmp 0x2380 <.plt> 0000000000002440 <_ZNSt3__18ios_base5clearEj@plt>: 2440: ff 25 4a 23 00 00 jmpq *9034(%rip) # 0x4790 <memset+0x4790> 2446: 68 0b 00 00 00 pushq $11 244b: e9 30 ff ff ff jmp 0x2380 <.plt> 0000000000002450 <_ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEE6sentryD1Ev@plt>: 2450: ff 25 42 23 00 00 jmpq *9026(%rip) # 0x4798 <memset+0x4798> 2456: 68 0c 00 00 00 pushq $12 245b: e9 20 ff ff ff jmp 0x2380 <.plt> 0000000000002460 <__cxa_begin_catch@plt>: 2460: ff 25 3a 23 00 00 jmpq *9018(%rip) # 0x47a0 <memset+0x47a0> 2466: 68 0d 00 00 00 pushq $13 246b: e9 10 ff ff ff jmp 0x2380 <.plt> 0000000000002470 <_ZNSt3__18ios_base33__set_badbit_and_consider_rethrowEv@plt>: 2470: ff 25 32 23 00 00 jmpq *9010(%rip) # 0x47a8 <memset+0x47a8> 2476: 68 0e 00 00 00 pushq $14 247b: e9 00 ff ff ff jmp 0x2380 <.plt> 0000000000002480 <__cxa_end_catch@plt>: 2480: ff 25 2a 23 00 00 jmpq *9002(%rip) # 0x47b0 <memset+0x47b0> 2486: 68 0f 00 00 00 pushq $15 248b: e9 f0 fe ff ff jmp 0x2380 <.plt> 0000000000002490 <_Znwm@plt>: 2490: ff 25 22 23 00 00 jmpq *8994(%rip) # 0x47b8 <memset+0x47b8> 2496: 68 10 00 00 00 pushq $16 249b: e9 e0 fe ff ff jmp 0x2380 <.plt> 00000000000024a0 <memset@plt>: 24a0: ff 25 1a 23 00 00 jmpq *8986(%rip) # 0x47c0 <memset+0x47c0> 24a6: 68 11 00 00 00 pushq $17 24ab: e9 d0 fe ff ff jmp 0x2380 <.plt> 00000000000024b0 <_ZdlPv@plt>: 24b0: ff 25 12 23 00 00 jmpq *8978(%rip) # 0x47c8 <memset+0x47c8> 24b6: 68 12 00 00 00 pushq $18 24bb: e9 c0 fe ff ff jmp 0x2380 <.plt> 00000000000024c0 <_ZSt9terminatev@plt>: 24c0: ff 25 0a 23 00 00 jmpq *8970(%rip) # 0x47d0 <memset+0x47d0> 24c6: 68 13 00 00 00 pushq $19 24cb: e9 b0 fe ff ff jmp 0x2380 <.plt>
There are some facts to consider.
A context switch could actually occur right in between any two successive assembly instructions, unless the code is wrapped into “critical sections” using appropriate syscalls to instruct an OS explicitly not to.
Thread suspension are resuming are context switches.
Which any shared mutable state (which is what imperative means) there is no guarantee that after resuming (switching of an execution context back to the next assembly instruction) the state has not been partially modified by other thread.
There is in principle no solution to sharing of a mutable state, it is just wrong. This is as fundamental as membranes of biological cells.
A few more facts
- The fucking
JVM
is written in an imperative C++98 - It uses
libstdc++.so
as its runtime (imperative) - JVM implements high-level concurrency primitives on top of that.
- The architecture of JVM is multithreaded imperative code.
- JDK compiles imperative Java into an imperative bytecode
- JVM compiles imperative bytecode into imperative machine code
You don’t see subtle bugs just because you are ignorant and intentionally not looking.
If your solution relies on Mutexes and Critical Sections, then it is just multiple threads busy-waiting on locks.
Concurrent code and data must be structured differently – in memory, by the runtime and by the language primitives.
Actual abstraction barriers must separate the data and only asynchronous structured message-passing can occur in code.
This requires a proper runtime support for sending and receiving of messages (with structural pattern-matching) and for the “mailboxes” (transactional, append-only storage), etc.
Just a bunch libraries on top of an imperative runtime won’t work. It will only seem to work. The runtime must itself be properly partitioned (structured).
Only Erlang guys did everything right – a principle-based research from the ground up (up to erts
).
Shared concurrency for imperative code is a myth. Period.
The solutions
There are mathematically-sound theoretical solutions.
Referential transparency
This is the fundamental property of all binding, both of symbols and within the compound and structured data - once bound everything stays forever - does not change behind your back.
This is the whole point of pure-functional languages like Haskell
. All the
effort is to preserve and even guarantee this referential transparency property.
These guarantees have various qualitative implications. The most important is the emergent declarative nature of resulting expressions, so they operationally have the same properties as mathematical formulas, expressions of pure logic, finite state machines and can be transformed (without any loss of information) into abstract syntax trees and acyclic graphs in general.
This is the ideal which Haskell community zealously seek to attain.
At most one mutable reference
This is another principal theoretical solution, which is partial referential transparency so to speak. The state has to be encapsulated into a closure, which is traditionally called a ref.
A ref or a reference is just an ADT, and the explicit abstraction barrier keeps the state “invisible” to the pure expressions. They only declare what eventually to do be done with it, whatever it actually might be.
It is remarkable that an Abstract Data Type is the fundamental building block here.
Generalized Abstraction barriers
There are emergent /standard idioms to establish and maintain actual abstraction barriers.
- type-classes allow to define abstract data types on the basis of behaviors or
sets of required interfaces to be implemented (to be an
X
is to quack like anX
and walk like anX
). Monad (in the context of programming languages) is “even more generic” type-class for defining Abstract Data Types, which in turn are abstraction barriers, partitions or abstract “membranes”. It is a “standard shape or form” for an ADT, which itself is also an /instance of a particular type-class (a set of required interfaces).
And also form (an instance of) a Monad (type-class). And this is literally it.
Keeping state “inside a Monad” (behind an abstraction barrier) is how a type-system-level guarantees of “safety” (yes, of non-broken referential transparency) could be obtained.
In languages other than Haskell care should be taken of maintaining at most one mutable reference. In Haskell there is no mutation possible in principle (only shadowing and extending).
Async
The most adequate case for using a Monad (which is, again, just an ADT, an instance of a standard type-class) is to separate pure functions form async procedures, which are fundamentally different forms of computation.
(An async procedure requires an implicit distinct execution context and special signalling for returning its result).
A monad (as a “generic” type-class) is the way to define abstraction barrier between pure mathematical functions and asynchronous procedures.
A vertical arrow
The fundamental principle is that values could be passed strictly in only one
direction - into an async context, and never “back”, which has a deep relation
with the concept of an “arrow”, found at the “core of everything” (a -> b
).
The point is that these async procedures could be composed just like ordinary
pure functions (using nesting, which is the only way a composition could be
defined declaratively), but behind its abstraction barrier, using a distinct
composition operator (not just our pure .
) or “inside of a Monad” as they prefer to say.
And a horizontal arrow
Think of introducing a vertical arrow for “lifting of values” (which will never
“come back” to the pure realm which they have left - notice this finest
esoteric prose) and a “lifted” (or specialized for this particular context) .
,
which is traditionally bound to a >>=
, and suggest an arrow, which imply a
direction (of a single step of any process).
And, again, this notion of a “vertical arrow”, which crosses an abstraction barrier (it is a Kleisli arrow after all) in only one direction, is “it”.
an ADT for ADTs
A Monad, as you see, is a “lift” (a vertical error) for values and “composition” (specialized dot) for computations in this particular instance or a context.
So one “lift” values (using Kleisli arrows) into a specific context and compose “computations” defined (and evaluated) with means of the same pure language, but behind a type-system ensured and guaranteed to be sound actual abstraction barrier.
This is general-enough (as general, in fact, as untyped Lambda Calculus) because it is, again, just an ADT for ADTs or nesting of ADTs. It corresponds to the fundamental notion of “AND ALSO” (an instance of) quacks like a duck.
Comosition of type-classes
Whole type-classes can be composed (to form a new whole), using instantiation (implementing an interface within an ADT module) instead of some explicit type-level combinators (the type-system will maintain its own context to track who implements what), and this is, for the third time in this essay, “it” again.
Everything boils down to partitions, nesting and arrows - proper abstractions, generalized form Molecular Biology and What Is.
Erlang
Erlang, being dynamically but strongly-typed language, reinforces abstraction barriers at interfaces (where else?). User-facing code is a pure-functional language, augmented with a minimum of carefully crafted concurrency primitives.
One can think of an actor as a tail-recursive function, which passes the state in a parameter (thus ensuring the at most one mutable reference property).
Asynchronous send
function (the !
infix operator) always succeeds and could be
seen as an identity function on the data argument (think that it could be
/partially applied to the “address” first).
The reseive
expression is just like case
(pattern-matching) expression on an
implicit parameter, which is has messages as values.
To be fair, these concurrency primitives are NOT strictly pure-functional, but the referential-transparency property is preserved - same input (/including the same contents of a mailbox) - same output. Always.
The spawn
primitive is definitely not a function, so it is a plain hack.
However, it is fully-deterministic within the context of the whole Erlang node.
Types are written in an additional DSL and checked by a specialized tools, which is also a proper design.
Behaviors
These building blocks (at the levels of libraries) can be thought off as sets of mutually-recursive generic functions (within a module), being parameterized by “callbacks” which contain user’s code.
This is the very same fundamental abstraction by parameterization principle, applied at the level of interfaces.
The functions (a callback is just a function) send and receive messages according to well-defined protocol.
Any protocol, in turn, is just an informally stated explicit set of rules to be implemented (followed).
At a system (OTP) level there are protocols (with rely on universal message-passing) everywhere. This is how telecommunication hardware (systems) has been build.
Erlang is simply a great language. It is well-researched and well-designed both at the user-facing (syntax, semantics) level and at the level of a properly designed standard library (OTP) and the partitioned /runtime system (written in Erlang itself and some portable C).
It takes time, like with any art form, to understand appreciate the details as well as the principles everything is based upon. Erlang is a marvel of craftsmanship.