📄 factor.asm
字号:
;; factor.asm: Copyright (C) 1999-2001 by Brian Raiter, under the GNU;; General Public License (version 2 or later). No warranty.;;;; To build:;; nasm -f bin -o factor factor.asm && chmod +x factor;;;; Usage: factor [N]...;; Prints the prime factors of each N. With no arguments, reads N;; from standard input. The valid range is 0 <= N < 2^64.BITS 32%define stdin 0%define stdout 1%define stderr 2;; The program's data%define iobuf_size 96STRUC datafactor: resd 2 ; number being tested for factorhoodgetchar_rel: resd 1 ; pointer to getchar() functionwrite_rel: resd 1 ; pointer to write() functionbuf: resd 3 ; general-purpose numerical bufferexit_rel: resd 1 ; pointer to _exit() functioniobuf: resb iobuf_size ; buffer for I/OENDSTRUC org 0x08048000;; The ELF header and the program header table. The last eight bytes;; of the former coexist with the first eight bytes of the former.;; The program header table contains three entries. The first is the;; interpreter pathname, the third is the _DYNAMIC section, and the;; middle one contains everything in the file. db 0x7F, 'ELF' db 1 ; ELFCLASS32 db 1 ; ELFDATA2LSB db 1 ; EV_CURRENT times 9 db 0 dw 2 ; ET_EXEC dw 3 ; EM_386 dd 1 ; EV_CURRENT dd _start dd phdrs - $$ dd 0 dd 0 dw 0x34 ; sizeof(Elf32_Ehdr) dw 0x20 ; sizeof(Elf32_Phdr)phdrs: dd 3 ; PT_INTERP dd interp - $$ dd interp dd interp dd interpsz dd interpsz dd 4 ; PF_R dd 1 dd 1 ; PT_LOAD dd 0 dd $$ dd $$ dd filesz dd memsz dd 7 ; PF_R | PF_W | PF_X dd 0x1000 dd 2 ; PT_DYNAMIC dd dynamic - $$ dd dynamic dd dynamic dd dynamicsz dd dynamicsz dd 6 ; PF_R | PF_W dd 4;; The hash table. Essentially a formality. The last entry in the hash;; table, a 1, overlaps with the next structure.hash: dd 1 dd 5 dd 4 dd 0, 2, 3, 0;; The _DYNAMIC section. Indicates the presence and location of the;; dynamic symbol section (and associated string table and hash table);; and the relocation section. The final DT_NULL entry in the dynamic;; section overlaps with the next structure.dynamic: dd 1, libc_name ; DT_NEEDED dd 4, hash ; DT_HASH dd 5, dynstr ; DT_STRTAB dd 6, dynsym ; DT_SYMTAB dd 10, dynstrsz ; DT_STRSZ dd 11, 0x10 ; DT_SYMENT dd 17, reltext ; DT_REL dd 18, reltextsz ; DT_RELSZ dd 19, 0x08 ; DT_RELENTdynamicsz equ $ - dynamic + 8;; The dynamic symbol table. Entries are included for the _DYNAMIC;; section and the three functions imported from libc: getchar(),;; write(), and _exit().dynsym: dd 0 dd 0 dd 0 dw 0 dw 0dynamic_sym equ 1 dd dynamic_name dd dynamic dd 0 dw 0x11 ; STB_GLOBAL, STT_OBJECT dw 0xFFF1 ; SHN_ABSexit_sym equ 2 dd exit_name dd 0 dd 0 dw 0x12 ; STB_GLOBAL, STT_FUNC dw 0getchar_sym equ 3 dd getchar_name dd 0 dd 0 dw 0x22 ; STB_WEAK, STT_FUNC dw 0write_sym equ 4 dd write_name dd 0 dd 0 dw 0x22 ; STB_WEAK, STT_FUNC dw 0;; The relocation table. The addresses of the three functions imported;; from libc are stored in the program's data area.reltext: dd dataorg + write_rel db 1 ; R_386_32 db write_sym dw 0 dd dataorg + getchar_rel db 1 ; R_386_32 db getchar_sym dw 0 dd dataorg + exit_rel db 1 ; R_386_32 db exit_sym dw 0reltextsz equ $ - reltext;; The interpreter pathname. The final NUL byte appears in the next;; section.interp: db '/lib/ld-linux.so.2'interpsz equ $ - interp + 1;; The string table for the dynamic symbol table.dynstr: db 0libc_name equ $ - dynstr db 'libc.so.6', 0dynamic_name equ $ - dynstr db '_DYNAMIC', 0exit_name equ $ - dynstr db '_exit', 0getchar_name equ $ - dynstr db 'getchar', 0write_name equ $ - dynstr db 'write', 0dynstrsz equ $ - dynstr;;;; The program proper;;;; The factorconst subroutine, called by factorize, repeatedly divides;; the number at the top of the floating-point stack by the integer;; stored in buf as long as the number continues to divide evenly. For;; each successful division, the number is also displayed on standard;; output. Upon return, the quotient of the failed division is at the;; top of the floating-point stack, just above the factored number.factorconst: ; num + fild dword [ebx] ; div num +.loop: fld st1 ; num div num + fdiv st0, st1 ; quot div num + fld st0 ; quot quot div num + frndint ; quoi quot div num + fcomp st1 ; quot div num + fnstsw ax and ah, 0x40 jz factorize.return fstp st2 ; div quot + call ebp jmp short .loop;; factorize is the main subroutine of the program. It is called with esi;; pointing to an NUL-terminated string representing the number to factor.;; Upon return, eax contains a nonzero value if an error occurred (i.e.,;; an invalid number stored at esi).factorize:;; The first step is to translate the string into a number. 10.0 and 0.0;; are pushed onto the floating-point stack. xor eax, eax fild word [byte ebx + ten - dataorg] ; 10.0 fldz ; num 10.0;; Each character in the string is checked; if it is not in the range;; of '0' to '9' inclusive, an error message is displayed and the;; subroutine aborts. Otherwise, the top of the stack is multiplied by;; ten and the value of the digit is added to the product. The loop;; exits when a NUL byte is found..atoiloop: lodsb or al, al jz .atoiloopend fmul st0, st1 ; n10 10.0 sub al, '0' jc .errbadnum cmp al, 10 jnc .errbadnum mov [byte ebx + buf], eax fiadd dword [byte ebx + buf] ; num 10.0 jmp short .atoiloop.errbadnum: push byte errmsgbadnumsz lea eax, [byte ebx + errmsgbadnum - dataorg] push eax push byte stderr call [byte ebx + write_rel] add esp, byte 12 mov al, 1.return: fcompp ret.atoiloopend:;; The number's exponent is examined, and if the number is 2^64 or;; greater, it is rejected. fld st0 ; num num 10.0 fstp tword [byte ebx + buf] ; num 10.0 mov eax, [byte ebx + buf + 8] cmp ax, 64 + 0x3FFF jge .errbadnum;; The number is displayed, followed by a colon. If the number is one;; or zero, no factoring should be done and the subroutine skips ahead;; to the end. ; num junk mov dl, ':' call itoa64nospc fxch st1 ; junk num fld1 ; 1.0 junk num fcom st2 fstsw ax and ah, 0x45 cmp ah, 1 jnz .earlyout fcompp ; num;; The factorconst subroutine is called three times, with the number;; in buf set to two, three, and five, respectively. mov edi, factorconst xor edx, edx mov dl, 2 mov [ebx], edx call edi inc dword [ebx] call edi mov byte [ebx], 5 call edi;; If the number is now equal to one, the subroutine is finished and;; exits immediately. fld1 ; 1.0 num fcom st1 fnstsw ax and ah, 0x40 jnz .quitfactorize;; factor is initialized to 7, and edi is initialized with a sequence;; of eight four-bit values that represent the cycle of differences;; between subsequent integers not divisible by 2, 3, or 5. The;; subroutine then enters its main loop. ; junk num mov byte [ebx], 7 fild qword [ebx] ; fact junk num fdivr st0, st2 ; quot junk num mov edi, 0x42424626;; The loop returns to this point when the last tested value was not a;; factor. The next value to test (for which the division operation;; should just be finishing up) is moved into esi, and factor is;; incremented by the value at the bottom of edi, which is first;; advanced to the next four-bit value. If it overflows, then we have;; exhausted all possible factors, and end..notafactor: mov esi, [ebx] rol edi, 4 mov eax, edi and eax, byte 0x0F add [ebx], eax jc .earlyout;; The main loop of the factorize subroutine. The quotient from the;; division of the number by the next potential factor is stored, and;; the division for the next iteration is started..mainloop: ; quot quo0 num fst st1 ; quot quot num fstp tword [byte ebx + buf] ; quot num fild qword [ebx] ; fact quot num fdivr st0, st2 ; quo2 quot num;; The integer portion of the quotient is isolated and tested against;; the divisor (i.e., the potential factor). If the quotient is;; smaller, then the loop has passed the number's square root, and no;; more factors will be found. In this case, the program prints out;; the current value for the number as the last factor, followed by a;; newline character, and the subroutine ends. mov edx, [byte ebx + buf + 4] mov ecx, 31 + 0x3FFF sub ecx, [byte ebx + buf + 8] js .keepgoing mov eax, edx shr eax, cl cmp eax, esi jnc .keepgoing.earlyout: fxch st2 call ebp fstp st0.quitfactorize: fcompp push byte 1 lea esi, [byte ebx + ten - dataorg] jmp short finalwrite;; Now the integer portion of the quotient is shifted out. If any;; nonzero bits are left, then the number being tested is not a;; factor, and the program loops back..keepgoing: mov eax, [byte ebx + buf] neg ecx js .shift32 xchg eax, edx xor eax, eax.shift32: shld edx, eax, cl shl eax, cl or eax, edx jnz .notafactor;; Otherwise, a new factor has been found. The number being factored;; is therefore replaced with the quotient, and the result of the;; division in progress is junked. The new factor is displayed, and;; then is tested again. If this was the first time this factor was;; tested, then edi is reset back. ; junk num num0 cmp [ebx], esi jz .repeating ror edi, 4 mov [ebx], esi.repeating: ffree st2 ; junk num fild qword [ebx] ; fact junk num call ebp fdivr st0, st2 ; quot junk num mov esi, [ebx] jmp short .mainloop;; itoa64 is the numeric output subroutine. When the subroutine is;; called, the number to be displayed should be on the top of the;; floating-point stack, and there should be no more than four other;; numbers on the stack. A space is prefixed to the output, unless;; itoa64nospc is called, in which case the character in dl is;; suffixed to the output.itoa64: mov dl, ' 'itoa64nospc:;; A copy of the number is made, and 10 is placed on the stack. esi is;; pointed to the end of the buffer that will hold the decimal;; representation, with ecx pointing just past the end. ; num + fld st0 ; num num + fild word [byte ebx + ten - dataorg] ; 10.0 num num + lea esi, [byte ebx + iobuf + 32] mov ecx, esi dec esi;; At each iteration, the number is reduced modulo 10. This remainder;; is subtracted from the number (and stored in iobuf as an ASCII;; digit). The difference is then divided by ten, and if the quotient;; is zero the loop exits. Otherwise, the quotient replaces the number;; for the next iteration..loop: fld st1 ; num 10.0 num num + fprem ; rem 10.0 num num + fist dword [ecx] ; rem 10.0 num num + fsubr st0, st2 ; 10n2 10.0 num num + ftst fstsw ax fstp st2 ; 10.0 10n2 num + fdiv st1, st0 ; 10.0 num2 num + mov al, '0' add al, [ecx] mov [esi], al dec esi and ah, 0x40 jz .loop fcompp ; num +;; If al contains a space, it is added to the start of the string.;; Otherwise, the character is added to the end. mov [esi], dl cmp dl, ' ' jz .prefix mov [ecx], dl inc ecx inc esi.prefix:;; The string is written to standard output, and the subroutine ends. sub ecx, esi push ecxfinalwrite: push esi push byte stdout call [byte ebx + write_rel] add esp, byte 12 dec eax ret;; Here is the program's entry point._start:;; argc and argv[0] are removed from the stack and discarded. ebx is;; initialized to point to the data. pop esi pop ebx mov ebx, dataorg mov ebp, itoa64;; If argv[1] is NULL, then the program proceed to the input loop. If;; argv[1] begins with a dash, then the help message is displayed.;; Otherwise, the program begins readings the command-line arguments. pop esi or esi, esi jz .inputloop cmp byte [esi], '-' jz .dohelp;; The factorize subroutine is called once for each command-line;; argument, and then the program exits, with the exit code being;; the return value from the last call to factorize..argloop: call factorize pop esi or esi, esi jnz .argloop.mainexit: push eax call [byte ebx + exit_rel];; The input loop routine. edi is pointed to iobuf, and esi is;; initialized to one less than the size of iobuf..inputloop: lea edi, [byte ebx + iobuf] push byte iobuf_size - 1 pop esi;; The program reads and discards one character at a time, until a;; non-whitespace character is seen (or until no more input is;; available, in which case the program exits)..preinloop: call [byte ebx + getchar_rel] or eax, eax js .mainexit cmp al, ' ' jz .preinloop cmp al, 9 jc .incharloop cmp al, 14 jc .preinloop;; The first non-whitespace character is stored at the beginning of;; iobuf. The program continues to read characters until there is no;; more input, there is no more room in iobuf, or until another;; whitespace character is found..incharloop: stosb dec esi jz .infinish call [byte ebx + getchar_rel] or eax, eax js .infinish cmp al, ' ' jz .infinish cmp al, 9 jc .incharloop cmp al, 14 jnc .incharloop.infinish:;; A NUL is appended to the string obtained from standard input, the;; factorize subroutine is called, and the program loops. mov byte [edi], 0 lea esi, [byte ebx + iobuf] call factorize jmp short .inputloop;; The program displays the help message on standard output and exits;; with an exit value of zero..dohelp: push byte 0 push dword [byte ebx + exit_rel] push byte helpmsgsz lea esi, [byte ebx + helpmsg - dataorg] jmp short finalwrite;; Messages to the user.helpmsg: db 'Finds the prime factors of args/input', 10 db 'v1.2', 10helpmsgsz equ $ - helpmsgerrmsgbadnum: db 'invalid number'ten: db 10errmsgbadnumsz equ $ - errmsgbadnum;; End of file image.filesz equ $ - $$dataorg equ $$ + ((filesz + 16) & ~15)memsz equ dataorg + data_size - $$
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -