помощь на ассемблере в поиске 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

Здесь нет проверки ошибок, чтобы упростить задачу. Вы можете проверить, превышает ли количество аргументов то, что вы ожидаете, или что-то еще.

введите здесь описание изображения

@ Эд Кашин, если ОП изучает синтаксис 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]. «т» — 116.

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

Изучив сгенерированную сборку, я вижу, что она не использует всплывающие окна, а просто загружает регистры в зависимости от положения параметров стека относительно базового указателя 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