Class: Omnizip::Filters::BcjArm64

Inherits:
FilterBase
  • Object
show all
Defined in:
lib/omnizip/filters/bcj_arm64.rb

Overview

BCJ filter for 64-bit ARM (AArch64) executables.

This filter preprocesses ARM64 machine code by converting relative addresses in B/BL (0x94) and ADRP (0x90) instructions to absolute addresses. ARM64 uses 4-byte aligned instructions with little-endian encoding.

The filter improves compression by making branch targets and page-aligned addresses position-independent.

Constant Summary collapse

OPCODE_B_BL =

B/BL instruction base opcode

0x94000000
MASK_B_BL =

B/BL instruction mask

0xFC000000
OPCODE_ADRP =

ADRP instruction base opcode

0x90000000
MASK_ADRP =

ADRP instruction mask for variant detection

0x9F000000
INSTRUCTION_SIZE =

Size of ARM64 instruction (4 bytes)

4

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.metadataHash

Get metadata about this filter.

Returns:

  • (Hash)

    Filter metadata



172
173
174
175
176
177
178
179
180
181
# File 'lib/omnizip/filters/bcj_arm64.rb', line 172

def 
  {
    name: "BCJ-ARM64",
    description: "Branch converter for 64-bit ARM (AArch64) " \
                 "executables",
    architecture: "ARM64 / AArch64",
    alignment: 4,
    endian: "little",
  }
end

Instance Method Details

#decode(data, position = 0) ⇒ String

Decode (postprocess) ARM64 executable data after decompression.

Reverses the encoding by converting absolute addresses back to relative addresses.

Parameters:

  • data (String)

    Binary executable data

  • position (Integer) (defaults to: 0)

    Current stream position

Returns:

  • (String)

    Decoded binary data



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/omnizip/filters/bcj_arm64.rb', line 117

def decode(data, position = 0)
  return data.dup if data.bytesize < INSTRUCTION_SIZE

  result = data.b
  size = data.bytesize & ~(INSTRUCTION_SIZE - 1)
  i = 0
  pc = position - INSTRUCTION_SIZE

  while i < size
    instruction = extract_uint32_le(result, i)
    pc += INSTRUCTION_SIZE

    # Check for B/BL instruction
    if (instruction & MASK_B_BL) == OPCODE_B_BL
      # Extract absolute address and convert to relative offset
      absolute = (instruction & 0x03FFFFFF) << 2
      offset = (absolute - pc) >> 2
      new_instruction = OPCODE_B_BL | (offset & 0x03FFFFFF)
      write_uint32_le(result, i, new_instruction)
      i += INSTRUCTION_SIZE
      next
    end

    # Check for ADRP instruction
    if (instruction & MASK_ADRP) == OPCODE_ADRP
      # Extract immlo and immhi
      immlo = (instruction >> 29) & 0x3
      immhi = (instruction >> 5) & 0x7FFFF

      # Combine into absolute page address
      absolute = ((immhi << 2) | immlo) << 12

      # Convert to relative offset
      offset = (absolute - (pc & ~0xFFF)) >> 12

      # Encode back as 21-bit value
      new_immlo = offset & 0x3
      new_immhi = (offset >> 2) & 0x7FFFF

      new_instruction = (instruction & 0x9F00001F) |
        (new_immlo << 29) |
        (new_immhi << 5)
      write_uint32_le(result, i, new_instruction)
    end

    i += INSTRUCTION_SIZE
  end

  result
end

#encode(data, position = 0) ⇒ String

Encode (preprocess) ARM64 executable data for compression.

Scans for B/BL (0x94xxxxxx) and ADRP (0x90xxxxxx) instructions and converts relative addresses to absolute addresses.

Parameters:

  • data (String)

    Binary executable data

  • position (Integer) (defaults to: 0)

    Current stream position

Returns:

  • (String)

    Encoded binary data



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/omnizip/filters/bcj_arm64.rb', line 54

def encode(data, position = 0)
  return data.dup if data.bytesize < INSTRUCTION_SIZE

  result = data.b
  size = data.bytesize & ~(INSTRUCTION_SIZE - 1)
  i = 0
  pc = position - INSTRUCTION_SIZE

  while i < size
    instruction = extract_uint32_le(result, i)
    pc += INSTRUCTION_SIZE

    # Check for B/BL instruction (0x94xxxxxx)
    if (instruction & MASK_B_BL) == OPCODE_B_BL
      # Extract 26-bit offset, sign-extend, and convert
      offset = sign_extend_26(instruction & 0x03FFFFFF)
      absolute = (offset << 2) + pc
      new_instruction = OPCODE_B_BL | ((absolute >> 2) & 0x03FFFFFF)
      write_uint32_le(result, i, new_instruction)
      i += INSTRUCTION_SIZE
      next
    end

    # Check for ADRP instruction (0x90xxxxxx or 0xB0xxxxxx variants)
    if (instruction & MASK_ADRP) == OPCODE_ADRP
      # Extract immlo (bits [30:29]) and immhi (bits [23:5])
      immlo = (instruction >> 29) & 0x3
      immhi = (instruction >> 5) & 0x7FFFF

      # Combine into 21-bit offset
      offset = (immhi << 2) | immlo

      # Sign-extend 21-bit to full integer
      offset = sign_extend_21(offset)

      # Convert to absolute address (page-aligned, << 12)
      absolute = (offset << 12) + (pc & ~0xFFF)

      # Encode back
      absolute >>= 12
      new_immlo = absolute & 0x3
      new_immhi = (absolute >> 2) & 0x7FFFF

      new_instruction = (instruction & 0x9F00001F) |
        (new_immlo << 29) |
        (new_immhi << 5)
      write_uint32_le(result, i, new_instruction)
    end

    i += INSTRUCTION_SIZE
  end

  result
end