асемблер помощ при намиране на agrv[1][0]

Опитвам се да получа първия елемент от това, което се съхранява в argv[1] на асемблерния език x86. Извадих стека два пъти до 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

person sir_t    schedule 02.11.2013    source източник
comment
Моля, не забравяйте да използвате кодови блокове. Поправих го за теб.   -  person ChiefTwoPencils    schedule 03.11.2013


Отговори (2)


Погледнете тук: NASM - команда за получаване на Linux параметри на линия

Ето как работи:

argc = [esp]
argv = [esp + 4 * ARG_NUMBER]

Където ARG_NUMBER е базиран на 1 индекс в argv

./test hello there
[esp] = 3
[esp + 4 * 1] = ./test (program path and name)
[esp + 4 * 2] = hello
[esp + 4 * 3] = there

Ще използвам printf от C библиотеката, за да стане по-ясно:

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?

person Gunner    schedule 03.11.2013
comment
Бих предложил да го напишете като [esp + 4 + 4 * ARG_INDEX], така че да не се налага да се забърквате с номерирането на базата на 1. Тогава всичко съвпада с C, къдетоargv[1] е първият аргумент след името на програмата. - person Peter Cordes; 30.05.2017

Имам три препоръки:

  1. Разгледайте http://www.nasm.us/doc/nasmdoc9.html, ако не съм вече,
  2. Минимизирайте кода, който се опитва да реши вашия непосредствен проблем и
  3. Проверете събранието, генерирано от C компилатор, ако е възможно, когато сте блокирани.

За да получа argv, мога просто да върна ASCII кода за първия знак в argv[1] от моята програма, за да избегна системното извикване. Системното извикване е различен проблем от получаването на 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

Програмата просто връща ASCII кода за първия знак в argv[1]. "t" е 116.

tmp$ ./a.out test
tmp$ echo $?
116
tmp$ 

Разглеждайки генерирания асембли, виждам, че той не използва pops, а по-скоро просто зарежда регистри въз основа на позицията на параметрите на стека спрямо базовия указател, 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$ 

Нямам под ръка nasm на 32-битова машина и конвенциите за извикване на x86_64 не са същите като тези, с които се занимавате, така че не преведох това асемблиране в синтаксис nasm.

  1. Компилаторът ще направи някои неща, които ви карат да се почешете по главата и да се чудите: "Това умно ли е или тъпо?" В този случай мисля, че вероятно бих използвал самия eax вместо стека за задържане на върнатата стойност, но понякога гугълът е образователен. Научих защо gcc понякога обича да нулира регистър с "xor reg, reg", например, чрез гугъл.
person Ed Cashin    schedule 03.11.2013
comment
@Gunner пита защо използвах синтаксис на AT&T, когато OP очевидно използва синтаксис на Intel. Причината е, че вярвам, че постигането на двуезично владеене е по-малко трудно, отколкото опитът да се избегне изцяло синтаксиса на AT&T. Възможността за проверка на изхода, оставен от -save-temps, компенсира всяко временно объркване. За допълнително облекчаване на дискомфорта предоставих мнемоника. Благодаря че попита. - person Ed Cashin; 05.11.2013