kompiler 0.3.0.pre.3 → 0.3.0

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.
@@ -0,0 +1,499 @@
1
+ #
2
+ # This file implements ELF Wrappers for raw programs.
3
+ #
4
+
5
+ require 'kompiler/wrappers/packed_bytes'
6
+
7
+
8
+ module Kompiler
9
+
10
+ module Wrappers
11
+
12
+ module ELF
13
+
14
+ # ELF Wrap Object
15
+ # Wraps a raw program in the ELF relocatable format
16
+ #
17
+ # Arguments:
18
+ # code - raw byte string
19
+ # symbols - a list of labels and other objects and their corresponding values
20
+ # options - a hash of configurable options:
21
+ # elf_class - 32 for ELFClass32 or 64 for ELFClass64 (default is 64)
22
+ # machine - a raw machine ID to fill in the e_machine field
23
+ #
24
+ def self.wrap_obj code, symbols, **options
25
+
26
+ elf_class = options[:elf_class] || 64
27
+ e_machine = options[:machine]
28
+
29
+ sections, shstrndx = create_default_sections(code, symbols, elf_class: elf_class).values
30
+
31
+ output = build(sections: sections, machine: e_machine, elf_class: elf_class, e_shstrndx: shstrndx, e_type: 1)
32
+
33
+ output
34
+ end
35
+
36
+
37
+ # ELF Wrap Executable
38
+ # Wraps a raw program in the ELF executable format
39
+ #
40
+ # Arguments:
41
+ # code - raw byte string
42
+ # options - a hash of configurable options:
43
+ # 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
+ # vaddr - virtual address for the program and entry (default is 0x80000)
46
+ def self.wrap_exec code, symbols, **options
47
+
48
+ elf_class = options[:elf_class] || 64
49
+ e_machine = options[:machine]
50
+ vaddr = options[:vaddr] || 0x80000
51
+
52
+ sections, shstrndx = create_default_sections(code, symbols, elf_class: elf_class).values
53
+
54
+ sections[1][:addr] = vaddr
55
+
56
+ segments = [
57
+ {
58
+ type: 1, # Loadable segment
59
+ flags: 7, # Execute, write, read permissions
60
+ vaddr: vaddr, # Virtual address of segment in memory
61
+ # content: code,
62
+ content_section_i: 1,
63
+ align: 4
64
+ },
65
+ ]
66
+
67
+ output = build(sections: sections, segments: segments, machine: e_machine, elf_class: elf_class, e_type: 2, virtual_entry_address: vaddr, e_shstrndx: shstrndx)
68
+
69
+ output
70
+ end
71
+
72
+
73
+
74
+
75
+ # Converts a label hash of name-address pairs into an array of symbols
76
+ def self.labels_to_symbols labels
77
+ out = []
78
+ labels.each do |name, value|
79
+ out << {name: name, value: value, type: 1, binding: 0}
80
+ end
81
+ out
82
+ end
83
+
84
+
85
+ # Build ELF
86
+ # Builds an ELF from provided sections, segments, and other options
87
+ #
88
+ # Arguments:
89
+ # sections - ELF sections (structure below)
90
+ # 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)
96
+ #
97
+ def self.build sections: [], segments: [], machine: nil, virtual_entry_address: 0, elf_class: 64, e_shstrndx: 0, e_type: 0
98
+
99
+ raise "Machine ID not specified for the ELF header." if machine == nil
100
+
101
+ case elf_class
102
+ when 64
103
+ elf_addr = 8
104
+ elf_off = 8
105
+ elf_half = 2
106
+ elf_word = 4
107
+ elf_char = 1
108
+ elf_xword = 8
109
+ elf_header_size = 64
110
+ elf_shentsize = 64
111
+ elf_phentsize = 56
112
+ when 32
113
+ elf_addr = 4
114
+ elf_off = 4
115
+ elf_half = 2
116
+ elf_word = 4
117
+ elf_char = 1
118
+ elf_xword = 4
119
+ elf_header_size = 52
120
+ elf_shentsize = 40
121
+ elf_phentsize = 32
122
+ else
123
+ raise "Invalid elf_class - must be 32 or 64."
124
+ end
125
+
126
+ # Calculate section header and program header sizes
127
+ sh_size = sections.size * elf_shentsize
128
+ ph_size = segments.size * elf_phentsize
129
+
130
+ # Calculate the offsets for the section header and program header
131
+ # In this method, the section header is placed right after the ELF header, and the program header is placed right after the section header
132
+ elf_shoff = elf_header_size
133
+ elf_phoff = elf_shoff + sh_size
134
+
135
+
136
+ file_content = PackedBytes.new
137
+
138
+ elf_header = PackedBytes.new
139
+
140
+ e_ident = PackedBytes.new
141
+
142
+ # Magic number
143
+ e_ident.bytes [127]
144
+ e_ident.bytes "ELF"
145
+
146
+ # EI_Class
147
+ case elf_class
148
+ when 64
149
+ e_ident.bytes 2, elf_char
150
+ when 32
151
+ e_ident.bytes 1, elf_char
152
+ end
153
+
154
+ # EI_Data (LSB)
155
+ e_ident.bytes 1, elf_char
156
+
157
+
158
+ # EI_Version (current)
159
+ e_ident.bytes 1, elf_char
160
+
161
+ e_ident.align 16, "\0"
162
+
163
+ elf_header.add e_ident
164
+
165
+ # E_Type (input)
166
+ elf_header.bytes e_type, elf_half
167
+
168
+ # E_Machine (input)
169
+ elf_header.bytes machine, elf_half
170
+
171
+ # E_version (current)
172
+ elf_header.bytes 1, elf_word
173
+
174
+ # E_entry (input)
175
+ elf_header.bytes virtual_entry_address, elf_addr
176
+
177
+ # E_phoff (calculated earlier)
178
+ # If there aren't any segments, the elf_phoff should be zero
179
+ if segments.size == 0
180
+ elf_header.bytes 0, elf_off
181
+ else
182
+ elf_header.bytes elf_phoff, elf_off
183
+ end
184
+
185
+ # E_shoff (calculated earlier)
186
+ # If there aren't any sections, the elf_shoff should be zero
187
+ if sections.size == 0
188
+ elf_header.bytes 0, elf_off
189
+ else
190
+ elf_header.bytes elf_shoff, elf_off
191
+ end
192
+
193
+ # E_flags
194
+ elf_header.bytes 0, elf_word
195
+
196
+ # E_ehsize (ELF header size)
197
+ elf_header.bytes elf_header_size, elf_half
198
+
199
+ # E_phentsize
200
+ elf_header.bytes elf_phentsize, elf_half
201
+
202
+ # E_phnum
203
+ elf_header.bytes segments.size, elf_half
204
+
205
+ # E_shentsize
206
+ elf_header.bytes elf_shentsize, elf_half
207
+
208
+ # E_shnum
209
+ elf_header.bytes sections.size, elf_half
210
+
211
+ # E_shstrndx
212
+ elf_header.bytes e_shstrndx, elf_half
213
+
214
+ file_content.add elf_header
215
+
216
+
217
+ # Used later
218
+ sections_contents_offset = elf_phoff + ph_size
219
+
220
+ sections_content = PackedBytes.new
221
+
222
+ sections.each_with_index do |section, section_i|
223
+ if !section.keys.include?(:content)
224
+ section[:size] = 0
225
+ section[:offset] = 0
226
+ next
227
+ end
228
+
229
+ matching_segments = segments.filter{_1[:content_section_i] == section_i}
230
+
231
+ largest_alignment = matching_segments.map{|segment| segment[:align] || 1}.max || 1
232
+
233
+ file_offset = sections_content.result.bytesize + sections_contents_offset
234
+
235
+ if file_offset % largest_alignment != 0
236
+ sections_content.bytes [0] * (largest_alignment - (file_offset % largest_alignment))
237
+ end
238
+
239
+ section[:size] = section[:content].bytesize
240
+ section[:offset] = sections_content.result.bytesize + sections_contents_offset
241
+
242
+ 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)
247
+ end
248
+
249
+ sections_content.add section[:content]
250
+ end
251
+
252
+
253
+ segments_content = PackedBytes.new
254
+
255
+ segments_contents_offset = sections_contents_offset + sections_content.result.bytesize
256
+
257
+ segments.each_with_index do |segment, segment_i|
258
+
259
+ if !segment.keys.include?(:align)
260
+ segment[:align] = 1
261
+ end
262
+
263
+ if !segment.keys.include?(:vaddr)
264
+ segment[:vaddr] = 0
265
+ end
266
+
267
+ if !segment.keys.include?(:paddr)
268
+ segment[:paddr] = segment[:vaddr]
269
+ end
270
+
271
+ if segment.keys.include?(:content)
272
+ segment[:size] = segment[:content].bytesize
273
+ end
274
+
275
+ if !segment.keys.include?(:vsize)
276
+ segment[:vsize] = segment[:size]
277
+ end
278
+
279
+ if segment[:vaddr] % segment[:align] != 0
280
+ raise "Improper segment at index #{segment_i} - the virtual address is not aligned to the specified alignment boundary."
281
+ end
282
+
283
+
284
+ if segment.keys.include? :content_section_i
285
+ next
286
+ end
287
+
288
+
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
292
+
293
+ pad_amount = 0
294
+ if file_offset % segment[:align] != 0
295
+ pad_amount = segment[:align] - (file_offset % segment[:align])
296
+ end
297
+
298
+ segments_content.bytes "\0" * pad_amount
299
+
300
+ segment[:offset] = segments_content.result.bytesize + segments_contents_offset
301
+ segments_content.add segment[:content]
302
+
303
+ end
304
+
305
+
306
+ sh = PackedBytes.new
307
+
308
+ sections.each do |section|
309
+ sh.bytes section[:name_offset], elf_word
310
+ sh.bytes section[:type], elf_word
311
+ sh.bytes section[:flags], elf_xword
312
+ sh.bytes section[:addr], elf_addr
313
+ sh.bytes section[:offset], elf_off
314
+ sh.bytes section[:size], elf_xword
315
+ sh.bytes section[:link], elf_word
316
+ sh.bytes section[:info], elf_word
317
+ sh.bytes section[:addralign], elf_xword
318
+ sh.bytes section[:entsize], elf_xword
319
+ end
320
+
321
+
322
+ file_content.add sh
323
+
324
+
325
+ ph = PackedBytes.new
326
+
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
336
+ end
337
+
338
+ file_content.add ph
339
+
340
+
341
+ file_content.add sections_content
342
+
343
+ file_content.add segments_content
344
+
345
+
346
+ return file_content.result
347
+ end
348
+
349
+
350
+
351
+ # Builds a basic section structure with one progbits section and one symbol table
352
+
353
+ def self.create_default_sections program_bytes, symbols, elf_class: 64
354
+
355
+ case elf_class
356
+ when 64
357
+ symtab_ent_size = 24
358
+ elf_char = 1
359
+ elf_half = 2
360
+ elf_word = 4
361
+ elf_addr = 8
362
+ elf_off = 8
363
+ elf_xword = 8
364
+ when 32
365
+ symtab_ent_size = 16
366
+ elf_char = 1
367
+ elf_half = 2
368
+ elf_word = 4
369
+ elf_addr = 8
370
+ elf_off = 8
371
+ elf_xword = 8
372
+ end
373
+
374
+ sym_strtab_section_index = 3
375
+ sh_strtab_section_index = 4
376
+ symtab_section_index = 2
377
+ progbits_section_index = 1
378
+
379
+ sections = [
380
+ # First section is all zeros
381
+ {
382
+ type: 0,
383
+ flags: 0,
384
+ addr: 0,
385
+ link: 0,
386
+ info: 0,
387
+ addralign: 0,
388
+ entsize: 0,
389
+ },
390
+ {
391
+ name: ".text",
392
+ type: 1,
393
+ flags: 7,
394
+ addr: 0,
395
+ link: 0,
396
+ info: 0,
397
+ addralign: 4,
398
+ entsize: 0,
399
+ content: program_bytes,
400
+ },
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
+ ]
435
+
436
+ symtab = PackedBytes.new
437
+
438
+ # First section is all zeros
439
+ symtab.bytes [0] * symtab_ent_size
440
+
441
+
442
+ symtab_string_table_content = "\0".encode("ASCII")
443
+
444
+ symbols.each do |symbol|
445
+
446
+ if symbol.keys.include? :name
447
+ name_offset = symtab_string_table_content.bytesize
448
+ symtab_string_table_content << symbol[:name] + "\0"
449
+ else
450
+ name_offset = 0
451
+ end
452
+
453
+ # name
454
+ symtab.bytes name_offset, elf_word
455
+
456
+ symbol_info = symbol[:type] | (symbol[:binding] << 4)
457
+
458
+ # info
459
+ symtab.bytes symbol_info, elf_char
460
+
461
+ # other (must be zero)
462
+ symtab.bytes 0, elf_char
463
+
464
+ # section index of where the symbol is located
465
+ symtab.bytes progbits_section_index, elf_half
466
+
467
+ # symbol value
468
+ symtab.bytes symbol[:value], elf_addr
469
+
470
+ # symbol size
471
+ symbol_size = symbol[:size] || 0
472
+ symtab.bytes symbol_size, elf_xword
473
+ end
474
+
475
+ sections[symtab_section_index][:content] = symtab.result
476
+ sections[sym_strtab_section_index][:content] = symtab_string_table_content
477
+
478
+
479
+ sections_string_table_content = "\0"
480
+
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
487
+ end
488
+ end
489
+
490
+ sections[sh_strtab_section_index][:content] = sections_string_table_content
491
+
492
+ return {sections: sections, shstrndx: sh_strtab_section_index}
493
+
494
+ end
495
+
496
+ end # Kompiler::ELF
497
+ end # Kompiler::Wrappers
498
+
499
+ end # Kompiler
@@ -0,0 +1,68 @@
1
+ class PackedBytes
2
+ def initialize bytes = ""
3
+ @bytes = bytes.dup
4
+ end
5
+
6
+ def uint8 n
7
+ n = [n] if n.is_a? Numeric
8
+ @bytes << n.pack("C*")
9
+ end
10
+
11
+ def uint16 n
12
+ n = [n] if n.is_a? Numeric
13
+ @bytes << n.pack("S<*")
14
+ end
15
+
16
+ def uint32 n
17
+ n = [n] if n.is_a? Numeric
18
+ @bytes << n.pack("L<*")
19
+ end
20
+
21
+ def uint64 n
22
+ n = [n] if n.is_a? Numeric
23
+ @bytes << n.pack("Q<*")
24
+ end
25
+
26
+ def bytes bytes, n_bytes=nil
27
+ if n_bytes == nil
28
+ if bytes.is_a? PackedBytes
29
+ @bytes += bytes.result
30
+ elsif bytes.is_a? String
31
+ @bytes += bytes
32
+ elsif bytes.is_a? Array
33
+ @bytes += bytes.pack("C*")
34
+ end
35
+ else
36
+ case n_bytes
37
+ when 1
38
+ self.uint8 bytes
39
+ when 2
40
+ self.uint16 bytes
41
+ when 4
42
+ self.uint32 bytes
43
+ when 8
44
+ self.uint64 bytes
45
+ end
46
+ end
47
+ end
48
+ alias_method :add, :bytes
49
+
50
+ def align n_bytes, pad_byte="\0"
51
+ if @bytes.size % n_bytes == 0
52
+ return
53
+ else
54
+ @bytes << pad_byte * (n_bytes - (@bytes.size % n_bytes))
55
+ end
56
+ end
57
+
58
+ def result
59
+ @bytes
60
+ end
61
+ alias_method :get_bytes, :result
62
+
63
+ def to_file filename
64
+ File.binwrite filename, self.result
65
+ end
66
+ alias_method :write, :to_file
67
+ alias_method :save, :to_file
68
+ end
@@ -0,0 +1 @@
1
+ require 'kompiler/wrappers/elf_wrapper'
data/lib/kompiler.rb CHANGED
@@ -12,9 +12,12 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require 'kompiler/config.rb'
15
16
  require 'kompiler/mc_builder.rb'
16
17
  require 'kompiler/parsers.rb'
17
18
  require 'kompiler/compiler_functions.rb'
18
19
  require 'kompiler/architecture.rb'
19
20
  require 'kompiler/directives.rb'
20
- require 'kompiler/arch_manager.rb'
21
+ require 'kompiler/arch_manager.rb'
22
+ require 'kompiler/math_ast.rb'
23
+ require 'kompiler/wrappers'
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.pre.3
4
+ version: 0.3.0
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-01-17 00:00:00.000000000 Z
11
+ date: 2025-04-02 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
@@ -30,12 +30,19 @@ files:
30
30
  - lib/kompiler/architectures/armv8a/instructions.rb
31
31
  - lib/kompiler/architectures/armv8a/load.rb
32
32
  - lib/kompiler/architectures/armv8a/registers.rb
33
+ - lib/kompiler/architectures/armv8a/simd_fp_instructions.rb
34
+ - lib/kompiler/architectures/armv8a/simd_fp_registers.rb
33
35
  - lib/kompiler/architectures/armv8a/sys_instructions.rb
34
36
  - lib/kompiler/architectures/armv8a/sys_registers.rb
35
37
  - lib/kompiler/compiler_functions.rb
38
+ - lib/kompiler/config.rb
36
39
  - lib/kompiler/directives.rb
40
+ - lib/kompiler/math_ast.rb
37
41
  - lib/kompiler/mc_builder.rb
38
42
  - lib/kompiler/parsers.rb
43
+ - lib/kompiler/wrappers.rb
44
+ - lib/kompiler/wrappers/elf_wrapper.rb
45
+ - lib/kompiler/wrappers/packed_bytes.rb
39
46
  homepage: https://github.com/kyryloshy/kompiler
40
47
  licenses:
41
48
  - Apache-2.0
@@ -57,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
57
64
  - !ruby/object:Gem::Version
58
65
  version: '0'
59
66
  requirements: []
60
- rubygems_version: 3.5.10
67
+ rubygems_version: 3.4.10
61
68
  signing_key:
62
69
  specification_version: 4
63
70
  summary: Kir's compiler for low-level machine code