当我尝试组装程序时,我收到一堆以下错误消息:
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
*/
答案 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语法,但真正重要的是机器代码中可编码的内容。)另请参阅x86标记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+0
,0x0a
进入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
然后使用简单的寄存器来保存代码字节寻址方式。