kompiler 0.3.2 → 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 +136 -29
- data/lib/kompiler/directives.rb +361 -5
- data/lib/kompiler/wrappers/elf_wrapper.rb +2 -2
- data/lib/kompiler/wrappers/macho_wrapper.rb +889 -56
- data/lib/kompiler/wrappers/packed_bytes.rb +18 -4
- metadata +2 -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
|
|
@@ -108,20 +108,27 @@ Available wrapping formats:
|
|
108
108
|
elf.obj
|
109
109
|
elf.exec
|
110
110
|
mach-o.obj
|
111
|
-
mach-o.exec
|
112
|
-
|
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
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)
|
119
125
|
|
120
126
|
Available options:
|
121
127
|
--help, -h Prints this information
|
122
128
|
--list-architectures Lists available architectures
|
123
129
|
--list-instructions [arch] Lists available instructions for the specified architecture
|
124
130
|
--list-registers [arch] Lists available registers for the specified architecture
|
131
|
+
--list-lexis Lists available lexis entries
|
125
132
|
"""
|
126
133
|
exit # Exit
|
127
134
|
end
|
@@ -141,14 +148,18 @@ end
|
|
141
148
|
if arg_opts == ["list-instructions"]
|
142
149
|
arch_name = arg_words[0] || "armv8a"
|
143
150
|
Kompiler::ArchManager.load_all_entries()
|
144
|
-
|
151
|
+
Kompiler::ArchManager.load_arch arch_name
|
145
152
|
|
146
|
-
|
153
|
+
Kompiler::Architecture.instructions.each do |instruction|
|
154
|
+
print instruction[:keyword]
|
155
|
+
|
156
|
+
print " "
|
157
|
+
|
158
|
+
puts instruction[:operands].map{"<" + (_1[:name] || _1[:type] || "") + ">"}.join(" ")
|
159
|
+
|
160
|
+
puts instruction[:description]
|
147
161
|
|
148
|
-
|
149
|
-
puts "#{instr[:keyword]} (#{instr[:operands].size} operands): #{instr[:name]}"
|
150
|
-
puts instr[:description]
|
151
|
-
puts
|
162
|
+
print "\n"
|
152
163
|
end
|
153
164
|
|
154
165
|
exit
|
@@ -235,6 +246,7 @@ else
|
|
235
246
|
arch_name = "armv8a"
|
236
247
|
end
|
237
248
|
|
249
|
+
|
238
250
|
# Load all the architecture entries
|
239
251
|
|
240
252
|
Kompiler::ArchManager.load_all_entries()
|
@@ -272,44 +284,139 @@ labels = detailed_out[:labels]
|
|
272
284
|
labels.delete "here"
|
273
285
|
|
274
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
|
+
|
275
311
|
case wrap_opt
|
276
312
|
when "none"
|
277
313
|
out = code
|
278
314
|
when "elf.obj"
|
279
|
-
elf_machine = arg_keys
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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
|
285
323
|
|
286
324
|
symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
|
287
325
|
out = Kompiler::Wrappers::ELF.wrap_obj(code, symbols, machine: elf_machine, elf_class: elf_class)
|
288
326
|
when "elf.exec"
|
289
|
-
|
290
|
-
elf_machine = (elf_machine != nil) ? elf_machine[1].to_i : 0
|
327
|
+
vaddr = 0x80000
|
291
328
|
|
292
|
-
|
293
|
-
elf_class
|
294
|
-
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
|
295
331
|
|
296
|
-
|
297
|
-
|
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
|
+
|
298
344
|
when "mach-o.obj"
|
299
|
-
macho_cpu = arg_keys
|
300
|
-
|
301
|
-
|
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
|
302
353
|
|
303
|
-
arch_type = arg_keys
|
304
|
-
|
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")
|
305
364
|
|
306
365
|
symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
|
307
366
|
out = Kompiler::Wrappers::MachO.wrap_obj(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type)
|
308
367
|
when "mach-o.exec"
|
309
|
-
|
310
|
-
|
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
|
311
414
|
end
|
312
415
|
|
313
416
|
|
314
417
|
File.binwrite out_filename, out
|
315
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,200 @@ 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
|
519
521
|
|
520
|
-
state[:line_i] +=
|
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
|
521
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
|
+
|
522
714
|
state
|
523
715
|
end
|
524
716
|
},
|
@@ -530,7 +722,7 @@ end
|
|
530
722
|
info_name = operands[0][:definition]
|
531
723
|
info_value = operands[1][:definition]
|
532
724
|
rescue
|
533
|
-
|
725
|
+
raise "Incorrect use of the \".info\" directive."
|
534
726
|
end
|
535
727
|
|
536
728
|
|
@@ -548,6 +740,170 @@ end
|
|
548
740
|
|
549
741
|
state[:extra_state][:info_entries][info_name] << {context_info: context_info, input_str: info_value, input_full: operands[1]}
|
550
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
|
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
|
+
|
551
907
|
state
|
552
908
|
end
|
553
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
|