📄 ls.asm
字号:
;; ls.asm: Copyright (C) 2001 by Brian Raiter <breadbox@muppetlabs.com>,;; under the GNU General Public License. No warranty. See COPYING.;;;; To build:;; nasm -f bin -o ls ls.asm && chmod +x ls;;;; Usage: ls [-abdilsBCFNR1] [--] [FILE]...;; -C columnar output; default if stdout is a tty;; -1 one file per output line; default if stdout is not a tty;; -l "long" output;; -F show file type suffixes;; -i show inode numbers;; -s show size of files in 1K-blocks;; -d don't expand directories;; -a show all (hidden) files;; -B don't show files ending in ~ (backups);; -N don't replace non-graphic characters with a question mark;; -b show non-graphic characters as octal escape sequences;; -R recursively list contents of subdirectories;;;; Differences from GNU ls:;; * Columnar output isn't very intelligent. (Real columns would;; require loading the entire directory contents before displaying.);; * There are no sorting options. (Ditto previous obstacle.);; * There is no option to print uid/gid by name. (This would require;; reading and parsing the appropriate /etc files.);; * Showing the file's modification age is less useful than the;; modification time. (This would require reading /etc/localtime to;; get the proper offset from UTC.)BITS 32STRUC dirent.d_ino: resd 1.d_off: resd 1.d_reclen: resw 1.d_name: resb 256ENDSTRUC;; The top word of the origin is the instruction "push byte 1".%define origin 0x016A0000%define pathbufsize 4096 ; maximum path length%define direntsbufsize 8192 ; a good buffer size;; ebp is used to point to the itoa subroutine and nearby data.%define DATAOFF(pos) byte ebp + ((pos) - _ebp_pos_);; The ELF header. org originehdr: ; Elf32_Ehdr db 0x7F, "ELF", 1, 1, 1 ; e_ident times 9 db 0 dw 2 ; e_type dw 3 ; e_machine dd 1 ; e_version dd _start ; e_entry dd phdr - $$ ; e_phoff dd 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_ehsize dw phdrsize ; e_phentsizephdr: ; Elf32_Phdr dw 1 ; e_phnum ; p_type dw 0 ; e_shentsize dw 0 ; e_shnum ; p_offset dw 0 ; e_shstrndxehdrsize equ $ - ehdr dw (origin & 0x000FFFF) ; p_vaddr_start: push byte 1 push byte 54 ; p_paddr jmp short main dd filesize ; p_filesz dd memsize ; p_memsz dd 7 ; p_flags dd 0x1000 ; p_alignphdrsize equ $ - phdr;; This is the top-level routine of the program. It parses the;; command-line options, and then calls ls on the remaining arguments.main:;; ebp is initialized as our global pointer into the bss area. mov ebp, itoa;; If calling ioctl on stdout returns a non-zero value, then stdout is;; not a tty. This determines whether the default output format is;; columnar (-C) or one-file-per-line (-1). pop eax pop ebx mov ecx, ebx mov ch, 0x54 lea edx, [DATAOFF(pathbuf)] int 0x80 mov [DATAOFF(oneperln)], al;; After skipping argc and argv[0], the command-line arguments are;; examined as options until: none are left, an argument that doesn't;; start with a dash is found, or an argument that starts with a;; double-dash is found. Each option maps to a byte that is used as a;; boolean value that is zero by default (with the exception of -1, as;; noted above, and -C, which causes -l and -1 to be turned off if;; they were previously on). xor edx, edx pop esi pop esi.argloop: pop esi or esi, esi jz .noargs cmp byte [esi], '-' jnz .argloopend lodsb cmp al, [esi] jz .argloopquit.optloop: lodsb or al, al jz .argloop lea edi, [DATAOFF(options)] lea ecx, [byte edx + optioncount] repnz scasb mov [DATAOFF(optionflags) + ecx], al cmp al, 'C' jnz .optloop mov [DATAOFF(longdisp)], edx jmp short .optloop;; If a double-dash was encountered, then the program skips to the;; next argument. If there are no arguments left, a pointer to the;; string "." is pushed onto the stack. If there is more than one;; argument present at this point, edx will be set to a non-zero;; value..argloopquit: pop esi or esi, esi jnz .argloopend.noargs: push byte '.' mov esi, esp push edx.argloopend: mov edx, [esp];; ls is called for each command-line argument remaining. The extra;; return value from ls is discarded, except to cause edx to be;; non-zero after the first iteration (if there is more than one;; iteration.).lsiterate: call ls pop edx pop esi or esi, esi jnz .lsiterate;; The directory headings flag is turned on, and any subdirectories;; stored from the previous calls to ls are displayed in full. mov byte [DATAOFF(dirheads)], ':' mov esi, dirqueue call lsrecurse;; The last error value, if any, is retrieved, and the program returns;; to the caller. xchg eax, ebx mov ebx, [DATAOFF(retval)] int 0x80;; ls produces output for a single file or directory.;;;; input:;; esi = buffer containing file name;; edx = boolean: zero indicates that directories should be expanded;; in full, non-zero indicates that a directory should be added;; to the directory queue and nothing should be displayed;;;; output:;; stack = pointer to byte following file name;; all registers (except ebp) are alteredls:;; The filename is copied to a special buffer, and the length is;; determined. The position following the filename in the source;; buffer is saved on the stack above the return value. lea edi, [DATAOFF(pathbuf)] mov ebx, edi.savenameloop: lodsb stosb or al, al jnz .savenameloop pop eax push esi push eax mov esi, ebx sub edi, ebx dec edi;; lstat is called. If an error is returned, the program returns at;; once. If the file is not a directory, the program jumps to lsfile.;; If the file is a directory and edx is zero, the program jumps to;; lsdir. Otherwise, the program falls through to dirpush. call dolstat jc quit1 jnz lsfile cmp al, [DATAOFF(hidedirs)] jnz lsfile or edx, edx jz near lsdir;; dirpush adds a directory to the directory queue.;;;; input:;; esi = pathname;;;; output:;; esi = first byte after pathname;; eax, ebx, and edi are altereddirpush:;; The pathname is copied to the end of the directory queue. The;; directory queue is then expanded by calling brk to ensure;; sufficient space for the next call. mov edi, [DATAOFF(dirqueuetop)].copypathloop: lodsb stosb or al, al jnz .copypathloop mov [DATAOFF(dirqueuetop)], edi xor ebx, ebx lea eax, [byte ebx + 45] mov bh, pathbufsize >> 8 add ebx, edi int 0x80quit1: ret;; lsfile produces output for a single file.;;;; input:;; pathbuf = file's complete pathname;; sts = file's stat information;; esi = filename (minus the path);; edi = length of filename;;;; output:;; all registers (except ebp) are alteredlsfile:;; edi is initialized to point to outbuf, which is where the output;; will be built up. The start of outbuf and the filename's length are;; stored on the stack. mov eax, outbuf push eax push edi xchg eax, edi cdq;; The inode number is displayed if -i was specified. cmp dh, [DATAOFF(showinod)] jz .noinode mov eax, [DATAOFF(sts.st_ino)] mov dl, 8 call ebp mov al, ' ' stosb.noinode:;; The size in blocks is displayed if -s was specified. cmp dh, [DATAOFF(showblks)] jz .noblocks mov eax, [DATAOFF(sts.st_blocks)] shr eax, 1 mov dl, 4 call ebp mov al, ' ' stosb.noblocks:;; A whole bunch of code is skipped over unless -l was specified. cmp dh, [DATAOFF(longdisp)] jz .briefrelay;; The first section of long output displays the mode value as a;; string of diverse characters. The mode is separated into the nine;; basic permission bits (dl plus the carry flag), the three upper;; permission bits (dh), and the top four bits which indicate the file;; type (al). The latter is, after being saved on the stack,;; translated directly into a lowercase letter and added to outbuf.;; ecx is initialized to a pattern which is used to indicate which of;; the nine basic permission bits are execute permissions, as well as;; when all nine bits have been examined. mov edx, [DATAOFF(sts.st_mode)] mov al, dh shr al, 4 push eax lea ebx, [DATAOFF(filetypes)] xlatb stosb lea ebx, [DATAOFF(permissions)] mov ecx, 444q rcr dh, 1;; This loop executes once for each basic permission bit, moving each;; bit into the carry flag in turn. al is set to 4, 2, or 1 if the bit;; is set, or 7 if it is turned off. If the bit is an execute;; permission, then the corresponding upper permission bit is also;; examined. If it is set, then an "x" must be changed to an "s" or;; "t", and a "-" to an "S" or "T"..permloop: mov al, 7 jnc .permoff and al, cl.permoff: test cl, 1 jz .nosbit shl dh, 1 test dh, 8 jz .nosbit add al, cl and al, 0x1F inc eax.nosbit: xlatb stosb shr ecx, 1 jz .permloopend rcl dl, 1 ; carry = next bit jmp short .permloop.briefrelay: jmp short .brief.permloopend:;; The number of hard links, the owner's user id, and the owner's;; group id are each in turn added to outbuf. lea ebx, [byte ecx + 3] lea edx, [byte ecx + 4] push esi lea esi, [DATAOFF(sts.st_nlink)].numoutloop: lodsw call ebp mov dl, 7 dec ebx jnz .numoutloop pop esi;; The file type is retrieved from the stack, and the values;; indicating a character or block device are checked for. If present,;; the file's rdev numbers are added to outbuf; otherwise, the file's;; size is used. pop eax and al, ~4 cmp al, 2 mov eax, [DATAOFF(sts.st_size)] mov dl, 10 jnz .nordev mov dl, 5 movzx eax, byte [DATAOFF(sts.st_rdev + 1)] call ebp mov al, [DATAOFF(sts.st_rdev)].nordev: call ebp;; The programs gets the current time and subtracts the file's;; modification time from it. This value is then subsequently divided;; by 60, 60, and 24, stopping if the quotient ever drops to zero. The;; remainder (or quotient, if it is non-zero after all three;; divisions) is added to outbuf as the file's modification age,;; followed by the appropriate letter to indicate the units. (The xlatb;; at the end works because al is always zero.) mov al, 78 mov ebx, edi xor ecx, ecx int 0x80 mov eax, [edi] sub eax, [DATAOFF(sts.st_mtime)] mov ebx, ebp.timingloop: cdq dec ebx mov cl, [byte ebx + timeunits - itoa] or ecx, ecx jz .showtime div ecx or eax, eax jnz .timingloop xchg eax, edx.showtime: mov dl, 5 call ebp xlatb stosb mov al, ' ' stosb;; End of long output..brief:;; The filename's length is retrieved, and the filename is copied into;; outbuf. pop ecx call copyfilename;; The mode bits are checked to see if the file is a symbolic link. mov al, [DATAOFF(sts.st_mode) + 1] and al, 0xF0 sub al, 0xA0 jnz .nosymlink cmp al, [DATAOFF(longdisp)] jz .nosymlink;; If it is, and the output is verbose, then the link destination is;; appended to outbuf, prefixed by an arrow. (The top of the directory;; queue is used as a convenient temporary buffer.) Then the original;; filename is given to stat, as opposed to lstat, in order to obtain;; the file type of the link destination. mov eax, ' -> ' stosd lea eax, [byte ecx + 85] lea ebx, [DATAOFF(pathbuf)] mov ecx, [DATAOFF(dirqueuetop)] cdq mov dh, pathbufsize >> 8 int 0x80 xchg eax, ecx xchg eax, esi mov [esi + ecx], dl call copyfilename xchg eax, ecx mov al, 106 lea ecx, [DATAOFF(sts)] int 0x80 or eax, eax js .endentry.nosymlink:;; The file's type is separated out from the other mode bits. If -F;; was specified, and the file type has an associated suffix;; character, this character is added to outbuf immediately following;; the filename. If the file type has no suffix, but is executable, an;; asterisk is used. mov eax, [DATAOFF(sts.st_mode) - 2] shr eax, 29 cmp ah, [DATAOFF(showtype)] jz .endentry mov al, [DATAOFF(suffixes) + eax] cmp al, '*' jnz .fmtchar test byte [DATAOFF(sts.st_mode)], 111q jz .endentry.fmtchar: stosb;; The entry is complete but for the whitespace. If -1 was specified,;; then a final newline is all that is needed..endentry: mov al, 10 xor ebx, ebx inc ebx cmp bh, [DATAOFF(oneperln)] jnz .endline;; Otherwise, the tabstop corresponding to the current cursor position;; is retrieved from xline and is added to the length of the text in;; outbuf (converted to tabstops). If it extends past the tenth;; tabstop, a newline is output to stdout immediately. If it extends;; past the eighth tabstop, a newline is added to outbuf. If the;; tabstop is an odd number, two tabs are added to outbuf. Otherwise,;; one tabstop is added to outbuf. In all cases, xline is updated to;; reflect the position of the cursor after output. mov ecx, edi sub ecx, [esp] shr ecx, 3 inc ecx mov dl, [DATAOFF(xline)] add dl, cl cmp al, dl jae .fitsinline push ecx call tobol pop edx.fitsinline: mov al, 9 cmp al, dl ja .roomformore inc eax mov dl, 0.roomformore: test dl, bl jz .setxline stosb inc edx.setxline: mov [DATAOFF(xline)], dl.endline:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -