4

I am trying to learn NASM on my 64-bit Macbook Pro. I have the following code where I am trying to assign the value of a variable to an initialised variable.

global start
default rel

section .data
a:      dq      1

section .bss

b:      resq    1

section .text

    start:
        mov rax, a
        mov [b], rax

The code compiles and links but produces a bus error when run. Does anyone have any ideas on how to overcome this?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
soarjay
  • 641
  • 4
  • 17
  • 7
    You need to end the program properly. Also, learn to use a debugger. – Jester Jun 19 '16 at 22:39
  • Thanks for the answer. Can you recommend a debugger? Sorry for thee noob question, assembly is new to me. – soarjay Jun 19 '16 at 22:46
  • 3
    Just use `lldb` that comes as part of OS X. – l'L'l Jun 19 '16 at 22:55
  • 2
    On OS/X you should look at `lldb` as a debugger (at least to start with). You may also want to read about [OS/X 64-bit syscalls](https://filippo.io/making-system-calls-from-assembly-in-mac-os-x/) . From that info you'd learn that to exit your program you'd do something like `mov eax, 0x2000001` `xor edi, edi ; Return exit value 0 to system` `syscall` . As well this line `mov rax, a` should probably be `mov rax, [a]` since I think you intend to move the quadword value at memory address `a` rather than its address. – Michael Petch Jun 19 '16 at 22:55
  • 1
    The `syscall` did the trick and I'll definitely look into `lldb` – soarjay Jun 19 '16 at 23:04
  • 3
    As an aside, when you see `BUS ERROR` that generally means you are trying to access a memory address outside the range available to your program within the system's reserved memory. There are any number of ways you can cause this to happen (as Michael's answer explains the result of failing to exit). You will also routinely see this with indirection errors (e.g. if you attempt to use the value of a pointer as its address, etc..) – David C. Rankin Jun 20 '16 at 02:15

1 Answers1

9

To answer the specific question about the BUS ERROR, it occurs because you haven't properly exited your application and the processor started executing what was in memory after the last instruction in your code. That eventually lead to a fault. Likely the BUS ERROR occurred once the processor reached the end of the executable page containing your code and started executing the .data section. .data section is non-executable so likely caused the error you observed. This is just an educated guess as it is highly dependent on the contents and layout of memory.

It appears you are bypassing the C runtime, so you can't use RET to return back to the OS. You'll need to invoke one of the 64-bit OS/X SYSCALLs.

A list of the 64-bit OS/X System Calls can be found on Apple's site. You can learn the basics from this tutorial (in the 64-bit section). The exit system call has an entry:

1    AUE_EXIT   ALL   { void exit(int rval); } 

From the tutorial, the parameter passing convention is described as:

  • arguments are passed on the registers rdi, rsi, rdx, r10, r8 and r9 syscall number in the rax register
  • the call is done via the syscall instruction
  • what OS X contributes to the mix is that you have to add 0x20000000 to the syscall number (still have to figure out why)

The complete calling convention is described in the 64-bit System V ABI. One other important note about SYSCALLs is:

  • A system-call is done via the syscall instruction. The kernel destroys registers %rcx and %r11.

With all this in mind we want to call 1 AUE_EXIT ALL { void exit(int rval); } . The system call number is in the first column. On 64-bit OS/X we add 0x2000000 to it and pass it in RAX. The exit system call takes one parameter, so it is passed in RDI. This is the exit value. Code that would use the exit system call and return 0 could look like this:

mov eax, 0x2000001 
xor edi, edi ; Return exit value 0 to system 
syscall

Debugging

As @paulsm4 correctly pointed out in his deleted answer:

Finally, I'm not sure where your "bus error" is coming from. But a debugger would tell you

To find SIGBUS and SIGSEGV errors it is best to use a debugger to step through the assembly instructions and find where the failure is at. In this case you would have discovered that an unexpected instruction was being called after mov [b], rax.

The most common command line debugger available on OS/X is LLDB. You can find more information on it usage in the LLDB tutorial.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • When does OS X produce a SIGSEGV instead of a SIGBUS? In my experience (Linux + Solaris), SIGBUS happens for unaligned memory accesses on SPARC Solaris. x86 Linux delivers SIGSEGV for pretty much everything (AFAIK), including unaligned `movdqa`, or for jumping to an unmapped page. (The OP's code here most likely just faults on a load/store, e.g. on a `00 00 add BYTE PTR [rax],al` before execution reaches the end of the page containing `_start`) – Peter Cordes Jun 20 '16 at 03:32
  • 1
    I'd have to check all the scenarios, but it isn't just unaligned access on Intel that will generate it in OS/X. My observation is that since the OPs code loads the address of `a` into RAX(valid memory location), and likely all the memory in the code segment is 0 initialized it would likely be the equivalent of continuous `add [rax], al` . Given that the data section will likely be after .text, the Bus Error likely occurs when an instruction straddles the executable/non-executable memory or is the first instruction in the data area. As I said, I am making an educated guess on this. – Michael Petch Jun 20 '16 at 03:50
  • I have a hunch executing code in a non-executable memory page is one of the scenarios where BSD raises SIGBUS rather than SIGSEGV, which in past experience differed from what Linux raises. – Michael Petch Jun 20 '16 at 04:03
  • Oh yeah, `rax` has a valid address here, so it could go either way. – Peter Cordes Jun 20 '16 at 04:07
  • So it's not always SIGBUS? Other kinds of faults can produce SIGSEGV? Presumably that tells you something, but I don't need to know the details. I was just interested that it uses both signals for different things. – Peter Cordes Jun 20 '16 at 04:09
  • @PeterCordes : I'd expect that had RAX been an invalid address, that the first instruction after the OPs code (add [rax], al) would have raised a SIGSEGV . – Michael Petch Jun 20 '16 at 04:09
  • 1
    @PeterCordes I had a chance this morning to confirm that executing code in a non-executable memory page on Intel OS/X generates a SIGBUS instead of SIGSEGV and that is what occurred here. As well this _C_ code also raises SIGBUS: `static unsigned char a = 0xc3; int main() { ((void (*)())&a)(); }` . Yes, it is undefined behavior but the debugger also shows that the mere attempt to execute the encoded _RETQ_ (0xc3) instruction raised SIGBUS. On Linux the _RETQ_ raised SIGSEGV. – Michael Petch Jun 20 '16 at 12:34