v / vlib / v2 / gen / x64 / elf_tiny.v
1221 lines · 1169 sloc · 41.81 KB · e7738c112c787d477501fa4a87edd0e1d72159bd
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module x64
6
7import encoding.binary
8import os
9
10const linux_tiny_base_vaddr = u64(0x400000)
11const linux_tiny_page_align = 0x1000
12const linux_sys_mmap = u32(9)
13const linux_sys_write = u32(1)
14const linux_sys_exit_group = u32(231)
15const linux_mmap_prot_read_write = u32(0x3)
16const linux_mmap_private_anonymous = u32(0x22)
17const linux_tiny_int_str_arena_bytes = u32(4096)
18const linux_tiny_int_str_slot_bytes = 32
19const linux_tiny_int_str_arena_metadata_bytes = 16
20const linux_tiny_rune_str_arena_bytes = u32(4096)
21const linux_tiny_rune_str_slot_bytes = 8
22const linux_tiny_rune_str_arena_metadata_bytes = 16
23const linux_tiny_string_plus_arena_bytes = u32(4096)
24const linux_tiny_string_plus_arena_metadata_bytes = 16
25
26pub const linux_tiny_not_eligible_prefix = 'Linux tiny executable is not eligible: '
27
28struct ElfTextRange {
29 name string
30 start u64
31 end u64
32}
33
34struct ElfDataRange {
35 section ObjectSection
36 start u64
37 end u64
38}
39
40struct ElfTinyRuntime {
41mut:
42 text []u8
43 symbols map[string]u64
44 int_str_arena_patches []int
45 rune_str_arena_patches []int
46 string_plus_arena_patches []int
47}
48
49struct ElfTinyReachable {
50 names []string
51 data []ElfDataRange
52 runtime_symbols map[string]bool
53}
54
55struct ElfTinyLinker {
56 elf &ElfObject
57}
58
59pub fn (mut g Gen) link_linux_tiny_executable(path string) ! {
60 if g.obj_format != .elf {
61 return error('Linux tiny executable linking requires ELF object output')
62 }
63 if g.abi != .sysv {
64 return error('Linux tiny executable linking requires SysV x64 code generation')
65 }
66 mut linker := ElfTinyLinker{
67 elf: g.elf
68 }
69 linker.write(path)!
70}
71
72fn (mut l ElfTinyLinker) write(path string) ! {
73 reachable := l.collect_reachable()!
74 if stdout := l.ultra_constant_stdout(reachable) {
75 l.write_ultra_executable(path, stdout)!
76 return
77 }
78 selected_names := reachable.names
79 selected_data := reachable.data
80 mut text := []u8{}
81 entry_call_field := elf_tiny_emit_start(mut text)
82 mut func_offsets := map[string]u64{}
83 for name in selected_names {
84 range := l.text_range(name)!
85 func_offsets[name] = u64(text.len)
86 text << l.elf.text_data[int(range.start)..int(range.end)]
87 }
88 mut runtime := l.build_runtime(reachable.runtime_symbols)
89 runtime_base := u64(text.len)
90 text << runtime.text
91
92 mut rodata := []u8{}
93 mut data := []u8{}
94 mut data_offsets := map[string]u64{}
95 l.copy_data_ranges(selected_data, .rodata, mut rodata, mut data_offsets)!
96 l.copy_data_ranges(selected_data, .data, mut data, mut data_offsets)!
97 int_str_runtime_needed := 'builtin__int__str' in reachable.runtime_symbols
98 || 'builtin__i64__str' in reachable.runtime_symbols
99 mut bss_bytes := 0
100 if int_str_runtime_needed {
101 bss_bytes += linux_tiny_int_str_arena_metadata_bytes
102 }
103 if 'builtin__rune__str' in reachable.runtime_symbols {
104 bss_bytes += linux_tiny_rune_str_arena_metadata_bytes
105 }
106 if 'builtin__string__+' in reachable.runtime_symbols {
107 bss_bytes += linux_tiny_string_plus_arena_metadata_bytes
108 }
109 if rodata.len > 0 {
110 for text.len % int(l.data_section_alignment(.rodata)) != 0 {
111 text << u8(0)
112 }
113 }
114
115 phnum := if data.len > 0 || bss_bytes > 0 { 2 } else { 1 }
116 text_off := elf_tiny_text_file_offset(phnum)
117 data_off := elf_tiny_data_file_offset(text_off, text.len, rodata.len)
118 text_vaddr := linux_tiny_base_vaddr + u64(text_off)
119 rodata_vaddr := text_vaddr + u64(text.len)
120 data_vaddr := linux_tiny_base_vaddr + u64(data_off)
121 bss_vaddr := if data.len > 0 { data_vaddr + u64(data.len) } else { data_vaddr }
122 if bss_bytes > 0 {
123 mut bss_offset := u64(0)
124 if int_str_runtime_needed {
125 int_str_arena_vaddr := bss_vaddr + bss_offset
126 for field_off in runtime.int_str_arena_patches {
127 elf_tiny_patch_rel32(mut text, int(runtime_base) + field_off, text_vaddr, 0,
128 int_str_arena_vaddr)
129 }
130 bss_offset += u64(linux_tiny_int_str_arena_metadata_bytes)
131 }
132 if 'builtin__rune__str' in reachable.runtime_symbols {
133 rune_str_arena_vaddr := bss_vaddr + bss_offset
134 for field_off in runtime.rune_str_arena_patches {
135 elf_tiny_patch_rel32(mut text, int(runtime_base) + field_off, text_vaddr, 0,
136 rune_str_arena_vaddr)
137 }
138 bss_offset += u64(linux_tiny_rune_str_arena_metadata_bytes)
139 }
140 if 'builtin__string__+' in reachable.runtime_symbols {
141 string_plus_arena_vaddr := bss_vaddr + bss_offset
142 for field_off in runtime.string_plus_arena_patches {
143 elf_tiny_patch_rel32(mut text, int(runtime_base) + field_off, text_vaddr, 0,
144 string_plus_arena_vaddr)
145 }
146 }
147 }
148 mut symbols := map[string]u64{}
149 for name, off in func_offsets {
150 symbols[name] = text_vaddr + off
151 }
152 for name, off in runtime.symbols {
153 symbols[name] = text_vaddr + runtime_base + off
154 }
155 for name, off in data_offsets {
156 symbols[name] = if name in l.rodata_symbol_names() {
157 rodata_vaddr + off
158 } else {
159 data_vaddr + off
160 }
161 }
162 main_vaddr := symbols['main'] or {
163 return error('Linux tiny executable requires a defined main symbol')
164 }
165 elf_tiny_patch_rel32(mut text, entry_call_field, text_vaddr, 0, main_vaddr)
166 l.apply_relocations(mut text, selected_names, func_offsets, symbols, text_vaddr)!
167 l.write_executable(path, text, rodata, data, bss_bytes, phnum, text_off, data_off)!
168}
169
170fn (l ElfTinyLinker) ultra_constant_stdout(reachable ElfTinyReachable) ?[]u8 {
171 expected_names := ['main', 'builtin__println', 'builtin___writeln_to_fd',
172 'builtin___write_buf_to_fd']
173 if !elf_tiny_string_arrays_equal(reachable.names, expected_names) {
174 return none
175 }
176 if reachable.data.len != 1 {
177 return none
178 }
179 if reachable.runtime_symbols.len != 2 || 'write' !in reachable.runtime_symbols
180 || 'fflush' !in reachable.runtime_symbols {
181 return none
182 }
183 data_range := reachable.data[0]
184 if data_range.section != .rodata {
185 return none
186 }
187 literal := 'Hello, World!'.bytes()
188 if data_range.end - data_range.start != u64(literal.len + 1) {
189 return none
190 }
191 for i, b in literal {
192 if l.elf.rodata[int(data_range.start) + i] != b {
193 return none
194 }
195 }
196 if l.elf.rodata[int(data_range.start) + literal.len] != 0 {
197 return none
198 }
199 if !l.ultra_hello_world_main_shape_matches(data_range) {
200 return none
201 }
202 mut stdout := literal.clone()
203 stdout << u8(`\n`)
204 return stdout
205}
206
207fn (l ElfTinyLinker) ultra_hello_world_main_shape_matches(data_range ElfDataRange) bool {
208 range := l.text_range('main') or { return false }
209 main_text := l.elf.text_data[int(range.start)..int(range.end)]
210 expected_main_text := [
211 u8(0xf3),
212 0x0f,
213 0x1e,
214 0xfa,
215 0x55,
216 0x48,
217 0x89,
218 0xe5,
219 0x53,
220 0x48,
221 0x83,
222 0xec,
223 0x38,
224 0x48,
225 0x8d,
226 0x05,
227 0x00,
228 0x00,
229 0x00,
230 0x00,
231 0x48,
232 0x89,
233 0x45,
234 0xe0,
235 0xb8,
236 0x0d,
237 0x00,
238 0x00,
239 0x00,
240 0x89,
241 0x45,
242 0xe8,
243 0xb8,
244 0x01,
245 0x00,
246 0x00,
247 0x00,
248 0x89,
249 0x45,
250 0xec,
251 0x48,
252 0x8d,
253 0x45,
254 0xe0,
255 0x49,
256 0x89,
257 0xc2,
258 0x49,
259 0x8b,
260 0x3a,
261 0x49,
262 0x8b,
263 0x72,
264 0x08,
265 0x31,
266 0xc0,
267 0xe8,
268 0x00,
269 0x00,
270 0x00,
271 0x00,
272 0x31,
273 0xc0,
274 0x48,
275 0x83,
276 0xc4,
277 0x38,
278 0x5b,
279 0x5d,
280 0xc3,
281 ]
282 if main_text.len != expected_main_text.len {
283 return false
284 }
285 for i, b in expected_main_text {
286 if main_text[i] != b {
287 return false
288 }
289 }
290 mut relocs := []ElfRela{}
291 for reloc in l.elf.text_relocs {
292 if reloc.offset >= range.start && reloc.offset < range.end {
293 relocs << reloc
294 }
295 }
296 for i := 1; i < relocs.len; i++ {
297 mut j := i
298 for j > 0 && relocs[j - 1].offset > relocs[j].offset {
299 relocs[j - 1], relocs[j] = relocs[j], relocs[j - 1]
300 j--
301 }
302 }
303 if relocs.len != 2 {
304 return false
305 }
306 rodata_reloc := relocs[0]
307 if rodata_reloc.offset - range.start != 16
308 || rodata_reloc.info & 0xffff_ffff != u64(r_x86_64_pc32) || rodata_reloc.addend != -4 {
309 return false
310 }
311 rodata_sym := l.reloc_symbol(rodata_reloc) or { return false }
312 if rodata_sym.shndx != u16(l.elf_section_index(.rodata)) || rodata_sym.value != data_range.start {
313 return false
314 }
315 println_reloc := relocs[1]
316 if println_reloc.offset - range.start != 57
317 || println_reloc.info & 0xffff_ffff != u64(r_x86_64_plt32) || println_reloc.addend != -4 {
318 return false
319 }
320 println_sym := l.reloc_symbol(println_reloc) or { return false }
321 return println_sym.name == 'builtin__println'
322 && println_sym.shndx == u16(l.elf_section_index(.text))
323}
324
325fn elf_tiny_string_arrays_equal(left []string, right []string) bool {
326 if left.len != right.len {
327 return false
328 }
329 for i, value in left {
330 if value != right[i] {
331 return false
332 }
333 }
334 return true
335}
336
337fn (mut l ElfTinyLinker) collect_reachable() !ElfTinyReachable {
338 if _ := l.text_range('main') {
339 } else {
340 return linux_tiny_not_eligible('missing main symbol')
341 }
342 mut selected := []string{}
343 mut seen := map[string]bool{}
344 mut queue := ['main']
345 mut selected_data := []ElfDataRange{}
346 mut runtime_symbols := map[string]bool{}
347 for queue.len > 0 {
348 name := queue[0]
349 queue.delete(0)
350 if seen[name] {
351 continue
352 }
353 if l.tiny_runtime_symbol_name(name) {
354 continue
355 }
356 if l.tiny_unsupported_defined_symbol_name(name) {
357 return linux_tiny_not_eligible('`${name}` is not covered by the tiny runtime yet')
358 }
359 range := l.text_range(name)!
360 seen[name] = true
361 selected << name
362 for reloc in l.elf.text_relocs {
363 if reloc.offset < range.start || reloc.offset >= range.end {
364 continue
365 }
366 sym := l.reloc_symbol(reloc)!
367 if l.tiny_runtime_symbol_name(sym.name) {
368 runtime_symbols[sym.name] = true
369 continue
370 }
371 if sym.shndx == u16(l.elf_section_index(.text)) {
372 if l.tiny_unsupported_defined_symbol_name(sym.name) {
373 return linux_tiny_not_eligible('`${sym.name}` is not covered by the tiny runtime yet')
374 }
375 if !seen[sym.name] {
376 queue << sym.name
377 }
378 } else if sym.shndx == u16(l.elf_section_index(.rodata)) {
379 l.add_data_range(mut selected_data, .rodata, sym.value)!
380 } else if sym.shndx == u16(l.elf_section_index(.data)) {
381 if x64_main_argc_global_name(sym.name) || x64_main_argv_global_name(sym.name) {
382 return linux_tiny_not_eligible('arguments() requires hosted argc/argv; Linux tiny _start argv is not implemented yet')
383 }
384 l.add_data_range(mut selected_data, .data, sym.value)!
385 } else if sym.shndx == 0 {
386 if !l.tiny_runtime_symbol_name(sym.name) {
387 return linux_tiny_not_eligible('external symbol `${sym.name}` referenced from `${name}` requires the hosted linker')
388 }
389 runtime_symbols[sym.name] = true
390 }
391 }
392 }
393 return ElfTinyReachable{
394 names: selected
395 data: selected_data
396 runtime_symbols: runtime_symbols
397 }
398}
399
400fn (l ElfTinyLinker) apply_relocations(mut text []u8, selected_names []string, func_offsets map[string]u64, symbols map[string]u64, text_vaddr u64) ! {
401 for name in selected_names {
402 range := l.text_range(name)!
403 new_base := func_offsets[name]
404 for reloc in l.elf.text_relocs {
405 if reloc.offset < range.start || reloc.offset >= range.end {
406 continue
407 }
408 reloc_type := reloc.info & 0xffff_ffff
409 if reloc_type !in [u64(r_x86_64_pc32), u64(r_x86_64_plt32)] {
410 return linux_tiny_not_eligible('ELF relocation type ${reloc_type} is not supported')
411 }
412 sym := l.reloc_symbol(reloc)!
413 target := symbols[sym.name] or {
414 return linux_tiny_not_eligible('symbol `${sym.name}` is not resolved by the tiny runtime')
415 }
416 field_off := int(new_base + reloc.offset - range.start)
417 field_vaddr := text_vaddr + u64(field_off)
418 disp := i64(target) + reloc.addend - i64(field_vaddr)
419 if disp < i64(-2147483648) || disp > i64(2147483647) {
420 return error('Linux tiny executable relocation for `${sym.name}` is out of range')
421 }
422 binary.little_endian_put_u32(mut text[field_off..field_off + 4], u32(i32(disp)))
423 }
424 }
425}
426
427fn (mut l ElfTinyLinker) build_runtime(runtime_symbols map[string]bool) ElfTinyRuntime {
428 mut rt := ElfTinyRuntime{
429 symbols: map[string]u64{}
430 }
431 if 'fflush' in runtime_symbols {
432 rt.symbols['fflush'] = u64(rt.text.len)
433 rt.text << [u8(0x31), 0xc0, 0xc3] // xor eax, eax; ret
434 }
435 if 'write' in runtime_symbols {
436 rt.symbols['write'] = u64(rt.text.len)
437 elf_tiny_emit_write(mut rt)
438 }
439 if 'exit' in runtime_symbols {
440 rt.symbols['exit'] = u64(rt.text.len)
441 rt.text << [u8(0xb8)]
442 write_u32_le(mut rt.text, linux_sys_exit_group)
443 rt.text << [u8(0x0f), 0x05, 0x0f, 0x0b] // syscall; ud2
444 }
445 if 'builtin__int__str' in runtime_symbols {
446 rt.symbols['builtin__int__str'] = u64(rt.text.len)
447 elf_tiny_emit_int_str(mut rt)
448 }
449 if 'builtin__i64__str' in runtime_symbols {
450 rt.symbols['builtin__i64__str'] = u64(rt.text.len)
451 elf_tiny_emit_i64_str(mut rt)
452 }
453 if 'builtin__rune__str' in runtime_symbols {
454 rt.symbols['builtin__rune__str'] = u64(rt.text.len)
455 elf_tiny_emit_rune_str(mut rt)
456 }
457 if 'builtin__string__+' in runtime_symbols {
458 rt.symbols['builtin__string__+'] = u64(rt.text.len)
459 elf_tiny_emit_string_plus(mut rt)
460 }
461 return rt
462}
463
464fn elf_tiny_emit_start(mut text []u8) int {
465 text << [u8(0x31), 0xed] // xor ebp, ebp
466 text << u8(0xe8)
467 call_field := text.len
468 text << [u8(0), 0, 0, 0]
469 text << [u8(0x89), 0xc7] // mov edi, eax
470 text << u8(0xb8)
471 write_u32_le(mut text, linux_sys_exit_group)
472 text << [u8(0x0f), 0x05, 0x0f, 0x0b] // syscall; ud2
473 return call_field
474}
475
476fn elf_tiny_emit_write(mut rt ElfTinyRuntime) {
477 rt.text << [u8(0x49), 0x89, 0xd0] // mov r8, rdx
478 rt.text << [u8(0x49), 0x89, 0xf1] // mov r9, rsi
479 rt.text << [u8(0x45), 0x31, 0xd2] // xor r10d, r10d
480 rt.text << [u8(0x48), 0x85, 0xd2] // test rdx, rdx
481 rt.text << [u8(0x7f), 0x03] // jg loop
482 rt.text << [u8(0x31), 0xc0] // xor eax, eax
483 rt.text << u8(0xc3) // ret
484 loop_start := rt.text.len
485 rt.text << u8(0xb8)
486 write_u32_le(mut rt.text, linux_sys_write)
487 rt.text << [u8(0x4c), 0x89, 0xce] // mov rsi, r9
488 rt.text << [u8(0x4c), 0x89, 0xc2] // mov rdx, r8
489 rt.text << [u8(0x0f), 0x05] // syscall
490 rt.text << [u8(0x48), 0x85, 0xc0] // test rax, rax
491 progress_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x7f) // jg progress
492 rt.text << [u8(0xbf), 0x01, 0, 0, 0] // mov edi, 1
493 rt.text << u8(0xb8)
494 write_u32_le(mut rt.text, linux_sys_exit_group)
495 rt.text << [u8(0x0f), 0x05, 0x0f, 0x0b] // syscall; ud2
496 progress_off := rt.text.len
497 rt.text << [u8(0x49), 0x01, 0xc1] // add r9, rax
498 rt.text << [u8(0x49), 0x29, 0xc0] // sub r8, rax
499 rt.text << [u8(0x49), 0x01, 0xc2] // add r10, rax
500 rt.text << [u8(0x4d), 0x85, 0xc0] // test r8, r8
501 loop_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x75) // jne loop
502 rt.text << [u8(0x4c), 0x89, 0xd0] // mov rax, r10
503 rt.text << u8(0xc3) // ret
504 elf_tiny_patch_rel8(mut rt.text, progress_field, progress_off)
505 elf_tiny_patch_rel8(mut rt.text, loop_field, loop_start)
506}
507
508fn elf_tiny_emit_ultra_stdout(mut text []u8, stdout_len int) int {
509 text << [u8(0x31), 0xed] // xor ebp, ebp
510 text << [u8(0x49), 0xc7, 0xc0]
511 write_u32_le(mut text, u32(stdout_len)) // mov r8, stdout_len
512 text << [u8(0x4c), 0x8d, 0x0d] // lea r9, [rip+stdout]
513 stdout_field := text.len
514 text << [u8(0), 0, 0, 0]
515 text << [u8(0x4d), 0x85, 0xc0] // test r8, r8
516 done_field := elf_tiny_emit_rel8_placeholder(mut text, 0x74) // je done
517 loop_start := text.len
518 text << u8(0xb8)
519 write_u32_le(mut text, linux_sys_write)
520 text << [u8(0xbf), 0x01, 0, 0, 0] // mov edi, 1
521 text << [u8(0x4c), 0x89, 0xce] // mov rsi, r9
522 text << [u8(0x4c), 0x89, 0xc2] // mov rdx, r8
523 text << [u8(0x0f), 0x05] // syscall
524 text << [u8(0x48), 0x85, 0xc0] // test rax, rax
525 fail_field := elf_tiny_emit_rel8_placeholder(mut text, 0x7e) // jle fail
526 text << [u8(0x49), 0x01, 0xc1] // add r9, rax
527 text << [u8(0x49), 0x29, 0xc0] // sub r8, rax
528 loop_field := elf_tiny_emit_rel8_placeholder(mut text, 0x75) // jne loop
529 done_off := text.len
530 text << [u8(0x31), 0xff] // xor edi, edi
531 text << u8(0xb8)
532 write_u32_le(mut text, linux_sys_exit_group)
533 text << [u8(0x0f), 0x05, 0x0f, 0x0b] // syscall; ud2
534 fail_off := text.len
535 text << [u8(0xbf), 0x01, 0, 0, 0] // mov edi, 1
536 text << u8(0xb8)
537 write_u32_le(mut text, linux_sys_exit_group)
538 text << [u8(0x0f), 0x05, 0x0f, 0x0b] // syscall; ud2
539 elf_tiny_patch_rel8(mut text, done_field, done_off)
540 elf_tiny_patch_rel8(mut text, fail_field, fail_off)
541 elf_tiny_patch_rel8(mut text, loop_field, loop_start)
542 return stdout_field
543}
544
545fn elf_tiny_emit_int_str(mut rt ElfTinyRuntime) {
546 elf_tiny_emit_signed_decimal_str(mut rt, false)
547}
548
549fn elf_tiny_emit_i64_str(mut rt ElfTinyRuntime) {
550 elf_tiny_emit_signed_decimal_str(mut rt, true)
551}
552
553fn elf_tiny_emit_signed_decimal_str(mut rt ElfTinyRuntime, is_i64 bool) {
554 rt.text << u8(0x57) // push rdi
555 rt.text << [u8(0x4c), 0x8d, 0x1d] // lea r11, [arena]
556 rt.int_str_arena_patches << rt.text.len
557 rt.text << [u8(0), 0, 0, 0]
558 rt.text << [u8(0x4d), 0x8b, 0x03] // mov r8, [r11]
559 rt.text << [u8(0x4d), 0x8b, 0x4b, 0x08] // mov r9, [r11+8]
560 rt.text << [u8(0x4d), 0x85, 0xc0] // test r8, r8
561 need_mmap_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x74) // je need_mmap
562 rt.text << [u8(0x49), 0x8d, 0x40, u8(linux_tiny_int_str_slot_bytes)] // lea rax, [r8+32]
563 rt.text << [u8(0x4c), 0x39, 0xc8] // cmp rax, r9
564 have_block_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x76) // jbe have_block
565 need_mmap_off := rt.text.len
566 rt.text << [u8(0x31), 0xff] // xor edi, edi
567 rt.text << u8(0xbe)
568 write_u32_le(mut rt.text, linux_tiny_int_str_arena_bytes)
569 rt.text << u8(0xba)
570 write_u32_le(mut rt.text, linux_mmap_prot_read_write)
571 rt.text << [u8(0x41), 0xba]
572 write_u32_le(mut rt.text, linux_mmap_private_anonymous)
573 rt.text << [u8(0x49), 0xc7, 0xc0]
574 write_u32_le(mut rt.text, u32(0xffff_ffff))
575 rt.text << [u8(0x45), 0x31, 0xc9] // xor r9d, r9d
576 rt.text << u8(0xb8)
577 write_u32_le(mut rt.text, linux_sys_mmap)
578 rt.text << [u8(0x0f), 0x05] // syscall
579 rt.text << [u8(0x48), 0x85, 0xc0] // test rax, rax
580 mmap_ok_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x79) // jns mmap_ok
581 rt.text << u8(0xbf)
582 write_u32_le(mut rt.text, 1)
583 rt.text << u8(0xb8)
584 write_u32_le(mut rt.text, linux_sys_exit_group)
585 rt.text << [u8(0x0f), 0x05, 0x0f, 0x0b] // syscall; ud2
586 mmap_ok_off := rt.text.len
587 rt.text << [u8(0x4c), 0x8d, 0x40, u8(linux_tiny_int_str_slot_bytes)] // lea r8, [rax+32]
588 rt.text << [u8(0x49), 0x89, 0xc1] // mov r9, rax
589 rt.text << [u8(0x49), 0x81, 0xc1]
590 write_u32_le(mut rt.text, linux_tiny_int_str_arena_bytes) // add r9, 4096
591 rt.text << [u8(0x4c), 0x8d, 0x1d] // lea r11, [arena]
592 rt.int_str_arena_patches << rt.text.len
593 rt.text << [u8(0), 0, 0, 0]
594 rt.text << [u8(0x4d), 0x89, 0x03] // mov [r11], r8
595 rt.text << [u8(0x4d), 0x89, 0x4b, 0x08] // mov [r11+8], r9
596 allocated_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0xeb) // jmp allocated
597 have_block_off := rt.text.len
598 rt.text << [u8(0x49), 0x89, 0x03] // mov [r11], rax
599 rt.text << [u8(0x49), 0x89, 0xc0] // mov r8, rax
600 allocated_off := rt.text.len
601 rt.text << u8(0x5f) // pop rdi
602 if is_i64 {
603 rt.text << [u8(0x48), 0x89, 0xf8] // mov rax, rdi
604 } else {
605 rt.text << [u8(0x89), 0xf8] // mov eax, edi
606 }
607 rt.text << [u8(0x45), 0x31, 0xc9] // xor r9d, r9d
608 rt.text << [u8(0x45), 0x31, 0xd2] // xor r10d, r10d
609 if is_i64 {
610 rt.text << [u8(0x48), 0x85, 0xc0] // test rax, rax
611 } else {
612 rt.text << [u8(0x85), 0xc0] // test eax, eax
613 }
614 non_negative_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x79) // jns non_negative
615 rt.text << [u8(0x41), 0xb2, 0x01] // mov r10b, 1
616 if is_i64 {
617 rt.text << [u8(0x48), 0xf7, 0xd8] // neg rax
618 } else {
619 rt.text << [u8(0xf7), 0xd8] // neg eax
620 }
621 non_negative_off := rt.text.len
622 if is_i64 {
623 rt.text << [u8(0x48), 0x85, 0xc0] // test rax, rax
624 } else {
625 rt.text << [u8(0x85), 0xc0] // test eax, eax
626 }
627 non_zero_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x75) // jne loop
628 rt.text << [u8(0x49), 0xff, 0xc8] // dec r8
629 rt.text << [u8(0x41), 0xc6, 0x00, 0x30] // mov byte ptr [r8], '0'
630 rt.text << [u8(0x41), 0xb9, 0x01, 0, 0, 0] // mov r9d, 1
631 maybe_sign_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0xeb) // jmp maybe_sign
632 loop_start := rt.text.len
633 rt.text << [u8(0x31), 0xd2] // xor edx, edx
634 rt.text << [u8(0xb9), 0x0a, 0, 0, 0] // mov ecx, 10
635 if is_i64 {
636 rt.text << [u8(0x48), 0xf7, 0xf1] // div rcx
637 } else {
638 rt.text << [u8(0xf7), 0xf1] // div ecx
639 }
640 rt.text << [u8(0x80), 0xc2, 0x30] // add dl, '0'
641 rt.text << [u8(0x49), 0xff, 0xc8] // dec r8
642 rt.text << [u8(0x41), 0x88, 0x10] // mov [r8], dl
643 rt.text << [u8(0x49), 0xff, 0xc1] // inc r9
644 if is_i64 {
645 rt.text << [u8(0x48), 0x85, 0xc0] // test rax, rax
646 } else {
647 rt.text << [u8(0x85), 0xc0] // test eax, eax
648 }
649 loop_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x75) // jne loop
650 maybe_sign_off := rt.text.len
651 rt.text << [u8(0x45), 0x84, 0xd2] // test r10b, r10b
652 done_field := elf_tiny_emit_rel8_placeholder(mut rt.text, 0x74) // je done
653 rt.text << [u8(0x49), 0xff, 0xc8] // dec r8
654 rt.text << [u8(0x41), 0xc6, 0x00, 0x2d] // mov byte ptr [r8], '-'
655 rt.text << [u8(0x49), 0xff, 0xc1] // inc r9
656 done_off := rt.text.len
657 rt.text << [u8(0x4c), 0x89, 0xc0] // mov rax, r8
658 rt.text << [u8(0x4c), 0x89, 0xca] // mov rdx, r9
659 rt.text << u8(0xc3)
660 elf_tiny_patch_rel8(mut rt.text, non_negative_field, non_negative_off)
661 elf_tiny_patch_rel8(mut rt.text, non_zero_field, loop_start)
662 elf_tiny_patch_rel8(mut rt.text, maybe_sign_field, maybe_sign_off)
663 elf_tiny_patch_rel8(mut rt.text, loop_field, loop_start)
664 elf_tiny_patch_rel8(mut rt.text, done_field, done_off)
665 elf_tiny_patch_rel8(mut rt.text, mmap_ok_field, mmap_ok_off)
666 elf_tiny_patch_rel8(mut rt.text, need_mmap_field, need_mmap_off)
667 elf_tiny_patch_rel8(mut rt.text, have_block_field, have_block_off)
668 elf_tiny_patch_rel8(mut rt.text, allocated_field, allocated_off)
669}
670
671fn elf_tiny_emit_rune_str(mut rt ElfTinyRuntime) {
672 rt.text << u8(0x57) // push rdi
673 rt.text << [u8(0x4c), 0x8d, 0x1d] // lea r11, [arena]
674 rt.rune_str_arena_patches << rt.text.len
675 rt.text << [u8(0), 0, 0, 0]
676 rt.text << [u8(0x4d), 0x8b, 0x03] // mov r8, [r11]
677 rt.text << [u8(0x4d), 0x8b, 0x4b, 0x08] // mov r9, [r11+8]
678 rt.text << [u8(0x4d), 0x85, 0xc0] // test r8, r8
679 need_mmap_field :=
680 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x84]) // je need_mmap
681 rt.text << [u8(0x49), 0x8d, 0x40, u8(linux_tiny_rune_str_slot_bytes)] // lea rax, [r8+8]
682 rt.text << [u8(0x4c), 0x39, 0xc8] // cmp rax, r9
683 have_block_field :=
684 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x86]) // jbe have_block
685 need_mmap_off := rt.text.len
686 rt.text << [u8(0x31), 0xff] // xor edi, edi
687 rt.text << u8(0xbe)
688 write_u32_le(mut rt.text, linux_tiny_rune_str_arena_bytes)
689 rt.text << u8(0xba)
690 write_u32_le(mut rt.text, linux_mmap_prot_read_write)
691 rt.text << [u8(0x41), 0xba]
692 write_u32_le(mut rt.text, linux_mmap_private_anonymous)
693 rt.text << [u8(0x49), 0xc7, 0xc0]
694 write_u32_le(mut rt.text, u32(0xffff_ffff))
695 rt.text << [u8(0x45), 0x31, 0xc9] // xor r9d, r9d
696 rt.text << u8(0xb8)
697 write_u32_le(mut rt.text, linux_sys_mmap)
698 rt.text << [u8(0x0f), 0x05] // syscall
699 rt.text << [u8(0x48), 0x85, 0xc0] // test rax, rax
700 mmap_ok_field := elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x89]) // jns mmap_ok
701 rt.text << u8(0xbf)
702 write_u32_le(mut rt.text, 1)
703 rt.text << u8(0xb8)
704 write_u32_le(mut rt.text, linux_sys_exit_group)
705 rt.text << [u8(0x0f), 0x05, 0x0f, 0x0b] // syscall; ud2
706 mmap_ok_off := rt.text.len
707 rt.text << [u8(0x49), 0x89, 0xc0] // mov r8, rax
708 rt.text << [u8(0x4c), 0x8d, 0x50, u8(linux_tiny_rune_str_slot_bytes)] // lea r10, [rax+8]
709 rt.text << [u8(0x4c), 0x8d, 0x88]
710 write_u32_le(mut rt.text, linux_tiny_rune_str_arena_bytes) // lea r9, [rax+4096]
711 rt.text << [u8(0x4c), 0x8d, 0x1d] // lea r11, [arena]
712 rt.rune_str_arena_patches << rt.text.len
713 rt.text << [u8(0), 0, 0, 0]
714 rt.text << [u8(0x4d), 0x89, 0x13] // mov [r11], r10
715 rt.text << [u8(0x4d), 0x89, 0x4b, 0x08] // mov [r11+8], r9
716 allocated_field := elf_tiny_emit_jmp32_placeholder(mut rt.text)
717 have_block_off := rt.text.len
718 rt.text << [u8(0x49), 0x89, 0x03] // mov [r11], rax
719 allocated_off := rt.text.len
720 rt.text << u8(0x5f) // pop rdi
721 rt.text << [u8(0x89), 0xf8] // mov eax, edi
722 rt.text << [u8(0x83), 0xf8, 0x7f] // cmp eax, 0x7f
723 two_byte_field := elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x87]) // ja two_byte
724 rt.text << [u8(0x41), 0x88, 0x00] // mov [r8], al
725 rt.text << [u8(0x41), 0xc6, 0x40, 0x01, 0x00] // mov byte ptr [r8+1], 0
726 rt.text << [u8(0xba)]
727 write_u32_le(mut rt.text, 1)
728 one_byte_done_field := elf_tiny_emit_jmp32_placeholder(mut rt.text)
729 two_byte_off := rt.text.len
730 rt.text << u8(0x3d)
731 write_u32_le(mut rt.text, 0x7ff) // cmp eax, 0x7ff
732 three_byte_field :=
733 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x87]) // ja three_byte
734 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
735 rt.text << [u8(0xc1), 0xe9, 0x06] // shr ecx, 6
736 rt.text << [u8(0x80), 0xc9, 0xc0] // or cl, 0xc0
737 rt.text << [u8(0x41), 0x88, 0x08] // mov [r8], cl
738 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
739 rt.text << [u8(0x80), 0xe1, 0x3f] // and cl, 0x3f
740 rt.text << [u8(0x80), 0xc9, 0x80] // or cl, 0x80
741 rt.text << [u8(0x41), 0x88, 0x48, 0x01] // mov [r8+1], cl
742 rt.text << [u8(0x41), 0xc6, 0x40, 0x02, 0x00] // mov byte ptr [r8+2], 0
743 rt.text << [u8(0xba)]
744 write_u32_le(mut rt.text, 2)
745 two_byte_done_field := elf_tiny_emit_jmp32_placeholder(mut rt.text)
746 three_byte_off := rt.text.len
747 rt.text << u8(0x3d)
748 write_u32_le(mut rt.text, 0xffff) // cmp eax, 0xffff
749 four_byte_field :=
750 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x87]) // ja four_byte
751 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
752 rt.text << [u8(0xc1), 0xe9, 0x0c] // shr ecx, 12
753 rt.text << [u8(0x80), 0xc9, 0xe0] // or cl, 0xe0
754 rt.text << [u8(0x41), 0x88, 0x08] // mov [r8], cl
755 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
756 rt.text << [u8(0xc1), 0xe9, 0x06] // shr ecx, 6
757 rt.text << [u8(0x80), 0xe1, 0x3f] // and cl, 0x3f
758 rt.text << [u8(0x80), 0xc9, 0x80] // or cl, 0x80
759 rt.text << [u8(0x41), 0x88, 0x48, 0x01] // mov [r8+1], cl
760 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
761 rt.text << [u8(0x80), 0xe1, 0x3f] // and cl, 0x3f
762 rt.text << [u8(0x80), 0xc9, 0x80] // or cl, 0x80
763 rt.text << [u8(0x41), 0x88, 0x48, 0x02] // mov [r8+2], cl
764 rt.text << [u8(0x41), 0xc6, 0x40, 0x03, 0x00] // mov byte ptr [r8+3], 0
765 rt.text << [u8(0xba)]
766 write_u32_le(mut rt.text, 3)
767 three_byte_done_field := elf_tiny_emit_jmp32_placeholder(mut rt.text)
768 four_byte_off := rt.text.len
769 rt.text << u8(0x3d)
770 write_u32_le(mut rt.text, 0x10ffff) // cmp eax, 0x10ffff
771 invalid_large_field :=
772 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x87]) // ja invalid
773 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
774 rt.text << [u8(0xc1), 0xe9, 0x12] // shr ecx, 18
775 rt.text << [u8(0x80), 0xc9, 0xf0] // or cl, 0xf0
776 rt.text << [u8(0x41), 0x88, 0x08] // mov [r8], cl
777 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
778 rt.text << [u8(0xc1), 0xe9, 0x0c] // shr ecx, 12
779 rt.text << [u8(0x80), 0xe1, 0x3f] // and cl, 0x3f
780 rt.text << [u8(0x80), 0xc9, 0x80] // or cl, 0x80
781 rt.text << [u8(0x41), 0x88, 0x48, 0x01] // mov [r8+1], cl
782 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
783 rt.text << [u8(0xc1), 0xe9, 0x06] // shr ecx, 6
784 rt.text << [u8(0x80), 0xe1, 0x3f] // and cl, 0x3f
785 rt.text << [u8(0x80), 0xc9, 0x80] // or cl, 0x80
786 rt.text << [u8(0x41), 0x88, 0x48, 0x02] // mov [r8+2], cl
787 rt.text << [u8(0x89), 0xc1] // mov ecx, eax
788 rt.text << [u8(0x80), 0xe1, 0x3f] // and cl, 0x3f
789 rt.text << [u8(0x80), 0xc9, 0x80] // or cl, 0x80
790 rt.text << [u8(0x41), 0x88, 0x48, 0x03] // mov [r8+3], cl
791 rt.text << [u8(0x41), 0xc6, 0x40, 0x04, 0x00] // mov byte ptr [r8+4], 0
792 rt.text << [u8(0xba)]
793 write_u32_le(mut rt.text, 4)
794 four_byte_done_field := elf_tiny_emit_jmp32_placeholder(mut rt.text)
795 invalid_off := rt.text.len
796 rt.text << [u8(0x41), 0xc6, 0x00, 0x00] // mov byte ptr [r8], 0
797 rt.text << [u8(0x31), 0xd2] // xor edx, edx
798 done_off := rt.text.len
799 rt.text << [u8(0x4c), 0x89, 0xc0] // mov rax, r8
800 rt.text << u8(0xc3)
801 elf_tiny_patch_rel32_local(mut rt.text, need_mmap_field, need_mmap_off)
802 elf_tiny_patch_rel32_local(mut rt.text, have_block_field, have_block_off)
803 elf_tiny_patch_rel32_local(mut rt.text, mmap_ok_field, mmap_ok_off)
804 elf_tiny_patch_rel32_local(mut rt.text, allocated_field, allocated_off)
805 elf_tiny_patch_rel32_local(mut rt.text, two_byte_field, two_byte_off)
806 elf_tiny_patch_rel32_local(mut rt.text, one_byte_done_field, done_off)
807 elf_tiny_patch_rel32_local(mut rt.text, three_byte_field, three_byte_off)
808 elf_tiny_patch_rel32_local(mut rt.text, two_byte_done_field, done_off)
809 elf_tiny_patch_rel32_local(mut rt.text, four_byte_field, four_byte_off)
810 elf_tiny_patch_rel32_local(mut rt.text, three_byte_done_field, done_off)
811 elf_tiny_patch_rel32_local(mut rt.text, invalid_large_field, invalid_off)
812 elf_tiny_patch_rel32_local(mut rt.text, four_byte_done_field, done_off)
813}
814
815fn elf_tiny_emit_string_plus(mut rt ElfTinyRuntime) {
816 // SysV ABI: first string in rdi/rsi, second string in rdx/rcx, return in rax/rdx.
817 rt.text << [u8(0x57), 0x56, 0x52, 0x51] // push rdi; push rsi; push rdx; push rcx
818 rt.text << [u8(0x4c), 0x8d, 0x1d] // lea r11, [arena]
819 rt.string_plus_arena_patches << rt.text.len
820 rt.text << [u8(0), 0, 0, 0]
821 rt.text << [u8(0x4d), 0x8b, 0x13] // mov r10, [r11]
822 rt.text << [u8(0x49), 0x8b, 0x43, 0x08] // mov rax, [r11+8]
823 rt.text << [u8(0x4d), 0x85, 0xd2] // test r10, r10
824 need_mmap_field :=
825 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x84]) // je need_mmap
826 rt.text << [u8(0x44), 0x8b, 0x44, 0x24, 0x10] // mov r8d, [rsp+16]
827 rt.text << [u8(0x44), 0x03, 0x04, 0x24] // add r8d, [rsp]
828 rt.text << [u8(0x4d), 0x89, 0xc1] // mov r9, r8
829 rt.text << [u8(0x49), 0xff, 0xc1] // inc r9
830 rt.text << [u8(0x4c), 0x89, 0xd2] // mov rdx, r10
831 rt.text << [u8(0x4c), 0x01, 0xca] // add rdx, r9
832 rt.text << [u8(0x48), 0x39, 0xc2] // cmp rdx, rax
833 have_block_field :=
834 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x86]) // jbe have_block
835 need_mmap_off := rt.text.len
836 rt.text << [u8(0x44), 0x8b, 0x44, 0x24, 0x10] // mov r8d, [rsp+16]
837 rt.text << [u8(0x44), 0x03, 0x04, 0x24] // add r8d, [rsp]
838 rt.text << [u8(0x4d), 0x89, 0xc1] // mov r9, r8
839 rt.text << [u8(0x49), 0xff, 0xc1] // inc r9
840 rt.text << [u8(0x31), 0xff] // xor edi, edi
841 rt.text << [u8(0x4c), 0x89, 0xce] // mov rsi, r9
842 rt.text << [u8(0x48), 0x81, 0xfe]
843 write_u32_le(mut rt.text, linux_tiny_string_plus_arena_bytes) // cmp rsi, 4096
844 large_alloc_field :=
845 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x83]) // jae large_alloc
846 rt.text << u8(0xbe)
847 write_u32_le(mut rt.text, linux_tiny_string_plus_arena_bytes) // mov esi, 4096
848 large_alloc_off := rt.text.len
849 rt.text << u8(0xba)
850 write_u32_le(mut rt.text, linux_mmap_prot_read_write)
851 rt.text << [u8(0x41), 0xba]
852 write_u32_le(mut rt.text, linux_mmap_private_anonymous)
853 rt.text << [u8(0x49), 0xc7, 0xc0]
854 write_u32_le(mut rt.text, u32(0xffff_ffff))
855 rt.text << [u8(0x45), 0x31, 0xc9] // xor r9d, r9d
856 rt.text << u8(0xb8)
857 write_u32_le(mut rt.text, linux_sys_mmap)
858 rt.text << [u8(0x0f), 0x05] // syscall
859 rt.text << [u8(0x48), 0x85, 0xc0] // test rax, rax
860 mmap_ok_field := elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x89]) // jns mmap_ok
861 rt.text << u8(0xbf)
862 write_u32_le(mut rt.text, 1)
863 rt.text << u8(0xb8)
864 write_u32_le(mut rt.text, linux_sys_exit_group)
865 rt.text << [u8(0x0f), 0x05, 0x0f, 0x0b] // syscall; ud2
866 mmap_ok_off := rt.text.len
867 rt.text << [u8(0x49), 0x89, 0xc2] // mov r10, rax
868 rt.text << [u8(0x44), 0x8b, 0x44, 0x24, 0x10] // mov r8d, [rsp+16]
869 rt.text << [u8(0x44), 0x03, 0x04, 0x24] // add r8d, [rsp]
870 rt.text << [u8(0x4d), 0x89, 0xc1] // mov r9, r8
871 rt.text << [u8(0x49), 0xff, 0xc1] // inc r9
872 rt.text << [u8(0x48), 0x89, 0xc2] // mov rdx, rax
873 rt.text << [u8(0x4c), 0x01, 0xca] // add rdx, r9
874 rt.text << [u8(0x4d), 0x89, 0xcb] // mov r11, r9
875 rt.text << [u8(0x49), 0x81, 0xfb]
876 write_u32_le(mut rt.text, linux_tiny_string_plus_arena_bytes) // cmp r11, 4096
877 large_end_field :=
878 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x83]) // jae large_end
879 rt.text << [u8(0x41), 0xbb]
880 write_u32_le(mut rt.text, linux_tiny_string_plus_arena_bytes) // mov r11d, 4096
881 large_end_off := rt.text.len
882 rt.text << [u8(0x4c), 0x89, 0xd0] // mov rax, r10
883 rt.text << [u8(0x4c), 0x01, 0xd8] // add rax, r11
884 rt.text << [u8(0x4c), 0x8d, 0x1d] // lea r11, [arena]
885 rt.string_plus_arena_patches << rt.text.len
886 rt.text << [u8(0), 0, 0, 0]
887 rt.text << [u8(0x49), 0x89, 0x13] // mov [r11], rdx
888 rt.text << [u8(0x49), 0x89, 0x43, 0x08] // mov [r11+8], rax
889 allocated_field := elf_tiny_emit_jmp32_placeholder(mut rt.text)
890 have_block_off := rt.text.len
891 rt.text << [u8(0x4c), 0x8d, 0x1d] // lea r11, [arena]
892 rt.string_plus_arena_patches << rt.text.len
893 rt.text << [u8(0), 0, 0, 0]
894 rt.text << [u8(0x49), 0x89, 0x13] // mov [r11], rdx
895 allocated_off := rt.text.len
896 rt.text << [u8(0x4c), 0x89, 0xd0] // mov rax, r10
897 rt.text << [u8(0x4d), 0x89, 0xd0] // mov r8, r10
898 rt.text << [u8(0x48), 0x8b, 0x74, 0x24, 0x18] // mov rsi, [rsp+24]
899 rt.text << [u8(0x8b), 0x4c, 0x24, 0x10] // mov ecx, [rsp+16]
900 rt.text << [u8(0x48), 0x85, 0xc9] // test rcx, rcx
901 copy_a_done_field :=
902 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x84]) // je copy_a_done
903 copy_a_loop := rt.text.len
904 rt.text << [u8(0x8a), 0x16] // mov dl, [rsi]
905 rt.text << [u8(0x41), 0x88, 0x10] // mov [r8], dl
906 rt.text << [u8(0x48), 0xff, 0xc6] // inc rsi
907 rt.text << [u8(0x49), 0xff, 0xc0] // inc r8
908 rt.text << [u8(0x48), 0xff, 0xc9] // dec rcx
909 copy_a_loop_field :=
910 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x85]) // jne copy_a_loop
911 copy_a_done_off := rt.text.len
912 rt.text << [u8(0x48), 0x8b, 0x74, 0x24, 0x08] // mov rsi, [rsp+8]
913 rt.text << [u8(0x8b), 0x0c, 0x24] // mov ecx, [rsp]
914 rt.text << [u8(0x48), 0x85, 0xc9] // test rcx, rcx
915 copy_b_done_field :=
916 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x84]) // je copy_b_done
917 copy_b_loop := rt.text.len
918 rt.text << [u8(0x8a), 0x16] // mov dl, [rsi]
919 rt.text << [u8(0x41), 0x88, 0x10] // mov [r8], dl
920 rt.text << [u8(0x48), 0xff, 0xc6] // inc rsi
921 rt.text << [u8(0x49), 0xff, 0xc0] // inc r8
922 rt.text << [u8(0x48), 0xff, 0xc9] // dec rcx
923 copy_b_loop_field :=
924 elf_tiny_emit_rel32_placeholder(mut rt.text, [u8(0x0f), 0x85]) // jne copy_b_loop
925 copy_b_done_off := rt.text.len
926 rt.text << [u8(0x41), 0xc6, 0x00, 0x00] // mov byte ptr [r8], 0
927 rt.text << [u8(0x8b), 0x54, 0x24, 0x10] // mov edx, [rsp+16]
928 rt.text << [u8(0x03), 0x14, 0x24] // add edx, [rsp]
929 rt.text << [u8(0x48), 0x83, 0xc4, 0x20] // add rsp, 32
930 rt.text << u8(0xc3)
931 elf_tiny_patch_rel32_local(mut rt.text, need_mmap_field, need_mmap_off)
932 elf_tiny_patch_rel32_local(mut rt.text, have_block_field, have_block_off)
933 elf_tiny_patch_rel32_local(mut rt.text, large_alloc_field, large_alloc_off)
934 elf_tiny_patch_rel32_local(mut rt.text, mmap_ok_field, mmap_ok_off)
935 elf_tiny_patch_rel32_local(mut rt.text, large_end_field, large_end_off)
936 elf_tiny_patch_rel32_local(mut rt.text, allocated_field, allocated_off)
937 elf_tiny_patch_rel32_local(mut rt.text, copy_a_done_field, copy_a_done_off)
938 elf_tiny_patch_rel32_local(mut rt.text, copy_a_loop_field, copy_a_loop)
939 elf_tiny_patch_rel32_local(mut rt.text, copy_b_done_field, copy_b_done_off)
940 elf_tiny_patch_rel32_local(mut rt.text, copy_b_loop_field, copy_b_loop)
941}
942
943fn (mut l ElfTinyLinker) write_executable(path string, text []u8, rodata []u8, data []u8, bss_bytes int, phnum int, text_off int, data_off int) ! {
944 rodata_off := text_off + text.len
945 text_filesz := rodata_off + rodata.len
946 mut buf := []u8{}
947 buf << [u8(ei_mag0), ei_mag1, ei_mag2, ei_mag3]
948 buf << [u8(elfclass64), elfdata2lsb, ev_current, 0]
949 for _ in 0 .. 8 {
950 buf << u8(0)
951 }
952 write_u16_le(mut buf, et_exec)
953 write_u16_le(mut buf, em_x86_64)
954 write_u32_le(mut buf, ev_current)
955 write_u64_le(mut buf, linux_tiny_base_vaddr + u64(text_off))
956 write_u64_le(mut buf, 64)
957 write_u64_le(mut buf, 0)
958 write_u32_le(mut buf, 0)
959 write_u16_le(mut buf, 64)
960 write_u16_le(mut buf, 56)
961 write_u16_le(mut buf, u16(phnum))
962 write_u16_le(mut buf, 0)
963 write_u16_le(mut buf, 0)
964 write_u16_le(mut buf, 0)
965 elf_tiny_write_phdr(mut buf, pt_load, pf_r | pf_x, 0, linux_tiny_base_vaddr, u64(text_filesz),
966 u64(text_filesz), linux_tiny_page_align)
967 if data.len > 0 || bss_bytes > 0 {
968 rw_filesz := u64(data.len)
969 rw_memsz := u64(data.len + bss_bytes)
970 rw_offset := if data.len > 0 { u64(data_off) } else { u64(0) }
971 elf_tiny_write_phdr(mut buf, pt_load, pf_r | pf_w, rw_offset, linux_tiny_base_vaddr +
972 u64(data_off), rw_filesz, rw_memsz, linux_tiny_page_align)
973 }
974 for buf.len < text_off {
975 buf << u8(0)
976 }
977 buf << text
978 buf << rodata
979 if data.len > 0 {
980 for buf.len < data_off {
981 buf << u8(0)
982 }
983 buf << data
984 }
985 os.write_file_array(path, buf)!
986 os.chmod(path, 0o755)!
987}
988
989fn (mut l ElfTinyLinker) write_ultra_executable(path string, stdout []u8) ! {
990 phnum := 1
991 text_off := elf_tiny_text_file_offset(phnum)
992 text_vaddr := linux_tiny_base_vaddr + u64(text_off)
993 mut text := []u8{}
994 stdout_field := elf_tiny_emit_ultra_stdout(mut text, stdout.len)
995 stdout_vaddr := text_vaddr + u64(text.len)
996 elf_tiny_patch_rel32(mut text, stdout_field, text_vaddr, 0, stdout_vaddr)
997 l.write_executable(path, text, stdout, []u8{}, 0, phnum, text_off, 0)!
998}
999
1000fn elf_tiny_emit_rel8_placeholder(mut text []u8, opcode u8) int {
1001 text << opcode
1002 field_off := text.len
1003 text << u8(0)
1004 return field_off
1005}
1006
1007fn elf_tiny_emit_rel32_placeholder(mut text []u8, opcode []u8) int {
1008 text << opcode
1009 field_off := text.len
1010 text << [u8(0), 0, 0, 0]
1011 return field_off
1012}
1013
1014fn elf_tiny_emit_jmp32_placeholder(mut text []u8) int {
1015 text << u8(0xe9)
1016 field_off := text.len
1017 text << [u8(0), 0, 0, 0]
1018 return field_off
1019}
1020
1021fn elf_tiny_patch_rel8(mut text []u8, field_off int, target_off int) {
1022 disp := target_off - (field_off + 1)
1023 if disp < -128 || disp > 127 {
1024 panic('Linux tiny executable short branch is out of range')
1025 }
1026 text[field_off] = if disp < 0 { u8(256 + disp) } else { u8(disp) }
1027}
1028
1029fn elf_tiny_patch_rel32_local(mut text []u8, field_off int, target_off int) {
1030 disp := target_off - (field_off + 4)
1031 if disp < -2147483648 || disp > 2147483647 {
1032 panic('Linux tiny executable near branch is out of range')
1033 }
1034 binary.little_endian_put_u32(mut text[field_off..field_off + 4], u32(i32(disp)))
1035}
1036
1037fn elf_tiny_write_phdr(mut b []u8, type_ u32, flags u32, off u64, vaddr u64, filesz u64, memsz u64, align u64) {
1038 write_u32_le(mut b, type_)
1039 write_u32_le(mut b, flags)
1040 write_u64_le(mut b, off)
1041 write_u64_le(mut b, vaddr)
1042 write_u64_le(mut b, vaddr)
1043 write_u64_le(mut b, filesz)
1044 write_u64_le(mut b, memsz)
1045 write_u64_le(mut b, align)
1046}
1047
1048fn (l ElfTinyLinker) text_range(name string) !ElfTextRange {
1049 ranges := l.text_symbol_ranges()
1050 for range in ranges {
1051 if range.name == name {
1052 return range
1053 }
1054 }
1055 return error('missing text symbol `${name}`')
1056}
1057
1058fn (l ElfTinyLinker) text_symbol_ranges() []ElfTextRange {
1059 mut syms := []ElfSymbol{}
1060 for sym in l.elf.symbols {
1061 if sym.name != '' && sym.shndx == u16(l.elf_section_index(.text)) {
1062 syms << sym
1063 }
1064 }
1065 for i := 1; i < syms.len; i++ {
1066 mut j := i
1067 for j > 0 && syms[j - 1].value > syms[j].value {
1068 syms[j - 1], syms[j] = syms[j], syms[j - 1]
1069 j--
1070 }
1071 }
1072 mut ranges := []ElfTextRange{}
1073 for i, sym in syms {
1074 mut end := u64(l.elf.text_data.len)
1075 for j := i + 1; j < syms.len; j++ {
1076 if syms[j].value > sym.value {
1077 end = syms[j].value
1078 break
1079 }
1080 }
1081 if end > sym.value {
1082 ranges << ElfTextRange{
1083 name: sym.name
1084 start: sym.value
1085 end: end
1086 }
1087 }
1088 }
1089 return ranges
1090}
1091
1092fn (l ElfTinyLinker) reloc_symbol(reloc ElfRela) !ElfSymbol {
1093 sym_idx := int(reloc.info >> 32)
1094 if sym_idx < 0 || sym_idx >= l.elf.symbols.len {
1095 return error('Linux tiny executable relocation references invalid symbol index ${sym_idx}')
1096 }
1097 return l.elf.symbols[sym_idx]
1098}
1099
1100fn (l ElfTinyLinker) elf_section_index(section ObjectSection) int {
1101 return match section {
1102 .text { 1 }
1103 .data { 2 }
1104 .rodata { 3 }
1105 }
1106}
1107
1108fn (l ElfTinyLinker) tiny_runtime_symbol_name(name string) bool {
1109 return name in ['write', 'exit', 'fflush', 'builtin__int__str', 'builtin__i64__str',
1110 'builtin__rune__str', 'builtin__string__+']
1111}
1112
1113fn (l ElfTinyLinker) tiny_unsupported_defined_symbol_name(name string) bool {
1114 return name in ['builtin__print', 'builtin__int__str_l']
1115}
1116
1117fn (mut l ElfTinyLinker) add_data_range(mut ranges []ElfDataRange, section ObjectSection, start u64) ! {
1118 end := l.data_symbol_range_end(section, start)!
1119 for range in ranges {
1120 if range.section == section && range.start == start && range.end == end {
1121 return
1122 }
1123 }
1124 ranges << ElfDataRange{
1125 section: section
1126 start: start
1127 end: end
1128 }
1129}
1130
1131fn (l ElfTinyLinker) data_symbol_range_end(section ObjectSection, start u64) !u64 {
1132 section_idx := u16(l.elf_section_index(section))
1133 section_len := match section {
1134 .rodata { u64(l.elf.rodata.len) }
1135 .data { u64(l.elf.data_data.len) }
1136 else { u64(0) }
1137 }
1138
1139 mut end := section_len
1140 for sym in l.elf.symbols {
1141 if sym.shndx == section_idx && sym.value > start && sym.value < end {
1142 end = sym.value
1143 }
1144 }
1145 if end < start {
1146 return error('invalid data symbol range')
1147 }
1148 return end
1149}
1150
1151fn (l ElfTinyLinker) rodata_symbol_names() map[string]bool {
1152 mut out := map[string]bool{}
1153 for sym in l.elf.symbols {
1154 if sym.shndx == u16(l.elf_section_index(.rodata)) {
1155 out[sym.name] = true
1156 }
1157 }
1158 return out
1159}
1160
1161fn (l ElfTinyLinker) copy_data_ranges(ranges []ElfDataRange, section ObjectSection, mut out []u8, mut offsets map[string]u64) ! {
1162 for dr in ranges {
1163 if dr.section != section {
1164 continue
1165 }
1166 align := int(l.data_section_alignment(section))
1167 for align > 1 && out.len % align != int(dr.start % u64(align)) {
1168 out << u8(0)
1169 }
1170 off := u64(out.len)
1171 for sym in l.elf.symbols {
1172 if sym.shndx == u16(l.elf_section_index(section)) && sym.value == dr.start {
1173 offsets[sym.name] = off
1174 }
1175 }
1176 match section {
1177 .rodata {
1178 out << l.elf.rodata[int(dr.start)..int(dr.end)]
1179 }
1180 .data {
1181 out << l.elf.data_data[int(dr.start)..int(dr.end)]
1182 }
1183 else {
1184 return error('Linux tiny executable cannot copy ${section} as data')
1185 }
1186 }
1187 }
1188}
1189
1190fn (l ElfTinyLinker) data_section_alignment(section ObjectSection) u64 {
1191 return match section {
1192 .data { u64(8) }
1193 .rodata { u64(4) }
1194 else { u64(1) }
1195 }
1196}
1197
1198fn elf_tiny_text_file_offset(phnum int) int {
1199 return 64 + phnum * 56
1200}
1201
1202fn elf_tiny_data_file_offset(text_off int, text_len int, rodata_len int) int {
1203 return elf_tiny_align(text_off + text_len + rodata_len, linux_tiny_page_align)
1204}
1205
1206fn elf_tiny_align(v int, align int) int {
1207 if align <= 1 || v % align == 0 {
1208 return v
1209 }
1210 return v + (align - (v % align))
1211}
1212
1213fn elf_tiny_patch_rel32(mut text []u8, field_off int, source_base_vaddr u64, source_off u64, target_vaddr u64) {
1214 field_vaddr := source_base_vaddr + source_off + u64(field_off)
1215 disp := i64(target_vaddr) - i64(field_vaddr + 4)
1216 binary.little_endian_put_u32(mut text[field_off..field_off + 4], u32(i32(disp)))
1217}
1218
1219fn linux_tiny_not_eligible(message string) IError {
1220 return error(linux_tiny_not_eligible_prefix + message)
1221}
1222