kompiler 0.3.0.pre.4 → 0.3.1

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,697 @@
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, e_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
+ # machine - a raw machine ID to fill in the e_machine field (default is 0)
44
+ # elf_class - 32 for ELFClass32 or 64 for ELFClass64 (default is 64)
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)
48
+ def self.wrap_exec code, symbols, **options
49
+
50
+ elf_class = options[:elf_class] || 64
51
+ e_machine = options[:machine] || 0
52
+ vaddr = options[:vaddr] || 0x80000
53
+ align = options[:align] || 0x1000
54
+ flags = options[:flags] || 0b111
55
+
56
+ sections, shstrndx = create_default_sections(code, symbols, elf_class: elf_class).values
57
+
58
+ sections[1][:addr] = vaddr
59
+
60
+ segments = [
61
+ {
62
+ type: 1, # Loadable segment
63
+ flags: flags, # Execute, write, read permissions
64
+ vaddr: vaddr, # Virtual address of segment in memory
65
+ # content: code,
66
+ content_section_i: 1,
67
+ align: align
68
+ },
69
+ ]
70
+
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)
72
+
73
+ output
74
+ end
75
+
76
+
77
+
78
+
79
+ # Converts a label hash of name-address pairs into an array of symbols
80
+ def self.labels_to_symbols labels
81
+ out = []
82
+ labels.each do |name, value|
83
+ out << {name: name, value: value, type: 0, binding: 0}
84
+ end
85
+ out
86
+ end
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
+
96
+
97
+ # Build ELF
98
+ # Builds an ELF from provided sections, segments, and other options
99
+ #
100
+ # Arguments:
101
+ # sections - ELF sections (structure below)
102
+ # segments - ELF segments (structure below)
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)
124
+ #
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
138
+
139
+
140
+ case elf_class
141
+ when 64
142
+ elf_addr = 8
143
+ elf_off = 8
144
+ elf_half = 2
145
+ elf_word = 4
146
+ elf_char = 1
147
+ elf_xword = 8
148
+ elf_header_size = 64
149
+ elf_shentsize = 64
150
+ elf_phentsize = 56
151
+ when 32
152
+ elf_addr = 4
153
+ elf_off = 4
154
+ elf_half = 2
155
+ elf_word = 4
156
+ elf_char = 1
157
+ elf_xword = 4
158
+ elf_header_size = 52
159
+ elf_shentsize = 40
160
+ elf_phentsize = 32
161
+ else
162
+ raise "Invalid elf_class - must be 32 or 64."
163
+ end
164
+
165
+ # Calculate section header and program header sizes
166
+ sh_size = sections.size * elf_shentsize
167
+ ph_size = segments.size * elf_phentsize
168
+
169
+ # Calculate the offsets for the section header and program header
170
+ # In this method, the section header is placed right after the ELF header, and the program header is placed right after the section header
171
+ elf_shoff = elf_header_size
172
+ elf_phoff = elf_shoff + sh_size
173
+
174
+
175
+ file_content = PackedBytes.new
176
+
177
+ elf_header = PackedBytes.new
178
+
179
+ e_ident = PackedBytes.new
180
+
181
+ # Magic number
182
+ e_ident.bytes [127]
183
+ e_ident.bytes "ELF"
184
+
185
+ # EI_Class
186
+ case elf_class
187
+ when 64
188
+ e_ident.bytes 2, elf_char
189
+ when 32
190
+ e_ident.bytes 1, elf_char
191
+ end
192
+
193
+ # EI_Data (LSB)
194
+ e_ident.bytes 1, elf_char
195
+
196
+
197
+ # EI_Version (current)
198
+ e_ident.bytes 1, elf_char
199
+
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
206
+
207
+ elf_header.add e_ident
208
+
209
+ # E_Type (input)
210
+ elf_header.bytes e_type, elf_half
211
+
212
+ # E_Machine (input)
213
+ elf_header.bytes e_machine, elf_half
214
+
215
+ # E_version (current)
216
+ elf_header.bytes 1, elf_word
217
+
218
+ # E_entry (input)
219
+ elf_header.bytes virtual_entry_address, elf_addr
220
+
221
+ # E_phoff (calculated earlier)
222
+ # If there aren't any segments, the elf_phoff should be zero
223
+ if segments.size == 0
224
+ elf_header.bytes 0, elf_off
225
+ else
226
+ elf_header.bytes elf_phoff, elf_off
227
+ end
228
+
229
+ # E_shoff (calculated earlier)
230
+ # If there aren't any sections, the elf_shoff should be zero
231
+ if sections.size == 0
232
+ elf_header.bytes 0, elf_off
233
+ else
234
+ elf_header.bytes elf_shoff, elf_off
235
+ end
236
+
237
+ # E_flags
238
+ elf_header.bytes 0, elf_word
239
+
240
+ # E_ehsize (ELF header size)
241
+ elf_header.bytes elf_header_size, elf_half
242
+
243
+ # E_phentsize
244
+ elf_header.bytes elf_phentsize, elf_half
245
+
246
+ # E_phnum
247
+ elf_header.bytes segments.size, elf_half
248
+
249
+ # E_shentsize
250
+ elf_header.bytes elf_shentsize, elf_half
251
+
252
+ # E_shnum
253
+ elf_header.bytes sections.size, elf_half
254
+
255
+ # E_shstrndx
256
+ elf_header.bytes e_shstrndx, elf_half
257
+
258
+ file_content.add elf_header
259
+
260
+
261
+ # Used later
262
+ sections_contents_offset = elf_phoff + ph_size
263
+
264
+ sections_content = PackedBytes.new
265
+
266
+ sections.each_with_index do |section, section_i|
267
+
268
+ if !section.keys.include?(:content)
269
+ section[:size] = 0
270
+ section[:offset] = 0
271
+ next
272
+ end
273
+
274
+ matching_segments = segments.filter{_1[:content_section_i] == section_i}
275
+
276
+ largest_alignment = matching_segments.map{|segment| segment[:align] || 1}.max || 1
277
+
278
+ file_offset = sections_content.result.bytesize + sections_contents_offset
279
+
280
+ if file_offset % largest_alignment != 0
281
+ sections_content.bytes [0] * (largest_alignment - (file_offset % largest_alignment))
282
+ end
283
+
284
+ section[:size] = section[:content].bytesize
285
+ section[:offset] = sections_content.result.bytesize + sections_contents_offset
286
+
287
+ matching_segments.each do |segment|
288
+ segment[:offset] ||= 0
289
+
290
+ segment[:size] ||= section[:size] - segment[:offset]
291
+
292
+ segment[:offset] += section[:offset]
293
+ end
294
+
295
+ sections_content.add section[:content]
296
+ end
297
+
298
+
299
+ segments_content = PackedBytes.new
300
+
301
+ segments_contents_offset = sections_contents_offset + sections_content.result.bytesize
302
+
303
+ segments.each_with_index do |segment, segment_i|
304
+
305
+ segment[:align] ||= 1
306
+
307
+ segment[:vaddr] ||= 0
308
+
309
+ segment[:paddr] ||= segment[:vaddr]
310
+
311
+ if segment.keys.include?(:content)
312
+ segment[:size] = segment[:content].bytesize
313
+ end
314
+
315
+ segment[:vsize] ||= segment[:size]
316
+
317
+
318
+ if segment[:vaddr] % segment[:align] != 0
319
+ raise "Improper segment at index #{segment_i} - the virtual address is not aligned to the specified alignment boundary."
320
+ end
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)
324
+
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
328
+
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]
338
+
339
+ end
340
+
341
+
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
353
+
354
+ end
355
+
356
+
357
+ sh = PackedBytes.new
358
+
359
+ sections.each do |section|
360
+ sh.bytes section[:name_offset], elf_word
361
+ sh.bytes section[:type], elf_word
362
+ sh.bytes section[:flags], elf_xword
363
+ sh.bytes section[:addr], elf_addr
364
+ sh.bytes section[:offset], elf_off
365
+ sh.bytes section[:size], elf_xword
366
+ sh.bytes section[:link], elf_word
367
+ sh.bytes section[:info], elf_word
368
+ sh.bytes section[:addralign], elf_xword
369
+ sh.bytes section[:entsize], elf_xword
370
+ end
371
+
372
+
373
+ file_content.add sh
374
+
375
+
376
+ ph = PackedBytes.new
377
+
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
401
+ end
402
+
403
+ file_content.add ph
404
+
405
+
406
+ file_content.add sections_content
407
+
408
+ file_content.add segments_content
409
+
410
+
411
+ return file_content.result
412
+ end
413
+
414
+
415
+
416
+ # Builds a basic section structure with one progbits section and one symbol table
417
+
418
+ def self.create_default_sections program_bytes, symbols, elf_class: 64
419
+
420
+ case elf_class
421
+ when 64
422
+ symtab_ent_size = 24
423
+ elf_char = 1
424
+ elf_half = 2
425
+ elf_word = 4
426
+ elf_addr = 8
427
+ elf_off = 8
428
+ elf_xword = 8
429
+ when 32
430
+ symtab_ent_size = 16
431
+ elf_char = 1
432
+ elf_half = 2
433
+ elf_word = 4
434
+ elf_addr = 8
435
+ elf_off = 8
436
+ elf_xword = 8
437
+ end
438
+
439
+ sym_strtab_section_index = 3
440
+ sh_strtab_section_index = 4
441
+ symtab_section_index = 2
442
+ progbits_section_index = 1
443
+
444
+ sections = [
445
+ # First section is all zeros
446
+ {
447
+ type: 0,
448
+ flags: 0,
449
+ addr: 0,
450
+ link: 0,
451
+ info: 0,
452
+ addralign: 0,
453
+ entsize: 0,
454
+ },
455
+ {
456
+ name: ".text",
457
+ type: 1,
458
+ flags: 7,
459
+ addr: 0,
460
+ link: 0,
461
+ info: 0,
462
+ addralign: 4,
463
+ entsize: 0,
464
+ content: program_bytes,
465
+ },
466
+ ]
467
+
468
+
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)
475
+
476
+ sections << symtab_section
477
+
478
+ sections << create_strtab_section(sym_strtab_contents, name: ".strtab", elf_class: elf_class)
479
+
480
+
481
+ sections << create_strtab_section("\0", name: ".shstrtab", elf_class: elf_class)
482
+
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"
488
+ else
489
+ section[:name_offset] = 0
490
+ end
491
+ end
492
+
493
+
494
+ return {sections: sections, shstrndx: sh_strtab_section_index}
495
+
496
+ end
497
+
498
+
499
+
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
532
+
533
+ # Check if the auto option was selected
534
+ if section_info == "first-non-local"
535
+
536
+ # Sort symbols for global symbols to come last
537
+
538
+ local_symbols = symbols.filter{_1[:binding] == 0}
539
+ non_local_symbols = symbols.filter{_1[:binding] != 0}
540
+
541
+ first_non_local_i = local_symbols.size
542
+
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
566
+ end
567
+
568
+ symtab_content = PackedBytes.new
569
+ sym_strtab_contents = "\0".encode "ASCII"
570
+
571
+ # First entry must be all zeros
572
+ symtab_content.bytes [0] * symtab_ent_size
573
+
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
583
+
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
630
+ end
631
+ end
632
+
633
+
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
647
+
648
+ end
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
+
694
+ end # Kompiler::ELF
695
+ end # Kompiler::Wrappers
696
+
697
+ end # Kompiler