我在64位Linux环境中运行我的代码,其中Linux内核是使用 IA32_EMULATION 和 X86_X32 禁用构建的。
在书Programming from the Ground Up中,第一个程序除了产生段错误之外什么都不做:
.section .data
.section .text
.globl _start
_start:
movl $1, %eax
movl $0, %ebx
int $0x80
我将代码转换为使用x86-64指令,但它也是段错误:
.section .data
.section .text
.globl _start
_start:
movq $1, %rax
movq $0, %rbx
int $0x80
我组装了这两个程序:
as exit.s -o exit.o
ld exit.o -o exit
正在运行./exit
会为{both} Segmentation fault
提供.section .data
.section .text
.globl _start
_start:
movq $60, %rax
xor %rbx, %rbx
syscall
。我做错了什么?
P.S。我看过很多用 gcc 组装代码的教程,但是我想使用 gas 。
结合评论和答案,这里是代码的最终版本:
Function GetSheetWithCodename(ByVal worksheetCodename As String, Optional wb As Workbook) As Worksheet
Dim iSheet As Long
If wb Is Nothing Then Set wb = ThisWorkbook ' mimics the default behaviour
For iSheet = 1 To wb.Worksheets.Count
If wb.Worksheets(iSheet).CodeName = worksheetCodename Then
Set GetSheetWithCodename = wb.Worksheets(iSheet)
Exit Function
End If
Next iSheet
End Function
答案 0 :(得分:1)
int $0x80
是32位ABI。在普通内核(使用IA32仿真编译)上,它可以在64位进程中使用,但是你不应该使用它,因为它只支持32位指针,而且某些结构具有不同的布局。
有关进行64位Linux系统调用的信息,请参阅x86标记wiki。 (也是ZX485对这个问题的回答)。存在许多差异,包括syscall
指令符号%rcx
和%r11
这一事实,与int $0x80
ABI不同。
在没有IA32仿真的内核中,与您的一样,运行int $0x80
可能与运行任何其他无效软件中断相同,例如int $0x79
。单步执行gdb中的指令(在我的64位4.2内核上,包括IA32仿真)会导致该指令出现段错误。
它不会返回并继续执行垃圾字节作为指令(这也会导致SIGSEGV或SIGILL),或者继续执行直到它跳转到(或正常到达)未映射的页面。如果确实如此,那将是segfaulting的机制。
您可以在strace
下运行流程,例如strace /bin/true --version
以确保它正在进行您认为可以进行的系统调用。您还可以使用gdb
查看程序段错误的位置。 使用调试器是必不可少的,比大多数语言更多,因为asm中的失败模式通常只是一个段错误。
答案 1 :(得分:1)
The first observation is that the code in both your examples effectively do the same thing, but are encoded differently. The site x86-64.org has some good information for those starting out with x86-64 development. The first code snippet that uses 32-bit registers is equivalent to the second because of Implicit Zero Extend:
Implicit zero extend
Results of 32-bit operations are implicitly zero extended to 64-bit values. This differs from 16 and 8 bit operations, that don't affect the upper part of registers. This can be used for code size optimisations in some cases, such as:
movl $1, %eax # one byte shorter movq $1, %rax xorq %rax, %rax # three byte equivalent of mov $0,%rax andl $5, %eax # equivalent for andq $5, %eax
The question is, why does this code segfault? If you had run this code on a typical x86-64 Linux distro your code may have exited as expected without generating a segfault
. The reason that your code is failing is because you are using a custom kernel with IA32 emulation off.
IA32 emulation in the Linux kernel does allow you to use the 32-bit int 0x80
interrupt to make calls using the traditional 32-bit system call mechanism. This is an emulation layer, and doesn't support passing pointers that can't be represented in a 32-bit register. This is the case for stack based pointers since they fall outside the 4gb address space, and can't be accessed with 32-bit pointers.
Your system has IA32 emulation off, and because of that int 0x80
doesn't exist for backwards compatibility. The result is that the int 0x80
interrupt will throw a segmentation fault and your application will fail.
In x86-64 code it is preferred that you use the syscall
instruction to make system calls to the 64-bit Linux kernel. This mechanism supports 64-bit operands and pointers where necessary. Ryan Chapman's site has some good information on the 64-bit SYSCALL interface which differs considerably from the 32-bit int 0x80
mechanism.
Your code could have been written this way to work in a 64-bit environment without IA32 emulation:
.section .text
.globl _start
_start:
mov $60, %eax
xor %ebx, %ebx
syscall
Other useful information on doing 64-bit development can be found in the 64-bit System V ABI. This document also better describes the general syscall
convention used by the Linux kernel including side effects in Section A.2 . This document is also very informative if you also wish to interface with third party libraries and modules (like the C library etc).
答案 2 :(得分:0)
原因是Linux System Call Table for x86_64与x86的表不同。
在x86中,SYS_EXIT为1
。在x64中,SYS_EXIT是60
,1
是SYS_WRITE的值,如果调用它,则期望%RSI中的const char *buf
。如果该缓冲区指针无效,则可能是段错误。
%rax System call %rdi %rsi %rdx %r10 %r8 %r9
1 sys_write unsigned int fd const char *buf size_t
60 sys_exit int error_code