From ad90f24ec3221495f9efa481958fca0143d6a4fc Mon Sep 17 00:00:00 2001 From: CleverMouse Date: Thu, 21 Jun 2012 19:18:22 +0000 Subject: [PATCH] use advanced dependency scanner for fasm programs git-svn-id: svn://kolibrios.org@2816 a494cfbc-eb01-0410-851d-a64ba20cac60 --- data/Vortex86MX-eng/Makefile | 2 +- data/Vortex86MX-eng/Makefile.fasm | 31 +- data/common/fasmdep/fas.inc | 90 +++ data/common/fasmdep/fasmdep.asm | 765 +++++++++++++++++++++++++ data/common/fasmdep/linux_header.inc | 73 +++ data/common/fasmdep/linux_sys.inc | 186 ++++++ data/common/fasmdep/unistd.inc | 322 +++++++++++ data/common/fasmdep/windows_header.inc | 6 + data/common/fasmdep/windows_sys.inc | 225 ++++++++ data/eng/Makefile | 2 +- data/eng/Makefile.fasm | 28 +- data/new-stack/Makefile | 2 +- data/new-stack/Makefile.fasm | 31 +- data/rus/Makefile | 2 +- data/rus/Makefile.fasm | 28 +- 15 files changed, 1733 insertions(+), 60 deletions(-) create mode 100644 data/common/fasmdep/fas.inc create mode 100644 data/common/fasmdep/fasmdep.asm create mode 100644 data/common/fasmdep/linux_header.inc create mode 100644 data/common/fasmdep/linux_sys.inc create mode 100644 data/common/fasmdep/unistd.inc create mode 100644 data/common/fasmdep/windows_header.inc create mode 100644 data/common/fasmdep/windows_sys.inc diff --git a/data/Vortex86MX-eng/Makefile b/data/Vortex86MX-eng/Makefile index 03f143df0a..55e69c70fc 100644 --- a/data/Vortex86MX-eng/Makefile +++ b/data/Vortex86MX-eng/Makefile @@ -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 diff --git a/data/Vortex86MX-eng/Makefile.fasm b/data/Vortex86MX-eng/Makefile.fasm index 26e456d6e3..67f4b4debe 100644 --- a/data/Vortex86MX-eng/Makefile.fasm +++ b/data/Vortex86MX-eng/Makefile.fasm @@ -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 diff --git a/data/common/fasmdep/fas.inc b/data/common/fasmdep/fas.inc new file mode 100644 index 0000000000..0199b639f8 --- /dev/null +++ b/data/common/fasmdep/fas.inc @@ -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 diff --git a/data/common/fasmdep/fasmdep.asm b/data/common/fasmdep/fasmdep.asm new file mode 100644 index 0000000000..7a22db0446 --- /dev/null +++ b/data/common/fasmdep/fasmdep.asm @@ -0,0 +1,765 @@ +; This program parses .fas file generated by fasm +; and prints the list of dependencies for make. +; Usage: fasmdep [-e] [ []]. +; 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 ';' +; 2) "quoted string" with code '"' +; 3) "word" with code 1Ah +; 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] [ []]',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' diff --git a/data/common/fasmdep/linux_header.inc b/data/common/fasmdep/linux_header.inc new file mode 100644 index 0000000000..cbbddf4451 --- /dev/null +++ b/data/common/fasmdep/linux_header.inc @@ -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 diff --git a/data/common/fasmdep/linux_sys.inc b/data/common/fasmdep/linux_sys.inc new file mode 100644 index 0000000000..48aa8d00b1 --- /dev/null +++ b/data/common/fasmdep/linux_sys.inc @@ -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 diff --git a/data/common/fasmdep/unistd.inc b/data/common/fasmdep/unistd.inc new file mode 100644 index 0000000000..cefad10833 --- /dev/null +++ b/data/common/fasmdep/unistd.inc @@ -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 diff --git a/data/common/fasmdep/windows_header.inc b/data/common/fasmdep/windows_header.inc new file mode 100644 index 0000000000..41493b6a09 --- /dev/null +++ b/data/common/fasmdep/windows_header.inc @@ -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 diff --git a/data/common/fasmdep/windows_sys.inc b/data/common/fasmdep/windows_sys.inc new file mode 100644 index 0000000000..444259a929 --- /dev/null +++ b/data/common/fasmdep/windows_sys.inc @@ -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 diff --git a/data/eng/Makefile b/data/eng/Makefile index a061377b38..772df2e9df 100644 --- a/data/eng/Makefile +++ b/data/eng/Makefile @@ -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 diff --git a/data/eng/Makefile.fasm b/data/eng/Makefile.fasm index 7e81ac5935..67f4b4debe 100644 --- a/data/eng/Makefile.fasm +++ b/data/eng/Makefile.fasm @@ -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 diff --git a/data/new-stack/Makefile b/data/new-stack/Makefile index 1ae9f71b30..d06ba98b14 100644 --- a/data/new-stack/Makefile +++ b/data/new-stack/Makefile @@ -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 diff --git a/data/new-stack/Makefile.fasm b/data/new-stack/Makefile.fasm index 26e456d6e3..67f4b4debe 100644 --- a/data/new-stack/Makefile.fasm +++ b/data/new-stack/Makefile.fasm @@ -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 diff --git a/data/rus/Makefile b/data/rus/Makefile index d5e69e4838..fd997947ed 100644 --- a/data/rus/Makefile +++ b/data/rus/Makefile @@ -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 diff --git a/data/rus/Makefile.fasm b/data/rus/Makefile.fasm index 7e81ac5935..67f4b4debe 100644 --- a/data/rus/Makefile.fasm +++ b/data/rus/Makefile.fasm @@ -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