Gas multi-file Engineering
From: programming from groundup Chapter 6
The main purpose is to understand how to organize multi-file projects (Forms)
File record-def.s:
. Equ record_firstname, 0
. Equ record_lastname, 40
. Equ record_address, 80
. Equ record_age, 320
. Equ record_size, 324
File Linux. S:
# Common Linux Definitions
# System call numbers
. Equ sys_exit, 1
. Equ sys_read, 3
. Equ sys_write, 4
. Equ sys_open, 5
. Equ sys_close, 6
. Equ sys_brk, 45
# System Call interrupt number
. Equ linux_syscall, 0x80
# Standard file descriptors
. Equ stdin, 0
. Equ stdout, 1
. Equ stderr, 2
# Common status codes
. Equ end_of_file, 0
First ProgramWriting records
File: write-record.s
. Include "Linux. s"
. Include "record-def.s"
# Purpose: This function writes a record
# The given file descriptor
#
# Input: The file descriptor and a buffer
#
# Output: This function produces a status code
#
# Stack local variables
. Equ st_write_buffer, 8
. Equ st_filedes, 12
. Section. Text
. Globl write_record
. Type write_record, @ Function
Write_record:
Pushl % EBP
Movl % ESP, % EBP
Pushl % EBX
Movl $ sys_write, % eax
Movl st_filedes (% EBP), % EBX
Movl st_write_buffer (% EBP), % ECx
Movl $ record_size, % edX
Int $ linux_syscall
# Note-% eax has the return value, which we will
# Give back to our calling program
Popl % EBX
Movl % EBP, % ESP
Popl % EBP
RET
File write-records.s:
. Include "Linux. s"
. Include "record-def.s"
. Section. Data
# Constant data of the records we want to write
# Each text data item is padded to the proper
# Length with null (I. e. 0) bytes.
#. Rept is used to pad each item .. rept tells
# The caller to repeat the section
#. Rept and. endr the number of times specified.
# This is used in this program to add extra null
# Characters at the end of each field to fill
# It Up
Record1:
. ASCII "Fredrick \ 0"
. Rept 31 # padding to 40 bytes
. Byte 0
. Endr
. ASCII "Bartlett \ 0"
. Rept 31 # padding to 40 bytes
. Byte 0
. Endr
. ASCII "4242 s prairie \ ntulsa, OK 55555 \ 0"
. Rept 209 # padding to 240 bytes
. Byte 0
. Endr
. Long 45
Record2:
. ASCII "Marilyn \ 0"
. Rept 32 # padding to 40 bytes
. Byte 0
. Endr
. ASCII "Taylor \ 0"
. Rept 33 # padding to 40 bytes
. Byte 0
. Endr
. ASCII "2224 s johannan ST \ nchicago, il 12345 \ 0"
. Rept 203 # padding to 240 bytes
. Byte 0
. Endr
. Long 29
Record3:
. ASCII "Derrick \ 0"
. Rept 32 # padding to 40 bytes
. Byte 0
. Endr
. ASCII "McIntire \ 0"
. Rept 31 # padding to 40 bytes
. Byte 0
. Endr
. ASCII "500 W Oakland \ nsan Diego, CA 54321 \ 0"
. Rept 206 # padding to 240 bytes
. Byte 0
. Endr
. Long 36
# This is the name of the file we will write
File_name:
. ASCII "test. dat \ 0"
. Equ st_file_descriptor,-4
. Globl _ start
_ Start:
# Copy the stack pointer to % EBP
Movl % ESP, % EBP
# Allocate space to hold the file descriptor
Subl $4, % ESP
# Open the file
Movl $ sys_open, % eax
Movl $ file_name, % EBX
Movl $0101, % ECx # This says to create if it
# Doesn' t exist, and open
# Writing
Movl $0666, % edX
Int $ linux_syscall
# Store the file descriptor away
Movl % eax, st_file_descriptor (% EBP)
# Write the first record
Pushl st_file_descriptor (% EBP)
Pushl $ record1
Call write_record
Addl $8, % ESP
# Write the second record
Pushl st_file_descriptor (% EBP)
Pushl $ record2
Call write_record
Addl $8, % ESP
# Write the third record
Pushl st_file_descriptor (% EBP)
Pushl $ record3
Call write_record
Addl $8, % ESP
# Close the file descriptor
Movl $ sys_close, % eax
Movl st_file_descriptor (% EBP), % EBX
Int $ linux_syscall
# Exit the program
Movl $ sys_exit, % eax
Movl $0, % EBX
Int $ linux_syscall
To build the application, run the commands:
As write-records.s-O write-record.o
As write-record.s-O write-record.o
LD write-record.o write-records.o-O write-records
Second ProgramReading records
File read-record.s:
. Include "record-def.s"
. Include "Linux. s"
# Purpose: This function reads a record from the file
# Descriptor
#
# Input: The file descriptor and a buffer
#
# Output: This function writes the data to the buffer
# And returns a status code.
#
# Stack local variables
. Equ st_read_buffer, 8
. Equ st_filedes, 12
. Section. Text
. Globl read_record
. Type read_record, @ Function
Read_record:
Pushl % EBP
Movl % ESP, % EBP
Pushl % EBX
Movl st_filedes (% EBP), % EBX
Movl st_read_buffer (% EBP), % ECx
Movl $ record_size, % edX
Movl $ sys_read, % eax
Int $ linux_syscall
# Note-% eax has the return value, which we will
# Give back to our calling program
Popl % EBX
Movl % EBP, % ESP
Popl % EBP
RET
File count-chars.s:
# Purpose: Count the characters until a NULL byte is reached.
#
# Input: the address of the character string
#
# Output: returns the count in % eax
#
# Process:
# Registers used:
# % ECx-Character Count
# % Al-Current Character
# % EdX-Current Character address
. Type count_chars, @ Function
. Globl count_chars
# This is where our one parameter is on the stack
. Equ st_string_start_address, 8
Count_chars:
Pushl % EBP
Movl % ESP, % EBP
# Counter starts at zero
Movl $0, % ECx
# Starting address of data
Movl st_string_start_address (% EBP), % edX
Count_loop_begin:
# Grab the current character
Movb (% EDX), % Al
# Is it null?
Cmpb $0, % Al
# If yes, we're done
Je count_loop_end
# Otherwise, increment the counter and the pointer
Incl % ECx
Incl % edX
# Go back to the beginning of the loop
JMP count_loop_begin
Count_loop_end:
# We're done. Move the count into % eax
# And return.
Movl % ECx, % eax
Popl % EBP
RET
File write-newline.s:
. Include "Linux. s"
. Globl write_newline
. Type write_newline, @ Function
. Section. Data
Newline:
. ASCII "\ n"
. Section. Text
. Equ st_filedes, 8
Write_newline:
Pushl % EBP
Movl % ESP, % EBP
Movl $ sys_write, % eax
Movl st_filedes (% EBP), % EBX
Movl $ newline, % ECx
Movl $1, % edX
Int $ linux_syscall
Movl % EBP, % ESP
Popl % EBP
RET
File read-records.s:
. Include "Linux. s"
. Include "record-def.s"
. Section. Data
File_name:
. ASCII "test. dat \ 0"
. Section. BSS
. Lcomm record_buffer, record_size
. Section. Text
# Main Program
. Globl _ start
_ Start:
# These are the locations on the stack where
# We will store the input and output Descriptors
# (FYI-We cocould have used memory addresses in
# A. Data section instead)
. Equ st_input_descriptor,-4
. Equ st_output_descriptor,-8
# Copy the stack pointer to % EBP
Movl % ESP, % EBP
# Allocate space to hold the file descriptors
Subl $8, % ESP
# Open the file
Movl $ sys_open, % eax
Movl $ file_name, % EBX
Movl $0, % ECx # This says to open read-only
Movl $0666, % edX
Int $ linux_syscall
# Save file descriptor
Movl % eax, st_input_descriptor (% EBP)
# Even though it's a constant, we are
# Saving the output file descriptor in
# A local variable so that if we later
# Decide that it isn' t always going
# Be stdout, we can change it easily.
Movl $ stdout, st_output_descriptor (% EBP)
Record_read_loop:
Pushl st_input_descriptor (% EBP)
Pushl $ record_buffer
Call read_record
Addl $8, % ESP
# Returns the number of bytes read.
# If it isn' t the same number we
# Requested, then it's either
# End-of-file, or an error, so we're re
# Quitting
CMPL $ record_size, % eax
JNE finished_reading
# Otherwise, print out the first name
# But first, we must know it's size
Pushl $ record_firstname + record_buffer
Call count_chars
Addl $4, % ESP
Movl % eax, % edX
Movl st_output_descriptor (% EBP), % EBX
Movl $ sys_write, % eax
Movl $ record_firstname + record_buffer, % ECx
Int $ linux_syscall
Pushl st_output_descriptor (% EBP)
Call write_newline
Addl $4, % ESP
JMP record_read_loop
Finished_reading:
Movl $ sys_exit, % eax
Movl $0, % EBX
Int $ linux_syscall
To build this program, we need to assemble all of the parts and link them together:
As read-record.s-O read-record.o
As count-chars.s-O count-chars.o
As write-newline.s-O write-newline.o
As read-records.s-O read-records.o
LD read-record.o count-chars.o write-newline.o \
Read-records.o-O read-records
Third ProgramModifying the records
File add-year.s:
. Include "Linux. s"
. Include "record-def.s"
. Section. Data
Input_file_name:
. ASCII "test. dat \ 0"
Output_file_name:
. ASCII "testout. dat \ 0"
. Section. BSS
. Lcomm record_buffer, record_size
# Stack offsets of local variables
. Equ st_input_descriptor,-4
. Equ st_output_descriptor,-8
. Section. Text
. Globl _ start
_ Start:
# Copy Stack pointer and make room for local variables
Movl % ESP, % EBP
Subl $8, % ESP
# Open File for reading
Movl $ sys_open, % eax
Movl $ input_file_name, % EBX
Movl $0, % ECx
Movl $0666, % edX
Int $ linux_syscall
Movl % eax, st_input_descriptor (% EBP)
# Open File for writing
Movl $ sys_open, % eax
Movl $ output_file_name, % EBX
Movl $0101, % ECx
Movl $0666, % edX
Int $ linux_syscall
Movl % eax, st_output_descriptor (% EBP)
Loop_begin:
Pushl st_input_descriptor (% EBP)
Pushl $ record_buffer
Call read_record
Addl $8, % ESP
# Returns the number of bytes read.
# If it isn' t the same number we
# Requested, then it's either
# End-of-file, or an error, so we're re
# Quitting
CMPL $ record_size, % eax
JNE loop_end
# Increment the age
Incl record_buffer + record_age
# Write the record out
Pushl st_output_descriptor (% EBP)
Pushl $ record_buffer
Call write_record
Addl $8, % ESP
JMP loop_begin
Loop_end:
Movl $ sys_exit, % eax
Movl $0, % EBX
Int $ linux_syscall
To build it, type the following4:
As add-year.s-O add-year.o
LD add-year.o read-record.o write-record.o-o add-year