"分段错误",x86_64汇编,AT& T语法

时间:2016-04-15 08:44:37

标签: linux assembly x86-64 gas

我在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

3 个答案:

答案 0 :(得分:1)

int $0x80是32位ABI。在普通内核(使用IA32仿真编译)上,它可以在64位进程中使用,但是你不应该使用它,因为它只支持32位指针,而且某些结构具有不同的布局。

有关进行64位Linux系统调用的信息,请参阅标记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是601是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