Advertisement
Guest User

Untitled

a guest
Jun 18th, 2019
103
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #  _   _                 _                 ____             _            
  2. # | \ | |_   _ _ __ ___ | |__   ___ _ __  / ___|  ___  _ __| |_ ___ _ __
  3. # |  \| | | | | '_ ` _ \| '_ \ / _ \ '__| \___ \ / _ \| '__| __/ _ \ '__|
  4. # | |\  | |_| | | | | | | |_) |  __/ |     ___) | (_) | |  | ||  __/ |  
  5. # |_| \_|\__,_|_| |_| |_|_.__/ \___|_|    |____/ \___/|_|   \__\___|_|  
  6. #
  7. # By Peter Severin Rasmussen
  8.  
  9. .section .data
  10. file_stat: .space 144   # Size of the fstat struct
  11.  
  12. .section .text
  13. .globl _start
  14. _start:
  15.  
  16.    # Note: xor register, register will be used to set register equal to 0
  17.    #       as it results in a smaller binary
  18.  
  19.    #################
  20.    #  File Reader  #
  21.    #################
  22.  
  23.    # Strategy
  24.    # --------
  25.    #
  26.    # Perform the following sequence of syscalls to read the file:
  27.    #
  28.    #           In                 Syscall                   Out
  29.    # file path (from stack) ->     open    ->  fd
  30.    # fd                     ->     fstat   ->  filesize
  31.    # filesize               ->     brk     ->  file buffer pointer to allocated memory
  32.    # (fd, filesize, f ptr)  ->     read    ->  Jackpot! (Data in buffer)
  33.    # And of course close the file to be nice.
  34.  
  35.    # Registers
  36.    # ---------
  37.    #
  38.    # r13       # fd
  39.    # r14       # filesize
  40.    # r15       # file buffer pointer
  41.  
  42.    # Open file
  43.    mov $2, %rax         # Syscall 2 tells Linux to open
  44.    mov 16(%rsp), %rdi   # The address of the file path is in the stack (argument)
  45.    mov $2, %rsi         # Flags - According to header files read-only is 2
  46.    xor %rdx, %rdx       # Access mode 0 - Read
  47.    syscall              # File descriptor (fd) or error will be in rax
  48.  
  49.    mov %rax, %r13       # Save fd, so we don't lose it
  50.  
  51.     # Get file stat
  52.     mov $5, %rax         # Syscall 5 - Fstat
  53.     mov %r13, %rdi       # File descriptor as first argument
  54.     mov $file_stat, %rsi # Use reserved space for the stat struct
  55.     syscall              # file_stat struct should be filled with data
  56.  
  57.     # Get file size
  58.     mov $file_stat, %rbx  
  59.     mov 48(%rbx), %rax    # Position of size in the struct
  60.  
  61.     mov %rax, %r14        # Save filesize
  62.  
  63.     # Get current end of heap
  64.     mov $12, %rax  # Syscall 12 - Brk
  65.     xor %rdi, %rdi # Allocate 0 bytes, i.e. get current end of heap
  66.     syscall        # End of heap will be in rax
  67.  
  68.     mov %rax, %r15 # Save current end of heap as file pointer
  69.  
  70.     # Allocate buffer for file
  71.     add %r14, %rax # Add filesize to file pointer
  72.     mov %rax, %rdi # Set file pointer as parameter
  73.     mov $12, %rax  # Syscall 12 - Brk. Allocates up till file pointer
  74.     syscall        
  75.  
  76.  
  77.     # Read file
  78.     xor %rax, %rax   # Syscall 0 - Read
  79.     mov %r13, %rdi   # Load fd
  80.     mov %r15, %rsi   # Where to put the read data
  81.     mov %r14, %rdx   # How much to read
  82.     syscall          # Allocated memory should now be filled with data
  83.    
  84.     # Close file
  85.     mov $3, %rax     # Syscall 3 - Close
  86.     mov %r13, %rdi   # Get file descriptor from where we saved it
  87.     syscall
  88.  
  89.  
  90.     #################
  91.     # Count numbers #
  92.     #################
  93.  
  94.     # Strategy
  95.     # --------
  96.     #
  97.     # Go through every character and check if it is 10 (\n)
  98.     # If yes, increment a counter
  99.     # Furthermore, allocate memory for the parsed numbers.
  100.     # The new buffer must be 8 times as big as the number of counted newlines,
  101.     # because every number uses 64-bits = 8 bytes.
  102.  
  103.     # Registers
  104.     # ---------
  105.     #
  106.     xor %rax, %rax  # Offset in file buffer
  107.     xor %rbx, %rbx  # Char currently being read
  108.     xor %rcx, %rcx  # Number counter
  109.     # r12           # Number counter (saved for later)
  110.     # r13           # Address of number buffer
  111.     # r15           # Address of file buffer
  112.  
  113. num_count:                  # Go through every char and compare it with 10 ('\n')
  114.     movb (%r15, %rax), %bl  # Load current byte we are at
  115.     cmp $10, %rbx           # Compare it
  116.     jne not_newline_count   # Skip increment of counter if not equal to 10
  117.     inc %rcx                # Otherwise increment
  118. not_newline_count:          #
  119.     inc %rax                # Move to next byte
  120.     cmp %r14, %rax          # Are we at the end? (Using filesize to determine)
  121.     jle num_count           # If not, loop again
  122.  
  123.     mov %rcx, %r12 # Save the count for later
  124.     imul $8, %rcx  # Find the actual size of the buffer (every entry is 64-bit)
  125.  
  126.     # Allocate another buffer
  127.     # that holds the actual numbers
  128.  
  129.     # Calculate current end of heap (who needs syscalls anyway?)
  130.     mov %r15, %rax # File buffer address
  131.     add %r14, %rax # Add filesize
  132.     # Voila!
  133.  
  134.     mov %rax, %r13 # Save current end of heap as number buffer
  135.  
  136.     # Allocate buffer for numbers
  137.     add %rcx, %rax # Add number counter * 8 to end of heap to get end address
  138.     mov %rax, %rdi # Set end address as parameter
  139.     mov $12, %rax  # Syscall 12 - Brk (Allocate)
  140.     syscall
  141.  
  142.  
  143.     #############
  144.     #  Parsing  #
  145.     #############
  146.  
  147.     # Strategy
  148.     # --------
  149.     #
  150.     # Parsing algorithm:
  151.     # - Start result at 0
  152.     # - Every time we read a non-newline char, shift result left in base 10 (mult by 10).
  153.     # - Convert char to integer by subtracting 48
  154.     # - Add converted integer to result
  155.     # - Repeat
  156.     # When reading newline char, we have parsed a number, so we save it and move to next
  157.     # number in number buffer.
  158.  
  159.     # Registers
  160.     # ---------
  161.     #
  162.     xor %rax, %rax  # Offset in file buffer
  163.     xor %rbx, %rbx  # Char currently being read
  164.     xor %rcx, %rcx  # Offset in number buffer
  165.     xor %rdx, %rdx  # Current parser result
  166.     # r12           # Size of number buffer
  167.     # r13           # Address of number buffer
  168.     # r14           # Size of file buffer
  169.     # r15           # Address to file buffer
  170.    
  171. parse:                      # Go through every char again and compare it with 10 ('\n')
  172.     xor %rbx, %rbx          # Clear rbx, just to be on the safe side
  173.     movb (%r15, %rax), %bl  # Load current byte we are at
  174.     cmp $10, %rbx           # Compare it
  175.     je parse_newline        # If 10, go to newline routine. Else:
  176.  
  177.     sub $48, %bl            # Convert char to actual integer
  178.     imul $10, %rdx          # Multiply the current result with 10 (shift left in base 10)
  179.     add %rbx, %rdx          # Add converted integer to the result
  180.  
  181.     jmp parse_end           # Jump over the newline routine
  182.  
  183. parse_newline:              # Newline routine:
  184.     mov %rdx, (%r13,%rcx,8) # Save current parser result
  185.     xor %rdx, %rdx          # Reset parser result
  186.     inc %rcx                # Move to the next number in number buffer
  187.  
  188. parse_end:                  # Last part of parse loop:
  189.     inc %rax                # Move to next byte
  190.     cmp %r14, %rax          # Are we at the end? (Using filesize to determine)
  191.     jle parse               # If not, repeat
  192.  
  193.  
  194.     ##########
  195.     #  Sort  #
  196.     ##########
  197.  
  198.     # Strategy
  199.     # --------
  200.     #
  201.     # For sorting, we will use Gnome sort as it is one of the simplest
  202.     # sorting algorithms and we strive for smallest binary.
  203.     #
  204.     # GnomeSort(A)
  205.     #   n = A.length
  206.     #   i = 0
  207.     #   while i < n do
  208.     #     if i = 0 or A[i-1] <= A[i] then
  209.     #       i = i + 1
  210.     #     else
  211.     #       swap A[i-1] and A[i]
  212.     #       i = i - 1
  213.  
  214.     # Registers
  215.     # ---------
  216.     #
  217.     # rax           # Content of (%r13, %r8, 8)   - A[i]
  218.     # rbx           # Content of -8(%r13, %r8, 8) - A[i-1]
  219.     xor %r8, %r8    # i - Offset in number buffer
  220.     #xor %rbp, %rbp # Number of comparisons - If someone asks: No I did not use the stack base pointer to count comparisons. Ehm. Nope.
  221.     # r12           # Count of numbers in number buffer
  222.     # r13           # Number buffer
  223.  
  224. gnome_sort_loop:
  225.     cmp $0, %r8                 # If i = 0
  226.     je gnome_sort_inc           # Increment i
  227.     mov (%r13, %r8, 8), %rax    # A[i]
  228.     mov -8(%r13, %r8, 8), %rbx  # A[i-1]
  229.     #inc %rbp                   # Increment # of comparisons - Your eyes must be deceiving you
  230.     cmp %rax, %rbx              # If A[i-1] <= A[i] (compare unsigned)
  231.     jbe gnome_sort_inc          # Increment i
  232.     jmp gnome_sort_swap         # Else swap and decrement i
  233.  
  234. gnome_sort_inc:                 # Increment i and jump to loop end
  235.     inc %r8
  236.     jmp gnome_sort_loop_end
  237.  
  238. gnome_sort_swap:                # Swap A[i] and A[i-1] and decrement i
  239.     mov %rbx, (%r13, %r8, 8)
  240.     mov %rax, -8(%r13, %r8, 8)
  241.     dec %r8
  242.  
  243. gnome_sort_loop_end:
  244.     cmp %r12, %r8               # Are we at the end?
  245.     jl gnome_sort_loop          # If not, repeat loop
  246.    
  247.  
  248.  
  249.  
  250.     ###########
  251.     #  Print  #
  252.     ###########
  253.  
  254.     # Strategy
  255.     # --------
  256.     #
  257.     # First, allocate 21 bytes to store any 64-bit number in base 10 => 20 bytes + 1 byte for newline
  258.     #
  259.     # Print algorithm:
  260.     # - Convert number back to characters
  261.     # - and fill the following space with the characters backwards up to MSD:
  262.     #   _ _ _ ... _ _ _ \n         |     E.g. _ _ _ ... 3 5 1 \n
  263.     # - Call write syscall and write from MSD to \n
  264.     # - Repeat
  265.     #
  266.     # Convert algorithm:
  267.     # - Divide n by 10
  268.     # - Remainder r is next digit to write (0 <= r < 10)
  269.     # - Add 48 to get to ascii char
  270.     # - When quotient is 0, we are done
  271.     #
  272.     # Register list follow
  273.  
  274.     mov %r13, %r15 # Address of number buffer
  275.     mov %r12, %r14 # Copy count of numbers, so we can multiply without destroying the original
  276.     imul $8, %r14  # Multiply by 8 to get actual size of buffer
  277.     add %r14, %r15 # Add size of buffer to get to the end of heap
  278.     mov %r15, %rdi # Store in rdi
  279.     add $21, %rdi  # Add 21 to allocate 21 bytes (needed to represent any 64-bit number + newline char)
  280.     mov $12, %rax  # Syscall 12 - Brk (Allocate)
  281.     syscall
  282.  
  283.     movb $10, (%rdi) # Insert newline (\n) at the end
  284.  
  285.     # Registers
  286.     # ---------
  287.     #
  288.     # rax           # Number currently being printed
  289.     xor %r9, %r9    # Offset in number buffer
  290.     # r8            # Offset in output buffer
  291.     mov $10, %r10   # Divisor
  292.     # r12           # Number of numbers in number buffer
  293.     # r13           # Address of number buffer
  294.     # r14           # Size of number buffer
  295.     # r15           # Address of output buffer
  296.  
  297.  
  298. print_loop:
  299.     mov $20, %r8            # Reset offset in output buffer
  300.     mov (%r13,%r9,8), %rax  # Load number to print
  301.  
  302. convert_back:
  303.     xor %rdx, %rdx          # When dividing, make sure there is no garbage in rdx
  304.     div %r10                # Divide number by 10. Quotient in rax, remainder in rdx
  305.     add $48, %rdx           # Remainder (in rdx) is the number to print. We convert it to a char
  306.     movb %dl, (%r15, %r8)   # Place char in the buffer starting from the end
  307.     dec %r8                 # Step backwards through the output buffer
  308.     cmp $0, %rax            # If quotient is 0, we are done
  309.     jne convert_back        # If not, we repeat
  310.  
  311.     inc %r8                 # Move from nothingness to most significant digit
  312.  
  313.     mov $1, %rax            # Syscall 1 - Write
  314.     mov $1, %rdi            # File descriptor 1 - Std out
  315.     mov %r15, %rsi          # The buffer to write starts at
  316.     add %r8, %rsi           #  r15 + offset
  317.     mov $22, %rdx           # The chars to read can also be calculated using
  318.     sub %r8, %rdx           #  22 - offset + 1 (to make interval inclusive) + 1 (\n)
  319.     syscall
  320.  
  321.     inc %r9                 # Move to next number
  322.     cmp %r12, %r9           # If there are still numbers left
  323.     jl print_loop           # We repeat
  324.  
  325.  
  326.     ##########
  327.     #  Exit  #
  328.     ##########
  329.  
  330.     mov $60, %rax   # Syscall 60 - Exit
  331.     xor %rdi, %rdi  # Error code 0
  332.     syscall         # Exit gracefully
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement