Author Topic: Segmentation fault problems  (Read 7127 times)

MAugustyniak

  • Guest
Segmentation fault problems
« on: November 11, 2005, 03:48:45 AM »
Sitch:
I;m trying to create a homework program where I determine the product between an array of word-sized entries and a word-sized scalar multiple. The result must be stored inside a double word=sized array.

I get segmentation faults at lines  23 and 38.

Cany anyone help me out with this?

segment .data
   addr_x dd   0,0,0,0,0,0   ; The X array
   addr_y dw    1,2,3,4,5,6   ; The Y array
   a dd   2         ; the scalar multiplier
   size db   6         ; the number of X and Y array elements to multiply by a
   msg1 dw   0xA,'The value of the array contents are: ',0xA
   len1 equ $-msg1

segment .text
   global _start

_start:
   ;; so first I set up the addresses
   mov esi,[addr_y]      ; the source is the address of y
   mov edi,[addr_x]      ; the destination is the address of x
   mov ecx,[size]      ; initializes the count to 6, or whatever the size of the array should be
   mov bx,[a]

mult_by_scalar:
   mov ax,[esi]      ; segmentation fault, it's not picking up my addr_y or addr_x
   mul bx         ; then we multiply ax by the value at a, the product is stored as dx:ax
   ;; Now we have to move our result from dx:ax into our X vector, this
   ;; will have to be done in two steps:
   ;; 1)we shift the lower 16 bits of edx into the higher bits
   ;; hence we move dx into the extended part of edx, we thus move the
   ;; half of our product aY (at the current index of Y)
   ;; 2)we move ax, which contains the lower 16 bits of our product into
   ;; dx, which is filled with 0s as a result of step 1, hence we join
   ;; the product of a*Y in one register edx instead of having
   ;; it concatenated into two 16 bit registers dx:ax
shl edx,16         ; we move dx into the extended part of edx not the problem
mov dx,ax         ; and we move the remaining part of the result iinto ax so that if follows
            ; the first 16 bits

mov [edi],edx      ; segmentation fault, a segmentation fault occurs when you attempt to access an unwritten
            ; memory location
   add esi,2
   add edi,4
loop   mult_by_scalar   ;decrements esi until it reaches 0 and then it             ;craps out

;; So now we output the result
   ;;       mov esi,addr _x
   ;;    mov ecx,0
   ;;    output:   mov eax,4
   ;;    mov ebx,1
   ;;    mov esi,ecx
   ;;    push ecx
   ;;    mov ecx,addr_x
   ;;    add ecx,esi
   ;;    mov edx,4
   ;;    int 0x80
   ;;    pop ecx
   ;;    inc ecx
   ;;    cmp ecx,[size]
   ;;    jl output


exit:
   mov eax,1
   mov ebx,0
   int 0x80

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: Segmentation fault problems
« Reply #1 on: November 11, 2005, 06:43:05 AM »
Hi maugustyniak,

Can try!

> segment .data
>    addr_x dd   0,0,0,0,0,0   ; The X array
>    addr_y dw    1,2,3,4,5,6   ; The Y array
>    a dd   2         ; the scalar multiplier
>    size db   6         ; the number of X and Y array elements to multiply by a

I'd make this "dd", too.

>    msg1 dw   0xA,'The value of the array contents are: ',0xA

This wants to be "db", not "dw".

>    len1 equ $-msg1
>
> segment .text
>    global _start
>
> _start:
>    ;; so first I set up the addresses
>    mov esi,[addr_y]      ; the source is the address of y

This is the root of the segfault! Remember that the "[]"s indicate *contents* of memory in Nasm syntax! To get the address, lose the "[]"!!!

>    mov edi,[addr_x]      ; the destination is the address of x

Likewise.

>    mov ecx,[size]      ; initializes the count to 6, or whatever the size of the array
> should be

Here you *do* want "[contents]", but you're moving a dword into ecx (the size of the register determines the size of the operation, in this case), but you've defined "size" as "db" - just a byte. So you're getting three bytes of whatever garbage comes after "size" into ecx. If this happens to be zero, all works as intended. If not, you loop for far longer than intended. This *could* even cause a segfault right here, if "size" happened to be the very last byte in memory you "own" (unlikely). You could "mov cl, [size]" - would work if the upper bytes of ecx were zero - you could "xor ecx, ecx" first, to be sure this is so. You could "movzx ecx, byte [size]" with the same effect. The easiest(?) thing is probably to define "size" as dword, even though the value will fit in a byte. You could not use the "loop" instruction - "dec cl"/"jnz mult_by_scalar", using only cl and not worry about the rest of ecx at all. "Programmer's Choice".

>    mov bx,[a]

You've defined "a" (nice "meaningful" variable name :) as "dd". This won't hurt, but it "wastes" the two bytes after 02 00. It would be more "right" to define "a" as "dw", or use ebx here. You can load ebx here and still "mul bx" (with correct results in this case). Since the assignment apparently specifies that this is "word" (16 bits), the "right" thing is probably to define "a" as "dw".

> mult_by_scalar:
>    mov ax,[esi]      ; segmentation fault, it's not picking up my addr_y or addr_x   

... caused by the wrong thing in esi, above. Although the segfault occurs here, this code is fine. The error is where you load esi.

>    mul bx         ; then we multiply ax by the value at a, the product is stored as
> dx:ax
>    ;; Now we have to move our result from dx:ax into our X vector, this
>    ;; will have to be done in two steps:
>    ;; 1)we shift the lower 16 bits of edx into the higher bits
>    ;; hence we move dx into the extended part of edx, we thus move the
>    ;; half of our product aY (at the current index of Y)
>    ;; 2)we move ax, which contains the lower 16 bits of our product into
>    ;; dx, which is filled with 0s as a result of step 1, hence we join
>    ;; the product of a*Y in one register edx instead of having
>    ;; it concatenated into two 16 bit registers dx:ax
> shl edx,16         ; we move dx into the extended part of edx not the problem
> mov dx,ax         ; and we move the remaining part of the result iinto ax so that
> if follows
>             ; the first 16 bits
>
>    mov [edi],edx      ; segmentation fault, a segmentation fault occurs when you attempt
> to access an unwritten
>             ; memory location

If we had the address in edi (no "[]" when you load edi above), this would work. Instead of getting dx:ax into a single register, you could also have done "mov [edi], ax"/"mov [edi + 2], dx" - does the same thing.

>    add esi,2
>    add edi,4
> loop   mult_by_scalar   ;decrements esi until it reaches 0 and then it             ;craps
> out

"Comment does not match code." - you mean ecx, not esi, of course.

>    ;; So now we output the result
>    ;;       mov esi,addr _x
>    ;;    mov ecx,0
>    ;;    output:   mov eax,4
>    ;;    mov ebx,1
>    ;;    mov esi,ecx
>    ;;    push ecx
>    ;;    mov ecx,addr_x
>    ;;    add ecx,esi
>    ;;    mov edx,4
>    ;;    int 0x80
>    ;;    pop ecx
>    ;;    inc ecx
>    ;;    cmp ecx,[size]
>    ;;    jl output

Nice try, but this isn't going to work. What you've got in your "addr_x" buffer is a number - a dword, so yeah, it takes up four bytes. But what we want to print is a series of ascii characters representing that number (presumably, as decimal). It could take as many as 10 (or only one) characters to print a dword.

To print a single (decimal) digit, we need to add 30h, or 48 decimal, or '0' to the number, to convert it to the right ascii character ("numeral", if you will).

To print a number that takes more than one digit to represent, we need to separate out the digits, and convert 'em to ascii one at a time. The "div" instruction - by ten, for decimal representation - will give us the quotient in eax, and the remainder in edx. Hang on to the quotient, so we can "div" it again - if we keep doing this until the quotient is zero, the remainders are the digits we want to print (after converting 'em to ascii!), but in the "wrong order". So, as we accumulate remainders in edx, we want to convert 'em to ascii and store 'em starting at the "end" of the buffer, and work toward the "front". (or you can push 'em on the stack, and pop 'em off in the right order to print). There's a "gotcha" with the "div" instruction - it divides edx:eax by what you say (a dword), not just eax. If we fail to make edx zero before the "div", we'll get wrong results, or cause an exception!

I've posted routines to do this "display a number" trick several times. Typically, what I post is intended as a "throwaway" routine - for debugging purposes mostly. It's "good" for that, since it doesn't require an explicitly allocated buffer, it uses the stack. A more flexible routine would be like "itoa()", and would take a buffer (address) as a parameter, as well as the number to convert. Then you can print the buffer when and how you like. A nice enhancement might be to "right justify" the numbers, so they'll print with the columns lined up, no matter how many digits... It would make the output of your program look nicer ("just like C" :)

> exit:
>    mov eax,1
>    mov ebx,0
>    int 0x80

Good! :)

Best,
Frank