kompiler 0.3.1 → 0.3.3
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 +4 -4
- data/bin/kompile +143 -28
- data/lib/kompiler/directives.rb +388 -3
- data/lib/kompiler/wrappers/elf_wrapper.rb +2 -2
- data/lib/kompiler/wrappers/macho_wrapper.rb +1274 -0
- data/lib/kompiler/wrappers/packed_bytes.rb +18 -4
- data/lib/kompiler/wrappers.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5938bafe84031bb6dfb99ab6452d66fb1da892a42520e5e05c274e2b89f0b73
|
4
|
+
data.tar.gz: 8d80c58a6d2b689fbd3bfcd69f780cb2aa258c0f02d5b905c0b16b7d1b2c8a12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e79f4af903656d29d7d2a4b9b5b1c1e75f7bc10461d4a1e203d68b98c70420d920e13e651e263469a679e361349e7b4106b519a3712efa5185d5124cbe71c1ed
|
7
|
+
data.tar.gz: b8065b466b2b5bca9234b7ba8fd00a5560914cc819cfa9c973884f5c489eed1d797d1eb1d633a47570629133c616caedff3ce6743d69502c7069715d1c0ae1ec
|
data/bin/kompile
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
17
|
require 'kompiler'
|
18
|
-
|
18
|
+
require 'fileutils'
|
19
19
|
|
20
20
|
arg_string = ARGV.join(" ")
|
21
21
|
|
@@ -107,20 +107,28 @@ Available wrapping formats:
|
|
107
107
|
none
|
108
108
|
elf.obj
|
109
109
|
elf.exec
|
110
|
-
mach-o.obj
|
111
|
-
mach-o.exec
|
112
|
-
|
110
|
+
mach-o.obj
|
111
|
+
mach-o.exec
|
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
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)
|
119
|
+
--exec-type=<type> Used with --wrap=mach-o.exec. Specifies whether the executable is
|
120
|
+
statically (static) or dynamically (dylink) linked (default is dylink)
|
121
|
+
--mach-o-threadstate=<type> Used with --wrap=mach-o.exec and --exec-type=static. Specifies which
|
122
|
+
thread state type to use (arm64, arm32, x86-64 or x86-32)
|
123
|
+
--codesign=<bool> Used with --wrap=mach-o.*. Specifies whether to add a basic
|
124
|
+
code signature to the Mach-O file (default is false)
|
118
125
|
|
119
126
|
Available options:
|
120
127
|
--help, -h Prints this information
|
121
128
|
--list-architectures Lists available architectures
|
122
129
|
--list-instructions [arch] Lists available instructions for the specified architecture
|
123
130
|
--list-registers [arch] Lists available registers for the specified architecture
|
131
|
+
--list-lexis Lists available lexis entries
|
124
132
|
"""
|
125
133
|
exit # Exit
|
126
134
|
end
|
@@ -140,14 +148,18 @@ end
|
|
140
148
|
if arg_opts == ["list-instructions"]
|
141
149
|
arch_name = arg_words[0] || "armv8a"
|
142
150
|
Kompiler::ArchManager.load_all_entries()
|
143
|
-
|
151
|
+
Kompiler::ArchManager.load_arch arch_name
|
144
152
|
|
145
|
-
|
153
|
+
Kompiler::Architecture.instructions.each do |instruction|
|
154
|
+
print instruction[:keyword]
|
146
155
|
|
147
|
-
|
148
|
-
|
149
|
-
puts
|
150
|
-
|
156
|
+
print " "
|
157
|
+
|
158
|
+
puts instruction[:operands].map{"<" + (_1[:name] || _1[:type] || "") + ">"}.join(" ")
|
159
|
+
|
160
|
+
puts instruction[:description]
|
161
|
+
|
162
|
+
print "\n"
|
151
163
|
end
|
152
164
|
|
153
165
|
exit
|
@@ -234,6 +246,7 @@ else
|
|
234
246
|
arch_name = "armv8a"
|
235
247
|
end
|
236
248
|
|
249
|
+
|
237
250
|
# Load all the architecture entries
|
238
251
|
|
239
252
|
Kompiler::ArchManager.load_all_entries()
|
@@ -261,7 +274,7 @@ code = File.binread(in_filename)
|
|
261
274
|
|
262
275
|
detailed_out = Kompiler::CompilerFunctions.detailed_compile(code)
|
263
276
|
|
264
|
-
#
|
277
|
+
# pp detailed_out
|
265
278
|
|
266
279
|
out = nil
|
267
280
|
|
@@ -271,37 +284,139 @@ labels = detailed_out[:labels]
|
|
271
284
|
labels.delete "here"
|
272
285
|
|
273
286
|
|
287
|
+
def get_number str
|
288
|
+
bool, val = Kompiler::Parsers.check_immediate_operand(str)
|
289
|
+
return (bool == false) ? nil : val
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
def get_arg_key arg_keys, key, default_value=0
|
294
|
+
filtered = arg_keys.filter{_1[0] == key}[0]
|
295
|
+
value = (filtered != nil) ? filtered[1] : default_value
|
296
|
+
return value
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
class String
|
301
|
+
def to_num
|
302
|
+
status, val = Kompiler::Parsers.check_immediate_operand(self)
|
303
|
+
return (status == false) ? nil : val[:value]
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
add_exec_permission = false
|
309
|
+
|
310
|
+
|
274
311
|
case wrap_opt
|
275
312
|
when "none"
|
276
313
|
out = code
|
277
314
|
when "elf.obj"
|
278
|
-
elf_machine = arg_keys
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
315
|
+
elf_machine = get_arg_key(arg_keys, "elf-machine", "0").to_num
|
316
|
+
elf_class = get_arg_key(arg_keys, "elf-class", "64").to_num
|
317
|
+
|
318
|
+
if ![32, 64].include?(elf_class)
|
319
|
+
puts "kompile: Invalid ELF class specified."
|
320
|
+
puts "Type \"kompile --help\" for more information."
|
321
|
+
exit
|
322
|
+
end
|
284
323
|
|
285
324
|
symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
|
286
325
|
out = Kompiler::Wrappers::ELF.wrap_obj(code, symbols, machine: elf_machine, elf_class: elf_class)
|
287
326
|
when "elf.exec"
|
288
|
-
|
289
|
-
elf_machine = (elf_machine != nil) ? elf_machine[1].to_i : 0
|
327
|
+
vaddr = 0x80000
|
290
328
|
|
291
|
-
|
292
|
-
elf_class
|
293
|
-
elf_class = elf_class[1].to_i
|
329
|
+
elf_machine = get_arg_key(arg_keys, "elf-machine", "0").to_num
|
330
|
+
elf_class = get_arg_key(arg_keys, "elf-class", "64").to_num
|
294
331
|
|
295
|
-
|
296
|
-
|
332
|
+
if ![32, 64].include?(elf_class)
|
333
|
+
puts "kompile: Invalid ELF class specified."
|
334
|
+
puts "Type \"kompile --help\" for more information."
|
335
|
+
exit
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels, vaddr: vaddr)
|
340
|
+
out = Kompiler::Wrappers::ELF.wrap_exec(code, symbols, machine: elf_machine, elf_class: elf_class, vaddr: vaddr)
|
341
|
+
|
342
|
+
add_exec_permission = true
|
343
|
+
|
297
344
|
when "mach-o.obj"
|
298
|
-
|
299
|
-
|
345
|
+
macho_cpu = get_arg_key(arg_keys, "mach-o-machine", "0.0")
|
346
|
+
cputype, cpusubtype = macho_cpu.split(".").map(&:to_num)
|
347
|
+
|
348
|
+
if [cputype, cpusubtype].include? nil
|
349
|
+
puts "kompile: Invalid Mach-O machine specified."
|
350
|
+
puts "Type \"kompile --help\" for more information."
|
351
|
+
exit
|
352
|
+
end
|
353
|
+
|
354
|
+
arch_type = get_arg_key(arg_keys, "mach-o-archtype", "64").to_num
|
355
|
+
|
356
|
+
codesign = get_arg_key(arg_keys, "codesign", "false")
|
357
|
+
if !["true", "false"].include?(codesign)
|
358
|
+
puts "kompile: Invalid --codesign value."
|
359
|
+
puts "Type \"kompile --help\" for more information."
|
360
|
+
exit
|
361
|
+
end
|
362
|
+
|
363
|
+
codesign = (codesign == "true")
|
364
|
+
|
365
|
+
symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
|
366
|
+
out = Kompiler::Wrappers::MachO.wrap_obj(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type)
|
300
367
|
when "mach-o.exec"
|
301
|
-
|
302
|
-
|
368
|
+
macho_cpu = get_arg_key(arg_keys, "mach-o-machine", "0.0")
|
369
|
+
cputype, cpusubtype = macho_cpu.split(".").map(&:to_num)
|
370
|
+
|
371
|
+
if [cputype, cpusubtype].include? nil
|
372
|
+
puts "kompile: Invalid Mach-O machine specified."
|
373
|
+
puts "Type \"kompile --help\" for more information."
|
374
|
+
exit
|
375
|
+
end
|
376
|
+
|
377
|
+
arch_type = get_arg_key(arg_keys, "mach-o-archtype", "64").to_num
|
378
|
+
|
379
|
+
exec_type = get_arg_key(arg_keys, "exec-type", "dylink")
|
380
|
+
|
381
|
+
|
382
|
+
codesign = get_arg_key(arg_keys, "codesign", "false")
|
383
|
+
if !["true", "false"].include?(codesign)
|
384
|
+
puts "kompile: Invalid --codesign value."
|
385
|
+
puts "Type \"kompile --help\" for more information."
|
386
|
+
exit
|
387
|
+
end
|
388
|
+
|
389
|
+
codesign = (codesign == "true")
|
390
|
+
|
391
|
+
|
392
|
+
case exec_type
|
393
|
+
when "dylink"
|
394
|
+
symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
|
395
|
+
out = Kompiler::Wrappers::MachO.wrap_exec_dylink(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type, codesign: codesign)
|
396
|
+
when "static"
|
397
|
+
symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
|
398
|
+
|
399
|
+
thread_state_arch = get_arg_key(arg_keys, "mach-o-threadstate", "arm64")
|
400
|
+
|
401
|
+
entry_address = 0x1000000
|
402
|
+
|
403
|
+
thread_state = Kompiler::Wrappers::MachO.build_thread_state arch: thread_state_arch, entry_address: 0x1000000, stack_pointer: 0
|
404
|
+
|
405
|
+
out = Kompiler::Wrappers::MachO.wrap_exec_static(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type, thread_state: thread_state, codesign: codesign, virtual_entry_address: entry_address)
|
406
|
+
|
407
|
+
else
|
408
|
+
puts "kompile: Invalid Mach-O executable type specified."
|
409
|
+
puts "Type \"kompile --help\" for more information."
|
410
|
+
exit
|
411
|
+
end
|
412
|
+
|
413
|
+
add_exec_permission = true
|
303
414
|
end
|
304
415
|
|
305
416
|
|
306
417
|
File.binwrite out_filename, out
|
307
418
|
|
419
|
+
if add_exec_permission
|
420
|
+
FileUtils.chmod "+x", out_filename
|
421
|
+
end
|
422
|
+
|
data/lib/kompiler/directives.rb
CHANGED
@@ -13,6 +13,8 @@
|
|
13
13
|
# If false, operands will be a raw string containing the string after the keyword.
|
14
14
|
#
|
15
15
|
|
16
|
+
require 'securerandom'
|
17
|
+
|
16
18
|
|
17
19
|
module Kompiler
|
18
20
|
|
@@ -250,7 +252,7 @@ end
|
|
250
252
|
state[:line_i] += 1
|
251
253
|
|
252
254
|
# Insert the lines at the correct place
|
253
|
-
state[:lines] = state[:lines][0...state[:line_i]] + total_load_lines + state[:lines][state[:line_i]..]
|
255
|
+
state[:lines] = state[:lines][0...state[:line_i]] + total_load_lines + state[:lines][state[:line_i]..]
|
254
256
|
|
255
257
|
state
|
256
258
|
end
|
@@ -415,7 +417,7 @@ end
|
|
415
417
|
end
|
416
418
|
|
417
419
|
# Skip string definitions
|
418
|
-
if
|
420
|
+
if Kompiler::Config.string_delimiters.include? line[start_i]
|
419
421
|
str, parsed_length = Kompiler::Parsers.parse_str line[start_i..]
|
420
422
|
start_i += parsed_length
|
421
423
|
next
|
@@ -515,10 +517,393 @@ end
|
|
515
517
|
line_i += build_lines.size
|
516
518
|
end
|
517
519
|
|
518
|
-
state[:lines] = state[:lines][...state[:line_i]] + scan_lines
|
520
|
+
state[:lines] = state[:lines][...(state[:line_i] + def_lines.size + 2)] + scan_lines
|
521
|
+
|
522
|
+
state[:line_i] += def_lines.size + 2
|
523
|
+
|
524
|
+
state
|
525
|
+
end
|
526
|
+
},
|
527
|
+
{
|
528
|
+
keyword: "isomacro",
|
529
|
+
collect_operands: false,
|
530
|
+
func: lambda do |_, state|
|
531
|
+
line_i = state[:line_i]
|
532
|
+
|
533
|
+
def_line = state[:lines][line_i]
|
534
|
+
|
535
|
+
# First: collect the part after ".macro"
|
536
|
+
|
537
|
+
char_i = 0
|
538
|
+
# Skip the whitespace before .macro
|
539
|
+
while char_i < def_line.size && Kompiler::Config.whitespace_chars.include?(def_line[char_i])
|
540
|
+
char_i += 1
|
541
|
+
end
|
542
|
+
# Skip the .macro
|
543
|
+
while char_i < def_line.size && Kompiler::Config.keyword_chars.include?(def_line[char_i])
|
544
|
+
char_i += 1
|
545
|
+
end
|
546
|
+
# Skip the whitespace after .macro
|
547
|
+
while char_i < def_line.size && Kompiler::Config.whitespace_chars.include?(def_line[char_i])
|
548
|
+
char_i += 1
|
549
|
+
end
|
550
|
+
|
551
|
+
# If the end of the line was reached, throw an error
|
552
|
+
raise "Incorrect .isomacro definition" if char_i == def_line.size
|
553
|
+
|
554
|
+
# Now char_i contains the first index of the text after .macro
|
555
|
+
|
556
|
+
macro_def = def_line[char_i..]
|
557
|
+
|
558
|
+
# Second: extract the macro's name
|
559
|
+
|
560
|
+
macro_name = ""
|
561
|
+
|
562
|
+
while char_i < def_line.size && Kompiler::Config.keyword_chars.include?(def_line[char_i])
|
563
|
+
macro_name << def_line[char_i]
|
564
|
+
char_i += 1
|
565
|
+
end
|
566
|
+
|
567
|
+
# Third: extract the operand names (code taken from parse_instruction_line in parsers.rb)
|
568
|
+
|
569
|
+
arg_names = Kompiler::Parsers.extract_instruction_operands(def_line[char_i..])
|
570
|
+
|
571
|
+
# Make sure that the arg names are unique
|
572
|
+
raise "Macro definition error - arguments cannot have the same name: Program build not possible" if arg_names.size != arg_names.uniq.size
|
573
|
+
|
574
|
+
# Extract the macro inside definition
|
575
|
+
|
576
|
+
line_i = state[:line_i] + 1
|
577
|
+
def_lines = []
|
578
|
+
|
579
|
+
whitespace_regexp = /[#{Kompiler::Config.whitespace_chars.join("|")}]*/
|
580
|
+
|
581
|
+
endmacro_regexp = /\A#{whitespace_regexp}\.?endisomacro#{whitespace_regexp}\z/
|
582
|
+
|
583
|
+
while line_i < state[:lines].size
|
584
|
+
break if state[:lines][line_i].match? endmacro_regexp # Check if it's an end macro instruction
|
585
|
+
def_lines << state[:lines][line_i]
|
586
|
+
line_i += 1
|
587
|
+
end
|
588
|
+
|
589
|
+
|
590
|
+
# Find insert indexes for each argument
|
591
|
+
arg_insert_locations = arg_names.map{|arg_name| [arg_name, []]}.to_h
|
592
|
+
|
593
|
+
def_lines.each_with_index do |line, line_i|
|
594
|
+
|
595
|
+
start_i = 0
|
596
|
+
|
597
|
+
# Loop through each character starting position
|
598
|
+
while start_i < line.size
|
599
|
+
|
600
|
+
# Skip whitespace characters
|
601
|
+
if Kompiler::Config.whitespace_chars.include?(line[start_i])
|
602
|
+
start_i += 1
|
603
|
+
next
|
604
|
+
end
|
605
|
+
|
606
|
+
# Skip string definitions
|
607
|
+
if Kompiler::Config.string_delimiters.include? line[start_i]
|
608
|
+
str, parsed_length = Kompiler::Parsers.parse_str line[start_i..]
|
609
|
+
start_i += parsed_length
|
610
|
+
next
|
611
|
+
end
|
612
|
+
|
613
|
+
cut_line = line[start_i..]
|
614
|
+
|
615
|
+
arg_found = false
|
616
|
+
|
617
|
+
# Check if one of the argument names works
|
618
|
+
arg_names.each do |arg_name|
|
619
|
+
next if !cut_line.start_with?(arg_name) # Skip the argument if the line doesn't begin with it
|
620
|
+
next if Kompiler::Config.keyword_chars.include?(cut_line[arg_name.size]) # Skip if the argument is a partial word. So, for the argument 'arg', this will skip in the case of 'arg1'
|
621
|
+
# Here if the argument name should be replaced with the contents
|
622
|
+
arg_found = true # Indicate that a replacement was found
|
623
|
+
|
624
|
+
arg_insert_locations[arg_name] << [line_i, start_i] # Add the insert location to the list
|
625
|
+
|
626
|
+
# start_i += arg_name.size
|
627
|
+
def_lines[line_i] = def_lines[line_i][...start_i] + (def_lines[line_i][(start_i+arg_name.size)..] || "")
|
628
|
+
line = def_lines[line_i]
|
629
|
+
|
630
|
+
break # Skip the arguments loop
|
631
|
+
end
|
632
|
+
|
633
|
+
# Check if an argument was found
|
634
|
+
# If not, skip the text until the next whitespace character
|
635
|
+
if !arg_found
|
636
|
+
while start_i < line.size && Kompiler::Config.keyword_chars.include?(line[start_i])
|
637
|
+
start_i += 1
|
638
|
+
end
|
639
|
+
start_i += 1 # Move one more character
|
640
|
+
end
|
641
|
+
|
642
|
+
end
|
643
|
+
|
644
|
+
end
|
645
|
+
|
646
|
+
state[:extra_state][:isomacros] = Array.new if !state[:extra_state].keys.include?(:macros)
|
647
|
+
|
648
|
+
state[:extra_state][:isomacros] << {name: macro_name, args: arg_names, def_lines: def_lines, arg_insert_locations: arg_insert_locations}
|
649
|
+
|
650
|
+
|
651
|
+
# Scan the lines after the macro for the macro call and replace it with the macro definition
|
652
|
+
|
653
|
+
scan_lines = state[:lines][(state[:line_i] + def_lines.size + 1 + 1)..]
|
654
|
+
|
655
|
+
line_i = 0
|
656
|
+
|
657
|
+
# Re-group argument insert locations by line -> [index, arg index]
|
658
|
+
|
659
|
+
arg_insert_locations_regrouped = def_lines.size.times.map{[]}
|
660
|
+
|
661
|
+
arg_insert_locations.each do |arg_name, insert_locations|
|
662
|
+
insert_locations.each do |line_i, char_i|
|
663
|
+
arg_insert_locations_regrouped[line_i] << [char_i, arg_names.index(arg_name)]
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
|
668
|
+
while line_i < scan_lines.size
|
669
|
+
keyword, operands = Kompiler::Parsers.extract_instruction_parts(scan_lines[line_i])
|
670
|
+
|
671
|
+
# If parsing failed, move on to the next line
|
672
|
+
if keyword == false
|
673
|
+
line_i += 1
|
674
|
+
next
|
675
|
+
end
|
676
|
+
|
677
|
+
# If the keyword isn't the macro's name, skip the line
|
678
|
+
if keyword != macro_name
|
679
|
+
line_i += 1
|
680
|
+
next
|
681
|
+
end
|
682
|
+
|
683
|
+
# Here when the keyword matches the macro name
|
684
|
+
|
685
|
+
# Check that the number of operands is correct
|
686
|
+
if operands.size != arg_names.size
|
687
|
+
raise "Incorrect use of the \"#{macro_name}\" macro - #{arg_names.size} operands expected, but #{operands.size} were given: Program build not possible."
|
688
|
+
end
|
689
|
+
|
690
|
+
# Build the replacement lines for the macro call
|
691
|
+
build_lines = def_lines.map{|line| line.dup} # Copying strings inside array, because array.dup doesn't work for elements
|
692
|
+
|
693
|
+
arg_insert_locations_regrouped.each_with_index do |locations, line_i|
|
694
|
+
# Sort the locations by the insert character from largest to smallest, so that the inserts are made from end to start
|
695
|
+
locations.sort_by{|el| el[0]}.reverse.each do |char_i, arg_index|
|
696
|
+
build_lines[line_i].insert char_i, operands[arg_index]
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
build_lines.insert 0, ".namespace macro.#{macro_name}.#{SecureRandom.uuid.gsub('-', '')}"
|
701
|
+
build_lines << ".endnamespace"
|
702
|
+
|
703
|
+
# Replace the macro call with the built lines
|
704
|
+
scan_lines = scan_lines[...line_i] + build_lines + scan_lines[(line_i + 1)..]
|
705
|
+
|
706
|
+
# Skip the inserted macro
|
707
|
+
line_i += build_lines.size
|
708
|
+
end
|
709
|
+
|
710
|
+
state[:lines] = state[:lines][...(state[:line_i] + def_lines.size + 2)] + scan_lines
|
711
|
+
|
712
|
+
state[:line_i] += def_lines.size + 2
|
713
|
+
|
714
|
+
state
|
715
|
+
end
|
716
|
+
},
|
717
|
+
{
|
718
|
+
keyword: "info",
|
719
|
+
func: lambda do |operands, state|
|
720
|
+
|
721
|
+
begin
|
722
|
+
info_name = operands[0][:definition]
|
723
|
+
info_value = operands[1][:definition]
|
724
|
+
rescue
|
725
|
+
raise "Incorrect use of the \".info\" directive."
|
726
|
+
end
|
519
727
|
|
728
|
+
|
520
729
|
state[:line_i] += 1
|
730
|
+
|
731
|
+
context_info = {}
|
732
|
+
|
733
|
+
context_info[:current_address] = state[:current_address]
|
734
|
+
|
735
|
+
context_info[:line_i] = state[:line_i]
|
736
|
+
|
737
|
+
state[:extra_state][:info_entries] ||= Hash.new
|
738
|
+
|
739
|
+
state[:extra_state][:info_entries][info_name] ||= Array.new
|
740
|
+
|
741
|
+
state[:extra_state][:info_entries][info_name] << {context_info: context_info, input_str: info_value, input_full: operands[1]}
|
742
|
+
|
743
|
+
state
|
744
|
+
end
|
745
|
+
},
|
746
|
+
{
|
747
|
+
keyword: "namespace",
|
748
|
+
func: lambda do |operands, state|
|
749
|
+
|
750
|
+
raise "Incorrect use of the \".namespace\" directive" if operands.size != 1
|
751
|
+
|
752
|
+
namespace_name = operands[0][:definition]
|
753
|
+
|
754
|
+
|
755
|
+
namespace_lines = []
|
756
|
+
|
757
|
+
line_i = state[:line_i] + 1
|
758
|
+
|
759
|
+
whitespace_regexp = /[#{Kompiler::Config.whitespace_chars.join("|")}]*/
|
760
|
+
end_namespace_regexp = /\A#{whitespace_regexp}\.?endnamespace#{whitespace_regexp}\z/
|
761
|
+
|
762
|
+
start_namespace_regexp = /\A#{whitespace_regexp}\.?namespace/
|
763
|
+
|
764
|
+
namespace_level = 1
|
765
|
+
|
766
|
+
while line_i < state[:lines].size
|
767
|
+
if state[:lines][line_i].match?(end_namespace_regexp)
|
768
|
+
namespace_level -= 1
|
769
|
+
end
|
770
|
+
break if namespace_level == 0
|
771
|
+
|
772
|
+
# Indicates whether a line should be looked at and changed based on whether it's on the first level
|
773
|
+
line_change = namespace_level == 1
|
774
|
+
|
775
|
+
namespace_lines << [line_change, state[:lines][line_i]]
|
776
|
+
|
777
|
+
if state[:lines][line_i].match?(start_namespace_regexp)
|
778
|
+
namespace_level += 1
|
779
|
+
end
|
780
|
+
|
781
|
+
line_i += 1
|
782
|
+
end
|
783
|
+
|
784
|
+
|
785
|
+
raise "\".endnamespace\" was not found for \".namespace\"" if line_i == state[:lines].size
|
786
|
+
|
787
|
+
|
788
|
+
# Collect the definitions that happen inside of the namespace
|
789
|
+
defs = []
|
790
|
+
|
791
|
+
namespace_lines.each do |line_info|
|
792
|
+
|
793
|
+
line_change, line = line_info
|
794
|
+
|
795
|
+
next if !line_change
|
796
|
+
|
797
|
+
keyword, operands = Kompiler::Parsers.extract_instruction_parts(line)
|
798
|
+
|
799
|
+
is_instruction = false
|
800
|
+
Kompiler::Architecture.instructions.each do |instr|
|
801
|
+
is_instruction = instr[:keyword] == keyword
|
802
|
+
break if is_instruction
|
803
|
+
end
|
804
|
+
|
805
|
+
next if is_instruction
|
806
|
+
|
807
|
+
keyword = "." + keyword if keyword[0] != "."
|
808
|
+
|
809
|
+
|
810
|
+
case keyword
|
811
|
+
when ".value"
|
812
|
+
value_name = operands[0]
|
813
|
+
defs << {type: "value", name: value_name}
|
814
|
+
when ".macro"
|
815
|
+
# Remove the ".macro"
|
816
|
+
macro_def = line.split(keyword)[1..].join(keyword)
|
817
|
+
macro_name, _ = Kompiler::Parsers.extract_instruction_parts(macro_def)
|
818
|
+
defs << {type: "macro", name: macro_name}
|
819
|
+
when ".label"
|
820
|
+
label_name = operands[0]
|
821
|
+
defs << {type: "label", name: label_name}
|
822
|
+
when ".namespace"
|
823
|
+
inside_namespace_name = operands[0]
|
824
|
+
defs << {type: "namespace", name: inside_namespace_name}
|
825
|
+
end
|
826
|
+
|
827
|
+
end
|
828
|
+
|
829
|
+
|
830
|
+
# Create replacements for each definition inside of the namespace
|
521
831
|
|
832
|
+
replacements = defs.map do |definition|
|
833
|
+
[definition[:name], namespace_name + "." + definition[:name]]
|
834
|
+
end.to_h
|
835
|
+
defs.each do |definition|
|
836
|
+
next if definition[:type] != "namespace"
|
837
|
+
replacements[definition[:name] + "."] = namespace_name + "." + definition[:name] + "."
|
838
|
+
end
|
839
|
+
|
840
|
+
|
841
|
+
# Replace all words in the namespace that are equal to replacement[:from] as replacement[:to]
|
842
|
+
# Logic same as .macro's
|
843
|
+
|
844
|
+
namespace_lines.each_with_index do |line_info, line_i|
|
845
|
+
|
846
|
+
line_change, line = line_info
|
847
|
+
|
848
|
+
next if !line_change
|
849
|
+
|
850
|
+
start_i = 0
|
851
|
+
|
852
|
+
# Loop through each character starting position
|
853
|
+
while start_i < line.size
|
854
|
+
|
855
|
+
# Skip whitespace characters
|
856
|
+
if Kompiler::Config.whitespace_chars.include?(line[start_i])
|
857
|
+
start_i += 1
|
858
|
+
next
|
859
|
+
end
|
860
|
+
|
861
|
+
# Skip string definitions
|
862
|
+
if Kompiler::Config.string_delimiters.include? line[start_i]
|
863
|
+
str, parsed_length = Kompiler::Parsers.parse_str line[start_i..]
|
864
|
+
start_i += parsed_length
|
865
|
+
next
|
866
|
+
end
|
867
|
+
|
868
|
+
cut_line = line[start_i..]
|
869
|
+
|
870
|
+
skip_to_next_word = true
|
871
|
+
|
872
|
+
# Check if one of the argument names works
|
873
|
+
replacements.each do |from, to|
|
874
|
+
next if !cut_line.start_with?(from) # Skip the argument if the line doesn't begin with it
|
875
|
+
next if Kompiler::Config.keyword_chars.include?(cut_line[from.size]) && from[-1] != "." # Skip if the argument is a partial word. So, for the argument 'arg', this will skip in the case of 'arg1'
|
876
|
+
# Here if the argument name should be replaced with the contents
|
877
|
+
skip_to_next_word = from[-1] == "." # Skip to the next word only if the replacement is a namespace call
|
878
|
+
|
879
|
+
new_line = line[...start_i] + to + cut_line[from.size..]
|
880
|
+
|
881
|
+
|
882
|
+
start_i += to.size
|
883
|
+
|
884
|
+
namespace_lines[line_i] = [true, new_line]
|
885
|
+
line = namespace_lines[line_i]
|
886
|
+
|
887
|
+
break # Skip the arguments loop
|
888
|
+
end
|
889
|
+
|
890
|
+
# Check if an argument was found
|
891
|
+
# If not, skip the text until the next whitespace character
|
892
|
+
if skip_to_next_word
|
893
|
+
while start_i < line.size && Kompiler::Config.keyword_chars.include?(line[start_i])
|
894
|
+
start_i += 1
|
895
|
+
end
|
896
|
+
start_i += 1 # Move one more character
|
897
|
+
end
|
898
|
+
|
899
|
+
end
|
900
|
+
|
901
|
+
end
|
902
|
+
|
903
|
+
namespace_lines.map!{_1[1]}
|
904
|
+
|
905
|
+
state[:lines] = state[:lines][...state[:line_i]] + namespace_lines + state[:lines][(state[:line_i] + namespace_lines.size + 2)..]
|
906
|
+
|
522
907
|
state
|
523
908
|
end
|
524
909
|
}
|
@@ -77,10 +77,10 @@ module Kompiler
|
|
77
77
|
|
78
78
|
|
79
79
|
# Converts a label hash of name-address pairs into an array of symbols
|
80
|
-
def self.labels_to_symbols labels
|
80
|
+
def self.labels_to_symbols labels, vaddr: 0
|
81
81
|
out = []
|
82
82
|
labels.each do |name, value|
|
83
|
-
out << {name: name, value: value, type: 0, binding: 0}
|
83
|
+
out << {name: name, value: value + vaddr, type: 0, binding: 0}
|
84
84
|
end
|
85
85
|
out
|
86
86
|
end
|