kompiler 0.3.0 → 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: 90ed49bc46f0641a23f0d7d561489d4ec326e8f3bb933a638ed789831e9ee362
4
- data.tar.gz: 9c0d4b1f795b9e7a2b73a145218a972854fdd8372808a6fb68b3c210899d2ad8
3
+ metadata.gz: 12bbbb642809ac234ac1cfdcd59b3d43362c0ba284a6286656fecc2cf5024e99
4
+ data.tar.gz: 86e653003489923b7a63bd24b0d6019b07fde08c10c8dc426357dca265b3445e
5
5
  SHA512:
6
- metadata.gz: 8a18cb49c4740e3f5d86a074c75a87c90db0de718dad6ec2547b802d1acf70dd4a726fcc163ba6bf17f83b58c91e02a0e331f48b707a2ac6d80ac331f4c852fc
7
- data.tar.gz: c54ac313fed9b03a22810678244e0809dc29f25c0974c2ded1251fac7e6d01f2c28f50a0844938532eda6d7ec90b78ddf8afb0634485662ef1246677c18bc5d5
6
+ metadata.gz: e729dfc9ee22702200ea082beef419bdbcbaa6bc2ba5a4cadbc2899f6442c6f65c163ec90675ce8d8e2fe41cd80e97cee445fb7386547e78f5569016bbba69ed
7
+ data.tar.gz: d88ba4f53b56086cae6e63f8308058f3f28735f8db671d122c3eecbd3270e5443a1945407b7e8e73b15859758897992f2183481f667282fea29ee57f47f2b494
data/bin/kompile CHANGED
@@ -107,13 +107,15 @@ 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:
114
114
  --elf-machine=<type> Specifies ELF header's e_machine to be the type provided (default is 0)
115
+ --elf-class=<class> Specifies the ELF file's class to either 32 or 64 (default is 64)
115
116
  --mach-o-machine=<cputype.subtype> Specifies Mach-O header's cputype and subtype
116
117
  to be the type provided
118
+ --mach-o-archtype=<type> Specifies the file architecture type to either 32 or 64 (default is 64)
117
119
 
118
120
  Available options:
119
121
  --help, -h Prints this information
@@ -260,7 +262,7 @@ code = File.binread(in_filename)
260
262
 
261
263
  detailed_out = Kompiler::CompilerFunctions.detailed_compile(code)
262
264
 
263
- # p detailed_out
265
+ # pp detailed_out
264
266
 
265
267
  out = nil
266
268
 
@@ -276,18 +278,35 @@ when "none"
276
278
  when "elf.obj"
277
279
  elf_machine = arg_keys.filter{_1[0] == "elf-machine"}[0]
278
280
  elf_machine = (elf_machine != nil) ? elf_machine[1].to_i : 0
281
+
282
+ elf_class = arg_keys.filter{_1[0] == "elf-class"}[0]
283
+ elf_class ||= ["elf-class", "64"]
284
+ elf_class = elf_class[1].to_i
285
+
279
286
  symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
280
- out = Kompiler::Wrappers::ELF.wrap_obj(code, symbols, machine: elf_machine)
287
+ out = Kompiler::Wrappers::ELF.wrap_obj(code, symbols, machine: elf_machine, elf_class: elf_class)
281
288
  when "elf.exec"
282
289
  elf_machine = arg_keys.filter{_1[0] == "elf-machine"}[0]
283
290
  elf_machine = (elf_machine != nil) ? elf_machine[1].to_i : 0
291
+
292
+ elf_class = arg_keys.filter{_1[0] == "elf-class"}[0]
293
+ elf_class ||= ["elf-class", "64"]
294
+ elf_class = elf_class[1].to_i
295
+
284
296
  symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
285
- out = Kompiler::Wrappers::ELF.wrap_exec(code, symbols, machine: elf_machine)
297
+ out = Kompiler::Wrappers::ELF.wrap_exec(code, symbols, machine: elf_machine, elf_class: elf_class)
286
298
  when "mach-o.obj"
287
- puts "Mach-O not yet implemented."
288
- 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)
289
308
  when "mach-o.exec"
290
- puts "Mach-O not yet implemented."
309
+ puts "Mach-O executable not yet implemented."
291
310
  exit
292
311
  end
293
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
  }
@@ -28,7 +28,7 @@ module Kompiler
28
28
 
29
29
  sections, shstrndx = create_default_sections(code, symbols, elf_class: elf_class).values
30
30
 
31
- output = build(sections: sections, machine: e_machine, elf_class: elf_class, e_shstrndx: shstrndx, e_type: 1)
31
+ output = build(sections: sections, e_machine: e_machine, elf_class: elf_class, e_shstrndx: shstrndx, e_type: 1)
32
32
 
33
33
  output
34
34
  end
@@ -40,14 +40,18 @@ module Kompiler
40
40
  # Arguments:
41
41
  # code - raw byte string
42
42
  # options - a hash of configurable options:
43
+ # machine - a raw machine ID to fill in the e_machine field (default is 0)
43
44
  # elf_class - 32 for ELFClass32 or 64 for ELFClass64 (default is 64)
44
- # machine - a raw machine ID to fill in the e_machine field
45
45
  # vaddr - virtual address for the program and entry (default is 0x80000)
46
+ # align - alignment value for the load segment (default is 0x1000)
47
+ # flags - segment flags (default is 0b111, bits are in form 0bRWX)
46
48
  def self.wrap_exec code, symbols, **options
47
49
 
48
50
  elf_class = options[:elf_class] || 64
49
- e_machine = options[:machine]
51
+ e_machine = options[:machine] || 0
50
52
  vaddr = options[:vaddr] || 0x80000
53
+ align = options[:align] || 0x1000
54
+ flags = options[:flags] || 0b111
51
55
 
52
56
  sections, shstrndx = create_default_sections(code, symbols, elf_class: elf_class).values
53
57
 
@@ -56,15 +60,15 @@ module Kompiler
56
60
  segments = [
57
61
  {
58
62
  type: 1, # Loadable segment
59
- flags: 7, # Execute, write, read permissions
63
+ flags: flags, # Execute, write, read permissions
60
64
  vaddr: vaddr, # Virtual address of segment in memory
61
65
  # content: code,
62
66
  content_section_i: 1,
63
- align: 4
67
+ align: align
64
68
  },
65
69
  ]
66
70
 
67
- output = build(sections: sections, segments: segments, machine: e_machine, elf_class: elf_class, e_type: 2, virtual_entry_address: vaddr, e_shstrndx: shstrndx)
71
+ output = build(sections: sections, segments: segments, e_machine: e_machine, elf_class: elf_class, e_type: 2, virtual_entry_address: vaddr, e_shstrndx: shstrndx)
68
72
 
69
73
  output
70
74
  end
@@ -76,11 +80,19 @@ module Kompiler
76
80
  def self.labels_to_symbols labels
77
81
  out = []
78
82
  labels.each do |name, value|
79
- out << {name: name, value: value, type: 1, binding: 0}
83
+ out << {name: name, value: value, type: 0, binding: 0}
80
84
  end
81
85
  out
82
86
  end
83
87
 
88
+ # Converts a values hash of name-value pairs into an array of symbols
89
+ def self.values_to_symbols values
90
+ out = []
91
+ values.each do |name, value|
92
+ out << {name: name, value: value, type: 0, binding: 0}
93
+ end
94
+ end
95
+
84
96
 
85
97
  # Build ELF
86
98
  # Builds an ELF from provided sections, segments, and other options
@@ -88,15 +100,42 @@ module Kompiler
88
100
  # Arguments:
89
101
  # sections - ELF sections (structure below)
90
102
  # segments - ELF segments (structure below)
91
- # machine - a raw machine ID for the ELF header
92
- # virtual_entry_address - the virtual entry address for the ELF header
93
- # elf_class - the ELF class (32 or 64)
94
- # e_shstrndx - the value for ELF header's e_shstrndx field (index of the section header string table section)
95
- # e_type - the value for ELF header's e_type field (e.g., 1 for relocatable, 3 for executable)
103
+ # virtual_entry_address - the virtual entry address for the ELF header (default is 0)
104
+ # elf_class - the ELF class (32 or 64) (default is 0)
105
+ # e_machine - a raw machine ID for the ELF header (default is 0)
106
+ # e_shstrndx - the value for ELF header's e_shstrndx field (index of the section header string table section) (default is 0)
107
+ # e_type - the value for ELF header's e_type field (e.g., 1 for relocatable, 3 for executable) (default is 0)
108
+ # e_os_abi - the value for the ELF Identification header's osabi field (default is 0) (not used with elf_class = 32)
109
+ # e_abi_version - the value for the ELF Identification header's abiversion field (default is 0) (not used with elf_class = 32)
110
+ #
111
+ # Section structure:
112
+ # name_offset - section's name field value
113
+ # type - section's type field value
114
+ # flags - section's flags field value
115
+ # addr - section's addr field value
116
+ # link - section's link field value
117
+ # info - section's info field value
118
+ # addralign - section's addralign field value
119
+ # entsize - section's entsize field value
120
+ # content - section's content (offset and size will be computed automatically)
121
+ # segment_content_i - index of segment that contains this section's content (useful to remove repeated info and 'link' a section with a segment)
122
+ # offset - if segment_content_i is present, an optional offset can be added to take only part of the segment's content (default is 0)
123
+ # size - if segment_content_i is present, size can be provided optionally to take only part of the segment's content (default is 0)
96
124
  #
97
- def self.build sections: [], segments: [], machine: nil, virtual_entry_address: 0, elf_class: 64, e_shstrndx: 0, e_type: 0
125
+ # Segment structure:
126
+ # type - segment's type field value
127
+ # flags - segment's flags field value
128
+ # vaddr - segment's vaddr field value (default is 0)
129
+ # paddr - segment's paddr field value (default is value of vaddr)
130
+ # vsize - segment's memsz / vsize field value (default is same as content.size)
131
+ # align - segment's alignment value. This will also ensure that the segment's content is aligned to this boundary in the file
132
+ # content - segment's content (offset and size will be computed automatically)
133
+ # section_content_i - index of section that contaisn this segment's content (idea is same as section[:segment_content_i])
134
+ # offset - idea same as section[:offset]
135
+ # size - idea same as section[:size]
136
+ #
137
+ def self.build sections: [], segments: [], virtual_entry_address: 0, elf_class: 64, e_machine: 0, e_shstrndx: 0, e_type: 0, e_os_abi: 0, e_abi_version: 0
98
138
 
99
- raise "Machine ID not specified for the ELF header." if machine == nil
100
139
 
101
140
  case elf_class
102
141
  when 64
@@ -158,7 +197,12 @@ module Kompiler
158
197
  # EI_Version (current)
159
198
  e_ident.bytes 1, elf_char
160
199
 
161
- e_ident.align 16, "\0"
200
+ e_ident.bytes e_os_abi, 1 # OS_ABI
201
+ e_ident.bytes e_abi_version, 1 # ABI_VERSION
202
+
203
+ e_ident.bytes [0] * 6 # Pad to 15
204
+
205
+ e_ident.bytes [16] # E_Ident size
162
206
 
163
207
  elf_header.add e_ident
164
208
 
@@ -166,7 +210,7 @@ module Kompiler
166
210
  elf_header.bytes e_type, elf_half
167
211
 
168
212
  # E_Machine (input)
169
- elf_header.bytes machine, elf_half
213
+ elf_header.bytes e_machine, elf_half
170
214
 
171
215
  # E_version (current)
172
216
  elf_header.bytes 1, elf_word
@@ -219,7 +263,8 @@ module Kompiler
219
263
 
220
264
  sections_content = PackedBytes.new
221
265
 
222
- sections.each_with_index do |section, section_i|
266
+ sections.each_with_index do |section, section_i|
267
+
223
268
  if !section.keys.include?(:content)
224
269
  section[:size] = 0
225
270
  section[:offset] = 0
@@ -240,10 +285,11 @@ module Kompiler
240
285
  section[:offset] = sections_content.result.bytesize + sections_contents_offset
241
286
 
242
287
  matching_segments.each do |segment|
243
- segment[:offset] = 0 if !segment.keys.include?(:offset)
244
- segment[:offset] += section[:offset]
245
-
246
- segment[:size] = section[:size] if !segment.keys.include?(:size)
288
+ segment[:offset] ||= 0
289
+
290
+ segment[:size] ||= section[:size] - segment[:offset]
291
+
292
+ segment[:offset] += section[:offset]
247
293
  end
248
294
 
249
295
  sections_content.add section[:content]
@@ -256,49 +302,54 @@ module Kompiler
256
302
 
257
303
  segments.each_with_index do |segment, segment_i|
258
304
 
259
- if !segment.keys.include?(:align)
260
- segment[:align] = 1
261
- end
305
+ segment[:align] ||= 1
262
306
 
263
- if !segment.keys.include?(:vaddr)
264
- segment[:vaddr] = 0
265
- end
307
+ segment[:vaddr] ||= 0
266
308
 
267
- if !segment.keys.include?(:paddr)
268
- segment[:paddr] = segment[:vaddr]
269
- end
309
+ segment[:paddr] ||= segment[:vaddr]
270
310
 
271
311
  if segment.keys.include?(:content)
272
312
  segment[:size] = segment[:content].bytesize
273
313
  end
274
314
 
275
- if !segment.keys.include?(:vsize)
276
- segment[:vsize] = segment[:size]
277
- end
315
+ segment[:vsize] ||= segment[:size]
316
+
278
317
 
279
318
  if segment[:vaddr] % segment[:align] != 0
280
319
  raise "Improper segment at index #{segment_i} - the virtual address is not aligned to the specified alignment boundary."
281
320
  end
282
321
 
322
+ # If the segment uses :content, add the segment's content to the file and compute the offset
323
+ if !segment.keys.include?(:content_section_i)
283
324
 
284
- if segment.keys.include? :content_section_i
285
- next
286
- end
287
-
325
+ # ELF requires file_offset and vaddr to be a multiple of the alignment value
326
+
327
+ file_offset = segments_contents_offset + segments_content.result.bytesize
288
328
 
289
- # ELF requires file_offset and vaddr to be a multiple of the alignment value
290
-
291
- file_offset = segments_contents_offset + segments_content.result.bytesize
329
+ pad_amount = 0
330
+ if file_offset % segment[:align] != 0
331
+ pad_amount = segment[:align] - (file_offset % segment[:align])
332
+ end
333
+
334
+ segments_content.bytes "\0" * pad_amount
335
+
336
+ segment[:offset] = segments_content.result.bytesize + segments_contents_offset
337
+ segments_content.add segment[:content]
292
338
 
293
- pad_amount = 0
294
- if file_offset % segment[:align] != 0
295
- pad_amount = segment[:align] - (file_offset % segment[:align])
296
339
  end
297
340
 
298
- segments_content.bytes "\0" * pad_amount
299
341
 
300
- segment[:offset] = segments_content.result.bytesize + segments_contents_offset
301
- segments_content.add segment[:content]
342
+ # If some sections depend on the segment, update their values
343
+
344
+ matching_sections = sections.filter{_1[:segment_content_i] == segment_i}
345
+
346
+ matching_sections.each do |section|
347
+ section[:offset] ||= 0
348
+
349
+ section[:size] ||= segment[:size] - section[:offset]
350
+
351
+ section[:offset] += segment[:offset]
352
+ end
302
353
 
303
354
  end
304
355
 
@@ -324,15 +375,29 @@ module Kompiler
324
375
 
325
376
  ph = PackedBytes.new
326
377
 
327
- segments.each do |segment|
328
- ph.bytes segment[:type], elf_word
329
- ph.bytes segment[:flags], elf_word
330
- ph.bytes segment[:offset], elf_off
331
- ph.bytes segment[:vaddr], elf_addr
332
- ph.bytes segment[:paddr], elf_addr
333
- ph.bytes segment[:size], elf_xword
334
- ph.bytes segment[:vsize], elf_xword
335
- ph.bytes segment[:align], elf_xword
378
+ case elf_class
379
+ when 64
380
+ segments.each do |segment|
381
+ ph.bytes segment[:type], elf_word
382
+ ph.bytes segment[:flags], elf_word
383
+ ph.bytes segment[:offset], elf_off
384
+ ph.bytes segment[:vaddr], elf_addr
385
+ ph.bytes segment[:paddr], elf_addr
386
+ ph.bytes segment[:size], elf_xword
387
+ ph.bytes segment[:vsize], elf_xword
388
+ ph.bytes segment[:align], elf_xword
389
+ end
390
+ when 32
391
+ segments.each do |segment|
392
+ ph.bytes segment[:type], elf_word
393
+ ph.bytes segment[:offset], elf_off
394
+ ph.bytes segment[:vaddr], elf_addr
395
+ ph.bytes segment[:paddr], elf_addr
396
+ ph.bytes segment[:size], elf_word
397
+ ph.bytes segment[:vsize], elf_word
398
+ ph.bytes segment[:flags], elf_word
399
+ ph.bytes segment[:align], elf_word
400
+ end
336
401
  end
337
402
 
338
403
  file_content.add ph
@@ -398,101 +463,234 @@ module Kompiler
398
463
  entsize: 0,
399
464
  content: program_bytes,
400
465
  },
401
- {
402
- name: ".symtab",
403
- type: 2,
404
- flags: 0,
405
- addr: 0,
406
- link: sym_strtab_section_index, # Index of string table used for symbol names
407
- info: symbols.size + 1, # Index of first non-local symbol
408
- addralign: 0,
409
- entsize: symtab_ent_size,
410
- content: "",
411
- },
412
- {
413
- name: ".strtab",
414
- type: 3,
415
- flags: 0,
416
- addr: 0,
417
- link: 0,
418
- info: 0,
419
- addralign: 0,
420
- entsize: 0,
421
- content: "",
422
- },
423
- {
424
- name: ".shstrtab",
425
- type: 3,
426
- flags: 0,
427
- addr: 0,
428
- link: 0,
429
- info: 0,
430
- addralign: 0,
431
- entsize: 0,
432
- content: "",
433
- },
434
466
  ]
435
467
 
436
- symtab = PackedBytes.new
437
468
 
438
- # First section is all zeros
439
- symtab.bytes [0] * symtab_ent_size
469
+ symbols.map! do |sym|
470
+ sym[:section_index] = sym[:section_index] || progbits_section_index
471
+ sym
472
+ end
473
+
474
+ symtab_section, sym_strtab_contents = create_symtab_section(symbols, link: sym_strtab_section_index, info: "first-non-local", elf_class: elf_class)
440
475
 
476
+ sections << symtab_section
441
477
 
442
- symtab_string_table_content = "\0".encode("ASCII")
478
+ sections << create_strtab_section(sym_strtab_contents, name: ".strtab", elf_class: elf_class)
443
479
 
444
- symbols.each do |symbol|
480
+
481
+ sections << create_strtab_section("\0", name: ".shstrtab", elf_class: elf_class)
445
482
 
446
- if symbol.keys.include? :name
447
- name_offset = symtab_string_table_content.bytesize
448
- symtab_string_table_content << symbol[:name] + "\0"
483
+
484
+ sections.each_with_index do |section, section_i|
485
+ if section.keys.include? :name
486
+ section[:name_offset] = sections[sh_strtab_section_index][:content].bytesize
487
+ sections[sh_strtab_section_index][:content] << section[:name] + "\0"
449
488
  else
450
- name_offset = 0
489
+ section[:name_offset] = 0
451
490
  end
491
+ end
492
+
493
+
494
+ return {sections: sections, shstrndx: sh_strtab_section_index}
495
+
496
+ end
497
+
452
498
 
453
- # name
454
- symtab.bytes name_offset, elf_word
455
499
 
456
- symbol_info = symbol[:type] | (symbol[:binding] << 4)
500
+ # Creates a section structure with type symtab, builds symbol entries and symbol string table contents
501
+ # Returns [symtab_section, sym_strtab_contents]
502
+ #
503
+ # Arguments:
504
+ # symbols - an array of hashes with info about each symbol (structure below)
505
+ # **options - additional options
506
+ #
507
+ # Options are:
508
+ # elf_class - elf class, either 64 or 32 (default is 64)
509
+ # name - the symtab section's name (default is .symtab)
510
+ # link - the symtab section's link field value (default is 0)
511
+ # info - the symtab section's info field value (default is 0)
512
+ # set to "first-non-local" to auto-sort all symbols and
513
+ # set info field to the index of the first non-local symbol
514
+ # flags - the symtab section's flags field value (default is 0)
515
+ #
516
+ # Symbol hash structure:
517
+ # :name - the symbol's name (optional)
518
+ # :type - symbol type (0 - notype, 1 - object, 2 - func, 3 - section, 4 - file)
519
+ # :binding - symbol binding (0 - local, 1 - global, 2 - weak)
520
+ # :section_index - corresponding section's index (0 - undef, 0xfff1 - absolute, 0xfff2 - common block)
521
+ # :value - the symbol's value
522
+ # :size - the symbol's size (default is 0)
523
+ #
524
+ def self.create_symtab_section symbols, **options
525
+
526
+ elf_class = options[:elf_class] || 64
527
+
528
+ section_name = options[:name] || ".symtab"
529
+ section_link = options[:link] || 0
530
+ section_info = options[:info] || 0
531
+ section_flags = options[:flags] || 0
457
532
 
458
- # info
459
- symtab.bytes symbol_info, elf_char
533
+ # Check if the auto option was selected
534
+ if section_info == "first-non-local"
460
535
 
461
- # other (must be zero)
462
- symtab.bytes 0, elf_char
536
+ # Sort symbols for global symbols to come last
463
537
 
464
- # section index of where the symbol is located
465
- symtab.bytes progbits_section_index, elf_half
538
+ local_symbols = symbols.filter{_1[:binding] == 0}
539
+ non_local_symbols = symbols.filter{_1[:binding] != 0}
466
540
 
467
- # symbol value
468
- symtab.bytes symbol[:value], elf_addr
541
+ first_non_local_i = local_symbols.size
469
542
 
470
- # symbol size
471
- symbol_size = symbol[:size] || 0
472
- symtab.bytes symbol_size, elf_xword
543
+ # Sort symbols for local ones to come first
544
+ symbols = local_symbols + non_local_symbols
545
+
546
+ # + 1 because the first symbol will be pre-forced to be all zeros and local
547
+ section_info = first_non_local_i + 1
548
+ end
549
+
550
+
551
+ case elf_class
552
+ when 64
553
+ elf_char = 1
554
+ elf_half = 2
555
+ elf_word = 4
556
+ elf_addr = 8
557
+ elf_xword = 8
558
+ symtab_ent_size = 24
559
+ when 32
560
+ elf_char = 1
561
+ elf_half = 2
562
+ elf_word = 4
563
+ elf_addr = 4
564
+ elf_xword = 4
565
+ symtab_ent_size = 16
473
566
  end
474
567
 
475
- sections[symtab_section_index][:content] = symtab.result
476
- sections[sym_strtab_section_index][:content] = symtab_string_table_content
568
+ symtab_content = PackedBytes.new
569
+ sym_strtab_contents = "\0".encode "ASCII"
477
570
 
571
+ # First entry must be all zeros
572
+ symtab_content.bytes [0] * symtab_ent_size
478
573
 
479
- sections_string_table_content = "\0"
574
+ case elf_class
575
+ when 64
576
+ symbols.each do |symbol|
577
+ if symbol.keys.include? :name
578
+ name_offset = sym_strtab_contents.bytesize
579
+ sym_strtab_contents << symbol[:name] + "\0"
580
+ else
581
+ name_offset = 0
582
+ end
480
583
 
481
- sections.each_with_index do |section, section_i|
482
- if section.keys.include? :name
483
- section[:name_offset] = sections_string_table_content.bytesize
484
- sections_string_table_content << section[:name] + "\0"
485
- else
486
- section[:name_offset] = 0
584
+ # name
585
+ symtab_content.bytes name_offset, elf_word
586
+
587
+ symbol_info = symbol[:type] | (symbol[:binding] << 4)
588
+ # info
589
+ symtab_content.bytes symbol_info, elf_char
590
+
591
+ # other
592
+ symtab_content.bytes 0, elf_char
593
+
594
+ # shndx
595
+ symtab_content.bytes symbol[:section_index], elf_half
596
+
597
+ # value
598
+ symtab_content.bytes symbol[:value], elf_addr
599
+
600
+ # size
601
+ symtab_content.bytes symbol[:size] || 0, elf_xword
602
+ end
603
+ when 32
604
+ symbols.each do |symbol|
605
+ if symbol.keys.include? :name
606
+ name_offset = sym_strtab_contents.bytesize
607
+ sym_strtab_contents << symbol[:name] + "\0"
608
+ else
609
+ name_offset = 0
610
+ end
611
+
612
+ # name
613
+ symtab_content.bytes name_offset, elf_word
614
+
615
+ # value
616
+ symtab_content.bytes symbol[:value], elf_addr
617
+
618
+ # size
619
+ symtab_content.bytes symbol[:size] || 0, elf_xword
620
+
621
+ symbol_info = symbol[:type] | (symbol[:binding] << 4)
622
+ # info
623
+ symtab_content.bytes symbol_info, elf_char
624
+
625
+ # other
626
+ symtab_content.bytes 0, elf_char
627
+
628
+ # shndx
629
+ symtab_content.bytes symbol[:section_index], elf_half
487
630
  end
488
631
  end
489
632
 
490
- sections[sh_strtab_section_index][:content] = sections_string_table_content
491
633
 
492
- return {sections: sections, shstrndx: sh_strtab_section_index}
634
+ symtab_section = {
635
+ name: ".symtab",
636
+ type: 2,
637
+ flags: section_flags,
638
+ addr: 0,
639
+ link: section_link,
640
+ info: section_info,
641
+ addralign: 0,
642
+ entsize: symtab_ent_size,
643
+ content: symtab_content.result,
644
+ }
645
+
646
+ return symtab_section, sym_strtab_contents
493
647
 
494
648
  end
495
649
 
650
+
651
+ # Create string table section
652
+ # Creates a hash with information for a string table section
653
+ #
654
+ # Arguments:
655
+ # string_content - the string table's content
656
+ # **options - additional options
657
+ #
658
+ # Options:
659
+ # name - the section's name (default is nil)
660
+ # info - the section's info field value (default is 0)
661
+ # link - the section's link field value (default is 0)
662
+ # flags - the section's flags field value (default is 0)
663
+ # addr - the section's addr field value (default is 0)
664
+ # addralign - the section's addralign field value (default is 0)
665
+ # elf_class - ELF class 32 or 64 (not used)
666
+ #
667
+ def self.create_strtab_section string_content, **options
668
+ name = options[:name]
669
+ info = options[:info] || 0
670
+ link = options[:link] || 0
671
+ flags = options[:flags] || 0
672
+ addr = options[:addr] || 0
673
+ addralign = options[:addralign] || 0
674
+
675
+
676
+ section = {
677
+ type: 3,
678
+ info: info,
679
+ link: link,
680
+ flags: flags,
681
+ addr: addr,
682
+ addralign: addralign,
683
+ content: string_content,
684
+ entsize: 0,
685
+ }
686
+
687
+ section[:name] = name if name != nil
688
+
689
+ return section
690
+ end
691
+
692
+
693
+
496
694
  end # Kompiler::ELF
497
695
  end # Kompiler::Wrappers
498
696
 
@@ -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.0
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-02 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: