/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 |
/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] [<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' |
/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 |
/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 |
/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 |
/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 |
/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 |