0

I'm implementing a linked list reversal in 64 bit assembly x86 for an assignment. Now, the node is a struct with three elements. First element is a union of int and char. Second element is a pointer to next node. Third element is an int, that holds a random value for testing.

We have a reverse function in C that takes reverse_list and passes in the head pointer and the offset to rdi and rsi respectively. The offset is the offset of the next node pointer from the beginning of the node (which I believe is 4 based on size of union).

So it's like

reverse_list(head, offset);

then that pops into

.intel_syntax noprefix

.text
.global reverse_list

reverse_list:
push rbx
mov rax, 0x0
mov rbx, rax
mov rcx, rax
mov rdx, rax
mov rax, [rdi]
cmp rax, 0
je null_ret
add rax, rsi
mov rbx, rax
while_start:
    #while (headptr)
cmp rbx, 0x0
je while_done
    #nextptr = (void *) (*((unsigned long *) headptr));
mov rcx, [rbx]
    # *(unsigned long *) headptr = (unsigned long) new_headptr;
mov [rbx], rdx
    #new_headptr = headptr
mov rdx, rbx
    #headptr = nextptr
mov rbx, rcx
jmp while_start

while_done:
mov rax, rdx
sub rax, rsi

null_ret:
pop rbx
ret

My question is why when I add offset (rsi) to rax, it adds the offset to the value inside of the memory as well (example, address and int inside both change from one operation) and then when I try to dereference the value, I'm not getting the next pointer, just a segmentation fault.

Avallauch
  • 65
  • 2
  • 11
  • (gdb) p $rbx $4 = 67977879068908 (gdb) p $rcx $5 = 0 – Avallauch Oct 21 '17 at 19:42
  • Taking your input from `[rsp + 8]` is obviously wrong – harold Oct 21 '17 at 19:46
  • Did you adjust stack offsets as well (e.g. rsp+0xC)? – Igor R. Oct 21 '17 at 19:46
  • How obviously? It works in 32 bit. The stack offsets should be the same shouldn't they? We're using the same node definition. – Avallauch Oct 21 '17 at 19:47
  • 2
    No, the first arguments are passed in registers in 64bit ABIs (so if these are pointers, they'll be in registers, if it's a struct maybe not, see the relevant ABI) and anyway the offset must be different (the `call` and the `push` both push 8 bytes now). `[rsp + 8]` is the return address. – harold Oct 21 '17 at 19:51
  • @Vallauch: no, because in 64 bit, 64 bit (8 byte) registers are pushed on the stack, so the offsets should be different too. – Rudy Velthuis Oct 21 '17 at 21:33
  • I added some more detail after working on this for a while. – Avallauch Oct 21 '17 at 23:56
  • 1
    Your asm is almost exactly identical to the code in the linked question, and has many of the same problems (e.g. checking `next + offset` for NULL instead of just `next`.) The label names and lots of other stuff is suspiciously similar, including the useless block at the top where you `mov rax, 0` and copy it to some other registers, then overwrite `rax` with something else. – Peter Cordes Oct 22 '17 at 02:20
  • Looks like a fellow student of mine honestly, the help and instructions we get on assignments don't explain the concepts very well. – Avallauch Oct 22 '17 at 04:00

0 Answers0