///|
// Route B object emission: turn the binary emitter's output into a Mach-O
// object with real relocations (BRANCH26 for calls, PAGE21/PAGEOFF12 for
// global addresses, UNSIGNED for data pointers). This is what lets --emit obj
// stop depending on clang.

///|
fn arm64_obj_bytes(a : Array[Int]) -> Bytes {
  let out : Array[Byte] = []
  for b in a {
    out.push(b.to_byte())
  }
  Bytes::from_array(out[:])
}

///|
// Every relocation target must have a symbol table entry; defined targets point
// at their section, undefined ones (libc calls) are N_UNDF/N_EXT.
fn arm64_obj_sym(
  syms : Array[@object.MachoSym],
  index : Map[String, Int],
  name : String,
  sect : Int,
  value : Int,
  is_ext : Bool,
) -> Int {
  match index.get(name) {
    Some(i) => i
    None => {
      let i = syms.length()
      index[name] = i
      syms.push(@object.MachoSym::{ name: "_" + name, sect, value, is_ext, })
      i
    }
  }
}

///|
pub fn emit_arm64_object(
  funcs : Array[@types.Fn],
  datas : Array[@types.Dat],
  interner : @util.Interner,
  apple : Bool,
) -> Bytes raise {
  arm64_emit_reset()
  let text_words : Array[Int] = []
  let fns : Array[(String, Int)] = []
  let calls : Array[(Int, String)] = []
  let addrs : Array[(Int, Int, Int, String, Int)] = []
  for fn_ in funcs {
    let (w, c, a) = emit_arm64_bin_fn_ex(fn_, interner, apple)
    let off = text_words.length()
    fns.push((fn_.name, off))
    for x in w {
      text_words.push(x)
    }
    for x in c {
      calls.push((off + x.0, x.1))
    }
    for x in a {
      addrs.push((off + x.0, off + x.1, x.2, x.3, x.4))
    }
  }
  let (datas_map, data_bytes0, data_refs) = arm64_bin_layout_data(datas)
  // Pointer entries are relocatable in an object file: zero the addend and let
  // the linker fill in the address via an UNSIGNED relocation.
  let data_bytes = data_bytes0.copy()

  let syms : Array[@object.MachoSym] = []
  let index : Map[String, Int] = Map([])
  for f in fns {
    let mut is_ext = false
    for fn_ in funcs {
      if fn_.name == f.0 && fn_.is_export {
        is_ext = true
      }
    }
    ignore(arm64_obj_sym(syms, index, f.0, 1, f.1 * 4, is_ext))
  }
  for d in datas_map {
    let mut is_ext = false
    for dt in datas {
      if dt.is_export && dt.str == d.0 {
        is_ext = true
      }
    }
    ignore(arm64_obj_sym(syms, index, d.0, 2, d.1, is_ext))
  }
  // Floating-point constants live in __TEXT,__const as local LfpN symbols.
  let (fp_map, fp_bytes) = arm64_fp_layout()
  for p in fp_map {
    index[p.0] = syms.length()
    syms.push(@object.MachoSym::{
      name: "L" + p.0,
      sect: 3,
      value: p.1,
      is_ext: false,
    })
  }

  let data_relocs : Array[@object.MachoReloc] = []
  for r in data_refs {
    let w = r.2
    for i in 0..