表达式后的垃圾 `(0,1,1)'
junk `(0,1,1)' after expression
当我尝试 assemble 该程序时,我收到了以下错误消息:
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 [=11=]x66, %ah # 66 is the hexadecimal value for 'f'
movb [=11=]x6F, %al # 6F is the hexadecimal value for 'o'
movb [=11=]x6F, %bh # 6F is the hexadecimal value for 'o'
movb [=11=]x0A, %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 , %eax # 4 is the number for the write system call
movl , %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 [=11=]x80 # Wake up the kernel to run the write system call
# exit
movl , %eax # 1 is the number for the exit system call
movl [=11=], %ebx # Exit status code (echo $?)
int [=11=]x80 # 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
*/
问题在于您如何引用数组成员。改用这个:
movb %ah, array + 0
movb %al, array + 1
movb %bh, array + 2
movb %bl, array + 3
没有用于多维数组的 asm 语法,除非您自己使用宏构建它。或者,也许您通过在 (base, index, scale)
语法中用 0
替换未使用的寄存器来想出这个。
你可以做的是使用 expression involving a 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 5075174, arr+4 #, MEM[(void *)&arr + 4B]
ret
.bss
.align 32
arr:
.zero 100
因此,arr+4
是语法。 我们可以编写 movl $const, arr+4(%eax)
来执行类似于 C 表达式 array[4 + i]
的操作。请参阅 for a complete list of x86 addressing modes (mostly NASM / MASM syntax, but what really matters is what's encodable in machine code.) See also the x86 标签 wiki。
还要注意 gcc 如何 puts uninitialized arrays in the .bss
(而不是 .data
或 .rodata
)。这意味着您的可执行文件中没有一堆零字节。除了切换到带有 .bss
的部分,您还可以在任何地方使用 .comm array 100
来声明 array
并在 bss 中为其保留 100 个字节。只使用 .bss
可能不会那么令人困惑
那个直接常数当然是 0x0a6f6f66
,我们的字符串。 gcc 巧妙地将 memcpy 优化为单个 4 字节的立即存储,因为它没有用处值(s)之后仍然在寄存器中。请记住,x86 是小端字节序的,因此 0x66
字节进入 array+0
并且 0x0a
进入 array+3
。 (不过,gcc 在合并单独的狭窄存储方面很糟糕,而不是与 memcpy;请参阅神栓 link。clang 在这方面做得更好。)
在 NASM 语法中,您甚至可以用 "string" 作为整数常量来编写它。
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
来节省代码字节,然后使用简单的寄存器寻址模式。
当我尝试 assemble 该程序时,我收到了以下错误消息:
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 [=11=]x66, %ah # 66 is the hexadecimal value for 'f'
movb [=11=]x6F, %al # 6F is the hexadecimal value for 'o'
movb [=11=]x6F, %bh # 6F is the hexadecimal value for 'o'
movb [=11=]x0A, %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 , %eax # 4 is the number for the write system call
movl , %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 [=11=]x80 # Wake up the kernel to run the write system call
# exit
movl , %eax # 1 is the number for the exit system call
movl [=11=], %ebx # Exit status code (echo $?)
int [=11=]x80 # 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
*/
问题在于您如何引用数组成员。改用这个:
movb %ah, array + 0
movb %al, array + 1
movb %bh, array + 2
movb %bl, array + 3
没有用于多维数组的 asm 语法,除非您自己使用宏构建它。或者,也许您通过在 (base, index, scale)
语法中用 0
替换未使用的寄存器来想出这个。
你可以做的是使用 expression involving a 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 5075174, arr+4 #, MEM[(void *)&arr + 4B]
ret
.bss
.align 32
arr:
.zero 100
因此,arr+4
是语法。 我们可以编写 movl $const, arr+4(%eax)
来执行类似于 C 表达式 array[4 + i]
的操作。请参阅
还要注意 gcc 如何 puts uninitialized arrays in the .bss
(而不是 .data
或 .rodata
)。这意味着您的可执行文件中没有一堆零字节。除了切换到带有 .bss
的部分,您还可以在任何地方使用 .comm array 100
来声明 array
并在 bss 中为其保留 100 个字节。只使用 .bss
那个直接常数当然是 0x0a6f6f66
,我们的字符串。 gcc 巧妙地将 memcpy 优化为单个 4 字节的立即存储,因为它没有用处值(s)之后仍然在寄存器中。请记住,x86 是小端字节序的,因此 0x66
字节进入 array+0
并且 0x0a
进入 array+3
。 (不过,gcc 在合并单独的狭窄存储方面很糟糕,而不是与 memcpy;请参阅神栓 link。clang 在这方面做得更好。)
在 NASM 语法中,您甚至可以用 "string" 作为整数常量来编写它。
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
来节省代码字节,然后使用简单的寄存器寻址模式。