主要问题是您的 st 变量被定义为指向字符串文字的指针。
char* st= "E X A M P L E";
String literals 在C 语言中被视为只读。修改这样的字符串是未定义的行为。发生的事情是未知的,并且将特定于编译器及其运行的环境。当您在汇编代码中编写该内存时,您的环境会引发异常。在大多数使用现代编译器的现代操作系统上,字符串文字被放置在不可写的内存中,因此它会生成一个异常,这就是你的情况。
如果你想在可写内存中创建一个字符数组,你可以这样定义st:
char st[] = "E X A M P L E";
汇编代码问题
一个问题是您对函数swap 的索引是int。在 64 位 GCC/CLANG 中,int 是 32 位。如果您将 32 位签名的 int 传递给汇编代码,则前 32 位可能包含垃圾。鉴于您的索引永远不会是负数,您应该使用无符号类型,最好是 64 位的。我会推荐 size_t 类型。 size_t 在 x86-64 代码中将是无符号的且大小为 64 位,因此当传递给汇编代码时,您无需在使用它们之前将索引值符号/零扩展为 64 位。我建议将swap 更改为:
char* swap(char* array, size_t index1, size_t index2)
如果您将 index1 和 index2 保留为有符号整数 (int),则汇编代码的开头必须在 ESI 和 EDXMOVSX /em> 注册。该代码如下所示:
swap:
movsx rsi, esi ; Sign extend 32-bit index1 parm in ESI to 64-bits
movsx rdx, edx ; Sign extend 32-bit index2 parm in EDX to 64-bits
; rest of function here
如果您将 32 位 unsigned int 用于 index 和 index2,则必须将 32 位值扩展为零:
mov esi, esi ; Zero extend 32-bit index1 parm in ESI to 64-bits
mov edx, edx ; Zero extend 32-bit index2 parm in EDX to 64-bits
; rest of function here
当操作的目标是 64 位模式下的 32 位寄存器时,CPU 会自动将目标寄存器的高 32 位归零。将像 ESI 这样的 32 位寄存器移动到自身将清除 RSI 的高 32 位。这对所有通用寄存器都是一样的。
RBX、RBP 和 R12–R15 是根据x86-64 System V ABI 的非易失性寄存器。如果您的函数修改了它们,则必须保留它们的内容。您可以将它们压入堆栈并在完成后将它们的原始值从堆栈中弹出。首选方法是使用不需要保留的易失性寄存器之一,例如 R8-R11、RAX、RCX、RDX、RDI、RSI。
当您使用 64 位寄存器将数据移入/移出内存时,将传输 64 位(8 个字节)。举个例子:
mov r14,[rdi+rsi]
移动从内存地址[rdi+rsi]开始的8个字节,并将其移动到64位寄存器R14。稍后的写入会执行类似的操作,但会更新内存中的 8 个字节而不是 1 个字节。如果字符数组放在堆栈上,更新 8 个字节的数据可能会破坏堆栈,而您的代码和环境恰好就是这种情况。
当使用编号寄存器 R8 到 R15 时,您可以通过在寄存器名称末尾添加 b 后缀来引用低 8 位 (w用于 16 位字,d 用于 32 位双字)。 64 位模式的 NASM/YASM 语法中所有寄存器名称的 complete chart 是:
mov r14,[rdi+rsi] 将被写为 mov mov r14b,[rdi+rsi] 以移动单个字节。您还必须对其他每个动作进行更改。
假设您将index1 和index2 更改为size_t(或uin64_t)类型,您的汇编代码可以写成:
segment .text
global swap
swap:
push r14 ; Save non-volatile registers we overwrite
push r15
mov r14b,[rdi+rsi] ; Move one byte from [rdi+rsi] to R14B. R14B is lower 8 bits of R14
mov r15b,[rdi+rdx] ; Move one byte from [rdi+rdx] to R15B. R15B is lower 8 bits of R15
mov [rdi+rsi],r15b ; Move the byte in R15B to [rdi+rsi]
mov [rdi+rdx],r14b ; Move the byte in R14B to [rdi+rdx]
mov rax,rdi
pop r15 ; Restore non-volatile registers
pop r14
ret
如果您要使用其他易失性寄存器而不是非易失性寄存器,则代码可以简化为:
segment .text
global swap
swap:
mov al,[rdi+rsi] ; Move one byte from [rdi+rsi] to AL. AL is lower 8 bits of RAX
mov cl,[rdi+rdx] ; Move one byte from [rdi+rdx] to CL. CL is lower 8 bits of RCX
mov [rdi+rsi],cl ; Move the byte in CL to [rdi+rsi]
mov [rdi+rdx],al ; Move the byte in AL to [rdi+rdx]
mov rax,rdi
ret
在这种情况下,我们使用易失性寄存器的低 8 位 RAX(AL) 和 RCX(CL em>) 进行交换。由于我们不必保留这些寄存器,因此无需保存和恢复它们。