use advanced dependency scanner for fasm programs

git-svn-id: svn://kolibrios.org@2816 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
CleverMouse 2012-06-21 19:18:22 +00:00
parent 56de4bac0f
commit ad90f24ec3
15 changed files with 1733 additions and 60 deletions

View File

@ -381,7 +381,7 @@ include Makefile.nasm
include Makefile.copy
# Special rules for copying sysfuncs.txt - it isn't directly included in the image.
docpack: $(DOCDIR)SYSFUNCS.TXT $(wildcard $(DOCDIR)*)
docpack: $(DOCDIR)SYSFUNCS.TXT
$(DOCDIR)SYSFUNCS.TXT: $(KERNEL)/docs/sysfuncs.txt
cp $(KERNEL)/docs/sysfuncs.txt $(DOCDIR)SYSFUNCS.TXT

View File

@ -28,25 +28,32 @@
# $(4) = name of program - without path and extension,
define fasm_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
fasm -m 65536 $$< "$$@" -s .deps/$(4).fas
prepsrc .deps/$(4).fas /dev/null
prepsrc .deps/$(4).fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
rm $$$$tmpfile) || (rm $$$$tmpfile; false)
kpack --nologo "$$@"
-include .deps/$(4).Po
endef
define fasm_nokpack_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
rm $$$$tmpfile) || (rm $$$$tmpfile; false)
-include .deps/$(4).Po
endef
progname=$(call respace,$(basename $(notdir $(call binarypart,$(f)))))
binarydir=$(subst ./,,$(dir $(call binarypart,$(f))))
$(foreach f,$(FASM_PROGRAMS) $(FASM_PROGRAMS_CD),$(eval $(call fasm_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
$(foreach f,$(FASM_PROGRAMS) $(FASM_PROGRAMS_CD) $(SKIN_SOURCES),$(eval $(call fasm_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
$(foreach f,$(FASM_NOKPACK_PROGRAMS),$(eval $(call fasm_nokpack_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
# Rule for the kernel differs: it uses kerpack instead of kpack.
kernel.mnt: $(KERNEL)/kernel.asm Makefile.fasm .deps/.dir
fasm -m 65536 $< "$@" -s .deps/kernel.fas
prepsrc .deps/kernel.fas /dev/null
prepsrc .deps/kernel.fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$a=join " \\\n ",@a;print "$@: $$a\n$$a:\n"}' > .deps/kernel.Po
kerpack $@
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$<" "$@" -s $$tmpfile && \
fasmdep -e $$tmpfile > .deps/kernel.Po && \
rm $$tmpfile) || (rm $$tmpfile; false)
-include .deps/kernel.Po

View File

@ -0,0 +1,90 @@
; Structures describing fasm symbol files.
define FAS_SIGNATURE 1A736166h
virtual at 0
fas_header:
.signature dd ? ; Signature 1A736166h.
.major db ? ; Major version of flat assembler.
.minor db ? ; Minor version of flat assembler.
.headersize dw ? ; Length of header.
.input dd ? ; Offset of input file name in the strings table.
.output dd ? ; Offset of output file name in the strings table.
.strings_offs dd ? ; Offset of strings table.
.strings_size dd ? ; Length of strings table.
.symbols_offs dd ? ; Offset of symbols table.
.symbols_size dd ? ; Length of symbols table.
.preproc_offs dd ? ; Offset of preprocessed source.
.preproc_size dd ? ; Length of preprocessed source.
.asm_offs dd ? ; Offset of assembly dump.
.asm_size dd ? ; Length of assembly dump.
.section_offs dd ? ; Offset of section names table.
.section_size dd ? ; Length of section names table.
.symref_offs dd ? ; Offset of symbol references dump.
.symref_size dd ? ; Length of symbol references dump.
.size:
end virtual
virtual at 0
preproc_line_header:
; When the line was loaded from source, this field contains
; either zero (if it is the line from the main input file), or
; an offset inside the preprocessed source to the name of file,
; from which this line was loaded (the name of file is zero-ended
; string).
; When the line was generated by macroinstruction, this field
; contains offset inside the preprocessed source to the
; pascal-style string specifying the name of macroinstruction,
; which generated this line.
.source_name dd ?
; Bits 0-30 contain the number of this line.
; If the highest bit is zeroed, this line was loaded from source.
; If the highest bit is set, this line was generated by
; macroinstruction.
.line_number dd ?
; If the line was loaded from source, this field contains
; the position of the line inside the source file, from which it
; was loaded.
; If line was generated by macroinstruction, this field contains
; the offset of preprocessed line, which invoked the
; macroinstruction. If line was generated by instantaneous macro,
; this field is equal to the next one.
.line_offset dd ?
; If the line was generated by macroinstruction, this field
; contains offset of the preprocessed line inside the definition
; of macro, from which this one was generated.
.line_macro_offs dd ?
; The tokenized contents of line.
.contents:
end virtual
virtual at 0
asm_row:
.out_offs dd ? ; Offset in output file.
.preproc_offs dd ? ; Offset of line in preprocessed source.
.this dq ? ; Value of $ address.
.extended_sib dd ? ; Extended SIB for the $ address, the first two bytes
; are register codes and the second two bytes are
; corresponding scales.
; If the $ address is relocatable, this field contains
; information about section or external symbol, to which
; it is relative - otherwise this field is zero.
; When the highest bit is cleared, the address is
; relative to a section, and the bits 0-30 contain
; the index (starting from 1) in the table of sections.
; When the highest bit is set, the address is relative
; to an external symbol, and the bits 0-30 contain the
; the offset of the name of this symbol in the strings
; table.
.this_base dd ?
.this_type db ? ; Type of $ address value (as in table 2.2).
.code_type db ? ; Type of code - possible values are 16, 32, and 64.
; If the bit 0 is set, then at this point the assembly
; was taking place inside the virtual block, and the
; offset in output file has no meaning here.
; If the bit 1 is set, the line was assembled at the
; point, which was not included in the output file for
; some other reasons (like inside the reserved data at
; the end of section).
.flags db ?
.this_high db ? ; The higher bits of value of $ address.
.sizeof:
end virtual

View File

@ -0,0 +1,765 @@
; This program parses .fas file generated by fasm
; and prints the list of dependencies for make.
; Usage: fasmdep [-e] [<input file> [<output file>]].
; If input file is not given, the program reads from stdin.
; If output file is not given, the program writes to stdout.
; If the option -e is given, the program also creates an empty
; goal for every dependency, this prevents make errors when
; files are renamed.
; This definition controls the choice of platform-specific code.
;define OS WINDOWS
define OS LINUX
; Program header.
match =WINDOWS,OS { include 'windows_header.inc' }
match =LINUX,OS { include 'linux_header.inc' }
include 'fas.inc'
; Main code
start:
; 1. Setup stack frame, zero-initialize all local variables.
virtual at ebp - .localsize
.begin:
.flags dd ? ; 1 if '-e' is given
.in dd ? ; handle of input file
.out dd ? ; handle of output file
.buf dd ? ; pointer to data of .fas file
.allocated dd ? ; number of bytes allocated for .buf
.free dd ? ; number of bytes free in .buf
.outstart dd ? ; offset in .buf for start of data to output
.names dd ? ; offset in .buf for start of output names
.include dd ? ; offset in .buf to value of %INCLUDE%
.testname dd ? ; offset in .buf for start of current file name
.prevfile dd ? ; offset in .buf for previous included file name
.prevfilefrom dd ? ; offset in .buf for .asm/.inc for .prevfile
.localsize = $ - .begin
match =LINUX,OS {
.argc dd ?
.argv:
}
end virtual
mov ebp, esp
xor eax, eax
repeat .localsize / 4
push eax
end repeat
; 2. Call the parser of the command line.
call get_params
; 3. Load the input file.
; Note that stdin can be a pipe,
; useful in bash-construction "fasm input.asm -s >(fasmdep > input.Po)",
; so its size is not always known in the beginning.
; So, we must read input file in portions, reallocating memory if needed.
.readloop:
; 3a. If the size is less than 32768 bytes, reallocate buffer.
; Otherwise, goto 3c.
cmp [.free], 32768
jae .norealloc
; 3b. Reallocate buffer.
; Start with 65536 bytes and then double the size every time.
mov eax, 65536
mov ecx, [.allocated]
add ecx, ecx
jz @f
mov eax, ecx
@@:
call realloc
.norealloc:
; 3c. Read the next portion.
call read
sub [.free], eax
test eax, eax
jnz .readloop
; 4. Sanity checks.
; We don't use .section_* and .symref_*, so allow them to be absent.
mov edi, [.allocated]
sub edi, [.free]
; Note that edi = number of bytes which were read
; and simultaneously pointer to free space in the buffer relative to [.buf].
cmp edi, fas_header.section_offs
jb badfile
mov ebx, [.buf]
cmp [ebx+fas_header.signature], FAS_SIGNATURE
jnz badfile
cmp [ebx+fas_header.headersize], fas_header.section_offs
jb badfile
; 5. Get %INCLUDE% environment variable, it will be useful.
mov [.include], edi
mov esi, include_variable
sub esi, ebx
call get_environment_variable
; 6. Start writing dependencies: copy output and input files.
mov [.outstart], edi
; 6a. Copy output name.
mov esi, [ebx+fas_header.output]
call copy_asciiz_escaped
; 6b. Write ": ".
stdcall alloc_in_buf, 2
mov word [edi+ebx], ': '
inc edi
inc edi
; 6c. Copy input name.
mov [.names], edi
mov esi, [ebx+fas_header.input]
call copy_asciiz_escaped
; 7. Scan for 'include' dependencies.
; 7a. Get range for scanning.
mov edx, [ebx+fas_header.preproc_size]
mov esi, [ebx+fas_header.preproc_offs]
add edx, esi
.include_loop:
; 7b. Loop over preprocessed lines in the range.
cmp esi, edx
jae .include_done
; 7c. For every line, ignore header and do internal loop over tokens.
add esi, preproc_line_header.contents
.include_loop_int:
; There are five types of tokens:
; 1) "start preprocessor data" with code ';' <byte length> <length*byte token>
; 2) "quoted string" with code '"' <dword length> <length*byte string>
; 3) "word" with code 1Ah <byte length> <length*byte word>
; 4) one-byte tokens like "+", "(" and so on
; 5) "end-of-line" token with code 0.
mov al, [esi+ebx]
inc esi
; 7d. First, check token type parsing the first byte.
; For preprocessor tokens, continue to 7e.
; For quoted strings, go to 7g.
; For words, go to 7h.
; For end-of-line, exit the internal loop, continue the external loop.
; Otherwise, just continue the internal loop.
cmp al, ';'
jnz .notprep
; 7e. For "include" tokens length=7, token is "include", the next token is
; quoted string.
; These tokens are handled in 7f, other preprocessor tokens in 7g.
cmp byte [esi+ebx], 7
jnz .notinclude
cmp dword [esi+ebx+1], 'incl'
jnz .notinclude
cmp dword [esi+ebx+5], 'ude"'
jnz .notinclude
; 7f. Skip over end of this token and the type byte of the next token;
; this gives the pointer to length-prefixed string, which should be added
; to dependencies. Note that doing that skips that string,
; so after copying just continue the internal loop.
add esi, 9
call add_separator
call copy_name_escaped
jmp .include_loop_int
.quoted:
; 7g. To skip a word, load the dword length and add it to pointer.
add esi, [esi+ebx]
add esi, 4
jmp .include_loop_int
.notinclude:
; 7h. To skip this token, load the byte length and add it to pointer.
; Note that word tokens and preprocessor tokens have a similar structure,
; so both are handled here.
movzx eax, byte [esi+ebx]
lea esi, [esi+eax+1]
jmp .include_loop_int
.notprep:
cmp al, 1Ah
jz .notinclude
cmp al, '"'
jz .quoted
test al, al
jnz .include_loop_int
jmp .include_loop
.include_done:
; 8. Scan for 'file' dependencies.
; 8a. Get range for scanning.
; Note that fas_header.asm_size can be slighly greater than the
; real size, so we break from the loop when there is no space left
; for the entire asm_row structure, not when .asm_size is exactly reached.
mov esi, [ebx+fas_header.asm_offs]
mov edx, [ebx+fas_header.asm_size]
add edx, esi
sub edx, asm_row.sizeof
push edx
jc .file_done
.file_loop:
; 8b. Loop over assembled lines in the range.
cmp esi, [esp]
ja .file_done
; 8c. For every assembled line, look at first token;
; go to 8d for token 'file',
; go to 8e for token 'format',
; go to 8f for token 'section',
; continue the loop otherwise.
mov eax, [esi+ebx+asm_row.preproc_offs]
add eax, [ebx+fas_header.preproc_offs]
cmp byte [eax+ebx+preproc_line_header.contents], 1Ah
jnz .file_next
movzx ecx, byte [eax+ebx+preproc_line_header.contents+1]
cmp cl, 4
jnz .file_no_file
cmp dword [eax+ebx+preproc_line_header.contents+2], 'file'
jnz .file_no_file4
; 8d. For lines starting with 'file' token, loop over tokens and get names.
; Note that there can be several names in one line.
; Parsing of tokens is similar to step 5 with the difference that
; preprocessor token stops processing: 'file' directives are processed
; in assembler stage.
; We save/restore esi since it is used for another thing in the internal loop;
; we push eax, which currently contains ebx-relative pointer to
; preproc_line_header, to be able to access it from resolve_name.
push esi eax
lea esi, [eax+preproc_line_header.contents+6]
.file_loop_int:
mov al, [esi+ebx]
inc esi
test al, al
jz .file_loop_int_done
cmp al, ';'
jz .file_loop_int_done
cmp al, 1Ah
jz .fileword
cmp al, '"'
jnz .file_loop_int
call resolve_name
add esi, [esi+ebx-4]
jmp .file_loop_int
.fileword:
movzx eax, byte [esi+ebx]
lea esi, [esi+eax+1]
jmp .file_loop_int
.file_loop_int_done:
pop eax esi
jmp .file_next
.file_no_file4:
cmp dword [eax+ebx+preproc_line_header.contents+2], 'data'
jnz .file_next
jmp .file_scan_from
.file_no_file:
cmp cl, 6
jnz .file_no_format
cmp dword [eax+ebx+preproc_line_header.contents+2], 'form'
jnz .file_no_format
cmp word [eax+ebx+preproc_line_header.contents+6], 'at'
jnz .file_no_format
; 8e. For lines starting with 'format' token, loop over tokens and look for stub name.
; Note that there can be another quoted string, namely file extension;
; stub name always follows the token 'on'.
mov edx, TokenOn
call scan_after_word
jmp .file_next
.file_no_format:
cmp cl, 7
jnz .file_no_section
cmp dword [eax+ebx+preproc_line_header.contents+2], 'sect'
jnz .file_no_section
mov edx, dword [eax+ebx+preproc_line_header.contents+6]
and edx, 0FFFFFFh
cmp edx, 'ion'
jnz .file_no_section
.file_scan_from:
mov edx, TokenFrom
call scan_after_word
.file_no_section:
.file_next:
add esi, asm_row.sizeof
jmp .file_loop
.file_done:
pop edx
; 9. Write result.
; 9a. Append two newlines to the end of buffer.
stdcall alloc_in_buf, 2
mov word [edi+ebx], 10 * 101h
inc edi
inc edi
; 9b. If '-e' option was given, duplicate dependencies list as fake goals
; = copy all of them and append ":\n\n".
cmp [.flags], 0
jz .nodup
lea ecx, [edi+1]
mov esi, [.names]
sub ecx, esi
stdcall alloc_in_buf, ecx
add esi, ebx
add edi, ebx
rep movsb
mov byte [edi-3], ':'
mov word [edi-2], 10 * 101h
sub edi, ebx
.nodup:
; 9c. Write to output file.
mov eax, [.outstart]
sub edi, eax
add eax, ebx
call write
; 10. Exit.
xor eax, eax
call exit
; Helper procedure for steps 8e and 8f of main algorithm.
; Looks for quoted strings after given word in edx.
scan_after_word:
push esi eax
lea esi, [eax+preproc_line_header.contents+2+ecx]
.loop:
xor ecx, ecx
.loop_afterword:
mov al, [esi+ebx]
inc esi
test al, al
jz .loop_done
cmp al, ';'
jz .loop_done
cmp al, 1Ah
jz .word
cmp al, '"'
jnz .loop
test ecx, ecx
jz .skip_quoted
call resolve_name
.loop_done:
pop eax esi
ret
.skip_quoted:
add esi, [esi+ebx]
add esi, 4
jmp .loop
.word:
movzx ecx, byte [esi+ebx]
lea esi, [esi+ecx+1]
cmp cl, byte [edx]
jnz .loop
push esi edi
add esi, ebx
sub esi, ecx
lea edi, [edx+1]
repz cmpsb
pop edi esi
jnz .loop
dec ecx
jmp .loop_afterword
; Helper procedure for step 6 of the main procedure.
; Copies the ASCIIZ name from strings section to the buffer.
copy_asciiz_escaped:
add esi, [ebx+fas_header.strings_offs]
.loop:
mov al, [esi+ebx]
test al, al
jz .nothing
call copy_char_escaped
jmp .loop
.nothing:
ret
; Helper procedure for step 7 of the main procedure.
; Copies the name with known length to the buffer.
copy_name_escaped:
mov ecx, [esi+ebx]
add esi, 4
test ecx, ecx
jz .nothing
push ecx
.loop:
mov al, [esi+ebx]
call copy_char_escaped
dec dword [esp]
jnz .loop
pop ecx
.nothing:
ret
; Helper procedure for steps 7 and 8 of the main procedure.
; Writes separator of file names in output = " \\\n".
add_separator:
stdcall alloc_in_buf, 3
mov word [edi+ebx], ' \'
mov byte [edi+ebx+2], 10
add edi, 3
ret
; Helper procedure for step 7 of the main procedure.
; Resolves the path to 'file' dependency and copies
; the full name to the buffer.
resolve_name:
; FASM uses the following order to search for referenced files:
; * path of currently assembling file, which may be .asm or .inc
; * paths from %INCLUDE% for versions >= 1.70
; * current directory = file name is taken as is, without prepending dir name
; We mirror this behaviour, trying to find an existing file somewhere.
; There can be following reasons for the file can not be found anywhere:
; * it has been deleted between compilation and our actions
; * it didn't exist at all, compilation has failed
; * we are running in environment different from fasm environment.
; Assume that the last reason is most probable and that invalid dependency
; is better than absent dependency (it is easier to fix an explicit error
; than a silent one) and output file name without prepending dir name,
; as in the last case. Actually, we even don't need to test existence
; of the file in the current directory.
add esi, 4 ; skip string length
; 1. Get ebx-relative pointer to preproc_line_header, see the comment in start.7d
mov eax, [esp+4]
; 2. Get the path to currently processing file.
push esi
.getpath:
test byte [eax+ebx+preproc_line_header.line_number+3], 80h
jz @f
mov eax, [eax+ebx+preproc_line_header.line_offset]
add eax, [ebx+fas_header.preproc_offs]
jmp .getpath
@@:
mov edx, [eax+ebx+preproc_line_header.source_name]
test edx, edx
jz .frommain
add edx, [ebx+fas_header.preproc_offs]
jmp @f
.frommain:
mov edx, [ebx+fas_header.input]
add edx, [ebx+fas_header.strings_offs]
@@:
; 3. Check that it is not a duplicate of the previous dependency.
; 3a. Compare preprocessor units.
cmp edx, [start.prevfilefrom]
jnz .nodup
; 3b. Compare string lengths.
mov eax, [start.prevfile]
mov ecx, [eax+ebx-4]
cmp ecx, [esi+ebx-4]
jnz .nodup
; 3c. Compare string contents.
push esi edi
lea edi, [eax+ebx]
add esi, ebx
rep cmpsb
pop edi esi
jnz .nodup
; 3d. It is duplicate, just return.
pop esi
ret
.nodup:
; 3e. It is not duplicate. Output separator.
mov [start.prevfilefrom], edx
mov [start.prevfile], esi
call add_separator
; 4. Cut the last component of the path found in step 2.
mov ecx, edx
mov esi, edx
.scanpath:
mov al, [edx+ebx]
test al, al
jz .scandone
cmp al, '/'
jz .slash
cmp al, '\'
jnz .scannext
.slash:
lea ecx, [edx+1]
.scannext:
inc edx
jmp .scanpath
.scandone:
sub ecx, esi
; 5. Try path found in step 4. If found, go to step 8.
mov [start.testname], edi
stdcall copy_string, esi, ecx
pop esi
call expand_environment
call test_file_exists
test eax, eax
jns .found
call revert_testname
; 6. Try each of include paths. For every path, if file is found, go to step 8.
; Otherwise, continue loop over include path.
; Skip this step before 1.70.
cmp [ebx+fas_header.major], 1
ja .checkenv
jb .nocheckenv
cmp [ebx+fas_header.minor], 70
jb .nocheckenv
.checkenv:
mov ecx, [start.include]
.includeloop_ext:
mov eax, ecx
.includeloop_int:
cmp byte [eax+ebx], 0
jz @f
cmp byte [eax+ebx], ';'
jz @f
inc eax
jmp .includeloop_int
@@:
push eax
sub eax, ecx
jz @f
stdcall copy_string, ecx, eax
cmp byte [edi+ebx-1], '/'
jz .hasslash
@@:
stdcall alloc_in_buf, 1
mov byte [edi+ebx], '/'
inc edi
.hasslash:
call expand_environment
call test_file_exists
pop ecx
test eax, eax
jns .found
call revert_testname
cmp byte [ecx+ebx], 0
jz .notfound
inc ecx
cmp byte [ecx+ebx], 0
jnz .includeloop_ext
.nocheckenv:
.notfound:
; 7. File not found neither near the current preprocessor unit nor in %INCLUDE%.
; Assume that it is in the current directory.
call expand_environment
.found:
; 8. Currently we have file name from [start.testname] to edi;
; it is zero-terminated and not space-escaped. Fix both issues.
dec edi
inc [start.free]
push esi
mov edx, [start.testname]
.escapeloop:
cmp edx, edi
jae .escapedone
cmp byte [edx+ebx], ' '
jnz .noescape
stdcall alloc_in_buf, 1
mov ecx, edi
sub ecx, edx
push edi
add edi, ebx
lea esi, [edi-1]
std
rep movsb
cld
pop edi
inc edi
mov byte [edx+ebx], '\'
inc edx
.noescape:
inc edx
jmp .escapeloop
.escapedone:
pop esi
ret
; Helper procedure for resolve_name.
; Allocates space in the buffer and appends the given string to the buffer.
copy_string:
mov eax, [esp+8]
test eax, eax
jz .nothing
stdcall alloc_in_buf, eax
mov ecx, [esp+4]
.copy:
mov al, [ecx+ebx]
inc ecx
cmp al, '\'
jnz @f
mov al, '/'
@@:
mov [edi+ebx], al
inc edi
dec dword [esp+8]
jnz .copy
.nothing:
ret 8
; Helper procedure for resolve_name. Undoes appending of last file name.
revert_testname:
add [start.free], edi
mov edi, [start.testname]
sub [start.free], edi
ret
; Helper procedure for resolve_name. Copies string from esi to edi,
; expanding environment variables.
expand_environment:
; 1. Save esi to restore it in the end of function.
push esi
; 2. Push string length to the stack to be used as a variable.
pushd [esi+ebx-4]
; 3. Scan loop.
.scan:
; 3a. Scan for '%' sign.
call find_percent
.justcopy:
; 3b. Copy the part from the beginning of current portion to '%' sign,
; advance pointer to '%' sign, or end-of-string if no '%' found.
push eax
sub eax, esi
stdcall copy_string, esi, eax
pop esi
; 3c. If string has ended, break from the loop.
cmp dword [esp], 0
jz .scandone
; 3d. Advance over '%' sign.
inc esi
dec dword [esp]
; 3e. Find paired '%'.
call find_percent
; 3f. If there is no paired '%', just return to 3b and copy remaining data,
; including skipped '%'; after that, 3c would break from the loop.
dec esi
cmp dword [esp], 0
jz .justcopy
; 3g. Otherwise, get the value of environment variable.
; Since get_environment_variable requires zero-terminated string
; and returns zero-terminated string, temporarily overwrite trailing '%'
; and ignore last byte in returned string.
; Also convert any backslashes to forward slashes.
inc esi
mov byte [eax+ebx], 0
push eax
push edi
call get_environment_variable
dec edi
inc [start.free]
pop eax
.replaceslash:
cmp eax, edi
jz .replaceslash_done
cmp byte [eax+ebx], '\'
jnz @f
mov byte [eax+ebx], '/'
@@:
inc eax
jmp .replaceslash
.replaceslash_done:
pop esi
mov byte [esi+ebx], '%'
; 3h. Advance over trailing '%'.
inc esi
dec dword [esp]
; 3i. Continue the loop.
jmp .scan
.scandone:
; 4. Zero-terminate resulting string.
stdcall alloc_in_buf, 1
mov byte [edi+ebx], 0
inc edi
; 5. Pop stack variable initialized in step 2.
pop eax
; 6. Restore esi saved in step 1 and return.
pop esi
ret
; Helper procedure for expand_environment.
; Scans the string in esi with length [esp+4]
; until '%' is found or line ended.
find_percent:
mov eax, esi
cmp dword [esp+4], 0
jz .nothing
.scan:
cmp byte [eax+ebx], '%'
jz .nothing
inc eax
dec dword [esp+4]
jnz .scan
.nothing:
ret
; Helper procedure for copy_{name,asciiz}_escaped.
; Allocates space and writes one character, possibly escaped.
copy_char_escaped:
cmp al, ' '
jnz .noescape
stdcall alloc_in_buf, 1
mov byte [edi+ebx], '\'
inc edi
.noescape:
stdcall alloc_in_buf, 1
mov al, [esi+ebx]
inc esi
cmp al, '\'
jnz @f
mov al, '/'
@@:
mov [edi+ebx], al
inc edi
ret
; Helper procedure for ensuring that there is at least [esp+4]
; free bytes in the buffer.
alloc_in_buf:
mov eax, [esp+4]
sub [start.free], eax
jb .need_realloc
ret 4
.need_realloc:
mov eax, [start.allocated]
add eax, eax
push ecx edx
call realloc
pop edx ecx
cmp [start.free], 0
jl .need_realloc
mov ebx, [start.buf]
ret 4
badfile:
mov esi, badfile_string
call sayerr
mov al, 1
call exit
information:
mov esi, information_string
call sayerr
mov al, 2
call exit
nomemory:
mov esi, nomemory_string
call sayerr
mov al, 3
call exit
in_openerr:
mov esi, in_openerr_string
jmp in_err
readerr:
mov esi, readerr_string
in_err:
call sayerr
mov al, 4
call exit
out_openerr:
mov esi, out_openerr_string
jmp out_err
writeerr:
mov esi, writeerr_string
out_err:
call sayerr
mov al, 5
call exit
; Platform-specific procedures.
match =WINDOWS,OS { include 'windows_sys.inc' }
match =LINUX,OS { include 'linux_sys.inc' }
; Data
macro string a, [b] {
common
db a ## _end - a
a db b
a ## _end:
}
string information_string, 'Usage: fasmdep [-e] [<input.fas> [<output.Po>]]',10
string badfile_string, 'Not .fas file',10
string nomemory_string, 'No memory',10
string in_openerr_string, 'Cannot open input file',10
string readerr_string, 'Read error',10
string out_openerr_string, 'Cannot create output file',10
string writeerr_string, 'Write error',10
include_variable db 'INCLUDE',0
TokenOn db 2,'on'
TokenFrom db 4,'from'

View File

@ -0,0 +1,73 @@
; Header for Linux program
format ELF executable 3
entry start
; for system calls
include 'unistd.inc'
macro __mov a,b
{
if b eq
else if ~(b eqtype 1)
mov a, b
else if b = 0
xor a, a
else if (b < 0x80) & (b >= -0x80)
push b
pop a
else
mov a, b
end if
}
macro kercall a,b,c,d,e,f,g
{
__mov eax, a
__mov ebx, b
__mov ecx, c
__mov edx, d
__mov esi, e
__mov edi, f
__mov ebp, g
int 0x80
}
macro stdcall func,[arg]
{
reverse
pushd arg
common
call func
}
PROT_READ = 0x1 ; page can be read
PROT_WRITE = 0x2 ; page can be written
PROT_EXEC = 0x4 ; page can be executed
PROT_SEM = 0x8 ; page may be used for atomic ops
PROT_NONE = 0x0 ; page can not be accessed
PROT_GROWSDOWN = 0x01000000 ; mprotect flag: extend change to start of growsdown vma
PROT_GROWSUP = 0x02000000 ; mprotect flag: extend change to end of growsup vma
MAP_SHARED = 0x01 ; Share changes
MAP_PRIVATE = 0x02 ; Changes are private
MAP_TYPE = 0x0f ; Mask for type of mapping
MAP_FIXED = 0x10 ; Interpret addr exactly
MAP_ANONYMOUS = 0x20 ; don't use a file
O_ACCMODE = 00000003
O_RDONLY = 00000000
O_WRONLY = 00000001
O_RDWR = 00000002
O_CREAT = 00000100 ; not fcntl
O_EXCL = 00000200 ; not fcntl
O_NOCTTY = 00000400 ; not fcntl
O_TRUNC = 00001000 ; not fcntl
O_APPEND = 00002000
O_NONBLOCK = 00004000
O_DSYNC = 00010000 ; used to be O_SYNC, see below
FASYNC = 00020000 ; fcntl, for BSD compatibility
O_DIRECT = 00040000 ; direct disk access hint
O_LARGEFILE = 00100000
O_DIRECTORY = 00200000 ; must be a directory
O_NOFOLLOW = 00400000 ; don't follow links
O_NOATIME = 01000000
O_CLOEXEC = 02000000 ; set close_on_exec
__O_SYNC = 04000000
O_SYNC = (__O_SYNC + O_DSYNC)
O_NDELAY = O_NONBLOCK
segment readable executable

View File

@ -0,0 +1,186 @@
; Platform-specific procedures for Linux.
; Reallocate memory at pointer [start.buf] and size [start.allocated],
; new size is in eax.
realloc:
push ebx esi edi
mov ecx, eax
push ebp
kercall __NR_mmap2, 0, , PROT_READ+PROT_WRITE, MAP_PRIVATE+MAP_ANONYMOUS, -1, 0
pop ebp
cmp eax, 0xFFFFF000
ja nomemory
add [start.free], ecx
xchg ecx, [start.allocated]
sub [start.free], ecx
mov edi, eax
xchg eax, [start.buf]
shr ecx, 2
jz .nothing
push ecx
mov esi, eax
rep movsd
pop ecx
shl ecx, 2
call free_eax_ecx
.nothing:
pop edi esi ebx
ret
; Read the next portion of input data to [start.buf].
read:
mov ecx, [start.buf]
add ecx, [start.allocated]
mov edx, [start.free]
sub ecx, edx
kercall __NR_read, [start.in], ,
test eax, eax
js readerr
ret
; Write output data: eax=pointer, edi=size.
write:
mov ecx, eax
kercall __NR_write, [start.out], , edi
cmp eax, edi
jnz writeerr
ret
; Parse command line, open input and output files.
get_params:
; 1. Initialize default streams: 0 for stdin, 1 for stdout.
inc [start.out]
; 2. Prepare for scanning, skipping argv[0].
mov ebx, [start.argc]
cmp ebx, 1
jbe .noargs
dec ebx
lea esi, [start.argv+4] ; skip argv[0]
xor edi, edi ; no args parsed yet
; 3. Parse loop.
.parse:
; 3a. Get the next argument.
lodsd
; 3b. Check whether it is a known option.
cmp word [eax], '-e'
jnz @f
cmp byte [eax+2], 0
jnz @f
; 3c. If it is, modify flags and continue the loop.
mov [start.flags], 1 ; '-e' is given
jmp .nextarg
@@:
; 3d. Otherwise, it is a name of input or output file.
; edi keeps the count of names encountered before;
; edi = 0 means this is input file, edi = 1 - output file,
; otherwise this is third arg, which is an error
cmp edi, 1
ja information
; 3e. Some parameters of __NR_open differ for input and output. Setup them.
mov ecx, O_WRONLY+O_CREAT+O_TRUNC
mov edx, 0644o
jz @f
mov ecx, O_RDONLY
@@:
; 3f. Open/create the file, save the handle.
push ebx
mov ebx, eax
kercall __NR_open
pop ebx
test eax, eax
js .fileerr
mov [start.in+edi*4], eax
inc edi
.nextarg:
dec ebx
jnz .parse
.noargs:
; 4. End of command line reached. Return.
ret
.fileerr:
test edi, edi
jz in_openerr
jmp out_openerr
; Exit, return code is in al.
exit:
movzx ebx, al
push ebx
mov eax, [start.buf]
test eax, eax
jz @f
mov ecx, [start.allocated]
call free_eax_ecx
@@:
kercall __NR_close, [start.in]
kercall __NR_close, [start.out]
pop ebx
kercall __NR_exit
; Helper procedure for realloc and exit.
free_eax_ecx:
mov ebx, eax
kercall __NR_munmap
ret
; Output the message given in esi to stderr.
sayerr:
movzx edx, byte [esi-1]
kercall __NR_write, 2, esi,
ret
; Get environment variable esi (ebx-relative pointer) to the buffer,
; expanding it if needed.
get_environment_variable:
mov ecx, [start.argc]
lea ecx, [start.argv+ecx*4+4]
.findvar:
mov edx, [ecx]
add ecx, 4
test edx, edx
jz .notfound
push esi
add esi, ebx
.comparename:
lodsb
cmp al, [edx]
jnz @f
inc edx
jmp .comparename
@@:
pop esi
test al, al
jnz .findvar
cmp byte [edx], '='
jnz .findvar
inc edx
xor eax, eax
@@:
inc eax
cmp byte [edx+eax-1], 0
jnz @b
stdcall alloc_in_buf, eax
@@:
mov al, [edx]
inc edx
mov [edi+ebx], al
inc edi
test al, al
jnz @b
ret
.notfound:
stdcall alloc_in_buf, 1
mov byte [edi+ebx], 0
inc edi
ret
; Test whether a file with name [.testname] exists.
; Returns eax<0 if not, nonzero otherwise.
test_file_exists:
push ebx
add ebx, [start.testname]
sub esp, 800h
kercall __NR_stat, , esp
add esp, 800h
pop ebx
ret

View File

@ -0,0 +1,322 @@
__NR_restart_syscall = 0
__NR_exit = 1
__NR_fork = 2
__NR_read = 3
__NR_write = 4
__NR_open = 5
__NR_close = 6
__NR_waitpid = 7
__NR_creat = 8
__NR_link = 9
__NR_unlink = 10
__NR_execve = 11
__NR_chdir = 12
__NR_time = 13
__NR_mknod = 14
__NR_chmod = 15
__NR_lchown = 16
__NR_break = 17
__NR_oldstat = 18
__NR_lseek = 19
__NR_getpid = 20
__NR_mount = 21
__NR_umount = 22
__NR_setuid = 23
__NR_getuid = 24
__NR_stime = 25
__NR_ptrace = 26
__NR_alarm = 27
__NR_oldfstat = 28
__NR_pause = 29
__NR_utime = 30
__NR_stty = 31
__NR_gtty = 32
__NR_access = 33
__NR_nice = 34
__NR_ftime = 35
__NR_sync = 36
__NR_kill = 37
__NR_rename = 38
__NR_mkdir = 39
__NR_rmdir = 40
__NR_dup = 41
__NR_pipe = 42
__NR_times = 43
__NR_prof = 44
__NR_brk = 45
__NR_setgid = 46
__NR_getgid = 47
__NR_signal = 48
__NR_geteuid = 49
__NR_getegid = 50
__NR_acct = 51
__NR_umount2 = 52
__NR_lock = 53
__NR_ioctl = 54
__NR_fcntl = 55
__NR_mpx = 56
__NR_setpgid = 57
__NR_ulimit = 58
__NR_oldolduname = 59
__NR_umask = 60
__NR_chroot = 61
__NR_ustat = 62
__NR_dup2 = 63
__NR_getppid = 64
__NR_getpgrp = 65
__NR_setsid = 66
__NR_sigaction = 67
__NR_sgetmask = 68
__NR_ssetmask = 69
__NR_setreuid = 70
__NR_setregid = 71
__NR_sigsuspend = 72
__NR_sigpending = 73
__NR_sethostname = 74
__NR_setrlimit = 75
__NR_getrlimit = 76
__NR_getrusage = 77
__NR_gettimeofday = 78
__NR_settimeofday = 79
__NR_getgroups = 80
__NR_setgroups = 81
__NR_select = 82
__NR_symlink = 83
__NR_oldlstat = 84
__NR_readlink = 85
__NR_uselib = 86
__NR_swapon = 87
__NR_reboot = 88
__NR_readdir = 89
__NR_mmap = 90
__NR_munmap = 91
__NR_truncate = 92
__NR_ftruncate = 93
__NR_fchmod = 94
__NR_fchown = 95
__NR_getpriority = 96
__NR_setpriority = 97
__NR_profil = 98
__NR_statfs = 99
__NR_fstatfs = 100
__NR_ioperm = 101
__NR_socketcall = 102
__NR_syslog = 103
__NR_setitimer = 104
__NR_getitimer = 105
__NR_stat = 106
__NR_lstat = 107
__NR_fstat = 108
__NR_olduname = 109
__NR_iopl = 110
__NR_vhangup = 111
__NR_idle = 112
__NR_vm86old = 113
__NR_wait4 = 114
__NR_swapoff = 115
__NR_sysinfo = 116
__NR_ipc = 117
__NR_fsync = 118
__NR_sigreturn = 119
__NR_clone = 120
__NR_setdomainname = 121
__NR_uname = 122
__NR_modify_ldt = 123
__NR_adjtimex = 124
__NR_mprotect = 125
__NR_sigprocmask = 126
__NR_create_module = 127
__NR_init_module = 128
__NR_delete_module = 129
__NR_get_kernel_syms = 130
__NR_quotactl = 131
__NR_getpgid = 132
__NR_fchdir = 133
__NR_bdflush = 134
__NR_sysfs = 135
__NR_personality = 136
__NR_afs_syscall = 137
__NR_setfsuid = 138
__NR_setfsgid = 139
__NR__llseek = 140
__NR_getdents = 141
__NR__newselect = 142
__NR_flock = 143
__NR_msync = 144
__NR_readv = 145
__NR_writev = 146
__NR_getsid = 147
__NR_fdatasync = 148
__NR__sysctl = 149
__NR_mlock = 150
__NR_munlock = 151
__NR_mlockall = 152
__NR_munlockall = 153
__NR_sched_setparam = 154
__NR_sched_getparam = 155
__NR_sched_setscheduler = 156
__NR_sched_getscheduler = 157
__NR_sched_yield = 158
__NR_sched_get_priority_max = 159
__NR_sched_get_priority_min = 160
__NR_sched_rr_get_interval = 161
__NR_nanosleep = 162
__NR_mremap = 163
__NR_setresuid = 164
__NR_getresuid = 165
__NR_vm86 = 166
__NR_query_module = 167
__NR_poll = 168
__NR_nfsservctl = 169
__NR_setresgid = 170
__NR_getresgid = 171
__NR_prctl = 172
__NR_rt_sigreturn = 173
__NR_rt_sigaction = 174
__NR_rt_sigprocmask = 175
__NR_rt_sigpending = 176
__NR_rt_sigtimedwait = 177
__NR_rt_sigqueueinfo = 178
__NR_rt_sigsuspend = 179
__NR_pread64 = 180
__NR_pwrite64 = 181
__NR_chown = 182
__NR_getcwd = 183
__NR_capget = 184
__NR_capset = 185
__NR_sigaltstack = 186
__NR_sendfile = 187
__NR_getpmsg = 188
__NR_putpmsg = 189
__NR_vfork = 190
__NR_ugetrlimit = 191
__NR_mmap2 = 192
__NR_truncate64 = 193
__NR_ftruncate64 = 194
__NR_stat64 = 195
__NR_lstat64 = 196
__NR_fstat64 = 197
__NR_lchown32 = 198
__NR_getuid32 = 199
__NR_getgid32 = 200
__NR_geteuid32 = 201
__NR_getegid32 = 202
__NR_setreuid32 = 203
__NR_setregid32 = 204
__NR_getgroups32 = 205
__NR_setgroups32 = 206
__NR_fchown32 = 207
__NR_setresuid32 = 208
__NR_getresuid32 = 209
__NR_setresgid32 = 210
__NR_getresgid32 = 211
__NR_chown32 = 212
__NR_setuid32 = 213
__NR_setgid32 = 214
__NR_setfsuid32 = 215
__NR_setfsgid32 = 216
__NR_pivot_root = 217
__NR_mincore = 218
__NR_madvise = 219
__NR_madvise1 = 219
__NR_getdents64 = 220
__NR_fcntl64 = 221
__NR_gettid = 224
__NR_readahead = 225
__NR_setxattr = 226
__NR_lsetxattr = 227
__NR_fsetxattr = 228
__NR_getxattr = 229
__NR_lgetxattr = 230
__NR_fgetxattr = 231
__NR_listxattr = 232
__NR_llistxattr = 233
__NR_flistxattr = 234
__NR_removexattr = 235
__NR_lremovexattr = 236
__NR_fremovexattr = 237
__NR_tkill = 238
__NR_sendfile64 = 239
__NR_futex = 240
__NR_sched_setaffinity = 241
__NR_sched_getaffinity = 242
__NR_set_thread_area = 243
__NR_get_thread_area = 244
__NR_io_setup = 245
__NR_io_destroy = 246
__NR_io_getevents = 247
__NR_io_submit = 248
__NR_io_cancel = 249
__NR_fadvise64 = 250
__NR_exit_group = 252
__NR_lookup_dcookie = 253
__NR_epoll_create = 254
__NR_epoll_ctl = 255
__NR_epoll_wait = 256
__NR_remap_file_pages = 257
__NR_set_tid_address = 258
__NR_timer_create = 259
__NR_statfs64 = 268
__NR_fstatfs64 = 269
__NR_tgkill = 270
__NR_utimes = 271
__NR_fadvise64_64 = 272
__NR_vserver = 273
__NR_mbind = 274
__NR_get_mempolicy = 275
__NR_set_mempolicy = 276
__NR_mq_open = 277
__NR_kexec_load = 283
__NR_waitid = 284
__NR_sys_setaltroot = 285
__NR_add_key = 286
__NR_request_key = 287
__NR_keyctl = 288
__NR_ioprio_set = 289
__NR_ioprio_get = 290
__NR_inotify_init = 291
__NR_inotify_add_watch = 292
__NR_inotify_rm_watch = 293
__NR_migrate_pages = 294
__NR_openat = 295
__NR_mkdirat = 296
__NR_mknodat = 297
__NR_fchownat = 298
__NR_futimesat = 299
__NR_fstatat64 = 300
__NR_unlinkat = 301
__NR_renameat = 302
__NR_linkat = 303
__NR_symlinkat = 304
__NR_readlinkat = 305
__NR_fchmodat = 306
__NR_faccessat = 307
__NR_pselect6 = 308
__NR_ppoll = 309
__NR_unshare = 310
__NR_set_robust_list = 311
__NR_get_robust_list = 312
__NR_splice = 313
__NR_sync_file_range = 314
__NR_tee = 315
__NR_vmsplice = 316
__NR_move_pages = 317
__NR_getcpu = 318
__NR_epoll_pwait = 319
__NR_utimensat = 320
__NR_signalfd = 321
__NR_timerfd_create = 322
__NR_eventfd = 323
__NR_fallocate = 324
__NR_timerfd_settime = 325
__NR_timerfd_gettime = 326
__NR_signalfd4 = 327
__NR_eventfd2 = 328
__NR_epoll_create1 = 329
__NR_dup3 = 330
__NR_pipe2 = 331
__NR_inotify_init1 = 332
__NR_preadv = 333
__NR_pwritev = 334
__NR_rt_tgsigqueueinfo = 335
__NR_perf_event_open = 336

View File

@ -0,0 +1,6 @@
; Header for Windows program
format PE console
include 'win32w.inc'
ERROR_BROKEN_PIPE = 109
ERROR_MORE_DATA = 234
section '.text' code readable executable

View File

@ -0,0 +1,225 @@
; Platform-specific procedures for Windows.
; Reallocate memory at pointer [start.buf] and size [start.allocated],
; new size is in eax.
realloc:
push eax
stdcall [VirtualAlloc], 0, eax, MEM_COMMIT + MEM_RESERVE, PAGE_READWRITE
pop ecx
test eax, eax
jz nomemory
add [start.free], ecx
xchg ecx, [start.allocated]
sub [start.free], ecx
push esi edi
mov edi, eax
xchg eax, [start.buf]
shr ecx, 2
jz .nothing
mov esi, eax
rep movsd
call free_eax
.nothing:
pop edi esi
ret
; Read the next portion of input data to [start.buf].
read:
push eax ; reserve space for *lpNumberOfBytesRead
mov ecx, esp
mov eax, [start.buf]
add eax, [start.allocated]
sub eax, [start.free]
stdcall [ReadFile], [start.in], eax, [start.free], ecx, 0
test eax, eax
jz @f
.nothing:
pop eax
ret
@@:
; ERROR_BROKEN_PIPE and ERROR_MORE_DATA are normal codes when dealing with pipes
call [GetLastError]
cmp eax, ERROR_BROKEN_PIPE
jz .nothing
cmp eax, ERROR_MORE_DATA
jz .nothing
pop eax
jmp readerr
; Write output data: eax=pointer, edi=size.
write:
push eax
mov ecx, esp
stdcall [WriteFile], [start.out], eax, edi, ecx, 0
test eax, eax
pop eax
jz writeerr
cmp eax, edi
jnz writeerr
ret
; Parse command line, open input and output files.
get_params:
; 1. Get the command line split to argv[] array.
call [GetCommandLineW]
push eax ; reserve space for *pNumArgs
stdcall [CommandLineToArgvW], eax, esp
pop ebx ; ebx = argc, eax = argv
push eax ; save argument for [LocalFree]
; 2. Prepare for scanning, skipping argv[0].
cmp ebx, 1
jbe .noargs
dec ebx
lea esi, [eax+4] ; skip argv[0]
xor edi, edi ; no args parsed yet
; 3. Parse loop.
.parse:
; 3a. Get the next argument.
lodsd
; 3b. Check whether it is a known option.
cmp dword [eax], '-' + 'e' * 65536
jnz @f
cmp word [eax+4], 0
jnz @f
; 3c. If it is, modify flags and continue the loop.
mov [start.flags], 1 ; '-e' is given
jmp .nextarg
@@:
; 3d. Otherwise, it is a name of input or output file.
; edi keeps the count of names encountered before;
; edi = 0 means this is input file, edi = 1 - output file,
; otherwise this is third arg, which is an error
cmp edi, 1
ja .toomanyargs
; 3e. Some parameters of CreateFileW differ for input and output. Setup them.
mov ecx, GENERIC_WRITE
mov edx, CREATE_ALWAYS
jz @f
add ecx, ecx ; GENERIC_READ
inc edx ; OPEN_EXISTING
@@:
; 3f. Open/create the file, save the handle.
stdcall [CreateFileW], eax, ecx, FILE_SHARE_READ+FILE_SHARE_DELETE, 0, edx, FILE_ATTRIBUTE_NORMAL, 0
cmp eax, INVALID_HANDLE_VALUE
jz .fileerr
mov [start.in+edi*4], eax
inc edi
.nextarg:
dec ebx
jnz .parse
.noargs:
; 4. End of command line reached. If input and/or output was not given, use defaults.
test edi, edi
jnz .hasinput
stdcall [GetStdHandle], STD_INPUT_HANDLE
mov [start.in], eax
.hasinput:
cmp edi, 1
ja .hasoutput
stdcall [GetStdHandle], STD_OUTPUT_HANDLE
mov [start.out], eax
.hasoutput:
; 5. Free memory allocated in step 1 and return.
call [LocalFree]
ret
.toomanyargs:
call [LocalFree]
jmp information
.fileerr:
call [LocalFree]
test edi, edi
jz in_openerr
jmp out_openerr
; Exit, return code is in al.
exit:
movzx eax, al
push eax ; save return code for [ExitProcess]
mov eax, [start.buf]
test eax, eax
jz @f
call free_eax
@@:
stdcall [CloseHandle], [start.in]
stdcall [CloseHandle], [start.out]
call [ExitProcess]
; Output the message given in esi to stderr.
sayerr:
stdcall [GetStdHandle], STD_ERROR_HANDLE
push eax
mov ecx, esp
movzx edx, byte [esi-1]
stdcall [WriteFile], eax, esi, edx, ecx, 0
pop ecx
ret
; Helper procedure for realloc and exit.
free_eax:
stdcall [VirtualFree], eax, 0, MEM_RELEASE
ret
; Get environment variable esi (ebx-relative pointer) to the buffer,
; expanding it if needed.
get_environment_variable:
lea eax, [edi+ebx]
lea ecx, [esi+ebx]
stdcall [GetEnvironmentVariableA], ecx, eax, [start.free]
; GetEnvironmentVariable returns one of following values:
; * if all is ok: number of characters copied to the buffer,
; not including terminating zero
; * if buffer size is too small: number of characters required in the buffer,
; including terminating zero
; * if environment variable not found: zero
; We treat the last case the same as first one.
cmp eax, [start.free]
jae .resize
inc eax ; include terminating zero
add edi, eax
sub [start.free], eax
mov byte [edi+ebx-1], 0 ; force zero-terminated or empty string
ret
.resize:
; esi can be inside the buffer or outside the buffer;
; we must not correct it in the first case,
; but advance relative to new value of ebx in the second one.
mov ecx, esi
cmp esi, [start.allocated]
jb @f
add esi, ebx
@@:
stdcall alloc_in_buf, eax
cmp esi, ecx
jz get_environment_variable
sub esi, ebx
jmp get_environment_variable
; Test whether a file with name [.testname] exists.
; Returns eax<0 if not, nonzero otherwise.
test_file_exists:
mov eax, [start.testname]
add eax, ebx
stdcall [GetFileAttributesA], eax
inc eax
ret
; Imports
align 4
data import
library kernel32,'kernel32.dll',shell32,'shell32.dll'
import kernel32, \
GetLastError, 'GetLastError', \
ExitProcess, 'ExitProcess', \
VirtualAlloc, 'VirtualAlloc', \
VirtualFree, 'VirtualFree', \
LocalFree, 'LocalFree', \
GetStdHandle, 'GetStdHandle', \
CreateFileW, 'CreateFileW', \
GetFileAttributesA, 'GetFileAttributesA', \
ReadFile, 'ReadFile', \
WriteFile, 'WriteFile', \
CloseHandle, 'CloseHandle', \
GetCommandLineW, 'GetCommandLineW', \
GetEnvironmentVariableA, 'GetEnvironmentVariableA'
import shell32, CommandLineToArgvW, 'CommandLineToArgvW'
end data

View File

@ -455,7 +455,7 @@ include Makefile.nasm
include Makefile.copy
# Special rules for copying sysfuncs.txt - it isn't directly included in the image.
docpack: $(DOCDIR)SYSFUNCS.TXT $(wildcard $(DOCDIR)*)
docpack: $(DOCDIR)SYSFUNCS.TXT
$(DOCDIR)SYSFUNCS.TXT: $(KERNEL)/docs/sysfuncs.txt
cp $(KERNEL)/docs/sysfuncs.txt $(DOCDIR)SYSFUNCS.TXT

View File

@ -28,22 +28,20 @@
# $(4) = name of program - without path and extension,
define fasm_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
fasm -m 65536 "$$<" "$$@" -s .deps/$(4).fas
prepsrc .deps/$(4).fas /dev/null
prepsrc .deps/$(4).fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
rm $$$$tmpfile) || (rm $$$$tmpfile; false)
kpack --nologo "$$@"
-include .deps/$(4).Po
endef
define fasm_nokpack_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
fasm -m 65536 "$$<" "$$@" -s .deps/$(4).fas
prepsrc .deps/$(4).fas /dev/null
prepsrc .deps/$(4).fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
rm $$$$tmpfile) || (rm $$$$tmpfile; false)
-include .deps/$(4).Po
endef
@ -54,10 +52,8 @@ $(foreach f,$(FASM_NOKPACK_PROGRAMS),$(eval $(call fasm_nokpack_meta_rule,$(fbin
# Rule for the kernel differs: it uses kerpack instead of kpack.
kernel.mnt: $(KERNEL)/kernel.asm Makefile.fasm .deps/.dir
fasm -m 65536 "$<" "$@" -s .deps/kernel.fas
prepsrc .deps/kernel.fas /dev/null
prepsrc .deps/kernel.fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$a=join " \\\n ",@a;print "$@: $$a\n$$a:\n"}' > .deps/kernel.Po
kerpack $@
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$<" "$@" -s $$tmpfile && \
fasmdep -e $$tmpfile > .deps/kernel.Po && \
rm $$tmpfile) || (rm $$tmpfile; false)
-include .deps/kernel.Po

View File

@ -299,7 +299,7 @@ include Makefile.nasm
include Makefile.copy
# Special rules for copying sysfuncs.txt - it isn't directly included in the image.
docpack: $(DOCDIR)SYSFUNCS.TXT $(wildcard $(DOCDIR)*)
docpack: $(DOCDIR)SYSFUNCS.TXT
$(DOCDIR)SYSFUNCS.TXT: $(TRUNKKERNEL)/docs/sysfuncs.txt
cp $(TRUNKKERNEL)/docs/sysfuncs.txt $(DOCDIR)SYSFUNCS.TXT

View File

@ -28,25 +28,32 @@
# $(4) = name of program - without path and extension,
define fasm_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
fasm -m 65536 $$< "$$@" -s .deps/$(4).fas
prepsrc .deps/$(4).fas /dev/null
prepsrc .deps/$(4).fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
rm $$$$tmpfile) || (rm $$$$tmpfile; false)
kpack --nologo "$$@"
-include .deps/$(4).Po
endef
define fasm_nokpack_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
rm $$$$tmpfile) || (rm $$$$tmpfile; false)
-include .deps/$(4).Po
endef
progname=$(call respace,$(basename $(notdir $(call binarypart,$(f)))))
binarydir=$(subst ./,,$(dir $(call binarypart,$(f))))
$(foreach f,$(FASM_PROGRAMS) $(FASM_PROGRAMS_CD),$(eval $(call fasm_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
$(foreach f,$(FASM_PROGRAMS) $(FASM_PROGRAMS_CD) $(SKIN_SOURCES),$(eval $(call fasm_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
$(foreach f,$(FASM_NOKPACK_PROGRAMS),$(eval $(call fasm_nokpack_meta_rule,$(fbinary),$(fsource),$(binarydir),$(progname))))
# Rule for the kernel differs: it uses kerpack instead of kpack.
kernel.mnt: $(KERNEL)/kernel.asm Makefile.fasm .deps/.dir
fasm -m 65536 $< "$@" -s .deps/kernel.fas
prepsrc .deps/kernel.fas /dev/null
prepsrc .deps/kernel.fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$a=join " \\\n ",@a;print "$@: $$a\n$$a:\n"}' > .deps/kernel.Po
kerpack $@
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$<" "$@" -s $$tmpfile && \
fasmdep -e $$tmpfile > .deps/kernel.Po && \
rm $$tmpfile) || (rm $$tmpfile; false)
-include .deps/kernel.Po

View File

@ -474,7 +474,7 @@ include Makefile.nasm
include Makefile.copy
# Special rules for copying sysfuncr.txt - it isn't directly included in the image.
docpack: $(DOCDIR)SYSFUNCR.TXT $(wildcard $(DOCDIR)*)
docpack: $(DOCDIR)SYSFUNCR.TXT
$(DOCDIR)SYSFUNCR.TXT: $(KERNEL)/docs/sysfuncr.txt
cp $(KERNEL)/docs/sysfuncr.txt $(DOCDIR)SYSFUNCR.TXT

View File

@ -28,22 +28,20 @@
# $(4) = name of program - without path and extension,
define fasm_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
fasm -m 65536 "$$<" "$$@" -s .deps/$(4).fas
prepsrc .deps/$(4).fas /dev/null
prepsrc .deps/$(4).fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
rm $$$$tmpfile) || (rm $$$$tmpfile; false)
kpack --nologo "$$@"
-include .deps/$(4).Po
endef
define fasm_nokpack_meta_rule
$(1): $(2) Makefile.fasm .deps/.dir $$(call respace,$$(addsuffix .dir,$(3)))
fasm -m 65536 "$$<" "$$@" -s .deps/$(4).fas
prepsrc .deps/$(4).fas /dev/null
prepsrc .deps/$(4).fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$$$a=join " \\\n ",@a;print q`$(1): `,"$$$$a\n$$$$a:\n"}' > .deps/$(4).Po
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$$<" "$$@" -s $$$$tmpfile && \
fasmdep -e $$$$tmpfile > .deps/$(4).Po && \
rm $$$$tmpfile) || (rm $$$$tmpfile; false)
-include .deps/$(4).Po
endef
@ -54,10 +52,8 @@ $(foreach f,$(FASM_NOKPACK_PROGRAMS),$(eval $(call fasm_nokpack_meta_rule,$(fbin
# Rule for the kernel differs: it uses kerpack instead of kpack.
kernel.mnt: $(KERNEL)/kernel.asm Makefile.fasm .deps/.dir
fasm -m 65536 "$<" "$@" -s .deps/kernel.fas
prepsrc .deps/kernel.fas /dev/null
prepsrc .deps/kernel.fas /dev/stdout | \
perl -n -e 's|\\|/|g;s| |\\ |g;push @a,$$1 if/^;include\\ \x27(.*?)\x27/;' \
-e 'END{$$a=join " \\\n ",@a;print "$@: $$a\n$$a:\n"}' > .deps/kernel.Po
kerpack $@
tmpfile=`mktemp --tmpdir build.XXXXXXXX` && \
(fasm -m 65536 "$<" "$@" -s $$tmpfile && \
fasmdep -e $$tmpfile > .deps/kernel.Po && \
rm $$tmpfile) || (rm $$tmpfile; false)
-include .deps/kernel.Po