# Copyright (C) 2020 Jeremiah Orians
# Copyright (C) 2020 Sanne Wouda
# This file is part of stage0.
#
# stage0 is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# stage0 is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with stage0.  If not, see <http://www.gnu.org/licenses/>.

:ELF_base

7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

02                             # e_ident[EI_CLASS] Indicating 64 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 0 because none cares
00                             # e_ident[EI_ABIVERSION] See above

00 00 00 00 00 00 00           # e_ident[EI_PAD]
02 00                          # e_type Indicating Executable
B7 00                          # e_machine Indicating AArch64
01 00 00 00                    # e_version Indicating original elf

&ELF_text 00 00 00 00          # e_entry Address of the entry point
%ELF_program_headers>ELF_base 00 00 00 00 # e_phoff Address of program header table
00 00 00 00 00 00 00 00 # e_shoff Address of section header table

00 00 00 00                    # e_flags
40 00                          # e_ehsize Indicating our 64 Byte header

38 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

00 00                          # e_shentsize size of a section header table
00 00                          # e_shnum number of entries in section table

00 00                          # e_shstrndx index of the section names

:ELF_program_headers
:ELF_program_header__text
01 00 00 00                   # ph_type: PT-LOAD = 1
07 00 00 00                   # ph_flags: PF-X|PF-W|PF-R = 7
00 00 00 00 00 00 00 00                   # ph_offset
&ELF_base 00 00 00 00                     # ph_vaddr
&ELF_base 00 00 00 00                     # ph_physaddr
%ELF_end>ELF_base 00 00 00 00             # ph_filesz
%ELF_end>ELF_base 00 00 00 00             # ph_memsz
01 00 00 00 00 00 00 00                   # ph_align



:ELF_text
# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
:_start
    e10b40f9                    ;    LDR_X1_[SP,16]              # Get the actual input name
    e2830091                    ;    ADD_X2_SP_32                # Get envp (hopefullY)
    3f0000f1                    ;    CMP_X1_TO_0                 # Check for missing output
    40000054                    ;    SKIP_INST_EQ                # Have real input
    ^~_start_out 14             ;    ^~_start_out FBRANCH
    e2630091                    ;    ADD_X2_SP_24                # Nope, envp is here!
    41000018                    ;    LOAD_W1_AHEAD               # Use "kaem.run"
    02000014                    ;    SKIP_32_DATA
    &default_file               ;    &default_file

:_start_out
    49000018                    ;    LOAD_W9_AHEAD
    02000014                    ;    SKIP_32_DATA
    &envp                       ;    &envp
    220100f9                    ;    STR_X2_[X9]                 # Protect envp

    600c8092                    ;    SET_X0_TO_FCNTL_H_AT_FDCWD  # AT_FDCWD, relative to current working directory
    020080d2                    ;    SET_X2_TO_0                 # prepare read_only
    080780d2                    ;    SET_X8_TO_SYS_OPENAT        # the syscall number for openat()
    010000d4                    ;    SYSCALL                     # Now open that damn file

    1f0000f1                    ;    CMP_X0_TO_0                 # IF NULL We couldn't open the file
    4c000054                    ;    SKIP_INST_GT                # Abort hard
    ^~Exit_Failure 14           ;    ^~Exit_Failure FBRANCH
    49000018                    ;    LOAD_W9_AHEAD               # Set input pointer
    02000014                    ;    SKIP_32_DATA
    &script                     ;    &script
    200100f9                    ;    STR_X0_[X9]

    c81a80d2                    ;    SET_X8_TO_SYS_BRK           # the Syscall # for SYS_BRK
    000080d2                    ;    SET_X0_TO_0                 # Get current brk
    010000d4                    ;    SYSCALL                     # Let the kernel do the work
    49000018                    ;    LOAD_W9_AHEAD               # Set our malloc pointer
    02000014                    ;    SKIP_32_DATA
    &MALLOC                     ;    &MALLOC
    200100f9                    ;    STR_X0_[X9]

    # Where the main work gets done
    # Using X1 for tokens and X2 for tokens[i]
:main_loop
    49000018                    ;    LOAD_W9_AHEAD               # Using 256 char* of space
    02000014                    ;    SKIP_32_DATA
    &max_args                   ;    &max_args
    200140f9                    ;    LDR_X0_[X9]
    ^~malloc 94                 ;    ^~malloc FCALL              # get it
    e10300aa                    ;    SET_X1_FROM_X0              # set tokens

    e20301aa                    ;    SET_X2_FROM_X1              # I = 0
    000080d2                    ;    SET_X0_TO_0                 # Using 0
    49000018                    ;    LOAD_W9_AHEAD               # status = 0
    02000014                    ;    SKIP_32_DATA
    &info                       ;    &info
    200d00f9                    ;    STR_X0_[X9,24]
    49000018                    ;    LOAD_W9_AHEAD               # command_done = 0
    02000014                    ;    SKIP_32_DATA
    &command_done               ;    &command_done
    200100f9                    ;    STR_X0_[X9]

    # Using X0 for result and X1 for tokens[i]
:collect_command
    ^~collect_token 94          ;    ^~collect_token FCALL       # Get another token
    1f0000f1                    ;    CMP_X0_TO_0                 # if NULL == result
    41000054                    ;    SKIP_INST_NE                # It is a comment, don't store
    ^~collect_command_comment 14 ;    ^~collect_command_comment FBRANCH

    400000f9                    ;    STR_X0_[X2]                 # tokens[i] = result
    42200091                    ;    ADD_X2_X2_8                 # i = i + 1 (adjusted for char* size)

:collect_command_comment
    49000018                    ;    LOAD_W9_AHEAD               # Using command_done
    02000014                    ;    SKIP_32_DATA
    &command_done               ;    &command_done
    200140f9                    ;    LDR_X0_[X9]
    1f0000f1                    ;    CMP_X0_TO_0                 # IF 0 == command_done
    41000054                    ;    SKIP_INST_NE                # keep looping
    ^~collect_command 17        ;    ^~collect_command RBRANCH

                                        # Deal with line comments
    5f0001eb                    ;    CMP_X2_X1                   # if 0 < i
    41000054                    ;    SKIP_INST_NE                # It was a comment
    ^~main_loop 17              ;    ^~main_loop RBRANCH

:collect_command_run
    ^~print_command 94          ;    ^~print_command FCALL       # print the command
    200040f9                    ;    LDR_X0_[X1]                 # program = tokens[0]
    1f0000f1                    ;    CMP_X0_TO_0                 # IF NULL == program
    41000054                    ;    SKIP_INST_NE                # Some shit went down, abort
    ^~Exit_Failure 14           ;    ^~Exit_Failure FBRANCH

    e10f1ff8                    ;    PUSH_X1                     # Protect Tokens
    200280d2                    ;    SET_X0_TO_17                # flags = SIGCHILD
    010080d2                    ;    SET_X1_TO_0                 # parent_tid
    020080d2                    ;    SET_X2_TO_0                 # tls
    030080d2                    ;    SET_X3_TO_0                 # child_tid
    881b80d2                    ;    SET_X8_TO_SYS_CLONE         # FORKing (SYS_clone)
    010000d4                    ;    SYSCALL                     # int f = clone()
    e10741f8                    ;    POP_X1                      # Restore Tokens

    1f0000f1                    ;    CMP_X0_TO_0                 # Check fork
    4a000054                    ;    SKIP_INST_GE                # IF f == -1 abort hard
    ^~Exit_Failure 14           ;    ^~Exit_Failure FBRANCH
    40000054                    ;    SKIP_INST_EQ                # IF f == 0 it is child
    ^~collect_command_parent 14 ;    ^~collect_command_parent FBRANCH

    # Deal with child case
    a81b80d2                    ;    SET_X8_TO_SYS_EXECVE        # EXECVE
    49000018                    ;    LOAD_W9_AHEAD               # third arg = envp
    02000014                    ;    SKIP_32_DATA
    &envp                       ;    &envp
    220140f9                    ;    LDR_X2_[X9]
                                                                # second arg = tokens, already in x1
    200040f9                    ;    LDR_X0_[X1]                 # program = tokens[0]
    010000d4                    ;    SYSCALL                     # execve(program, tokens, envp);
    ^~Exit_Failure 14           ;    ^~Exit_Failure FBRANCH      # return error

:collect_command_parent
    e10300aa                    ;    SET_X1_FROM_X0              # id = f
    200080d2                    ;    SET_X0_TO_1                 # id_type = P_PID
    42000018                    ;    LOAD_W2_AHEAD               # infop
    02000014                    ;    SKIP_32_DATA
    &info                       ;    &info
    830080d2                    ;    SET_X3_TO_4                 #  options = WEXITED

    e80b80d2                    ;    SET_X8_TO_SYS_WAITID        # WAITID
    010000d4                    ;    SYSCALL                     # waitid(P_PID, f, &info, 0);

    49000018                    ;    LOAD_W9_AHEAD               # Using status
    02000014                    ;    SKIP_32_DATA
    &info                       ;    &info
    200d40f9                    ;    LDR_X0_[X9,24]
    1f0000f1                    ;    CMP_X0_TO_0                 # IF 0 == status
    41000054                    ;    SKIP_INST_NE                # Loop forever
    ^~main_loop 17              ;    ^~main_loop RBRANCH

                                                                # Deal with very unhappy case
    40000098                    ;    LOAD_W0_AHEAD               # Using "Subprocess error\nABORTING HARD\n"
    02000014                    ;    SKIP_32_DATA
    &hard                       ;    &hard
    ^~File_Print 94             ;    ^~File_Print FCALL          # Print it

# Exit_Failure function
# Receives nothing
# And aborts hard
# DOES NOT RETURN
:Exit_Failure
    210080d2                    ;    SET_X1_TO_1                 # All is wrong
    a80b80d2                    ;    SET_X8_TO_SYS_EXIT          # put the exit syscall number in x8
    010000d4                    ;    SYSCALL                     # Call it a bad day


# print_command function
# Recieves tokens[j] in X1 and tokens[i] in X2
# Modifies X0
:print_command
    fe0f1ff8                    ;    PUSH_LR
    e10f1ff8                    ;    PUSH_X1                     # Protect X1
    40000098                    ;    LOAD_W0_AHEAD               # using " +> "
    02000014                    ;    SKIP_32_DATA
    &prefix                     ;    &prefix
    ^~File_Print 94             ;    ^~File_Print FCALL          # print it
:print_command_loop
    200040f9                    ;    LDR_X0_[X1]                 # using tokens[j]
    ^~File_Print 94             ;    ^~File_Print FCALL          # print it
    21200091                    ;    ADD_X1_X1_8                 # j = j + 1
    000480d2                    ;    SET_X0_TO_32                # using ' '
    ^~fputc 94                  ;    ^~fputc FCALL               # print it
    3f0002eb                    ;    CMP_X1_X2                   # IF j < i
    40000054                    ;    SKIP_INST_EQ                # otherwise keep looping
    ^~print_command_loop 17     ;    ^~print_command_loop RBRANCH

    400180d2                    ;    SET_X0_TO_10                # using '\n'
    ^~fputc 94                  ;    ^~fputc FCALL               # print it
    e10741f8                    ;    POP_X1                      # Restore X1
    fe0741f8                    ;    POP_LR
    c0035fd6                    ;    RETURN


# collect_token function
# Recieves nothing
# Overwrites X0
# Uses X0 as C, X1 as token and X2 as token[i]
:collect_token
    fe0f1ff8                    ;    PUSH_LR
    e10f1ff8                    ;    PUSH_X1                     # Protect X1
    e20f1ff8                    ;    PUSH_X2                     # Protect X2
    49000018                    ;    LOAD_W9_AHEAD               # Using max_string
    02000014                    ;    SKIP_32_DATA
    &max_string                 ;    &max_string
    200140f9                    ;    LDR_X0_[X9]
    ^~malloc 94                 ;    ^~malloc FCALL              # allocate space
    e10300aa                    ;    SET_X1_FROM_X0              # token = malloc(max_string);
    e20300aa                    ;    SET_X2_FROM_X0              # i = 0; set token[i]

:collect_token_loop
    ^~fgetc 94                  ;    ^~fgetc FCALL               # c = fgetc(input);
    1f1000b1                    ;    CMP_X0_TO_MINUS_4           # if C == EOF
    41000054                    ;    SKIP_INST_NE                # We are done
    ^~Done 14                   ;    ^~Done FBRANCH

    1f8000f1                    ;    CMP_X0_TO_32                # IF C == ' '
    41000054                    ;    SKIP_INST_NE                # Space terminates token
    ^~collect_token_done 14     ;    ^~collect_token_done FBRANCH

    1f2400f1                    ;    CMP_X0_TO_9                 # IF C == '\t'
    41000054                    ;    SKIP_INST_NE                # tab terminates token
    ^~collect_token_done 14     ;    ^~collect_token_done FBRANCH

    1f2800f1                    ;    CMP_X0_TO_10                # IF C == '\n'
    40000054                    ;    SKIP_INST_EQ                # otherwise check next
    ^~collect_token_string 14   ;    ^~collect_token_string FBRANCH

    # It is a newline
    200080d2                    ;    SET_X0_TO_1                 # Using 1
    49000018                    ;    LOAD_W9_AHEAD               # Set command_done = TRUE
    02000014                    ;    SKIP_32_DATA
    &command_done               ;    &command_done
    200100f9                    ;    STR_X0_[X9]
    ^~collect_token_done 14     ;    ^~collect_token_done FBRANCH # Be done

:collect_token_string
    1f8800f1                    ;    CMP_X0_TO_34                # IF C == '"'
    40000054                    ;    SKIP_INST_EQ                # otherwise check next
    ^~collect_token_comment 14  ;    ^~collect_token_comment FBRANCH

    # It is a RAW STRING
    ^~collect_string 94         ;    ^~collect_string FCALL      # Get the rest of the string
    ^~collect_token_done 14     ;    ^~collect_token_done FBRANCH # Be done

:collect_token_comment
    1f8c00f1                    ;    CMP_X0_TO_35                # IF C == '#'
    40000054                    ;    SKIP_INST_EQ                # otherwise check next
    ^~collect_token_escape 14   ;    ^~collect_token_escape FBRANCH

# It is a line comment
    ^~collect_comment 94        ;    ^~collect_comment FCALL     # Read it all
    200080d2                    ;    SET_X0_TO_1                 # Using 1
    49000018                    ;    LOAD_W9_AHEAD               # Set command_done = TRUE
    02000014                    ;    SKIP_32_DATA
    &command_done               ;    &command_done
    200100f9                    ;    STR_X0_[X9]
    ^~collect_token_done 14     ;    ^~collect_token_done FBRANCH # Be done

:collect_token_escape
    1f7001f1                    ;    CMP_X0_TO_92                # IF C == '\\'
    40000054                    ;    SKIP_INST_EQ                # otherwise just store it
    ^~collect_token_other 14    ;    ^~collect_token_other FBRANCH

    # It is an escape char
    ^~fgetc 94                  ;    ^~fgetc FCALL               # Read the char to drop
    ^~collect_token_done 14     ;    ^~collect_token_done FBRANCH # Be done

:collect_token_other
    40000039                    ;    STR_BYTE_W0_[X2]            # token[i] = C
    42040091                    ;    ADD_X2_X2_1                 # i = i + 1
    ^~collect_token_loop 17     ;    ^~collect_token_loop RBRANCH # Keep going

:collect_token_done
    3f0002eb                    ;    CMP_X1_X2                   # IF i == 0
    40000054                    ;    SKIP_INST_EQ                # otherwise return the token
    ^~collect_token_good 14     ;    ^~collect_token_good FBRANCH
    010080d2                    ;    SET_X1_TO_0                 # token = NULL

:collect_token_good
    e00301aa                    ;    SET_X0_FROM_X1              # Return token
    e20741f8                    ;    POP_X2                      # Restore X2
    e10741f8                    ;    POP_X1                      # Restore X1
    fe0741f8                    ;    POP_LR
    c0035fd6                    ;    RETURN


# collect_string function
# Recieves target[index] in X2
# Modifies X0
# Uses X0 as C
:collect_string
    fe0f1ff8                    ;    PUSH_LR
:collect_string_loop
    ^~fgetc 94                  ;    ^~fgetc FCALL               # C = fgetc(input)
    1f1000b1                    ;    CMP_X0_TO_MINUS_4           # if C == EOF
    41000054                    ;    SKIP_INST_NE                # Something went horriably wrong
    ^~Exit_Failure 17           ;    ^~Exit_Failure RBRANCH

    1f8800f1                    ;    CMP_X0_TO_34                # IF C == '"'
    41000054                    ;    SKIP_INST_NE                # be done
    ^~collect_string_done 14    ;    ^~collect_string_done FBRANCH

    # deal with inside of string
    40000039                    ;    STR_BYTE_W0_[X2]            # target[index] = C
    42040091                    ;    ADD_X2_X2_1                 # index = index + 1
    ^~collect_string_loop 17    ;    ^~collect_string_loop RBRANCH # Keep going

:collect_string_done
    fe0741f8                    ;    POP_LR
    c0035fd6                    ;    RETURN


# collect_comment function
# Recieves nothing
# Modifies X0
# uses X0 as Int C
# Just throws away everything it reads
:collect_comment
    fe0f1ff8                    ;    PUSH_LR
:collect_comment_loop
    ^~fgetc 94                  ;    ^~fgetc FCALL               # C = fgetc(input)
    1f1000b1                    ;    CMP_X0_TO_MINUS_4           # IF C == EOF
    41000054                    ;    SKIP_INST_NE                # abort hard
    ^~Exit_Failure 17           ;    ^~Exit_Failure RBRANCH

    1f2800f1                    ;    CMP_X0_TO_10                # IF C == '\n'
    40000054                    ;    SKIP_INST_EQ                # otherwise keep looping
    ^~collect_comment_loop 17   ;    ^~collect_comment_loop RBRANCH
    fe0741f8                    ;    POP_LR
    c0035fd6                    ;    RETURN


# Malloc isn't actually required if the program being built fits in the initial memory
# However, it doesn't take much to add it.
# Requires [MALLOC] to be initialized and X0 to have the number of desired bytes
:malloc
    e10f1ff8                    ;    PUSH_X1                     # Protect X1
    49000018                    ;    LOAD_W9_AHEAD
    02000014                    ;    SKIP_32_DATA
    &MALLOC                     ;    &MALLOC
    210140f9                    ;    LDR_X1_[X9]                 # Using the current pointer
    e10f1ff8                    ;    PUSH_X1                     # Save to return later
    0000018b                    ;    ADD_X0_X0_X1                # Request the number of desired bytes
    c81a80d2                    ;    SET_X8_TO_SYS_BRK           # the Syscall # for SYS_BRK
    010000d4                    ;    SYSCALL                     # call the Kernel
    200100f9                    ;    STR_X0_[X9]                 # Update pointer
    e00741f8                    ;    POP_X0
    e10741f8                    ;    POP_X1                      # Restore X1
    c0035fd6                    ;    RETURN


# fgetc function
# Loads FILE* from [script]
# Returns -4 (EOF) or char in X0
:fgetc
    e10f1ff8                    ;    PUSH_X1                     # Protect X1
    e20f1ff8                    ;    PUSH_X2                     # Protect X2
    60008092                    ;    SET_X0_TO_MINUS_4           # Put EOF in x0
    e00f1ff8                    ;    PUSH_X0                     # Assume bad (If nothing read, value will remain EOF)
    e1030091                    ;    SET_X1_FROM_SP              # Get stack address
    49000018                    ;    LOAD_W9_AHEAD               # Where are we reading from
    02000014                    ;    SKIP_32_DATA
    &script                     ;    &script
    200140f9                    ;    LDR_X0_[X9]
    e80780d2                    ;    SET_X8_TO_SYS_READ          # the syscall number for read
    220080d2                    ;    SET_X2_TO_1                 # set the size of chars we want

    010000d4                    ;    SYSCALL                     # call the Kernel

    e00741f8                    ;    POP_X0                      # Get either char or EOF
    1f1000b1                    ;    CMP_X0_TO_MINUS_4           # Check for EOF
    41000054                    ;    SKIP_INST_NE                # Return as is
    ^~fgetc_done 14             ;    ^~fgetc_done FBRANCH
    001c4092                    ;    AND_X0_X0_0xFF              # Make it useful
:fgetc_done
    e20741f8                    ;    POP_X2                      # Restore X2
    e10741f8                    ;    POP_X1                      # Restore X1
    c0035fd6                    ;    RETURN


# File_Print function
# Receives CHAR* in X0
# calls fputc for every non-null char
:File_Print
    fe0f1ff8                    ;    PUSH_LR
    e10f1ff8                    ;    PUSH_X1                     # Protect X1
    e20f1ff8                    ;    PUSH_X2                     # Protect X2
    e10300aa                    ;    SET_X1_FROM_X0              # Protect S
    1f0000f1                    ;    CMP_X0_TO_0                 # Protect against nulls
    41000054                    ;    SKIP_INST_NE                # Simply don't try to print them
    ^~File_Print_Done 14        ;    ^~File_Print_Done FBRANCH
:File_Print_Loop
    20004039                    ;    LDR_BYTE_W0_[X1]            # Read byte
    1f0000f1                    ;    CMP_X0_TO_0                 # Check for NULL
    41000054                    ;    SKIP_INST_NE                # Stop at NULL
    ^~File_Print_Done 14        ;    ^~File_Print_Done FBRANCH

    ^~fputc 94                  ;    ^~fputc FCALL               # write it
    21040091                    ;    ADD_X1_X1_1                 # S = S + 1
    ^~File_Print_Loop 17        ;    ^~File_Print_Loop RBRANCH   # Keep going

:File_Print_Done
    e20741f8                    ;    POP_X2                      # Restore X2
    e10741f8                    ;    POP_X1                      # Restore X1
    fe0741f8                    ;    POP_LR
    c0035fd6                    ;    RETURN


# fputc function
# receives CHAR in X0 and load FILE* from stdout
# writes char and returns
:fputc
    e10f1ff8                    ;    PUSH_X1                     # Protect X1
    e20f1ff8                    ;    PUSH_X2                     # Protect X2

    e00f1ff8                    ;    PUSH_X0                     # We are writing x0
    e1030091                    ;    SET_X1_FROM_SP              # Get stack address
    200080d2                    ;    SET_X0_TO_1                 # Write to target file
    080880d2                    ;    SET_X8_TO_SYS_WRITE         # the syscall number for write
    220080d2                    ;    SET_X2_TO_1                 # set the size of chars we want
    010000d4                    ;    SYSCALL                     # call the Kernel
    e00741f8                    ;    POP_X0                      # Restore stack
    e20741f8                    ;    POP_X2                      # Restore X2
    e10741f8                    ;    POP_X1                      # Restore X1
    c0035fd6                    ;    RETURN


:Done
# program completed Successfully
    000080d2                    ;    SET_X0_TO_0                 # All is well
    a80b80d2                    ;    SET_X8_TO_SYS_EXIT          # put the exit syscall number in x0
    010000d4                    ;    SYSCALL                     # Call it a good day

:ELF_data
:envp           0000000000000000        ; NULL64
:script         0000000000000000        ; NULL64
:tokens         0000000000000000        ; NULL64
:command_done   0000000000000000        ; NULL64
:max_args       00080000 00000000       ; %2048 %0               # 256 * sizeof(char*)
:max_string     00100000 00000000       ; %4096 %0               # 4096 * sizeof(char)
:MALLOC         0000000000000000        ; NULL64

:info
    0000000000000000 0000000000000000 0000000000000000 0000000000000000     ; NULL64 NULL64 NULL64 NULL64
    0000000000000000 0000000000000000 0000000000000000 0000000000000000     ; NULL64 NULL64 NULL64 NULL64
    0000000000000000 0000000000000000 0000000000000000 0000000000000000     ; NULL64 NULL64 NULL64 NULL64
    0000000000000000 0000000000000000 0000000000000000 0000000000000000     ; NULL64 NULL64 NULL64 NULL64

:default_file
    6B 61 65 6D 2E 72 75 6E 00  ; "kaem.run"
:prefix
    20 2B 3E 20 00              ; " +> "
:hard
    53 75 62 70 72 6F 63 65 73 73 20 65 72 72 6F 72 0A  ; "Subprocess error"
    41 42 4F 52 54 49 4E 47 20 48 41 52 44 0A           ; "ABORTING HARD"
    00

:ELF_end
:ELF_sym
:ELF_str
