kompiler 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18cda0db09b288b5ad14ebb5feb99e13fe03ea5a7897d603285b0aab0c8bdb77
4
- data.tar.gz: e84247276f40846adef9798d8ea6584dc3fe156caaa18495c668ee7f99506e9e
3
+ metadata.gz: 12bbbb642809ac234ac1cfdcd59b3d43362c0ba284a6286656fecc2cf5024e99
4
+ data.tar.gz: 86e653003489923b7a63bd24b0d6019b07fde08c10c8dc426357dca265b3445e
5
5
  SHA512:
6
- metadata.gz: d682ae38a0d09b9ca8c584de5a3687554a97962ab227fcfb1bd7f458d0f5c48ff1ca0386db8ddcd6f80832e5d94a53b12a655164a23e58bd168128dd8a05106e
7
- data.tar.gz: 04e832aff94a2948615b3f9ca03db1cc75648673873ec9a4678aab6f09aa24b595d9a276910cf53f00de34dffebb0bfc7675b1150749bdfcbe6225bae2778401
6
+ metadata.gz: e729dfc9ee22702200ea082beef419bdbcbaa6bc2ba5a4cadbc2899f6442c6f65c163ec90675ce8d8e2fe41cd80e97cee445fb7386547e78f5569016bbba69ed
7
+ data.tar.gz: d88ba4f53b56086cae6e63f8308058f3f28735f8db671d122c3eecbd3270e5443a1945407b7e8e73b15859758897992f2183481f667282fea29ee57f47f2b494
data/bin/kompile CHANGED
@@ -107,7 +107,7 @@ Available wrapping formats:
107
107
  none
108
108
  elf.obj
109
109
  elf.exec
110
- mach-o.obj [not implemented yet]
110
+ mach-o.obj
111
111
  mach-o.exec [not implemented yet]
112
112
 
113
113
  Additional options for wrapping are:
@@ -115,6 +115,7 @@ Additional options for wrapping are:
115
115
  --elf-class=<class> Specifies the ELF file's class to either 32 or 64 (default is 64)
116
116
  --mach-o-machine=<cputype.subtype> Specifies Mach-O header's cputype and subtype
117
117
  to be the type provided
118
+ --mach-o-archtype=<type> Specifies the file architecture type to either 32 or 64 (default is 64)
118
119
 
119
120
  Available options:
120
121
  --help, -h Prints this information
@@ -261,7 +262,7 @@ code = File.binread(in_filename)
261
262
 
262
263
  detailed_out = Kompiler::CompilerFunctions.detailed_compile(code)
263
264
 
264
- # p detailed_out
265
+ # pp detailed_out
265
266
 
266
267
  out = nil
267
268
 
@@ -295,10 +296,17 @@ when "elf.exec"
295
296
  symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
296
297
  out = Kompiler::Wrappers::ELF.wrap_exec(code, symbols, machine: elf_machine, elf_class: elf_class)
297
298
  when "mach-o.obj"
298
- puts "Mach-O not yet implemented."
299
- exit
299
+ macho_cpu = arg_keys.filter{_1[0] == "mach-o-machine"}[0]
300
+ macho_cpu = (macho_cpu != nil) ? macho_cpu[1] : "0.0"
301
+ cputype, cpusubtype = macho_cpu.split(".").map(&:to_i)
302
+
303
+ arch_type = arg_keys.filter{_1[0] == "mach-o-archtype"}[0]
304
+ arch_type = (arch_type != nil) ? arch_type[1].to_i : 64
305
+
306
+ symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
307
+ out = Kompiler::Wrappers::MachO.wrap_obj(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type)
300
308
  when "mach-o.exec"
301
- puts "Mach-O not yet implemented."
309
+ puts "Mach-O executable not yet implemented."
302
310
  exit
303
311
  end
304
312
 
@@ -519,6 +519,35 @@ end
519
519
 
520
520
  state[:line_i] += 1
521
521
 
522
+ state
523
+ end
524
+ },
525
+ {
526
+ keyword: "info",
527
+ func: lambda do |operands, state|
528
+
529
+ begin
530
+ info_name = operands[0][:definition]
531
+ info_value = operands[1][:definition]
532
+ rescue
533
+ puts "Incorrect use of the \".info\" directive."
534
+ end
535
+
536
+
537
+ state[:line_i] += 1
538
+
539
+ context_info = {}
540
+
541
+ context_info[:current_address] = state[:current_address]
542
+
543
+ context_info[:line_i] = state[:line_i]
544
+
545
+ state[:extra_state][:info_entries] ||= Hash.new
546
+
547
+ state[:extra_state][:info_entries][info_name] ||= Array.new
548
+
549
+ state[:extra_state][:info_entries][info_name] << {context_info: context_info, input_str: info_value, input_full: operands[1]}
550
+
522
551
  state
523
552
  end
524
553
  }
@@ -0,0 +1,441 @@
1
+ require 'kompiler/wrappers/packed_bytes'
2
+
3
+
4
+ module Kompiler
5
+
6
+ module Wrappers
7
+
8
+
9
+ module MachO
10
+
11
+ MH_MAGIC_32 = 0xfeedface
12
+ MH_MAGIC_64 = 0xfeedfacf
13
+
14
+
15
+ def self.wrap_obj code, symbols, arch_type: 64, cputype: 0, cpusubtype: 0
16
+
17
+ segments = [
18
+ {
19
+ name: "__TEXT",
20
+ vaddr: 0,
21
+ vsize: code.bytesize,
22
+ maxprot: 7,
23
+ initprot: 7,
24
+ flags: 0,
25
+
26
+ sections: [
27
+ {
28
+ name: "__text",
29
+ vaddr: 0,
30
+ vsize: 0,
31
+ align: 1,
32
+ flags: 0,
33
+ content: code,
34
+ },
35
+ ]
36
+ }
37
+ ]
38
+
39
+
40
+ output = MachO.build(segments: segments, symbols: symbols, arch_type: arch_type, cputype: cputype, cpusubtype: cpusubtype, filetype: 1, align_section_contents: false)
41
+
42
+ return output
43
+
44
+ end
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+ def self.build_macho_header arch_type: 64, cputype: 0, cpusubtype: 0, filetype: 0, ncmds: 0, sizeofcmds: 0, flags: 0
53
+
54
+ macho_header = PackedBytes.new
55
+
56
+ case arch_type
57
+ when 64
58
+ macho_header.uint32 MH_MAGIC_64
59
+ when 32
60
+ macho_header.uint32 MH_MAGIC_32
61
+ end
62
+
63
+ macho_header.uint32 cputype
64
+ macho_header.uint32 cpusubtype
65
+
66
+ macho_header.uint32 filetype
67
+
68
+ macho_header.uint32 ncmds
69
+
70
+ macho_header.uint32 sizeofcmds
71
+
72
+ macho_header.uint32 flags
73
+
74
+ if arch_type == 64
75
+ # Reserved field in 64-bit
76
+ macho_header.uint32 0
77
+ end
78
+
79
+ return macho_header
80
+ end
81
+
82
+
83
+ # Build a Mach-O file from the input segments, symbols and other pre-built load commands
84
+ #
85
+ # Arguments:
86
+ # segments - list of segments to include in the file (structure below)
87
+ # symbols - list of symbols to be included inside a single symtab (structure below)
88
+ # prebuilt_lcs - a list of other already built load commands (strings) to be included in the file
89
+ # arch_type - architecture type, either 32- or 64-bit (default is 64)
90
+ # cputype - Mach-O header cputype field
91
+ # cpusubtype - Mach-O header cpusubtype field
92
+ # filetype - Mach-O file type
93
+ # align_section_contents - specifies whether to align the contents of each section to its alignment boundary (default is false)
94
+ #
95
+ # Segment structure:
96
+ # name - the segment's name
97
+ # vaddr - virtual load address of the segment
98
+ # vsize - virtual size in memory of the segment
99
+ # maxprot - Mach-O segment maxprot field
100
+ # initprot - Mach-O segment initprot field
101
+ # flags - Mach-O segment flags field
102
+ # sections - a list of segment's section (structure below)
103
+ #
104
+ # Segment section structure:
105
+ # name - the section's name
106
+ # vaddr - virtual load address of section
107
+ # vsize - virtual size in memory of the section
108
+ # align - section's byte alignment
109
+ # flags - section's flags
110
+ # content - section's content
111
+ #
112
+ # Symbol structure:
113
+ # name - symbol's name
114
+ # value - symbol's value
115
+ # type - symbol's type field value
116
+ # sect - symbol's sect field value
117
+ # desc - symbol's desc field value
118
+ #
119
+ def self.build segments: [], symbols: [], prebuilt_lcs: [], arch_type: 64, cputype: 0, cpusubtype: 0, filetype: 0, align_section_contents: false
120
+
121
+ file_content = PackedBytes.new
122
+
123
+ case arch_type
124
+ when 32
125
+ flexval_size = 4
126
+ when 64
127
+ flexval_size = 8
128
+ end
129
+
130
+ segment_entry_size = 4 * 2 + 16 + flexval_size * 4 + 4 * 2 + 4 * 2
131
+
132
+ section_entry_size = 16 + 16 + flexval_size * 2 + 7 * 4
133
+
134
+ if arch_type == 64
135
+ section_entry_size += 4 # one more reserved field for 64-bit
136
+ end
137
+
138
+ symtab_entry_size = 4 * 6
139
+
140
+
141
+ n_sections = 1
142
+
143
+ prebuilt_lcs_size = prebuilt_lcs.map(&:bytesize).sum
144
+
145
+ cmds_size = segment_entry_size + section_entry_size * n_sections + symtab_entry_size + prebuilt_lcs_size
146
+
147
+
148
+ n_cmds = segments.size + 1 + prebuilt_lcs.size # One segment (with possibly multiple sections), one symtab
149
+
150
+ macho_header = build_macho_header(
151
+ arch_type: 64,
152
+ cputype: cputype,
153
+ cpusubtype: cpusubtype,
154
+ filetype: filetype,
155
+ ncmds: n_cmds,
156
+ sizeofcmds: cmds_size,
157
+ flags: 0
158
+ )
159
+
160
+
161
+ file_content.add macho_header
162
+
163
+
164
+
165
+ # Calculate where all of the content can be placed
166
+ contents_offset = cmds_size + file_content.result.bytesize
167
+
168
+
169
+ contents = ""
170
+
171
+
172
+
173
+ segments.each do |segment|
174
+
175
+ raise "Segment name is larger than 16 characters" if segment[:name].bytesize > 16
176
+
177
+ start_size = contents.bytesize
178
+
179
+ segment[:sections].each_with_index do |section, section_i|
180
+
181
+ raise "Section name \"#{section[:name]}\" is larger than 16 characters" if section[:name].bytesize > 16
182
+
183
+ section[:align] ||= 1
184
+
185
+ raise "Section alignment value cannot be 0" if section[:align] == 0
186
+
187
+ sec_offset = contents_offset + contents.bytesize
188
+
189
+ pad_amount = 0
190
+ if (sec_offset % section[:align] != 0) && align_sections
191
+ pad_amount = section[:align] - (sec_offset % section[:align])
192
+ end
193
+
194
+ sec_offset += pad_amount
195
+ contents << "\0" * pad_amount
196
+
197
+ section[:file_offset] = sec_offset
198
+
199
+ if section_i == 0
200
+ segment[:file_offset] = sec_offset
201
+ start_size = contents.bytesize
202
+ end
203
+
204
+ contents << section[:content]
205
+
206
+ section[:vaddr] ||= 0
207
+ section[:vsize] ||= section[:content].bytesize
208
+ end
209
+
210
+ end_size = contents.bytesize
211
+
212
+ segment[:file_offset] ||= 0
213
+ segment[:filesize] = end_size - start_size
214
+
215
+ segment[:vaddr] ||= 0
216
+ segment[:vsize] ||= segment[:filesize]
217
+
218
+ segment[:flags] ||= 0
219
+
220
+ segment[:maxprot] ||= 0
221
+ segment[:initprot] ||= 0
222
+ end
223
+
224
+
225
+
226
+
227
+ symtab_contents = PackedBytes.new
228
+
229
+ sym_strtab_contents = "\0"
230
+
231
+ symbols.each do |symbol|
232
+ if symbol.keys.include? :name
233
+ symbol[:name_offset] = sym_strtab_contents.bytesize
234
+ sym_strtab_contents << symbol[:name] + "\0"
235
+ else
236
+ symbol[:name_offset] = 0
237
+ end
238
+
239
+ symtab_contents.uint32 symbol[:name_offset]
240
+ symtab_contents.uint8 symbol[:type]
241
+ symtab_contents.uint8 symbol[:sect]
242
+ symtab_contents.uint16 symbol[:desc]
243
+ symtab_contents.bytes symbol[:value], flexval_size
244
+ end
245
+
246
+
247
+ symtab_offset = contents.bytesize + contents_offset
248
+ symtab_align = 8
249
+
250
+ if symtab_offset % symtab_align != 0
251
+ pad = (symtab_align - (symtab_offset % symtab_align))
252
+ contents << "\0" * pad
253
+ symtab_offset += pad
254
+ end
255
+
256
+ contents << symtab_contents.result
257
+
258
+ sym_strtab_offset = contents.bytesize + contents_offset
259
+
260
+ contents << sym_strtab_contents
261
+
262
+
263
+
264
+ load_commands = PackedBytes.new
265
+
266
+
267
+ segments.each do |segment|
268
+
269
+ case arch_type
270
+ when 64
271
+ load_commands.uint32 0x19 # LC_SEGMENT_64
272
+ when 32
273
+ load_commands.uint32 0x1 # LC_SEGMENT
274
+ end
275
+
276
+ load_commands.uint32 segment_entry_size + section_entry_size * segment[:sections].size
277
+
278
+ load_commands.bytes segment[:name] + "\0" * (16 - segment[:name].bytesize)
279
+
280
+ load_commands.bytes segment[:vaddr], flexval_size
281
+ load_commands.bytes segment[:vsize], flexval_size
282
+
283
+ load_commands.bytes segment[:file_offset], flexval_size
284
+ load_commands.bytes segment[:filesize], flexval_size
285
+
286
+ load_commands.uint32 segment[:maxprot]
287
+ load_commands.uint32 segment[:initprot]
288
+
289
+ load_commands.uint32 segment[:sections].size
290
+
291
+ load_commands.uint32 segment[:flags]
292
+
293
+
294
+
295
+ segment[:sections].each do |section|
296
+
297
+ load_commands.bytes section[:name] + "\0" * (16 - section[:name].bytesize)
298
+ load_commands.bytes segment[:name] + "\0" * (16 - segment[:name].bytesize)
299
+
300
+ load_commands.bytes section[:vaddr], flexval_size
301
+ load_commands.bytes section[:vsize], flexval_size
302
+
303
+ load_commands.uint32 section[:file_offset]
304
+ load_commands.uint32 section[:align]
305
+
306
+ load_commands.uint32 0 # reloff
307
+ load_commands.uint32 0 # nreloc
308
+
309
+ load_commands.uint32 section[:flags]
310
+
311
+ load_commands.uint32 0 # reserved 1
312
+ load_commands.uint32 0 # reserved 2
313
+
314
+ if arch_type == 64
315
+ load_commands.uint32 0 # reserved 3 (only in 64-bit)
316
+ end
317
+
318
+ end
319
+ end
320
+
321
+
322
+
323
+ # Symtab load command
324
+
325
+ load_commands.uint32 0x2 # LC_SYMTAB
326
+ load_commands.uint32 symtab_entry_size
327
+
328
+ load_commands.uint32 symtab_offset
329
+ load_commands.uint32 symbols.size
330
+
331
+ load_commands.uint32 sym_strtab_offset
332
+ load_commands.uint32 sym_strtab_contents.bytesize
333
+
334
+
335
+ load_commands.add prebuilt_lcs
336
+
337
+
338
+ file_content.add load_commands
339
+
340
+
341
+ file_content.add contents
342
+
343
+
344
+ return file_content.result
345
+
346
+ end
347
+
348
+
349
+ # Converts a hash of label name-address pairs into section symbols suitable for the MachO.build method
350
+ #
351
+ # Arguments:
352
+ # labels - the hash of labels
353
+ # section_index - index of the section the symbols belong to (default is 1)
354
+ # external - specifies if the label is external or not (default is false)
355
+ # private_external - specifies if the label is private external or not (default is false)
356
+ # debug_entry - specifies if the label is a debug entry or not (default is false)
357
+ def self.labels_to_symbols labels,
358
+ out = []
359
+ labels.each do |name, value|
360
+ out << create_symbol(name: name, value: value, type: :sect, external: false, section_number: 1)
361
+ end
362
+ return out
363
+ end
364
+
365
+
366
+ # Creates a symbol from input information suitable for the MachO.build method
367
+ #
368
+ # Arguments:
369
+ # name - the symbol's name
370
+ # value - the symbol's value
371
+ # type - the symbol's type: :undef, :abs, :sect, :prebound, :indirect (default is :sect)
372
+ # private_external - specifies whether the symbol is private external (default is false)
373
+ # external - specifies whether the symbol is external (default is false)
374
+ # debug_entry - specifies whether the symbol is a debugging entry (default is false)
375
+ # options - options depending on the symbol's type
376
+ #
377
+ # Type == :sect options:
378
+ # section_number - section number the symbol is defined in
379
+ #
380
+ def self.create_symbol name: nil, value: nil, type: :sect, private_external: false, external: false, debug_entry: false, **options
381
+
382
+ raise "No name provided for the symbol" if name == nil
383
+ raise "No value provided for the symbol" if value == nil
384
+
385
+ symbol = {name: name, value: value}
386
+
387
+ type_encodings = {undef: 0x0, abs: 0x2, sect: 0xe, prebound: 0xc, indirect: 0xa}
388
+ encoded_type = type_encodings[type]
389
+
390
+ raise "Unknown symbol type \"#{type}\" - must be :undef, :abs, :sect, :prebound, or :indirect." if encoded_type == nil
391
+
392
+
393
+ pext_bit = 0x0
394
+ if private_external == true
395
+ pext_bit = 0x10
396
+ end
397
+
398
+ ext_bit = 0x0
399
+ if external == true
400
+ ext_bit = 0x1
401
+ end
402
+
403
+ debug_entry_bit = 0x0
404
+ if debug_entry == true
405
+ debug_entry_bit = 0xe
406
+ end
407
+
408
+ type_field = encoded_type | pext_bit | ext_bit | debug_entry_bit
409
+
410
+ symbol[:type] = type_field
411
+
412
+
413
+ case type
414
+ when :undef
415
+ symbol[:sect] = 0
416
+ when :abs
417
+ symbol[:sect] = 0
418
+ when :sect
419
+ section_number = options[:section_number] || 0
420
+ raise "Section number too high - maximum is 255." if section_number > 255
421
+ symbol[:sect] = section_number
422
+ when :prebound
423
+ symbol[:sect] = 0
424
+ when :indirect
425
+ symbol[:sect] = 0
426
+ end
427
+
428
+ symbol[:desc] = 0
429
+
430
+ return symbol
431
+ end
432
+
433
+
434
+
435
+ end # Kompiler::Wrappers::MachO
436
+
437
+ end # Kompiler::Wrappers
438
+
439
+ end # Kompiler
440
+
441
+
@@ -1 +1,2 @@
1
1
  require 'kompiler/wrappers/elf_wrapper'
2
+ require 'kompiler/wrappers/macho_wrapper'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kompiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kyryl Shyshko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-04 00:00:00.000000000 Z
11
+ date: 2025-04-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 'Kompiler is a low-level, modular and extendable compiler for any architecture.
14
14
  By default Kompiler supports ARMv8-a, but other architecture extensions can be downloaded
@@ -42,6 +42,7 @@ files:
42
42
  - lib/kompiler/parsers.rb
43
43
  - lib/kompiler/wrappers.rb
44
44
  - lib/kompiler/wrappers/elf_wrapper.rb
45
+ - lib/kompiler/wrappers/macho_wrapper.rb
45
46
  - lib/kompiler/wrappers/packed_bytes.rb
46
47
  homepage: https://github.com/kyryloshy/kompiler
47
48
  licenses: