assembly 如何比较命令行参数(x86_64)

tvmytwxo  于 2022-11-13  发布在  其他
关注(0)|答案(1)|浏览(236)

我正在处理一个Assembly GAS/AT&T x86_64赋值,它要求我们获取一些命令行参数并使用它们进行一些操作。
我已经知道了它们在堆栈中的位置,但是我不知道如何将参数的内容与另一个字符串进行比较,以检测用户是否输入了特定的参数。下面是一个最小的例子,说明我正在尝试做的事情。但是,执行永远不会到达he子例程。

.text

output: .asciz "%s"

arg: .ascii "-i"

.global main

main:

movq 8(%rsi), %rsi

movq arg, %rdi

cmpq %rsi, %rdi
je he

movq    $0, %rdi            
call    exit                

he:

movq $output, %rdi

movq $0, %rax
call printf

movq    $0, %rdi            
call    exit

我做错了什么?提前感谢你的帮助!

a7qyws3x

a7qyws3x1#

您正在比较指向arg字符串的8个字节的指针。
要比较字符串,由于您使用的是C运行时,因此可以像在C中一样执行此操作:用strcmp表示。

.global main
.text

 strIArg: .asciz "-i"
 strHello: .asciz "Hello.\n"

main:
 sub $8, %rsp
 
 #At least two args?
 cmp $2, %edi
 jb 1f

 #2nd arg is equal to strIArg?
 mov 8(%rsi), %rdi           #argv[1] in RDI
 lea strIArg(%rip), %rsi     #"-i" in RSI (I'm making a PIE, hence the RIP-relative addressing)
 call strcmp
 
 test %eax, %eax            #strcmp returns 0 if the two strings are equal
 jnz 1f
 
 #OK, arg found
 
 lea strHello(%rip), %rdi
 call printf

1:
 xor %edi, %edi
 call exit

或者,如果参数足够短并且程序非常简单,则可以取消对strcmp的调用以提高性能。

.global main

.text

strHello: .asciz "Hello.\n"

main:
 sub $8, %rsp
 
 #At least two args?
 cmp $2, %edi
 jb 1f

 #2nd arg is equal to strIArg?
 mov 8(%rsi), %rdi
 
 cmpb $0, (%rdi)
 je 1f          #Empty string?
 
 cmpw $0x692d, (%rdi)   #Starts with -i ?
 jne 1f
 
 cmpb $0, 2(%rdi)   #And then it ends?
 jne 1f
 
 #OK, arg found
 
 lea strHello(%rip), %rdi
 call printf

1:
 xor %edi, %edi
 call exit

但我不建议这样做,除非是在最简单的情况下,因为GAS doesn't support string literals as immediates和您需要自己转换字符串(注意x86的小字节序),这降低了代码的可读性。
最后,对于在POSIX系统上运行的更复杂的程序,您可能需要考虑getopt_long and variants
下面是一个程序示例,该程序会问候在命令行上传递的名称,并使用两个可选参数来修改其行为。
请注意getopt_long将如何处理参数的重新排序、处理极端情况(例如,当用户将-un X作为-u -n X的缩写传递时)以及为我们处理--

.global main

.data

 #Name to use for the greetings
 name: .quad defaultName
 
 #Greeting string to use
 greetings: .quad strHello

 #The long options accepted
 
 nameOpt: 
    .quad nameOptName   #name
    .quad 1         #has arg
    .quad 0         #ptr to flag to update with val (0 to make getopt_long return val instead)
    .quad 'n'       #val
 uppercaseOpt: 
    .quad uppercaseOptName
    .quad 0
    .quad 0
    .quad 'u'
 nullOpt:
    .quad 0 
    .quad 0
    .quad 0
    .quad 0     #Last option must be null

.text

 #Greetings strings
 strHello: .asciz "Hello %s from %s!\n"
 strHelloUpper: .asciz "HELLO %s FROM %s!\n"
 
 #Default name
 defaultName: .asciz "Margaret"

 
 nameOptName: .asciz "name"
 uppercaseOptName: .asciz "upper"
 
 #The short options accepted, note how we use "n" and "u" for both the long and short options
 #this is to reuse the logic but getopt_long allows to distinguish the two cases
 
 shortOpts: .asciz "n:u"

main:
 sub $8, %rsp
 
 #If you return from main, push r12 and r13 (and then pop them)
 
 #Move the args to non-volatile registers
 mov %rdi, %r12     #R12 = argc
 mov %rsi, %r13     #R13 = argv
 
parseArgs:
 mov %r12, %rdi
 mov %r13, %rsi
 lea shortOpts(%rip), %rdx
 lea nameOpt(%rip), %rcx
 xor %r8, %r8
 call getopt_long
 
 #Found --name/-n?
 cmp $'n', %al
 je foundName
 
 
 #Found --upper/-u?
 cmp $'u', %al
 je foundUpper
 
 #Everything else is an error or end of option args (-1)
 test %eax, %eax
 jns parseError

 #Args are parsed, optind is the index of the first non optional arg
 
 lea (%r13, %r12, 8), %r12  #R12 = one past the last argument       
 mov optind(%rip), %ecx     #RCX = current index
 lea (%r13, %rcx, 8), %r13  #R13 = pointer to pointer to current argument
 
 #Print the greetings
doGreetings:
 #Stop?
 cmp %r12, %r13
 jae end
 
 #Print the current greetings
 mov greetings(%rip), %rdi
 mov (%r13), %rsi
 mov name(%rip), %rdx
 call printf
 
 #Next arg
 add $8, %r13
jmp doGreetings
 
end:
 xor %edi, %edi
 call exit

foundName:
 #Here optarg is a pointer to the argument value
 #Copy the pointer to name
 
 mov optarg(%rip), %rdx
 mov %rdx, name(%rip)
jmp parseArgs

foundUpper:
 #This option has no argument, we just set the greetings to strHelloUpper
 
 lea strHelloUpper(%rip), %rcx
 mov %rcx, greetings(%rip)
jmp parseArgs

parseError:
 #Just return 1
 
 mov $1, %edi
 call exit

您可以使用GCC编译此程序,然后以下列方式运行:

./greet Alice Bob Eve
./geeet Alice --name Bob
./greet Alice --upper
./greet --name Eve --upper Alice
./greet -u Alice
./greet Alice -un Bob

相关问题