【问题标题】:Displaying environment variables in assembly language用汇编语言显示环境变量
【发布时间】:2015-11-18 15:59:15
【问题描述】:

我试图通过制作一个基本程序来显示环境变量,例如

C 代码:

    int         main(int ac, char **av, char **env)
    {
     int        x;
     int        y;

     y = -1;
     while (env[++y])
       {
        x = -1;
        while (env[y][++x])
          {
           write(1, &(env[y][x]), 1);
          }
       }
     return (0);
    }

我用 gcc -S 编译了它(在 cygwin64 上)看看怎么做,并用我自己的方式编写它(类似但不一样),但它不起作用......

    $>gcc my_av.s && ./a.exe

    HOMEPATH=\Users\hadrien▒2▒p

我的汇编代码:

        .file   "test.c"
    .LC0:
        .ascii "\n\0"
    .LC1:
        .ascii "\033[1;31m.\033[0m\0"   
    .LC2:
        .ascii "\033[1;31m#\033[0m\0"
    .LCtest0:
        .ascii "\033[1;32mdebug\033[0m\0"
    .LCtest1:
        .ascii "\033[1;31mdebug\033[0m\0"
    .LCtest2:
        .ascii "\033[1;34mdebug\033[0m\0"

        .def    main;   .scl    2;  .type   32; .endef
    main:
        /* initialisation du main */
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $48, %rsp
        movl    %ecx, 16(%rbp) /* int argc */
        movq    %rdx, 24(%rbp) /* char **argv */
        movq    %r8, 32(%rbp) /* char **env */

        /* saut de ligne */
        /* write init */
        movl    $1, %r8d /* write size */
        movl    $1, %ecx /* sortie standart */
        leaq    .LC0(%rip), %rdx
        /* write */
        call    write

        /* debut du code */
        movl    $-1, -8(%rbp) /* y = -1 */
        jmp .Loop_1_condition

    .Loop_1_body:
        movl    $-1, -4(%rbp)
        jmp .Loop_2_condition

    .Loop_2_body:
        /* affiche le charactere */
        movl    $1, %r8d
        movl    $1, %ecx
        call    write

    .Loop_2_condition:
        addl    $1, -4(%rbp) /* x = -1 */
        movl    -8(%rbp), %eax
        cltq
        addq    32(%rbp), %rax
        movq    (%rax), %rax
        movq    %rax, %rdx
        movl    -4(%rbp), %eax
        cltq
        addq    %rdx, %rax
        movq    %rax, %rdx
        movq    (%rax), %rax
        cmpq    $0, %rax
        jne .Loop_2_body

        /* saut de ligne */
        movl    $1, %r8d /* write size */
        movl    $1, %ecx /* sortie standart */
        leaq    .LC0(%rip), %rdx
        call    write

    .Loop_1_condition:
        addl    $1, -8(%rbp) /* ++y */
        movl    -8(%rbp), %eax
        cltq /* passe eax en 64bits */
        addq    32(%rbp), %rax
        movq    (%rax), %rax
        cmpq    $0, %rax
        jne .Loop_1_body

        movl    $1, %r8d /* write size */
        movl    $1, %ecx /* sortie standart */
        leaq    .LC0(%rip), %rdx
        call    write

        /* fin du programme */
        movl    $0, %eax /* return (0) */
        addq    $48, %rsp
        popq    %rbp
        ret
        .def    write;  .scl    2;  .type   32; .endef

谁能解释一下这段代码有什么问题?

另外,在尝试解决问题时,我厌倦了在 cmpq 操作中将 $0 替换为 $97,认为它会在 'a' 字符上停止,但它没有......为什么?

【问题讨论】:

  • 您在汇编代码中使用y 偏移env 的方式看起来不正确。 char* 大于一个字节。
  • 另外,使用 as 或 gcc 组装时环境不同。 Gcc 在程序序言中添加了“C 接口”。
  • Michael > 是的,你是对的...即使使用简单的循环 env [y],我也没有得到好的变量编号... turboscrew > 是的,gcc 不会让我编译,除非我有主标签,我去看看你的链接谢谢

标签: parsing gcc assembly x86-64


【解决方案1】:

您有一些问题。在这段代码(loop2)中,你有:

    addq    %rdx, %rax
    movq    %rax, %rdx
    movq    (%rax), %rax
    cmpq    $0, %rax

movq (%rax), %rax 已读取 %rax 中接下来的 8 个字符。您只对第一个字符感兴趣。实现此目的的一种方法是将 %rax 中的最低有效字节与 0 进行比较。您可以使用 cmpb 并使用 %al 寄存器:

    cmpb    $0, %al

但最大的问题是理解char **env 是指向char * 数组的指针。您首先需要获取数组的基指针,然后用y 索引该基指针。索引看起来像 basepointer + (y * 8) 。您需要将 y 乘以 8,因为每个指针都是 8 个字节宽。该位置的指针将是特定环境字符串的char *。然后你可以索引字符串数组中的每个字符,直到找到一个 NUL (0) 终止字符。

我稍微修改了代码,并在我更改的几行添加了 cmets:

    .file   "test.c"
.LC0:
    .ascii "\x0a\0"
.LC1:
    .ascii "\033[1;31m.\033[0m\0"
.LC2:
    .ascii "\033[1;31m#\033[0m\0"
.LCtest0:
    .ascii "\033[1;32mdebug\033[0m\0"
.LCtest1:
    .ascii "\033[1;31mdebug\033[0m\0"
.LCtest2:
    .ascii "\033[1;34mdebug\033[0m\0"

    .def    main;   .scl    2;  .type   32; .endef
main:
    /* initialisation du main */
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $48, %rsp
    movl    %ecx, 16(%rbp) /* int argc */
    movq    %rdx, 24(%rbp) /* char **argv */
    movq    %r8, 32(%rbp) /* char **env */

    /* saut de ligne */
    /* write init */
    movl    $1, %r8d /* write size */
    movl    $1, %ecx /* sortie standart */
    leaq    .LC0(%rip), %rdx
    /* write */
    call    write

    /* debut du code */
    movl    $-1, -8(%rbp) /* y = -1 */
    jmp .Loop_1_condition

.Loop_1_body:
    movl    $-1, -4(%rbp)
    jmp .Loop_2_condition

.Loop_2_body:
    /* affiche le charactere */
    movl    $1, %r8d
    movl    $1, %ecx
    call    write

.Loop_2_condition:
    addl    $1, -4(%rbp) /* x = -1 */
    movl    -8(%rbp), %eax        /* get y index */
    cltq
    movq    32(%rbp), %rbx        /* get envp (pointer to element 0 of char * array) */
    movq    (%rbx,%rax,8), %rdx   /* get pointer at envp+y*8
                                     pointers are 8 bytes wide */

    movl    -4(%rbp), %eax        /* get x */
    cltq
    leaq    (%rdx, %rax), %rdx    /* Get current character's address */
    cmpb    $0, (%rdx)            /* Compare current byte to char 0
                                     using cmpq will compare the next 8 bytes */
    jne .Loop_2_body

    /* saut de ligne */
    movl    $1, %r8d /* write size */
    movl    $1, %ecx /* sortie standart */
    leaq    .LC0(%rip), %rdx
    call    write

.Loop_1_condition:
    addl    $1, -8(%rbp) /* ++y */
    movl    -8(%rbp), %eax
    cltq /* passe eax en 64bits */
    movq    32(%rbp), %rbx        /* get envp (pointer to element 0 of char * array) */
    movq    (%rbx,%rax,8), %rax   /* get pointer at envp+y*8
                                     pointers are 8 bytes wide */
    cmpq    $0, %rax              /* Compare to NULL ptr */
    jne .Loop_1_body

    movl    $1, %r8d /* write size */
    movl    $1, %ecx /* sortie standart */
    leaq    .LC0(%rip), %rdx
    call    write

    /* fin du programme */
    movl    $0, %eax /* return (0) */
    addq    $48, %rsp
    popq    %rbp
    ret
    .def    write;  .scl    2;  .type   32; .endef

【讨论】:

  • 非常感谢!感谢您花时间更正我的代码!您解决了问题,并解释了问题所在以及原因……完美!爱你
  • 如果我解析的是 int 数组而不是 char 数组,我会更改:leaq (%rdx, %rax), %rdx, 为 : leaq (%rdx, %rax, 4), %rdx ?
  • @Hadrien:另一个观察结果。您似乎注意到了cltq 指令。该符号将 (EAX) 扩展到完整的 64 位 RAX 寄存器。这样做是因为您将 xy 声明为有符号整数。如果您使它们未签名,则生成的代码将不需要 cltq 。由于这种情况下的数组索引始终为正,您可以避免使用cltq。当你mov 一个值到 32 位寄存器(例如 EAX)时,默认情况下 64 位寄存器(RAX)的高 32 位将自动归零。
  • 感谢您提供此详细信息!但这在我的脑海中并不完全清楚。我将寻找更多文档以了解内存的工作原理以及位相互遵循的顺序。不过,感谢您的帮助和时间!
猜你喜欢
  • 2013-07-24
  • 1970-01-01
  • 1970-01-01
  • 2020-02-26
  • 1970-01-01
  • 2017-03-12
  • 2020-07-10
  • 1970-01-01
相关资源
最近更新 更多