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()
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤
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");
call_error:
_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.