Search code examples
assemblyx86gnu-assembleratt

access array through .data variable or through register


I have an array and I'm loading the base address of this array into the %esi register. I can access the first element in the array for instance like this: pushl 0(%esi), the second like this: pushl 4(%esi), and so on. But I get an segmentation fault if I'm replacing the number before (%esi) with a variable from the .data section:

.data
array: .long 1,2,3,4,5
k: .long 4
out: .string "out: %d\n"

.globl main
main:

  #load array-baseaddress into %esi
  leal  array, %esi

  # Will print 1
  pushl 0(%esi)
  pushl $out
  call printf

  # Will print 2
  pushl 4(%esi)
  pushl $out
  call printf

  # Will result in segmentation fault
  pushl k(%esi)
  pushl $out
  call printf

  call exit

Can somebody explain to me why this is not working and if it's possible to access an element of an array in such a manner?


Solution

  • First of all you have this at the top of your function main:

    main:
      # Will print 1
      pushl 0(%esi)
      pushl $out
      call printf
    

    The first issue is that you use indexed addressing (with displacement) with the value in ESI as the address. So you have the equivalent of the value at memory address ESI+0 . The problem is that you don't initialize ESI . You should be initializing it with the address of array . So change the code to:

    main:
      mov $array, %esi    /* initialize ESI with address of array. */
    
      # Will print 1
      pushl 0(%esi)
      pushl $out
      call printf
    

    In the last section of code you do:

    # Will result in segmentation fault
    pushl k(%esi)
    pushl $out
    call printf
    

    You are looking to use the value in variable k as a displacement when you do k(%esi) . Unfortunately indexed addressing (with displacement) only supports a displacement as a constant. In your case k(%esi) is taking the address of k and adding it to ESI and pushing the value at that address on the stack. That isn't what you want. You can't do this in one operation. You'll have to retrieve the value in variable k and put it in a free register and then use that register to compute the address using based indexed addressing mode. You can do that with code like this:

    movl   k, %eax         /* move 32-bit value in k to temporary register */
    pushl  (%esi, %eax)    /* Push 32-bit value @ memory location %esi+%eax to stack */
    pushl $out
    call printf
    

    The output would look like:

    out: 1
    out: 2
    out: 2
    

    From your code it is uncertain if k was holding an element number within the array or if it was just a byte offset in the array. If you were looking to access the kth element in the array (0 based) then you need to use a scaling factor on the based indexed addressing mode like this:

    movl   k, %eax         /* move 32-bit value in k to temporary register */
    pushl  (%esi, %eax, 4) /* Push 32-bit value @ memory location %esi+(%eax*4) to stack */
    pushl $out
    call printf
    

    This would print out the 4th element (0 based) in array or the value 5 in your case. The output would look like:

    out: 1
    out: 2
    out: 5