我正在尝试获取 x86 汇编语言中存储在
argv[1]
中的第一个元素。
最初我已经将堆栈弹出两次到eax
,因为我想要argc
,这样我就可以计算argc
的数量。然后将 argv
弹出到 ebx
。我正在考虑将 [ebx]
放入 bl
。从这里我迷路了。我对组装经验很少或没有,我只是想了解它。
main:
;
mov ecx, 0 ;count output characters
pop eax ;reject this 32 bits
pop eax ;get argc
;
pop ebx ; get argv
;mov bl, [ebx]
;
add al, 30H ;convert integer to ascii
mov edi, cline ;put char in output buffer
mov byte [edi],al
;inc edi
;mov [edi], bl
inc ecx ;increment output char count
inc edi ;increment pointer to o/p buffer
mov al, 0aH ;LF to end line
mov byte[edi],al ;put it at end of output line
inc ecx ;increment output char count
push ecx ;save char count on stack
mov edx,len ;length of string to write
mov ecx,msg ;addr of string
mov ebx,1 ;file descriptor 1 = stdout
mov eax,4 ;"write" system call
int 0x80 ;call the kernel
;
pop edx ;restore char count into edx for system call
mov ecx,cline ;address of string
mov ebx,1 ;file descriptor 1 = stdout
其工作原理如下:
argc = [esp]
argv = [esp + 4 * ARG_NUMBER]
其中 ARG_NUMBER 是 argv 中基于 1 的索引
./test hello there
[esp] = 3
[esp + 4 * 1] = ./test (program path and name)
[esp + 4 * 2] = hello
[esp + 4 * 3] = there
我将使用 C 库中的 printf 来使其更清楚:
extern printf, exit
section .data
fmtint db "%d", 10, 0
fmtstr db "%s", 10, 0
section .text
global main
main:
push dword[esp]
push fmtint
call printf ; print argc
add esp, 4 * 2
mov ebx, 1
PrintArgV:
push dword [esp + 4 * ebx]
push fmtstr
call printf ; print each param in argv
add esp, 4 * 2
inc ebx
cmp ebx, dword [esp]
jng PrintArgV
call exit
为了简单起见,这里没有错误检查。您可以检查参数的数量是否超出您的预期或其他。
@Ed Cashin,如果 OP 正在学习 INTEL 语法,为什么要将它们与 AT&T 混淆?
我有三点建议:
为了获取 argv,我可以简单地从程序中返回 argv[1] 中第一个字符的 ASCII 代码,以避免系统调用。系统调用与获取 argv 是不同的问题,因此避免它会将注意力集中在手头的问题上。
然后我可以编译一个最小的 C 程序并检查生成的程序集。如果您还记得,当您前往位于新泽西州的 AT&T 时,目的地位于美国的右侧,那么阅读 AT&T 语法汇编并没有那么糟糕;)
tmp$ cat main.c
int main(int argc, char *argv[])
{
if (argc > 1)
return argv[1][0];
return 0;
}
tmp$ gcc -Wall -save-temps main.c
程序仅返回 argv[1] 中第一个字符的 ASCII 代码。 “t”是 116。
tmp$ ./a.out test
tmp$ echo $?
116
tmp$
检查生成的程序集,我发现它不使用 pop,而只是根据堆栈参数相对于基指针 ebp 的位置加载寄存器。我发现我喜欢这种使用 mov 与基指针的风格。
我没有按照你想要的方式使用 pop,所以其他人可能会评论如何使用 pop 来做到这一点。
我已经对程序集做了一些注释,其中包含我认为正在发生的事情的评论。欢迎指正。
tmp$ cat main.s
.file "main.c"
.text
.globl main
.type main,@function
main:
pushl %ebp ; push the callers base pointer onto the stack
movl %esp, %ebp ; save esp into the base pointer
subl $8, %esp ; make some room on the stack for main ...
andl $-16, %esp ; but pad to an aligned stack pointer
movl $0, %eax
subl %eax, %esp ; dunno why gcc subtracts 0 from stack pointer
cmpl $1, 8(%ebp) ; compare 1 and argc, which is 8 past the base pointer
jle .L2 ; jump to .L2 if argc <= 1
movl 12(%ebp), %eax ; fetch argv into eax
addl $4, %eax ; skip the first 32 bits at that address
movl (%eax), %eax ; fetch address from the resulting address
movsbl (%eax),%eax ; load byte from that address into eax
movl %eax, -4(%ebp) ; put that byte onto the stack (see note 1.)
jmp .L1
.L2:
movl $0, -4(%ebp)
.L1:
movl -4(%ebp), %eax ; load return value from stack (see note 1.)
leave
ret
.Lfe1:
.size main,.Lfe1-main
.ident "GCC: (GNU) 3.2.2"
tmp$
我在32位机器上没有方便的nasm,而且x86_64调用约定与你正在处理的调用约定不一样,所以我没有将这个程序集翻译成nasm语法。