kompiler 0.3.2 → 0.3.4

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: 12bbbb642809ac234ac1cfdcd59b3d43362c0ba284a6286656fecc2cf5024e99
4
- data.tar.gz: 86e653003489923b7a63bd24b0d6019b07fde08c10c8dc426357dca265b3445e
3
+ metadata.gz: 70dad0d1372236affa1fba712fff8b5a7d07d5cb99aa13fe41f4e39b92294901
4
+ data.tar.gz: 38345074dffd505b6ba7f99283bc851ed17e0d7afc7df684933e8bd3420854b6
5
5
  SHA512:
6
- metadata.gz: e729dfc9ee22702200ea082beef419bdbcbaa6bc2ba5a4cadbc2899f6442c6f65c163ec90675ce8d8e2fe41cd80e97cee445fb7386547e78f5569016bbba69ed
7
- data.tar.gz: d88ba4f53b56086cae6e63f8308058f3f28735f8db671d122c3eecbd3270e5443a1945407b7e8e73b15859758897992f2183481f667282fea29ee57f47f2b494
6
+ metadata.gz: 42997a073796509d38379a196c0caafd0dfa3e5ee3c0f588072dd936dd84220e4186a963d78f75525075d79a72d549667daaae79915f60629bdca3b51b06461e
7
+ data.tar.gz: 925cccfb86ddd19a56798b8af0070e7217678fa4a0d3159064057885e40790a26a3b7e07ccc4775bd2a93614f428082bab8c8b68fe13c18c191fd456a1236330
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
 
@@ -26,10 +26,18 @@ arg_letters = []
26
26
 
27
27
  current_i = 0
28
28
 
29
- permitted_word_chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["_", ".", "-"]
29
+ permitted_word_chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["_", ".", "-", "[", "]"]
30
30
 
31
31
  whitespace_chars = ["\t", " "]
32
32
 
33
+
34
+ $positive_opts = ["true", "yes"]
35
+ $negative_opts = ["false", "no"]
36
+
37
+ $bool_opts = $positive_opts + $negative_opts
38
+
39
+
40
+
33
41
  def get_word string, permitted_word_chars, offset = 0
34
42
  if string[0] == '"'
35
43
  str_content, n_parsed = Kompiler::Parsers.parse_str(string[offset..])
@@ -77,7 +85,17 @@ while current_i < arg_string.size
77
85
 
78
86
  letters, current_i = get_word(arg_string, permitted_word_chars, current_i)
79
87
 
80
- arg_letters += letters.chars
88
+ if arg_string[current_i] == "="
89
+ current_i += 1
90
+ value, current_i = get_word(arg_string, permitted_word_chars, current_i)
91
+
92
+ arg_keys << [letters, value]
93
+ elsif whitespace_chars.include?(arg_string[current_i]) || current_i == arg_string.size
94
+ arg_letters += letters.chars
95
+ else
96
+ puts "Unrecognized argument syntax at index #{current_i}"
97
+ exit
98
+ end
81
99
 
82
100
  next
83
101
  end
@@ -90,6 +108,44 @@ end
90
108
 
91
109
 
92
110
 
111
+
112
+ def get_number str
113
+ bool, val = Kompiler::Parsers.check_immediate_operand(str)
114
+ return (bool == false) ? nil : val
115
+ end
116
+
117
+
118
+ def get_arg_key arg_keys, keys, default_value=0
119
+ keys = [keys] if !keys.is_a?(Array)
120
+ filtered = arg_keys.filter{keys.include? _1[0]}[0]
121
+ value = (filtered != nil) ? filtered[1] : default_value
122
+ return value
123
+ end
124
+
125
+
126
+ class String
127
+ def to_num
128
+ status, val = Kompiler::Parsers.check_immediate_operand(self)
129
+ return (status == false) ? nil : val[:value]
130
+ end
131
+ end
132
+
133
+
134
+ def is_bool_opt opt
135
+ $bool_opts.include?(opt)
136
+ end
137
+
138
+ def is_pos_bool opt
139
+ $positive_opts.include? opt
140
+ end
141
+
142
+ def is_neg_bool opt
143
+ $negative_opts.include? opt
144
+ end
145
+
146
+
147
+
148
+
93
149
  # Print the help screen if requested
94
150
  if arg_opts.include?("help") || arg_letters.include?("h")
95
151
  puts """Usage: kompile <input_file> [<output_file>] [compile_options]
@@ -100,35 +156,170 @@ Compile a file:
100
156
  output_file Path to the output file (optional, default is out.bin)
101
157
 
102
158
  Compilation options:
103
- --arch=<arch_name> Compile for the specified architecture (optional, default is armv8a)
104
- --wrap=<format> Wrap the compiled program in the specified format (default is none)
159
+ --arch=<arch_name>, -ar Compile for the specified architecture (optional, default is armv8a)
160
+ --wrap=<format>, -w Wrap the compiled program in the specified format (default is none)
161
+ --no-aliases, -n Specifies whether to use the aliases configuration file (default is yes)
162
+ --aliases-file=<file>, -a Loads the provided aliases file instead of the usual one
105
163
 
106
164
  Available wrapping formats:
107
165
  none
108
166
  elf.obj
109
167
  elf.exec
110
168
  mach-o.obj
111
- mach-o.exec [not implemented yet]
112
-
169
+ mach-o.exec
170
+
113
171
  Additional options for wrapping are:
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)
116
- --mach-o-machine=<cputype.subtype> Specifies Mach-O header's cputype and subtype
117
- to be the type provided
118
- --mach-o-archtype=<type> Specifies the file architecture type to either 32 or 64 (default is 64)
172
+ --elf-machine=<type>, -m Specifies ELF header's e_machine to be the type provided (default is 0)
173
+ --elf-class=<class>, -c Specifies the ELF file's class to either 32 or 64 (default is 64)
174
+ --mach-o-machine=<cputype.subtype>, -m Specifies Mach-O header's cputype and subtype
175
+ to be the type provided
176
+ --mach-o-archtype=<type>, -t Specifies the file architecture type to either 32 or 64 (default is 64)
177
+ --exec-type=<type>, -et Used with --wrap=mach-o.exec. Specifies whether the executable is
178
+ statically (static) or dynamically (dylink) linked (default is dylink)
179
+ --mach-o-threadstate=<type>, -ts Used with --wrap=mach-o.exec and --exec-type=static. Specifies which
180
+ thread state type to use (arm64, arm32, x86-64 or x86-32)
181
+ --codesign=<yes|no>, -cs Used with --wrap=mach-o.*. Specifies whether to add a basic
182
+ code signature to the Mach-O file (default is no)
119
183
 
120
184
  Available options:
121
- --help, -h Prints this information
122
- --list-architectures Lists available architectures
123
- --list-instructions [arch] Lists available instructions for the specified architecture
124
- --list-registers [arch] Lists available registers for the specified architecture
185
+ --help, -h Prints this information
186
+ --list-architectures,-lar Lists available architectures
187
+ --list-instructions [arch],-li Lists available instructions for the specified architecture
188
+ --list-registers [arch],-lr Lists available registers for the specified architecture
189
+ --list-aliases,-la Lists instruction aliases in the current configuration
190
+ --compact,-c Used with --list-instructions or --list-registers to print compact information
191
+
192
+ Available options for controlling aliases:
193
+ --add-alias[es] index keyword aliases Add the specified aliases to the configuration
194
+ --remove-alias[es] index keyword aliases Remove the specified aliases from the configuration
195
+ --reset-aliases Resets aliases to an empty configuration
196
+ --import-aliases filename Set the provided configuration as the default aliases
197
+ --export-aliases filename Export the default aliases into a file
125
198
  """
126
199
  exit # Exit
127
200
  end
128
201
 
202
+ no_aliases = arg_opts.include?("no-aliases") || arg_letters.include?("n")
203
+
204
+ arg_opts.delete "no-aliases"
205
+ arg_letters.delete "n"
206
+
207
+ use_aliases = !no_aliases
208
+
209
+ aliases_file = get_arg_key(arg_keys, ["aliases-file", "a"], nil)
210
+
211
+ if aliases_file
212
+ aliases_file = File.expand_path(aliases_file)
213
+ end
214
+
215
+ if use_aliases
216
+ if aliases_file
217
+ Kompiler::AliasManager.import_aliases_file aliases_file
218
+ else
219
+ Kompiler::AliasManager.load_aliases()
220
+ end
221
+ end
222
+
223
+
224
+ if arg_opts == ["add-aliases"] || arg_opts == ["add-alias"]
225
+ idx = arg_words[0]
226
+ keyword = arg_words[1]
227
+ aliases = arg_words[2..]
228
+ if idx[0] == "[" && idx[-1] == "]"
229
+ idx = idx[1...-1]
230
+ end
231
+ idx = idx.to_i
232
+
233
+ Kompiler::AliasManager.add_alias idx, keyword, *aliases
234
+
235
+ if aliases_file
236
+ Kompiler::AliasManager.export_aliases_file aliases_file
237
+ else
238
+ Kompiler::AliasManager.save_aliases()
239
+ end
240
+
241
+ exit
242
+ end
243
+
244
+ if arg_opts == ["remove-aliases"] || arg_opts == ["remove-alias"]
245
+ idx = arg_words[0]
246
+ keyword = arg_words[1]
247
+ aliases = arg_words[2..]
248
+ if idx[0] == "[" && idx[-1] == "]"
249
+ idx = idx[1...-1]
250
+ end
251
+ idx = idx.to_i
252
+
253
+ Kompiler::AliasManager.remove_alias idx, keyword, *aliases
254
+
255
+ if aliases_file
256
+ Kompiler::AliasManager.export_aliases_file aliases_file
257
+ else
258
+ Kompiler::AliasManager.save_aliases()
259
+ end
260
+
261
+ exit
262
+ end
263
+
264
+
265
+ if arg_opts == ["reset-aliases"]
266
+ Kompiler::AliasManager.reset_aliases()
267
+
268
+ if aliases_file
269
+ Kompiler::AliasManager.export_aliases_file aliases_file
270
+ else
271
+ Kompiler::AliasManager.save_aliases()
272
+ end
273
+
274
+ exit
275
+ end
276
+
277
+
278
+ if arg_opts == ["import-aliases"]
279
+ filename = arg_words[0]
280
+ if filename == nil
281
+ puts "kompile: A file path must be provided with --aliases-import."
282
+ puts "Type \"kompile --help\" for more information."
283
+ exit
284
+ end
285
+ filename = File.expand_path(filename)
286
+ if !File.exist?(filename)
287
+ puts "kompile: File \"#{filename}\" does not exist."
288
+ exit
289
+ end
290
+ Kompiler::AliasManager.import_aliases_file(filename)
291
+ Kompiler::AliasManager.save_aliases()
292
+
293
+ exit
294
+ end
295
+
296
+ if arg_opts == ["export-aliases"]
297
+ filename = arg_words[0]
298
+ if filename == nil
299
+ puts "kompile: A file path must be provided with --aliases-import."
300
+ puts "Type \"kompile --help\" for more information."
301
+ exit
302
+ end
303
+ filename = File.expand_path(filename)
304
+
305
+ Kompiler::AliasManager.export_aliases_file(filename)
306
+
307
+ exit
308
+ end
309
+
310
+ if arg_opts.include?("list-aliases") || arg_letters == ["l", "a"]
311
+ max_index = Kompiler::AliasManager.aliases.map{_1[:index]}.max
312
+ pad_width = max_index.to_s.size + 2
313
+ Kompiler::AliasManager.aliases.each do |alias_entry|
314
+ idx_string = alias_entry[:index].to_s
315
+ puts "[#{idx_string}]" + " " * (pad_width - idx_string.size) + "#{alias_entry[:keyword]}: " + alias_entry[:aliases].join(" ")
316
+ end
317
+ exit
318
+ end
319
+
129
320
 
130
321
  # Print the available architectures if requested
131
- if arg_opts == ["list-architectures"]
322
+ if arg_opts.include?("list-architectures") || arg_letters == ["l", "a", "r"]
132
323
  Kompiler::ArchManager.load_all_entries()
133
324
  puts "Available architectures:"
134
325
 
@@ -138,31 +329,82 @@ if arg_opts == ["list-architectures"]
138
329
  exit # Exit
139
330
  end
140
331
 
141
- if arg_opts == ["list-instructions"]
332
+ if arg_opts.include?("list-instructions") || arg_letters[...2] == ["l", "i"]
142
333
  arch_name = arg_words[0] || "armv8a"
143
334
  Kompiler::ArchManager.load_all_entries()
144
- arch = Kompiler::ArchManager.get_arch(arch_name)
335
+ Kompiler::ArchManager.load_arch arch_name
336
+
337
+ Kompiler::AliasManager.apply_aliases()
338
+
339
+ if !(arg_opts.include?("compact") || arg_letters.include?("c"))
340
+
341
+ # Calculate the size of the index column
342
+ idx_col_width = [7, (Kompiler::Architecture.instructions.length + 1).to_s.size + 3].max
343
+
344
+ console_width = IO.console.winsize[1] rescue 80
345
+
346
+ desc_col_width = console_width - idx_col_width
347
+
348
+ desc_col_padding = " " * idx_col_width
145
349
 
146
- require arch[:include_path]
350
+ Kompiler::Architecture.instructions.each_with_index do |instruction, index|
351
+
352
+ display_index = (index + 1).to_s
353
+
354
+ print "[#{display_index}]"
355
+
356
+ print " " * (idx_col_width - 2 - display_index.bytesize)
357
+
358
+ print instruction[:keyword]
359
+
360
+ print " "
361
+
362
+ puts instruction[:operands].map{"<" + (_1[:name] || _1[:type] || "") + ">"}.join(" ")
363
+
364
+ desc = instruction[:description] || ""
365
+
366
+ puts desc_col_padding + desc
367
+
368
+
369
+ if instruction.keys.include?(:aliases) && instruction[:aliases].size > 0
370
+ aliases_str = "Aliases: " + instruction[:aliases].join(" ")
371
+ puts desc_col_padding + aliases_str
372
+ end
373
+
374
+ print "\n"
375
+ end
376
+
377
+ else
378
+
379
+ strings = []
380
+
381
+ Kompiler::Architecture.instructions.each_with_index do |instruction, index|
382
+ str = "#{index + 1} #{instruction[:keyword]}"
383
+ strings << str
384
+ end
385
+
386
+ puts strings.join(", ")
147
387
 
148
- Kompiler::Architecture.instructions.each do |instr|
149
- puts "#{instr[:keyword]} (#{instr[:operands].size} operands): #{instr[:name]}"
150
- puts instr[:description]
151
- puts
152
388
  end
153
389
 
154
390
  exit
155
391
  end
156
392
 
157
- if arg_opts == ["list-registers"]
393
+
394
+
395
+
396
+
397
+ if arg_opts.include?("list-registers") || arg_letters[...2] == ["l", "r"]
158
398
  arch_name = arg_words[0] || "armv8a"
159
399
  Kompiler::ArchManager.load_all_entries()
160
- arch = Kompiler::ArchManager.get_arch(arch_name)
161
-
162
- require arch[:include_path]
400
+ Kompiler::ArchManager.load_arch(arch_name)
163
401
 
164
- Kompiler::Architecture.registers.each do |reg|
165
- puts "#{reg[:reg_name]}"
402
+ if !(arg_opts.include?("compact") || arg_letters.include?("c"))
403
+ Kompiler::Architecture.registers.each do |reg|
404
+ puts "#{reg[:reg_name]}"
405
+ end
406
+ else
407
+ puts Kompiler::Architecture.registers.map{_1[:reg_name]}.join(", ")
166
408
  end
167
409
 
168
410
  exit
@@ -199,19 +441,7 @@ end
199
441
 
200
442
 
201
443
 
202
-
203
- wrap_opt = arg_keys.select{_1[0] == "wrap"}
204
-
205
-
206
- if wrap_opt.size > 1
207
- puts "kompile: Only one wrapping option can be provided with \"--wrap\"."
208
- puts "Type \"kompile --help\" for more information."
209
- exit
210
- elsif wrap_opt.size == 1
211
- wrap_opt = wrap_opt[0][1]
212
- else
213
- wrap_opt = "none"
214
- end
444
+ wrap_opt = get_arg_key(arg_keys, ["wrap", "w"], "none")
215
445
 
216
446
 
217
447
  if !["elf.obj", "elf.exec", "mach-o.obj", "mach-o.exec", "none"].include?(wrap_opt)
@@ -222,18 +452,9 @@ end
222
452
 
223
453
 
224
454
 
455
+ arch_name = get_arg_key(arg_keys, ["arch", "ar"], "armv8a")
225
456
 
226
- arch_opt = arg_keys.select{_1[0] == "arch"}
227
457
 
228
- if arch_opt.size > 1
229
- puts "kompile: Only one architecture can be provided with \"--arch\"."
230
- puts "Type \"kompile --help\" for more information."
231
- exit
232
- elsif arch_opt.size == 1
233
- arch_name = arch_opt[0][1]
234
- else
235
- arch_name = "armv8a"
236
- end
237
458
 
238
459
  # Load all the architecture entries
239
460
 
@@ -258,6 +479,10 @@ end
258
479
 
259
480
 
260
481
 
482
+ Kompiler::AliasManager.apply_aliases()
483
+
484
+
485
+
261
486
  code = File.binread(in_filename)
262
487
 
263
488
  detailed_out = Kompiler::CompilerFunctions.detailed_compile(code)
@@ -272,44 +497,119 @@ labels = detailed_out[:labels]
272
497
  labels.delete "here"
273
498
 
274
499
 
500
+
501
+ add_exec_permission = false
502
+
503
+
275
504
  case wrap_opt
276
505
  when "none"
277
506
  out = code
278
507
  when "elf.obj"
279
- elf_machine = arg_keys.filter{_1[0] == "elf-machine"}[0]
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
508
+ elf_machine = get_arg_key(arg_keys, ["elf-machine", "m"], "0").to_num
509
+ elf_class = get_arg_key(arg_keys, ["elf-class", "c"], "64").to_num
510
+
511
+ if ![32, 64].include?(elf_class)
512
+ puts "kompile: Invalid ELF class specified."
513
+ puts "Type \"kompile --help\" for more information."
514
+ exit
515
+ end
285
516
 
286
517
  symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
287
518
  out = Kompiler::Wrappers::ELF.wrap_obj(code, symbols, machine: elf_machine, elf_class: elf_class)
288
519
  when "elf.exec"
289
- elf_machine = arg_keys.filter{_1[0] == "elf-machine"}[0]
290
- elf_machine = (elf_machine != nil) ? elf_machine[1].to_i : 0
520
+ vaddr = 0x80000
291
521
 
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
522
+ elf_machine = get_arg_key(arg_keys, ["elf-machine", "m"], "0").to_num
523
+ elf_class = get_arg_key(arg_keys, ["elf-class", "c"], "64").to_num
295
524
 
296
- symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
297
- out = Kompiler::Wrappers::ELF.wrap_exec(code, symbols, machine: elf_machine, elf_class: elf_class)
525
+ if ![32, 64].include?(elf_class)
526
+ puts "kompile: Invalid ELF class specified."
527
+ puts "Type \"kompile --help\" for more information."
528
+ exit
529
+ end
530
+
531
+
532
+ symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels, vaddr: vaddr)
533
+ out = Kompiler::Wrappers::ELF.wrap_exec(code, symbols, machine: elf_machine, elf_class: elf_class, vaddr: vaddr)
534
+
535
+ add_exec_permission = true
536
+
298
537
  when "mach-o.obj"
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)
538
+ macho_cpu = get_arg_key(arg_keys, ["mach-o-machine", "m"], "0.0")
539
+ cputype, cpusubtype = macho_cpu.split(".").map(&:to_num)
540
+
541
+ if [cputype, cpusubtype].include? nil
542
+ puts "kompile: Invalid Mach-O machine specified."
543
+ puts "Type \"kompile --help\" for more information."
544
+ exit
545
+ end
546
+
547
+ arch_type = get_arg_key(arg_keys, ["mach-o-archtype", "at"], "64").to_num
548
+
549
+ codesign = get_arg_key(arg_keys, ["codesign", "cs"], "false")
550
+ if !is_bool_opt(codesign)
551
+ puts "kompile: Invalid --codesign value."
552
+ puts "Type \"kompile --help\" for more information."
553
+ exit
554
+ end
302
555
 
303
- arch_type = arg_keys.filter{_1[0] == "mach-o-archtype"}[0]
304
- arch_type = (arch_type != nil) ? arch_type[1].to_i : 64
556
+ codesign = is_pos_bool(codesign)
305
557
 
306
558
  symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
307
559
  out = Kompiler::Wrappers::MachO.wrap_obj(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type)
308
560
  when "mach-o.exec"
309
- puts "Mach-O executable not yet implemented."
310
- exit
561
+ macho_cpu = get_arg_key(arg_keys, ["mach-o-machine", "m"], "0.0")
562
+ cputype, cpusubtype = macho_cpu.split(".").map(&:to_num)
563
+
564
+ if [cputype, cpusubtype].include? nil
565
+ puts "kompile: Invalid Mach-O machine specified."
566
+ puts "Type \"kompile --help\" for more information."
567
+ exit
568
+ end
569
+
570
+ arch_type = get_arg_key(arg_keys, ["mach-o-archtype", "t"], "64").to_num
571
+
572
+ codesign = get_arg_key(arg_keys, ["codesign", "cs"], "false")
573
+ if !is_bool_opt(codesign)
574
+ puts "kompile: Invalid --codesign value."
575
+ puts "Type \"kompile --help\" for more information."
576
+ exit
577
+ end
578
+
579
+ exec_type = get_arg_key(arg_keys, ["exec-type", "et"], "dylink")
580
+
581
+
582
+ codesign = is_pos_bool(codesign)
583
+
584
+
585
+ case exec_type
586
+ when "dylink"
587
+ symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
588
+ out = Kompiler::Wrappers::MachO.wrap_exec_dylink(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type, codesign: codesign)
589
+ when "static"
590
+ symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
591
+
592
+ thread_state_arch = get_arg_key(arg_keys, ["mach-o-threadstate", "ts"], "arm64")
593
+
594
+ entry_address = 0x1000000
595
+
596
+ thread_state = Kompiler::Wrappers::MachO.build_thread_state arch: thread_state_arch, entry_address: 0x1000000, stack_pointer: 0
597
+
598
+ 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)
599
+
600
+ else
601
+ puts "kompile: Invalid Mach-O executable type specified."
602
+ puts "Type \"kompile --help\" for more information."
603
+ exit
604
+ end
605
+
606
+ add_exec_permission = true
311
607
  end
312
608
 
313
609
 
314
610
  File.binwrite out_filename, out
315
611
 
612
+ if add_exec_permission
613
+ FileUtils.chmod "+x", out_filename
614
+ end
615
+