| 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 | |
| 5 | module x64 |
| 6 | |
| 7 | import encoding.binary |
| 8 | import os |
| 9 | |
| 10 | const linux_tiny_base_vaddr = u64(0x400000) |
| 11 | const linux_tiny_page_align = 0x1000 |
| 12 | const linux_sys_mmap = u32(9) |
| 13 | const linux_sys_write = u32(1) |
| 14 | const linux_sys_exit_group = u32(231) |
| 15 | const linux_mmap_prot_read_write = u32(0x3) |
| 16 | const linux_mmap_private_anonymous = u32(0x22) |
| 17 | const linux_tiny_int_str_arena_bytes = u32(4096) |
| 18 | const linux_tiny_int_str_slot_bytes = 32 |
| 19 | const linux_tiny_int_str_arena_metadata_bytes = 16 |
| 20 | const linux_tiny_rune_str_arena_bytes = u32(4096) |
| 21 | const linux_tiny_rune_str_slot_bytes = 8 |
| 22 | const linux_tiny_rune_str_arena_metadata_bytes = 16 |
| 23 | const linux_tiny_string_plus_arena_bytes = u32(4096) |
| 24 | const linux_tiny_string_plus_arena_metadata_bytes = 16 |
| 25 | |
| 26 | pub const linux_tiny_not_eligible_prefix = 'Linux tiny executable is not eligible: ' |
| 27 | |
| 28 | struct ElfTextRange { |
| 29 | name string |
| 30 | start u64 |
| 31 | end u64 |
| 32 | } |
| 33 | |
| 34 | struct ElfDataRange { |
| 35 | section ObjectSection |
| 36 | start u64 |
| 37 | end u64 |
| 38 | } |
| 39 | |
| 40 | struct ElfTinyRuntime { |
| 41 | mut: |
| 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 | |
| 49 | struct ElfTinyReachable { |
| 50 | names []string |
| 51 | data []ElfDataRange |
| 52 | runtime_symbols map[string]bool |
| 53 | } |
| 54 | |
| 55 | struct ElfTinyLinker { |
| 56 | elf &ElfObject |
| 57 | } |
| 58 | |
| 59 | pub 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 | |
| 72 | fn (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 | |
| 170 | fn (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 | |
| 207 | fn (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 | |
| 325 | fn 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 | |
| 337 | fn (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 | |
| 400 | fn (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 | |
| 427 | fn (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 | |
| 464 | fn 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 | |
| 476 | fn 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 | |
| 508 | fn 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 | |
| 545 | fn elf_tiny_emit_int_str(mut rt ElfTinyRuntime) { |
| 546 | elf_tiny_emit_signed_decimal_str(mut rt, false) |
| 547 | } |
| 548 | |
| 549 | fn elf_tiny_emit_i64_str(mut rt ElfTinyRuntime) { |
| 550 | elf_tiny_emit_signed_decimal_str(mut rt, true) |
| 551 | } |
| 552 | |
| 553 | fn 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 | |
| 671 | fn 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 | |
| 815 | fn 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 | |
| 943 | fn (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 | |
| 989 | fn (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 | |
| 1000 | fn 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 | |
| 1007 | fn 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 | |
| 1014 | fn 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 | |
| 1021 | fn 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 | |
| 1029 | fn 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 | |
| 1037 | fn 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 | |
| 1048 | fn (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 | |
| 1058 | fn (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 | |
| 1092 | fn (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 | |
| 1100 | fn (l ElfTinyLinker) elf_section_index(section ObjectSection) int { |
| 1101 | return match section { |
| 1102 | .text { 1 } |
| 1103 | .data { 2 } |
| 1104 | .rodata { 3 } |
| 1105 | } |
| 1106 | } |
| 1107 | |
| 1108 | fn (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 | |
| 1113 | fn (l ElfTinyLinker) tiny_unsupported_defined_symbol_name(name string) bool { |
| 1114 | return name in ['builtin__print', 'builtin__int__str_l'] |
| 1115 | } |
| 1116 | |
| 1117 | fn (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 | |
| 1131 | fn (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 | |
| 1151 | fn (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 | |
| 1161 | fn (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 | |
| 1190 | fn (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 | |
| 1198 | fn elf_tiny_text_file_offset(phnum int) int { |
| 1199 | return 64 + phnum * 56 |
| 1200 | } |
| 1201 | |
| 1202 | fn 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 | |
| 1206 | fn 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 | |
| 1213 | fn 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 | |
| 1219 | fn linux_tiny_not_eligible(message string) IError { |
| 1220 | return error(linux_tiny_not_eligible_prefix + message) |
| 1221 | } |
| 1222 | |