///|
pub fn PEImage::base_relocations(
  self : Self,
) -> Array[RelocationBlock] raise PeError {
  let directory = match self.directory(5) {
    Some(directory) if directory.virtual_address != 0UL && directory.size > 0 =>
      directory
    _ => return []
  }
  let start = self.rva_to_offset(directory.virtual_address)
  let end = start + directory.size
  ensure_range(self.data, start, directory.size)
  let blocks = []
  let mut offset = start
  while offset + 8 <= end && blocks.length() < 8192 {
    let page_rva = read_u32(self.data, offset)
    let block_size = checked_int(
      read_u32(self.data, offset + 4),
      offset + 4,
      "relocation block size",
    )
    if page_rva == 0UL && block_size == 0 {
      break
    }
    if block_size < 8 || block_size % 4 != 0 || offset + block_size > end {
      raise PeError(offset, "invalid base relocation block size")
    }
    let entries = []
    let mut entry_offset = offset + 8
    while entry_offset < offset + block_size {
      let value = read_u16(self.data, entry_offset)
      let relocation_type = value >> 12
      let relocation_offset = value & 0x0fff
      if relocation_type != 0 {
        entries.push({
          relocation_type,
          offset: relocation_offset,
          target_rva: page_rva + relocation_offset.to_uint64(),
        })
      }
      entry_offset = entry_offset + 2
    }
    blocks.push({ page_rva, entries, })
    offset = offset + block_size
  }
  blocks
}

///|
pub fn relocation_type_name(relocation_type : Int32) -> String {
  match relocation_type {
    1 => "high"
    2 => "low"
    3 => "high-low"
    4 => "high-adj"
    5 => "mips-jump"
    7 => "thumb-mov32"
    9 => "mips-jump16"
    10 => "dir64"
    _ => "unknown"
  }
}