垃圾`(0,1,1)'表达后

时间:2016-06-03 21:36:59

标签: gcc assembly gas

当我尝试组装程序时,我收到一堆以下错误消息:

misha@hp-laptop:~/test$ as -gstabs test.s -o test.o && ld test.o -o a.out && rm test.o && ./a.out
test.s: Assembler messages:
test.s:19: Error: junk `(0,0,1)' after expression
test.s:20: Error: junk `(0,1,1)' after expression
test.s:21: Error: junk `(0,2,1)' after expression
test.s:22: Error: junk `(0,3,1)' after expression

任何人都可以告诉我,我的程序运行错误到底是什么错误吗?显然,这与我尝试访问每个长度为一个字节的数组元素的方式有关。这是程序本身:

/******************************************************************************
 *                                                                            *
 * This program prints the string "foo" on the console.                       *
 *                                                                            *
 ******************************************************************************/

.section .data
    array: .byte 0x00, 0x00, 0x00, 0x00  # An array of four bytes
    size:  .int  4                       # The size of the array


.section .text
.globl _start
_start:
    movb   $0x66, %ah   # 66 is the hexadecimal value for 'f'
    movb   $0x6F, %al   # 6F is the hexadecimal value for 'o'
    movb   $0x6F, %bh   # 6F is the hexadecimal value for 'o'
    movb   $0x0A, %bl   # A is the hexadecimal value for '\n'
    movb   %ah, array(0, 0, 1)
    movb   %al, array(0, 1, 1)
    movb   %bh, array(0, 2, 1)
    movb   %bl, array(0, 3, 1)

    # print
    movl   $4, %eax       # 4 is the number for the write system call
    movl   $1, %ebx       # The file descriptor to write to (1 - STDOUT)
    movl   $array, %ecx   # The starting address of the string to print
    movl   size, %edx     # The number of bytes to print
    int    $0x80          # Wake up the kernel to run the write system call

    # exit
    movl   $1, %eax       # 1 is the number for the exit system call
    movl   $0, %ebx       # Exit status code (echo $?)
    int    $0x80          # Wake up the kernel to run the exit system call

/*

Compile and run:

as -gstabs test.s -o test.o && \
ld test.o -o a.out && \
rm test.o && \
./a.out

*/

2 个答案:

答案 0 :(得分:1)

问题在于你如何引用数组成员。请改用:

movb   %ah, array + 0
movb   %al, array + 1
movb   %bh, array + 2 
movb   %bl, array + 3

答案 1 :(得分:0)

多维数组没有asm语法,除非你自己用宏构建它。或者,您可以通过0语法中的(base, index, scale)替换未使用的寄存器来实现这一点。

您可以做的是使用expression label来{{}}}来取消movb $constant, array + 4的偏移量。

查看编译器输出通常是学习如何在asm中执行操作的好方法,从语法基础知识到巧妙的优化技巧。在Godbolt compiler explorer

#include <string.h>
char arr[100];   // uninitialized global array
void copy_string(){ memcpy(&arr[4], "foo\n", 4); }

    // -O3 -fverbose-asm output:
    movl    $175075174, arr+4       #, MEM[(void *)&arr + 4B]
    ret

    .bss
    .align 32
arr:
    .zero   100

因此,arr+4是语法。我们可以编写movl $const, arr+4(%eax)来执行类似C表达式array[4 + i]的操作。有关x86寻址模式的完整列表,请参阅this answer(主要是NASM / MASM语法,但真正重要的是机器代码中可编码的内容。)另请参阅标记wiki。

另请注意gcc puts uninitialized arrays in the .bss(而不是.data.rodata)。这意味着您的可执行文件中没有一堆零字节。您可以在任何地方使用.bss来声明.comm array 100并在bss中为其保留100个字节,而不是切换到array部分。使用.bss

可能不那么容易混淆

那个直接常数当然是0x0a6f6f66,我们的字符串。 gcc巧妙地将memcpy优化为一个4字节的立即存储,因为它之后仍然没有使用值。请记住,x86是little-endian,因此0x66字节进入array+00x0a进入array+3。 (gcc很难合并除memcpy之外的其他狭窄商店;请参阅godbolt链接.clang在这方面更好。)

在NASM语法中,您甚至可以使用“字符串”作为整数常量来编写它。

mov    dword [array+off], `foo\n`    ;backquote for C-style \-escapes, unlike '' or ""

GNU as不允许这样做,除非有一些比十六进制常量+注释更难读的内容:

movl $('f' | 'o'<<8 | 'o'<<16 | '\n'<<24), array

GNU as语法对于手写的asm和NASM / YASM不太友好,但在某些方面它很好。 (%reg等等可以很容易地看到什么是注册名称,什么不是。)

说到立即:你的size = 4应该是一个直接的常数,而不是一个负载

##  size:  .int  4   # Don't need this stored in memory anywhere
.equ size, 4
...

movl   $array, %ecx   # The starting address of the string to print
movl   $size, %edx    # The number of bytes to print

另请注意,movl $constant, (%ecx)编码的字节数少于movl $constant, array,因此您可以通过将$array更快地转换为%ecx然后使用简单的寄存器来保存代码字节寻址方式。