[resistor-color] example solution crashes

For the color_array array you’ll need to do_relocs - since the members are actually references to other places in the binary.

See musl ld source as the test runner is actually alpine

You’ll get a crash when loading the test binary on alpine but maybe not on your machine. The crash looks like

0x7fb81021808c <do_relocs+739>  mov    QWORD PTR [r12], rcx

where r12 is the address for color_array

$r12   : 0x00555ace6500bb  →  <color_array+0>

and rcx is the relocated address of the first member of this array

$rcx   : 0x00555ace650080  →  0x7262006b63616c62 ("black"?)

Ideally the musl libc should change protections for this section(.rodata) or check if its writable - but it doesn’t and hence crashes while writing to a readable section of memory

gef➤  vmmap 0x00555ace6500bb
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x00555ace650000 0x00555ace651000 0x00000000004000 r-- /tmp/test/exercises/practice/resistor-color/tests

TLDR : Any variable that is actually a reference to another for our submissions should be in .data instead of .rodata due to a weird behaviour of the alpine linux’s musl loader - maybe should be reported upstream?

To debug the crash I built the test-runner docker with this diff

diff --git a/Dockerfile b/Dockerfile
index 2eb47d7..5bc1f98 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,6 @@
 FROM alpine:3.10
-RUN apk add --no-cache coreutils gcc libc-dev make nasm python3
+RUN apk add --no-cache coreutils gcc libc-dev make nasm python3 gdb file musl-dbg
+RUN sh -c "$(wget https://gef.blah.cat/sh -O -)"
 WORKDIR /opt/test-runner
 COPY run.sh bin/
 COPY process_results.py ./

Crash with more context

$rax   : 0x0
$rbx   : 0x8
$rcx   : 0x0055eea43d1080  →  0x7262006b63616c62 ("black"?)
$rdx   : 0x0
$rsp   : 0x007ffcf3236a00  →  0x0000000000000000
$rbp   : 0x007fe6bfdafb40  →  0x007fe6bfd1f000  →  0x00010102464c457f
$rsi   : 0x007fe6bfdaf8a0  →  0x0055eea43cd000  →   jg 0x55eea43cd047
$rdi   : 0x007fe6bfdaf8a0  →  0x0055eea43cd000  →   jg 0x55eea43cd047
$rip   : 0x007fe6bfd7608c  →  <do_relocs+739> mov QWORD PTR [r12], rcx
$r8    : 0x007fe6bfd2eaa0  →  0x0800120000094c ("L\t"?)
$r9    : 0x0055eea43d10bb  →  <color_array+0> add BYTE PTR [rax+0x0], 0x0
$r10   : 0x0055eea43cd452  →  "__libc_start_main"
$r11   : 0x0
$r12   : 0x0055eea43d10bb  →  <color_array+0> add BYTE PTR [rax+0x0], 0x0
$r13   : 0x0
$r14   : 0x007fe6bfdaf8a0  →  0x0055eea43cd000  →   jg 0x55eea43cd047
$r15   : 0x0055eea43cd4c8  →   mov ebx, 0x40
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── stack ────
0x007ffcf3236a00│+0x0000: 0x0000000000000000     ← $rsp
0x007ffcf3236a08│+0x0008: 0x0000000000000003
0x007ffcf3236a10│+0x0010: 0x0000000000000018
0x007ffcf3236a18│+0x0018: 0x0000000000000000
0x007ffcf3236a20│+0x0020: 0x0000000000000000
0x007ffcf3236a28│+0x0028: 0x0000000000000180
0x007ffcf3236a30│+0x0030: 0x0055eea43cd000  →   jg 0x55eea43cd047
0x007ffcf3236a38│+0x0038: 0x0055eea43cd2c0  →   add BYTE PTR [rax], al
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── code:x86:64 ────
   0x7fe6bfd76080 <do_relocs+727>  jmp    0x7fe6bfd7608c <do_relocs+739>
   0x7fe6bfd76082 <do_relocs+729>  add    rcx, rdx
   0x7fe6bfd76085 <do_relocs+732>  sub    rcx, QWORD PTR [rsi+0x110]
 → 0x7fe6bfd7608c <do_relocs+739>  mov    QWORD PTR [r12], rcx
   0x7fe6bfd76090 <do_relocs+743>  jmp    0x7fe6bfd7618a <do_relocs+993>
   0x7fe6bfd76095 <do_relocs+748>  cmp    QWORD PTR [rsp+0x8], 0x2
   0x7fe6bfd7609b <do_relocs+754>  jne    0x7fe6bfd760a2 <do_relocs+761>
   0x7fe6bfd7609d <do_relocs+756>  mov    rcx, QWORD PTR [r12+0x8]
   0x7fe6bfd760a2 <do_relocs+761>  cmp    DWORD PTR [rip+0x3a1ab], 0x0        # 0x7fe6bfdb0254 <runtime>
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── threads ────
[#0] Id 1, Name: "tests", stopped 0x7fe6bfd7608c in do_relocs (), reason: SIGSEGV
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7fe6bfd7608c → do_relocs(dso=0x7fe6bfdaf8a0 <app>, rel=0x55eea43cd4c8, rel_size=0x180, stride=0x3)
[#1] 0x7fe6bfd76bde → reloc_all(p=0x7fe6bfdaf8a0 <app>)
[#2] 0x7fe6bfd7838a → __dls3(sp=0x7ffcf3236e80)
[#3] 0x7fe6bfd77ba7 → __dls2b(sp=0x7ffcf3236e80)
[#4] 0x7fe6bfd77b4c → __dls2(base=<optimized out>, sp=0x7ffcf3236e80)
[#5] 0x7fe6bfd75750 → _dlstart()

It is to note that the glibc’s ld implementation does actually change(mprotect) the permissions to writable and then relocates here

if (__mprotect (newp->start, newp->len, newp->prot|PROT_WRITE) < 0)
		errstring = N_("cannot make segment writable for relocation");
		_dl_signal_error (errno, l->l_name, NULL, errstring);

PS: I created an issue with resistor-color: example solution crashes · Issue #171 · exercism/x86-64-assembly · GitHub but it was automatically closed.