1

I'm trying to learn x86 assembly. The book I'm using is Assembly Language - Step by Step, Programming With Linux (and I'd have to say it's pretty good). I've learned a lot so far, but I feel as though I should also be challenging myself to stay ahead in many respects so I can learn faster through doing (I can do follow along, top-down learning, but I find it tediously slow).

So, I figured it would be a cool idea to try and multiply two registers (32-bit) and then output the data to the console.

The problem is that when I execute the program (I'm using NASM, just as the book does - no Insight debugger though), I receive a segmentation fault. I've done a fair amount of debugging in gdb with this little hammer out, but for whatever reason I can't seem to figure out what the issue is.

I'd like to know why I'm receiving a segmentation fault, and what a good way would be to reprimand the issue. Also, if the comments I've made in the code don't match up with what exactly is happening, I'd be grateful if anyone could correct me on that.

Here's my code so far (it's well commented)

Thanks.

teh codez

section .data
;TODO

section .bss
valueToPrint: resb 4            ;alloc 4 bytes of data in 'valueToPrint'

section .text

global _start

_mul:
    mov eax, 0x2A ;store 42 in eax
    mov edx, 0x2A ;store 42 in edx
    mul eax
    ret

_safe_exit:
    mov eax, 1  ;initiate 'exit' syscall
    mov ebx, 0  ;exit with error code 0
    int 0x80    ;invoke kernel to do its bidding 

_start:
    nop                         ;used to keep gdb from complaining

    call _mul                       ;multiply the values
    mov [valueToPrint], eax         ;store address of eax in the contents of valueToPrint
    mov eax, 4                      ;specify a system write call - aka syswrite
    mov ebx, 1                      ;direction used to make the syswrite call output to console - i.e. stdout
    mov dword [ecx], valueToPrint   ;store valueToPrint in ecx: ecx represents the syswrite register
    int 0x80                        ;invoke kernel based on the given parameters

    call _safe_exit

Edit

Also, I'm running Arch Linux, if that makes a difference.

zeboidlund
  • 9,731
  • 31
  • 118
  • 180
  • in your _mul subroutine, you're using `mul eax`, which multiplies eax, with eax... since you're using 42 in both eax and edx, you're getting (well, you WOULD be getting) the right answer, but you're not involving the value in edx. @Adam Rosenfield (below) has the right answer though, you're not creating the TEXT/ASCII string to print in the first place. assembly does not automatically do anything for you really. Which is why you have so much control... but you have to tell it how to do everything. You're not creating the txt string to point ecx at to print. (you also need a length) – lornix Jun 29 '12 at 05:16
  • Also, `mul` (multiply) multiplies the value in eax (ax/rax,etc) with whatever's specified and puts the resultant 64-bit value into EDX:EAX... so if you're looking for the full answer, remember to also use the edx value too. 42*42=1764, so you're nowhere near the answer carrying into edx, but you still should be aware that's what happens so you can expect edx to change and be usable if needed. – lornix Jun 29 '12 at 05:19
  • 1
    (gosh I'm talkative tonight!) Little nit-picky stuff.... your '_safe_exit' subroutine... since it never returns, you should `jmp` to it instead. Yes, the system dumps everything when the program exits... but again, you should be aware of how things work. `call _save_exit` works great, but to me it leaves a value on the stack. Nit-picky stuff. My apologies. I started assembly on a 16K machine way back when... Memory was expensive and you wrote tight code. I still think that way, even with 8G of ram. Smaller runs faster generally. – lornix Jun 29 '12 at 05:22
  • Awesome, lornix. Thank you. How is it that I get eax to be multipled by edx? – zeboidlund Jun 29 '12 at 05:23
  • `mul edx`. My apologies on nit-picking... assembly is awesome, but you have to be able to juggle everything, since it really doesn't help you at all, and not knowing how things interact will bite you. – lornix Jun 29 '12 at 05:24
  • Thanks! And no worries, I actually appreciate the criticism. I'm here to learn, after all :) btw, if you're interested in further discussion in regards to low level programming, meet in here: http://chat.stackoverflow.com/rooms/13202/low-level-programming – zeboidlund Jun 29 '12 at 05:26

2 Answers2

3

This line is causing the segmentation fault:

mov dword [ecx], valueToPrint

You're telling it to store valueToPrint in the memory location at address ecx. You never initialize ecx (the kernel probably initializes it to 0 on program start for you), so when you dereference it, you're going to access an invalid memory location.

The write(2) system call takes 3 parameters: the file descriptor number in register ebx, a pointer to the string to write in ecx, and the number of bytes to write in edx. So, if you want to just print the raw binary data of the result, you can pass the address of valueToPrint, and tell it to print 4 bytes from that address. In this case, valueToPrint is 1764 (0x6e4 in hex), so this code would print out the 4 bytes e4 06 00 00 on x86, which is little-endian:

mov [valueToPrint], eax   ; store the result into memory
mov eax, 4                ; system call #4 = sys_write
mov ebx, 1                ; file descriptor 1 = stdout
mov ecx, valueToPrint     ; store *address* of valueToPrint into ecx
mov edx, 4                ; write out 4 bytes of data
int 0x80                  ; syscall
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • First off, I'd like to say thanks for the clarifications. I was curious, however, as to how hard it'd be to print the integer in base-10, exactly? – zeboidlund Jun 29 '12 at 05:17
  • To print it out in base 10, you'd need to reserve a character array large enough to hold the output (at least 11 bytes for a signed 32-bit integer). Then, repeatedly divide the number by 10 until you reach 0, saving the remained plus 48 into a character in the array (48 being ASCII `'0'`), going from right-to-left. Finally, pass the address of that buffer to `sys_write()`, and keep track of how many bytes you're printing. Or, if you want to skip all this, just call `printf(3)`. – Adam Rosenfield Jun 29 '12 at 19:13
1

EDITIT! (forgot a cmp!)

To output value in base 10...

; assuming value is in EAX (only)
.loop1:
    div   10      ; divide by 10, leave result in eax, REMAINDER in edx
    push  eax     ; save value
    mov   eax,edx
    or    eax,0x30 ; convert 0-9 to '0'-'9' (ascii 0x30='0')
    call  display_a_char ; (you write this!)
    pop   eax
    or    eax,eax ; set flags (edit!)
    jnz   .loop1
; all done, maybe put a \n return or something here
lornix
  • 1,946
  • 17
  • 14
  • this is off-the-cuff ... so it SHOULD work just fine, minor issues only – lornix Jun 29 '12 at 05:33
  • as an aside... change one line, add two lines... and this will convert the output to ANY base, 2-36 quite easily. Yay! – lornix Jun 29 '12 at 05:59