package linksem

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file command_line.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
(*Generated by Lem from command_line.lem.*)
open Lem_basic_classes
open Lem_function
open Lem_string
open Lem_string_extra
open Lem_tuple
open Lem_bool
open Lem_list
open Lem_list_extra
(*import Set*)
(*import Set_extra*)
open Lem_sorting
open Lem_num
open Lem_maybe
open Lem_assert_extra

open Byte_sequence
open Default_printing
open Error
open Missing_pervasives
open Show

(* Here we try to model the command line of GNU ld.bfd.
 * 
 * Some options are global modifiers affecting the link output. 
 * Others have effect only for some subset of input files.
 * Typically some mutually-exclusive possibilities exist
 * whereby each argument selects one such possibility for all subsequent input files, 
 * until a different argument selects another possibility for ensuring inputs.
 *)

type input_file_spec = Filename of string    (* /path/to/file.{o,a,so,...} -- might be script! *)
                     | Libname of string     (* -llib *)

(*val string_of_input_file_spec : input_file_spec -> string*)
let string_of_input_file_spec spec:string= 
     ((match spec with
        Filename(s) -> "file `" ^ (s ^ "'")
        | Libname(s) -> "library `" ^ (s ^ "'")
    ))

let instance_Show_Show_Command_line_input_file_spec_dict:(input_file_spec)show_class= ({

  show_method = string_of_input_file_spec})

type input_file_options = { input_fmt : string
                           ; input_libpath : string list
                           ; input_link_sharedlibs : bool      (* -Bstatic *)
                           ; input_check_sections : bool
                           ; input_copy_dt_needed : bool
                           ; input_whole_archive : bool
                           ; input_as_needed : bool
                           }
   
(*val null_input_file_options : input_file_options*)
let null_input_file_options:input_file_options= 
                       ({ input_fmt = ""
                       ; input_libpath = ([])
                       ; input_link_sharedlibs = false
                       ; input_check_sections = false
                       ; input_copy_dt_needed = false
                       ; input_whole_archive = false
                       ; input_as_needed = false
                       })

type output_kind = Executable
                 | SharedLibrary

type link_option = OutputFilename of string
                 | OutputKind of output_kind
                 | ForceCommonDefined of bool        (* -d, -dc, -dp *)
                 | Soname of string                  (* -soname *)
                 | EntryAddress of Nat_big_num.num
                 | TextSegmentStart of Nat_big_num.num
                 | RodataSegmentStart of Nat_big_num.num
                 | LdataSegmentStart of Nat_big_num.num
                 | BindFunctionsEarly                (* -Bsymbolic-functions *)
                 | BindNonFunctionsEarly              (* the remainder of -Bsymbolic *)
                 (* more here! *) 

(*val tagEqual : link_option -> link_option -> bool*)
let tagEqual opt1 opt2:bool=  ((match (opt1, opt2) with
    (* FIXME: Lem BUG here! says "duplicate binding" *)
    (OutputFilename(_), OutputFilename(_)) -> true
    | (OutputKind(_), OutputKind(_)) -> true
    (* | (ForceCommonDefined, ForceCommonDefined) -> true *)
    | (Soname(_), Soname(_)) -> true
    (* | (EntryAddress, EntryAddress) -> true *)
    | (TextSegmentStart(_), TextSegmentStart(_)) -> true
    | (RodataSegmentStart(_), RodataSegmentStart(_)) -> true
    | (LdataSegmentStart(_), LdataSegmentStart(_)) -> true
    (* | (BindFunctionsEarly, BindFunctionsEarly) -> true *)
    (* | (BindNonFunctionsEarly, BindNonFunctionsEarly) -> true *)
    | _ -> false
))

(* To allow filtering out a previous setting for a given option, we define
 * an equality relation that is true if options are of the same constructor.
 * Seems like a bit of a HACK. *)
let instance_Basic_classes_Eq_Command_line_link_option_dict:(link_option)eq_class= ({

  isEqual_method = (fun opt1 -> 
        (fun opt2 -> 
            (match (opt1, opt2) with 
                | (OutputFilename(_), OutputFilename(_))        -> true
                | (ForceCommonDefined(_), ForceCommonDefined(_)) -> true
                | (Soname(_), Soname(_)) -> true
                | (EntryAddress(_), EntryAddress(_)) -> true
                | _ -> false
            )
        ));

  isInequal_method = (fun opt1 -> (fun opt2 -> not ( ((fun opt1 -> 
        (fun opt2 -> 
            (match (opt1, opt2) with 
                | (OutputFilename(_), OutputFilename(_))        -> true
                | (ForceCommonDefined(_), ForceCommonDefined(_)) -> true
                | (Soname(_), Soname(_)) -> true
                | (EntryAddress(_), EntryAddress(_)) -> true
                | _ -> false
            )
        ))opt1 opt2))))})

type input_file_and_options = input_file_spec * input_file_options
type input_unit = File of input_file_and_options
                | Group of (input_file_and_options) list   (* NOT recursive *)
                | BuiltinControlScript (* for uniformity when processing script defs *)

(*val string_of_input_unit : input_unit -> string*)
let string_of_input_unit u:string= 
     ((match u with
        File(spec, opts) -> 
            "single " ^ (string_of_input_file_spec spec)
       | Group(spec_opt_list) -> 
            "group: [" ^ ((string_of_list 
  instance_Show_Show_Command_line_input_file_spec_dict (Lem_list.map (fun (spec, opts) -> spec) spec_opt_list)) ^ "]")
       | BuiltinControlScript -> "(built-in control script)"
    ))

let instance_Show_Show_Command_line_input_unit_dict:(input_unit)show_class= ({

  show_method = string_of_input_unit})

(* Reading the command-line: 
 * we encode the meaning of a linker command token 
 * using a reader function interpreting a list of argument definitions.
 * Lookahead is necessary: sometimes the interpretation of an option
 * depends on the next argument (e.g. whether it's a file, directory or another option).
 * The list of argument definitions is from lists of strings to constructor function invocations.
 * We use lists of strings since many options have synonyms.
 * The strings are interpreted as regular expressions and any matched groups are collected together
 * as a second argument list; this is because some arguments are of the form --blah=NUM or similar. *)
 
(* As we read the command line, we keep a current state which is the collection
 * of seen input files, seen whole-link options, and input file options that will
 * apply to any input files we add subsequently. *)
type command_state = { input_units           : input_unit list
                      ; link_options          :  link_option Pset.set
                      ; current_input_options : input_file_options
                      ; current_group         :  ( input_file_and_options list)option
                      }
                      
(* This is the "default state" when we start reading input options *)
(*val initial_state : list command_state*) (* the stack *)
let initial_state0:(command_state)list=  ([{ input_units = ([])
                     ; link_options =(Pset.from_list compare [OutputFilename("a.out"); OutputKind(Executable)])
                     ; current_input_options = ({ input_fmt = "elf64-x86-64"   (* FIXME *)
                                                ; input_libpath = (["/usr/lib"]) (* FIXME: this probably isn't the right place to supply the default search path *)
                                                ; input_link_sharedlibs = true
                                                ; input_check_sections = true
                                                ; input_copy_dt_needed = false
                                                ; input_whole_archive = false
                                                ; input_as_needed = true (* FIXME *)
                                                })
                     ; current_group = None
                     }])

type interpreted_command_line = input_unit list * link_option Pset.set

(*val add_input_file : list command_state -> string -> list command_state*)
let add_input_file (state1 :: more) s:(command_state)list= 
     (let chars = (Xstring.explode s) 
    in
    let spec = ((match chars with 
        '-' :: 'l' :: more -> Libname(Xstring.implode more)
        | '-' :: more -> failwith ("not a valid option or input file: " ^ s)
        | _ -> Filename(s)
    ))
    in
    if (Lem.option_equal (listEqualBy (Lem.pair_equal (=) (=))) state1.current_group None) 
    then
        { input_units =  (List.rev_append (List.rev state1.input_units) [File(spec, state1.current_input_options)])
         ; link_options = (state1.link_options)
         ; current_input_options = (state1.current_input_options)
         ; current_group = (state1.current_group)
         } :: more
    else 
        { input_units = (state1.input_units)
         ; link_options = (state1.link_options)
         ; current_input_options = (state1.current_input_options)
         ; current_group = (let toAppend = ([(spec, state1.current_input_options)]) in 
            (match state1.current_group with Some l -> Some( List.rev_append (List.rev l) toAppend) | None -> Some(toAppend) 
            ))
         } :: more)

(*val start_group : list command_state -> list command_state*)
let start_group (state1 :: more):(command_state)list=  ({
           input_units = (state1.input_units)
         ; link_options = (state1.link_options)
         ; current_input_options = (state1.current_input_options)
         ; current_group = ((match state1.current_group with
                None -> Some []
                | _ -> failwith "cannot nest groups"
            ))
         } :: more)

(*val end_group : list command_state -> list command_state*)
let end_group (state1 :: more):(command_state)list=  ({
           input_units =  (List.rev_append (List.rev state1.input_units) ((match state1.current_group with 
                Some l -> [Group(l)]
                | None -> failwith "end group without start group"
            )))
         ; link_options = (state1.link_options)
         ; current_input_options = (state1.current_input_options)
         ; current_group = None
         } :: more)

type option_token = string
type option_argspecs = string list * string list
type option_argvals = string list * string list

(*val set_or_replace_option : link_option -> list command_state -> list command_state*)
let set_or_replace_option opt state_list:(command_state)list= 
     ((match state_list with
        [] -> failwith "error: no state"
        | state1 :: more -> 
            { input_units = (state1.input_units)
             ; link_options = (Pset.add opt (Pset.filter (fun existing -> ((fun opt1 -> (fun opt2 -> not ( ((fun opt1 -> 
        (fun opt2 -> 
            (match (opt1, opt2) with 
                | (OutputFilename(_), OutputFilename(_))        -> true
                | (ForceCommonDefined(_), ForceCommonDefined(_)) -> true
                | (Soname(_), Soname(_)) -> true
                | (EntryAddress(_), EntryAddress(_)) -> true
                | _ -> false
            )
        ))opt1 opt2)))) existing opt)) state1.link_options))
             ; current_input_options = (state1.current_input_options)
             ; current_group = (state1.current_group)
             } :: more
    ))

(*val find_option_matching_tag : link_option -> set link_option -> maybe link_option*)
let rec find_option_matching_tag tag options:(link_option)option= 
     (Lem_list.list_find_opt (tagEqual tag) (Pset.elements options))

(*val extract_hex_addend : char -> maybe natural*)
let extract_hex_addend x:(Nat_big_num.num)option=
   (if x = '0' then
    Some( (Nat_big_num.of_int 0))
  else if x = '1' then
    Some( (Nat_big_num.of_int 1))
  else if x = '2' then
    Some( (Nat_big_num.of_int 2))
  else if x = '3' then
    Some( (Nat_big_num.of_int 3))
  else if x = '4' then
    Some( (Nat_big_num.of_int 4))
  else if x = '5' then
    Some( (Nat_big_num.of_int 5))
  else if x = '6' then
    Some( (Nat_big_num.of_int 6))
  else if x = '7' then
    Some( (Nat_big_num.of_int 7))
  else if x = '8' then
    Some( (Nat_big_num.of_int 8))
  else if x = '9' then
    Some( (Nat_big_num.of_int 9))
  else if x = 'a' then
    Some( (Nat_big_num.of_int 10))
  else if x = 'b' then
    Some( (Nat_big_num.of_int 11))
  else if x = 'c' then
    Some( (Nat_big_num.of_int 12))
  else if x = 'd' then
    Some( (Nat_big_num.of_int 13))
  else if x = 'e' then
    Some( (Nat_big_num.of_int 14))
  else if x = 'f' then
    Some( (Nat_big_num.of_int 15))
  else
    None)

(*val accumulate_hex_chars : natural -> list char -> natural*)
let rec accumulate_hex_chars acc chars:Nat_big_num.num=
   ((match chars with
    | [] -> acc
    | x::xs ->
      (match extract_hex_addend x with
        | None     -> acc
        | Some addend ->
            accumulate_hex_chars ( Nat_big_num.add (Nat_big_num.mul acc( (Nat_big_num.of_int 16))) addend) xs
      )
  ))

(*val extract_dec_addend : char -> maybe natural*)
let extract_dec_addend x:(Nat_big_num.num)option=
   (if x = '0' then
    Some( (Nat_big_num.of_int 0))
  else if x = '1' then
    Some( (Nat_big_num.of_int 1))
  else if x = '2' then
    Some( (Nat_big_num.of_int 2))
  else if x = '3' then
    Some( (Nat_big_num.of_int 3))
  else if x = '4' then
    Some( (Nat_big_num.of_int 4))
  else if x = '5' then
    Some( (Nat_big_num.of_int 5))
  else if x = '6' then
    Some( (Nat_big_num.of_int 6))
  else if x = '7' then
    Some( (Nat_big_num.of_int 7))
  else if x = '8' then
    Some( (Nat_big_num.of_int 8))
  else if x = '9' then
    Some( (Nat_big_num.of_int 9))
  else
    None)

(*val accumulate_dec_chars : natural -> list char -> natural*)
let rec accumulate_dec_chars acc chars:Nat_big_num.num=
   ((match chars with
    | [] -> acc
    | x::xs ->
      (match extract_dec_addend x with
        | None     -> acc
        | Some addend ->
            accumulate_hex_chars ( Nat_big_num.add (Nat_big_num.mul acc( (Nat_big_num.of_int 16))) addend) xs
      )
  ))

(*val parse_address : string -> natural*)
let parse_address s:Nat_big_num.num=  ((match Xstring.explode s with
    '0' :: 'x' :: more -> accumulate_hex_chars( (Nat_big_num.of_int 0)) more
    | chars -> accumulate_dec_chars( (Nat_big_num.of_int 0)) chars
))

type option_def = ( option_token list) * option_argspecs * (option_argvals -> command_state list -> command_state list) * string

(* the table is a list of: ... options    and their arg names ... and the option's meaning as a function... and a help string *)
(*val command_line_table : list option_def*)
let command_line_table:((string)list*((string)list*(string)list)*((string)list*(string)list ->(command_state)list ->(command_state)list)*string)list=  ([
  (* per-input options *) 
  (["-b"; "--format"],                            (["TARGET"], []),    (fun args -> (fun state1 -> state1)), "Specify target for following input files");
  (["-L"; "--library-path"],                      (["DIRECTORY"], []), (fun args -> (fun state1 -> state1)), "Add DIRECTORY to library search path");
  (["--as-needed"],                               ([], []),            (fun _    -> (fun state1 -> state1)), "Only set DT_NEEDED for following dynamic libs if used");
  (["--no-as-needed"],                            ([], []),            (fun _    -> (fun state1 -> state1)), "Always set DT_NEEDED for dynamic libraries mentioned on the command line");
  (["-Bdynamic"; "-dy"; "-call_shared"],          ([], []),            (fun _    -> (fun state1 -> state1)), "Link against shared libraries");
  (["-Bstatic"; "-dn"; "-non_shared"; "-static"], ([], []),            (fun _    -> (fun state1 -> state1)), "Do not link against shared libraries");
  (["--check-sections"],                          ([], []),            (fun _    -> (fun state1 -> state1)), "Check section addresses for overlaps (default)  **srk** not sure it's per-input!");
  (["--no-check-sections"],                       ([], []),            (fun _    -> (fun state1 -> state1)), "Do not check section addresses for overlaps     **srk** not sure it's per-input!");
  (["--copy-dt-needed-entries"],                  ([], []),            (fun _    -> (fun state1 -> state1)), "Copy DT_NEEDED links mentioned inside DSOs that follow");
  (["--no-copy-dt-needed-entries"],               ([], []),            (fun _    -> (fun state1 -> state1)), "Do not copy DT_NEEDED links mentioned inside DSOs that follow");
  (["--no-whole-archive"],                        ([], []),            (fun _    -> (fun state1 -> state1)), "Turn off --whole-archive");
  (["-rpath-link"],                               (["PATH"], []),      (fun _    -> (fun state1 -> state1)), "Set link time shared library search path        **srk** not sure it's per-input!");
  (["--whole-archive"],                           ([], []),            (fun _    -> (fun state1 -> state1)), "Include all objects from following archives");
  (* linker plugin control *)
  (["-plugin"],                                   (["PLUGIN"], []),    (fun _    -> (fun state1 -> state1)), "Load named plugin");
  (["-plugin-opt"],                               (["ARG"], []),       (fun _    -> (fun state1 -> state1)), "Send arg to last-loaded plugin");
  (* output / whole-job options (some may be repeated with different args, but most not): *)
  (["-A"; "--architecture"],                      (["ARCH"], []),      (fun _ -> (fun state1 -> state1)), "Set architecture");
  (["-EB"],                                       ([], []),            (fun _ -> (fun state1 -> state1)), "Link big-endian objects");
  (["-EL"],                                       ([], []),            (fun _ -> (fun state1 -> state1)), "Link little-endian objects");
  (["-R"; "--just-symbols"],                      (["DIR"], []),       (fun _ -> (fun state1 -> state1)), "**srk** (if directory, same as --rpath)");
  (["-d"; "-dc"; "-dp"],                          ([], []),            (fun _ -> (fun state1 -> state1)), "Force common symbols to be defined");
  (["-e"; "--entry"],                             (["ADDRESS"], []),   (fun _ -> (fun state1 -> state1)), "Set start address");
  (["-E"; "--export-dynamic"],                    ([], []),            (fun _ -> (fun state1 -> state1)), "Export all dynamic symbols");
  (["--no-export-dynamic"],                       ([], []),            (fun _ -> (fun state1 -> state1)), "Undo the effect of --export-dynamic");
  (["-f"; "--auxiliary"],                         (["SHLIB"], []),     (fun _ -> (fun state1 -> state1)), "Auxiliary filter for shared object symbol table");
  (["-F"; "--filter"],                            (["SHLIB"], []),     (fun _ -> (fun state1 -> state1)), "Filter for shared object symbol table");
  (["-G"; "--gpsize"],                            (["SIZE"], []),      (fun _ -> (fun state1 -> state1)), "Small data size (if no size, same as --shared) **srk NOTE this quirk!**");
  (["-h"; "-soname"],                             (["FILENAME"], []),  (fun _ -> (fun state1 -> state1)), "Set internal name of shared library");
  (["-I"; "--dynamic-linker"],                    (["PROGRAM"], []),   (fun _ -> (fun state1 -> state1)), "Set PROGRAM as the dynamic linker to use");
  (["--sysroot="],                                ([], ["DIRECTORY"]), (fun _ -> (fun state1 -> state1)), "Override the default sysroot location");
  (["-m"],                                        (["EMULATION"], []), (fun _ -> (fun state1 -> state1)), "Set emulation");
  (["-n"; "--nmagic"],                            ([], []),            (fun _ -> (fun state1 -> state1)), "Do not page align data");
  (["-N"; "--omagic"],                            ([], []),            (fun _ -> (fun state1 -> state1)), "Do not page align data, do not make text readonly");
  (["--no-omagic"],                               ([], []),            (fun _ -> (fun state1 -> state1)), "Page align data, make text readonly");
  (["-o"; "--output"],                            (["FILE"], []),      (fun argvals -> set_or_replace_option (OutputFilename(List.hd (fst argvals)))), "Set output file name");
  (["-O"],                                        ([], []),            (fun _ -> (fun state1 -> state1)), "Optimise output file");
  (["-q"; "--emit-relocs"],                       ([], []),            (fun _ -> (fun state1 -> state1)), "Generate relocations in final output");
  (["-r"; "-i"; "--relocatable"],                 ([], []),            (fun _ -> (fun state1 -> state1)), "Generate relocatable output");
  (["-s"; "--strip-all"],                         ([], []),            (fun _ -> (fun state1 -> state1)), "Strip all symbols");
  (["-S"; "--strip-debug"],                       ([], []),            (fun _ -> (fun state1 -> state1)), "Strip debugging symbols");
  (["--strip-discarded"],                         ([], []),            (fun _ -> (fun state1 -> state1)), "Strip symbols in discarded sections");
  (["--no-strip-discarded"],                      ([], []),            (fun _ -> (fun state1 -> state1)), "Do not strip symbols in discarded sections");
  (["--default-script"; "-dT"],                   (["FILE"], []),      (fun _ -> (fun state1 -> state1)), "Read default linker script");
  (["--unique="],                                 ([], ["SECTION"]),   (fun _ -> (fun state1 -> state1)), "Don't merge input [SECTION | orphan] sections");
  (["-Ur"],                                       ([], []),            (fun _ -> (fun state1 -> state1)), "Build global constructor/destructor tables ( **srk**: like -r, but... )");
  (["-x"; "--discard-all"],                       ([], []),            (fun _ -> (fun state1 -> state1)), "Discard all local symbols");
  (["-X"; "--discard-locals"],                    ([], []),            (fun _ -> (fun state1 -> state1)), "Discard temporary local symbols (default)");
  (["--discard-none"],                            ([], []),            (fun _ -> (fun state1 -> state1)), "Don't discard any local symbols");
  (["-Bsymbolic"],                                ([], []),            (fun argvals -> (fun state1 -> set_or_replace_option BindFunctionsEarly (set_or_replace_option BindNonFunctionsEarly state1))), "Bind global references locally");
  (["-Bsymbolic-functions"],                      ([], []),            (fun argvals -> set_or_replace_option (BindFunctionsEarly)), "Bind global function references locally");
  (["--force-exe-suffix"],                        ([], []),            (fun _ -> (fun state1 -> state1)), "Force generation of file with .exe suffix");
  (["--gc-sections"],                             ([], []),            (fun _ -> (fun state1 -> state1)), "**srk: uncertain: can repeat?** Remove unused sections (on some targets)");
  (["--no-gc-sections"],                          ([], []),            (fun _ -> (fun state1 -> state1)), "**srk: uncertain: can repeat?** Don't remove unused sections (default)");
  (["--hash-size="],                              ([], ["NUMBER"]),    (fun _ -> (fun state1 -> state1)), "Set default hash table size close to <NUMBER>");
  (["--no-define-common"],                        ([], []),            (fun _ -> (fun state1 -> state1)), "Do not define Common storage");
  (["--no-undefined"],                            ([], []),            (fun _ -> (fun state1 -> state1)), "Do not allow unresolved references in object files");
  (["--allow-shlib-undefined"],                   ([], []),            (fun _ -> (fun state1 -> state1)), "Allow unresolved references in shared libraries");
  (["--no-allow-shlib-undefined"],                ([], []),            (fun _ -> (fun state1 -> state1)), "Do not allow unresolved references in shared libs");
  (["--default-symver"],                          ([], []),            (fun _ -> (fun state1 -> state1)), "Create default symbol version");
  (["--default-imported-symver"],                 ([], []),            (fun _ -> (fun state1 -> state1)), "Create default symbol version for imported symbols");
  (["-nostdlib"],                                 ([], []),            (fun _ -> (fun state1 -> state1)), "Only use library directories specified on the command line");
  (["--oformat"],                                 (["TARGET"], []),    (fun _ -> (fun state1 -> state1)), "Specify target of output file");
  (["--relax"],                                   ([], []),            (fun _ -> (fun state1 -> state1)), "Reduce code size by using target specific optimisations");
  (["--no-relax"],                                ([], []),            (fun _ -> (fun state1 -> state1)), "Do not use relaxation techniques to reduce code size");
  (["--retain-symbols-file"],                     (["FILE"], []),      (fun _ -> (fun state1 -> state1)), "Keep only symbols listed in FILE");
  (["-rpath"],                                    (["PATH"], []),      (fun _ -> (fun state1 -> state1)), "Set runtime shared library search path");
  (["-shared"; "-Bshareable"],                    ([], []),            (fun argvals -> set_or_replace_option (OutputKind(SharedLibrary))), "Create a shared library");
  (["-pie"; "--pic-executable"],                  ([], []),            (fun _ -> (fun state1 -> state1)), "Create a position independent executable");
  (["--sort-common="],(* (ascending|descending) *)([], ["order"]),     (fun _ -> (fun state1 -> state1)), "Sort common symbols by alignment [in specified order]");
  (["--sort-section="],(* (name|alignment) *)     ([], ["key"]),       (fun _ -> (fun state1 -> state1)), "Sort sections by name or maximum alignment");
  (["--spare-dynamic-tags"],                      (["COUNT"], []),     (fun _ -> (fun state1 -> state1)), "How many tags to reserve in .dynamic section");
  (["--split-by-file="],                          ([], ["SIZE"]),      (fun _ -> (fun state1 -> state1)), "Split output sections every SIZE octets");
  (["--split-by-reloc="],                         ([], ["COUNT"]),     (fun _ -> (fun state1 -> state1)), "Split output sections every COUNT relocs");
  (["--traditional-format"],                      ([], []),            (fun _ -> (fun state1 -> state1)), "Use same format as native linker");
  (["--unresolved-symbols="],                     ([], ["method"]),    (fun _ -> (fun state1 -> state1)), "How to handle unresolved symbols.  <method> is: ignore-all, report-all, ignore-in-object-files, ignore-in-shared-libs");
  (["--dynamic-list-data"],                       ([], []),            (fun _ -> (fun state1 -> state1)), "Add data symbols to dynamic list");
  (["--dynamic-list-cpp-new"],                    ([], []),            (fun _ -> (fun state1 -> state1)), "Use C++ operator new/delete dynamic list");
  (["--dynamic-list-cpp-typeinfo "],              ([], []),            (fun _ -> (fun state1 -> state1)), "Use C++ typeinfo dynamic list");
  (["--dynamic-list"],                            (["FILE"], []),      (fun _ -> (fun state1 -> state1)), "Read dynamic list");
  (["--wrap"],                                    (["SYMBOL"], []),    (fun _ -> (fun state1 -> state1)), "Use wrapper functions for SYMBOL");
  (* the following are specific to ELF emulations *)
  (["--audit=(.*)"],                              ([], ["AUDITLIB"]),  (fun _ -> (fun state1 -> state1)), "Specify a library to use for auditing");
  (["-Bgroup"],                                   ([], []),            (fun _ -> (fun state1 -> state1)), "Selects group name lookup rules for DSO");
  (["--build-id="],                               ([], ["STYLE"]),     (fun _ -> (fun state1 -> state1)), "Generate build ID note");
  (["-P"],                                        (["AUDITLIB"], []),  (fun _ -> (fun state1 -> state1)), "Specify a library to use for auditing dependencies");
  (["--depaudit="],                               ([], ["AUDITLIB"]),  (fun _ -> (fun state1 -> state1)), "Specify a library to use for auditing dependencies");
  (["--disable-new-dtags"],                       ([], []),            (fun _ -> (fun state1 -> state1)), "Disable new dynamic tags");
  (["--enable-new-dtags"],                        ([], []),            (fun _ -> (fun state1 -> state1)), "Enable new dynamic tags");
  (["--eh-frame-hdr"],                            ([], []),            (fun _ -> (fun state1 -> state1)), "Create .eh_frame_hdr section");
  (["--exclude-libs="],                           ([], ["LIBS"]),      (fun _ -> (fun state1 -> state1)), "Make all symbols in LIBS hidden");
  (["--hash-style="],                             ([], ["STYLE"]),     (fun _ -> (fun state1 -> state1)), "Set hash style to sysv, gnu or both");
  (* NOTE: for these to work, we hack our word-splitter to merge -z options into a single word with a single space in *)
  (["-z combreloc"],                              ([], []),            (fun _ -> (fun state1 -> state1)), "Merge dynamic relocs into one section and sort");
  (["-z common-page-size="],                      ([], ["SIZE"]),      (fun _ -> (fun state1 -> state1)), "Set common page size to SIZE");
  (["-z defs"],                                   ([], []),            (fun _ -> (fun state1 -> state1)), "Report unresolved symbols in object files.");
  (["-z execstack"],                              ([], []),            (fun _ -> (fun state1 -> state1)), "Mark executable as requiring executable stack");
  (["-z global"],                                 ([], []),            (fun _ -> (fun state1 -> state1)), "Make symbols in DSO available for subsequently loaded objects");
  (["-z initfirst"],                              ([], []),            (fun _ -> (fun state1 -> state1)), "Mark DSO to be initialized first at runtime");
  (["-z interpose"],                              ([], []),            (fun _ -> (fun state1 -> state1)), "Mark object to interpose all DSOs but executable");
  (["-z lazy"],                                   ([], []),            (fun _ -> (fun state1 -> state1)), "Mark object lazy runtime binding (default)");
  (["-z loadfltr"],                               ([], []),            (fun _ -> (fun state1 -> state1)), "Mark object requiring immediate process");
  (["-z max-page-size="],                         ([], ["SIZE"]),      (fun _ -> (fun state1 -> state1)), "Set maximum page size to SIZE");
  (["-z nocombreloc"],                            ([], []),            (fun _ -> (fun state1 -> state1)), "Don't merge dynamic relocs into one section");
  (["-z nocopyreloc"],                            ([], []),            (fun _ -> (fun state1 -> state1)), "Don't create copy relocs");
  (["-z nodefaultlib"],                           ([], []),            (fun _ -> (fun state1 -> state1)), "Mark object not to use default search paths");
  (["-z nodelete"],                               ([], []),            (fun _ -> (fun state1 -> state1)), "Mark DSO non-deletable at runtime");
  (["-z nodlopen"],                               ([], []),            (fun _ -> (fun state1 -> state1)), "Mark DSO not available to dlopen");
  (["-z nodump"],                                 ([], []),            (fun _ -> (fun state1 -> state1)), "Mark DSO not available to dldump");
  (["-z noexecstack"],                            ([], []),            (fun _ -> (fun state1 -> state1)), "Mark executable as not requiring executable stack");
  (["-z norelro"],                                ([], []),            (fun _ -> (fun state1 -> state1)), "Don't create RELRO program header");
  (["-z now"],                                    ([], []),            (fun _ -> (fun state1 -> state1)), "Mark object non-lazy runtime binding");
  (["-z origin"],                                 ([], []),            (fun _ -> (fun state1 -> state1)), "Mark object requiring immediate $ORIGIN processing at runtime");
  (["-z relro"],                                  ([], []),            (fun _ -> (fun state1 -> state1)), "Create RELRO program header");
  (["-z stacksize="],                             ([], ["SIZE"]),      (fun _ -> (fun state1 -> state1)), "Set size of stack segment");
  (["-z bndplt"],                                 ([], []),            (fun _ -> (fun state1 -> state1)), "Always generate BND prefix in PLT entries");
  (["--ld-generated-unwind-info"],                ([], []),            (fun _ -> (fun state1 -> state1)), "Generate exception handling info for PLT.");
  (["--no-ld-generated-unwind-info"],             ([], []),            (fun _ -> (fun state1 -> state1)), "Don't do so.");
  (* quasi-input options (can be repeated): *)
  (["-c"; "--mri-script"],                        (["FILE"], []),            (fun _ -> (fun state1 -> state1)), "Read MRI format linker script");
  (["-l"; "--library"],                           (["LIBNAME"], []),         (fun _ -> (fun state1 -> state1)), "Search for library LIBNAME");
  (* (["-R" ,"--just-symbols"],                   (["FILE"], []),            fun _ -> (fun state -> state), "Just link symbols"), *) (* Handled above! *)
  (["-T"; "--script"],                            (["FILE"], []),            (fun _ -> (fun state1 -> state1)), "Read linker script");
  (["-u"; "--undefined"],                         (["SYMBOL"], []),          (fun _ -> (fun state1 -> state1)), "Start with undefined reference to SYMBOL");
  (["-("; "--start-group"],                       ([], []),                  (fun _ -> (fun state1 -> start_group state1)), "Start a group");
  (["-)"; "--end-group"],                         ([], []),                  (fun _ -> (fun state1 -> end_group state1)), "End a group");
  (["--defsym"],                                  (["SYMBOL=EXPRESSION"], []), (fun _ -> (fun state1 -> state1)), "Define a symbol");
  (["-fini"],                                     (["SYMBOL"], []),          (fun _ -> (fun state1 -> state1)), "Call SYMBOL at unload-time");
  (["-init"],                                     (["SYMBOL"], []),          (fun _ -> (fun state1 -> state1)), "Call SYMBOL at load-time");
  (["--section-start"],                           (["SECTION=ADDRESS"], []), (fun _ -> (fun state1 -> state1)), "Set address of named section");
  (["-Tbss"],                                     (["ADDRESS"], []),         (fun _ -> (fun state1 -> state1)), "Set address of .bss section");
  (["-Tdata"],                                    (["ADDRESS"], []),         (fun _ -> (fun state1 -> state1)), "Set address of .data section");
  (["-Ttext"],                                    (["ADDRESS"], []),         (fun _ -> (fun state1 -> state1)), "Set address of .text section");
  (["-Ttext-segment"],                            (["ADDRESS"], []),         (fun argvals -> set_or_replace_option (TextSegmentStart(parse_address (List.hd (fst argvals))))), "Set address of text segment");
  (["-Trodata-segment"],                          (["ADDRESS"], []),         (fun argvals -> set_or_replace_option (RodataSegmentStart(parse_address (List.hd (fst argvals))))), "Set address of rodata segment");
  (["-Tldata-segment"],                           (["ADDRESS"], []),         (fun argvals -> set_or_replace_option (LdataSegmentStart(parse_address (List.hd (fst argvals))))), "Set address of ldata segment");
  (["--version-script"],                          (["FILE"], []),            (fun _ -> (fun state1 -> state1)), "Read version information script");
  (["--version-exports-section"],                 (["SYMBOL"], []),          (fun _ -> (fun state1 -> state1)), "Take export symbols list from .exports, using SYMBOL as the version.");
  (* linker internal debugging/diagnostics and performance tuning *)
  (["-M"; "--print-map"],                         ([], []),                  (fun _ -> (fun state1 -> state1)), "Print map file on standard output");
  (["-t"; "--trace"],                             ([], []),                  (fun _ -> (fun state1 -> state1)), "Trace file opens");
  (["-v"; "--version"],                           ([], []),                  (fun _ -> (fun state1 -> state1)), "Print version information");
  (["-V"],                                        ([], []),                  (fun _ -> (fun state1 -> state1)), "Print version and emulation information");
  (["-y"; "--trace-symbol"],                      (["SYMBOL"], []),          (fun _ -> (fun state1 -> state1)), "Trace mentions of SYMBOL");
  (["--cref"],                                    ([], []),                  (fun _ -> (fun state1 -> state1)), "Output cross reference table");
  (["--demangle="],                               ([], ["STYLE"]),             (fun _ -> (fun state1 -> state1)), "Demangle symbol names [using STYLE]");
  (["--print-gc-sections"],                       ([], []),                  (fun _ -> (fun state1 -> state1)), "List removed unused sections on stderr");
  (["--no-print-gc-sections"],                    ([], []),                  (fun _ -> (fun state1 -> state1)), "Do not list removed unused sections");
  (["-Map"],                                      (["FILE"], []),            (fun _ -> (fun state1 -> state1)), "Write a map file");
  (["-Map="],                                     ([], ["FILE"]),            (fun _ -> (fun state1 -> state1)), "Write a map file");
  (["--help"],                                    ([], []),                  (fun _ -> (fun state1 -> state1)), "Print option help");
  (["--no-keep-memory"],                          ([], []),                  (fun _ -> (fun state1 -> state1)), "Use less memory and more disk I/O");
  (["--no-demangle"],                             ([], []),                  (fun _ -> (fun state1 -> state1)), "Do not demangle symbol names");
  (["--print-output-format"],                     ([], []),                  (fun _ -> (fun state1 -> state1)), "Print default output format");
  (["--print-sysroot"],                           ([], []),                  (fun _ -> (fun state1 -> state1)), "Print current sysroot");
  (["--reduce-memory-overheads"],                 ([], []),                  (fun _ -> (fun state1 -> state1)), "Reduce memory overheads, possibly taking much longer");
  (["--stats"],                                   ([], []),                  (fun _ -> (fun state1 -> state1)), "Print memory usage statistics");
  (["--target-help"],                             ([], []),                  (fun _ -> (fun state1 -> state1)), "Display target specific options");
  (["--verbose="],                                ([], ["NUMBER"]),          (fun _ -> (fun state1 -> state1)), "Output lots of information during link");
  (* unknown *)
  (["--embedded-relocs"],                         ([], []),                  (fun _ -> (fun state1 -> state1)), "Generate embedded relocs");
  (["--task-link"],                               (["SYMBOL"], []),          (fun _ -> (fun state1 -> state1)), "Do task level linking");
  (* compatibility *)
  (["-a"],                                        (["KEYWORD"], []),         (fun _ -> (fun state1 -> state1)), "Shared library control for HP/UX compatibility");
  (["-Y"],                                        (["PATH"], []),            (fun _ -> (fun state1 -> state1)), "Default search path for Solaris compatibility");
  (* permissiveness controls (tightening/loosening) *)
  (["--accept-unknown-input-arch"],               ([], []),                  (fun _ -> (fun state1 -> state1)), "Accept input files whose architecture cannot be determined");
  (["--no-accept-unknown-input-arch"],            ([], []),                  (fun _ -> (fun state1 -> state1)), "Reject input files whose architecture is unknown");
  (["--fatal-warnings"],                          ([], []),                  (fun _ -> (fun state1 -> state1)), "Treat warnings as errors");
  (["--no-fatal-warnings"],                       ([], []),                  (fun _ -> (fun state1 -> state1)), "Do not treat warnings as errors (default)");
  (["--allow-multiple-definition"],               ([], []),                  (fun _ -> (fun state1 -> state1)), "Allow multiple definitions");
  (["--no-undefined-version"],                    ([], []),                  (fun _ -> (fun state1 -> state1)), "Disallow undefined version");
  (["--noinhibit-exec"],                          ([], []),                  (fun _ -> (fun state1 -> state1)), "Create an output file even if errors occur");
  (["--error-unresolved-symbols"],                ([], []),                  (fun _ -> (fun state1 -> state1)), "Report unresolved symbols as errors");
  (["--ignore-unresolved-symbol"],                (["SYMBOL"], []),          (fun _ -> (fun state1 -> state1)), "Unresolved SYMBOL will not cause an error or warning");
  (* permissiveness, specific to ELF emulation *)
  (["-z muldefs"],                                ([], []),                  (fun _ -> (fun state1 -> state1)), "Allow multiple definitions");
  (* warnings (enabling/disabling) *)
  (["--no-warn-mismatch"],                        ([], []),                  (fun _ -> (fun state1 -> state1)), "Don't warn about mismatched input files");
  (["--no-warn-search-mismatch"],                 ([], []),                  (fun _ -> (fun state1 -> state1)), "Don't warn on finding an incompatible library");
  (["--warn-common"],                             ([], []),                  (fun _ -> (fun state1 -> state1)), "Warn about duplicate common symbols");
  (["--warn-constructors"],                       ([], []),                  (fun _ -> (fun state1 -> state1)), "Warn if global constructors/destructors are seen");
  (["--warn-multiple-gp"],                        ([], []),                  (fun _ -> (fun state1 -> state1)), "Warn if the multiple GP values are used");
  (["--warn-once"],                               ([], []),                  (fun _ -> (fun state1 -> state1)), "Warn only once per undefined symbol");
  (["--warn-section-align"],                      ([], []),                  (fun _ -> (fun state1 -> state1)), "Warn if start of section changes due to alignment");
  (["--warn-shared-textrel"],                     ([], []),                  (fun _ -> (fun state1 -> state1)), "Warn if shared object has DT_TEXTREL");
  (["--warn-alternate-em"],                       ([], []),                  (fun _ -> (fun state1 -> state1)), "Warn if an object has alternate ELF machine code");
  (["--warn-unresolved-symbols"],                 ([], []),                  (fun _ -> (fun state1 -> state1)), "Report unresolved symbols as warnings");
  (* meta-options *)
  (["--push-state"],                              ([], []),                  (fun _ -> (fun state1 -> state1)), "Push state of flags governing input file handling");
  (["--pop-state"],                               ([], []),                  (fun _ -> (fun state1 -> state1)), "Pop state of flags governing input file handling")
(*(["@FILE"], [], fun _ -> (fun state -> state), "Read options from FILE") *) (* processed during word-splitting phase *);
])

(*val delete_trailing_equals: string -> maybe string*)
let delete_trailing_equals str:(string)option= 
     (let cs = (Xstring.explode str)
    in
    if (listEqualBy (=) ['='] (drop0 ( Nat_big_num.sub_nat(length cs)( (Nat_big_num.of_int 1))) cs))
        then Some (Xstring.implode ((take0 ( Nat_big_num.sub_nat(length cs)( (Nat_big_num.of_int 1))) cs)))
        else (* let _ = Missing_pervasives.errln ("No trailing equals: " ^ str)
            in *)
            None)

(*val string_following_equals_at : nat -> string -> maybe string*)
let string_following_equals_at pos str:(string)option= 
     (let (first, second) = (Lem_list.split_at pos (Xstring.explode str))
    in (match second with 
        '=' :: rest -> Some (Xstring.implode rest)
        | _ -> (* let _ = Missing_pervasives.errln ("No trailing equals at " ^ (show pos) ^ ": " ^ str)
            in *)
            None
    ))

(*val equal_modulo_trailing_equals : string -> string -> bool*)
let equal_modulo_trailing_equals argstr argdef:bool=
     ( 
    (* we allow argdef to have a trailing equals; if it does, 
     * we allow the argstring to have the equals (or not) and trailing stuff,
     * which will become an arg  *)let result = ((match (delete_trailing_equals argdef) with 
        Some matched -> 
            let following_equals = (string_following_equals_at (String.length matched) argstr)
            in
            (match following_equals with 
                Some following -> (* okay; does the pre-equals part match? *)
                    matched = Xstring.implode (Lem_list.take ( Nat_num.nat_monus(String.length argdef) 1) (Xstring.explode argstr))
                | _ -> (* the argstr is allowed not to have a trailing equals *) argstr = matched
            )
        | None -> (* no trailing equals *) argdef = argstr
    ))
    in 
    (* let _ = Missing_pervasives.errln ("Do '" ^ argstr ^ "' and '" ^ argdef ^ "' match modulo trailing equals? " ^ (show result))
    in *) result)
    

(*val matching_arg_and_alias : string -> list option_def -> maybe (string * option_def)*)
let rec matching_arg_and_alias arg options:(string*((string)list*((string)list*(string)list)*(option_argvals ->(command_state)list ->(command_state)list)*string))option=  ((match options with 
    [] -> None
    | (aliases, argspec, meaning, doc) :: more_opts -> 
        (match list_find_opt (fun alias -> equal_modulo_trailing_equals arg alias) aliases with 
            Some found_alias -> Some (found_alias, (aliases, argspec, meaning, doc))
            | None -> matching_arg_and_alias arg more_opts
        )
    ))

(* We don't try to convert from strings to other things here; 
 * everything we record is either a bool, meaning option -A was "present", for some A,
 * or a string somearg, meaning option -A somearg was present, for some A. *)

(* The above suffices to understand each concrete argument. 
 * Now we define an "interpreted command line" that includes 
 * some useful structure. *)

(*val read_one_arg : list command_state -> list string -> (list command_state * list string)*)
let read_one_arg state_stack args:(command_state)list*(string)list=
     (
    (* Get the first string and look it up in our table. *)(match args with
        [] -> (state_stack, [])
    |   some_arg :: more -> (match (matching_arg_and_alias some_arg command_line_table) with
            (* We need to handle argdefs that have trailing equals. This means 
             * an extra arg might follow the equals. We need some helper functions. *)
                Some (alias, (aliases, (argspec_extras, argspec_regex), meaning, doc)) ->
                    (* Return a new state, by applying the argument's meaning. 
                     * We have to supply the option's argument strings to the meaning function. *)
                    let argstrings = (Lem_list.take (List.length argspec_extras) more)
                    in 
                    let regex_matches = ((match delete_trailing_equals some_arg with
                        Some prefix -> 
                            (match (string_following_equals_at ( Nat_num.nat_monus(String.length alias) 1) some_arg) with 
                                Some following_equals -> [following_equals]
                                | None -> failwith "impossible: '=' not where it was a moment ago"
                            )
                        | None -> []
                    ))
                    in 
                    let new_state_stack = (meaning (argstrings, regex_matches) state_stack)
                    in
                    (new_state_stack, drop0 (length argspec_extras) more)
                | None -> 
                    (* If we didn't match any args, we ought to be an input file. *)
                    (add_input_file state_stack some_arg, more)
            )
    ))

(* To fold over the command-line arguments we need a fold that passes 
 * suffixes of the list, not individual elements, and gives us back
 * the continuation that we need to fold over: a pair of folded-value, new-list. *)
(*val foldl_suffix : forall 'a 'b. ('a -> list 'b -> ('a * list 'b)) -> 'a -> list 'b -> 'a*) (* originally foldl *)
let rec foldl_suffix f a l:'a=  ((match l with
  | []      -> a
  | x :: xs -> 
    let (new_a, new_list) = (f a l)
    in foldl_suffix f new_a new_list
))

(* the word-splitting in argv needs a little fixing up. *)
(*val cook_argv : list string -> list string -> list string*)
let rec cook_argv acc args:(string)list= 
     ((match args with
        [] -> acc
      | "-z" :: more -> (match more with 
         [] -> failwith "-z must be followed by another argument"
         | something :: yetmore -> cook_argv ( List.rev_append (List.rev acc) [("-z " ^ something)]) yetmore
         )
      | something :: more -> cook_argv ( List.rev_append (List.rev acc) [something]) more
    ))

(*val command_line : unit -> interpreted_command_line*)
let command_line:unit ->(input_unit)list*(link_option)Pset.set=  (fun _ -> (
    let cooked_argv = (cook_argv [] (List.tl Ml_bindings.argv_list))
    in
    (* Now we use our fold-alike. *)
    (match foldl_suffix read_one_arg initial_state0 cooked_argv with
        state1 :: rest_of_stack -> (state1.input_units, state1.link_options)
        | _ -> failwith "no command state left"
    )
))
OCaml

Innovation. Community. Security.