如何从x86程序集中的单个链接列表中删除所有节点

时间:2016-08-31 04:50:30

标签: assembly linked-list x86

目前我获得了一个链接列表,只有一个下一个指针但不是一个prev指针。从名单上走下来,相对容易。

    movl NEXT(%ebx), %ebx # ebx is the current node

但是,当尝试从头到尾逐个删除列表时,我似乎无法制定解决方案。目前,我已经想到了1.保持上一次保存。

    subl $4, %esp # reserve space to put in the prev pointer
    movl %ebx, -4(%ebp) # store current pointer into prev pointer 
    movl NEXT(%ebx), %ebx # walk down the list

然后在

    movl -4(%ebp), %edx # putting the prev pointer into edx
    pushl (%edx) # two pushes to give parameters for func call
    pushl (%ebx)
    call remove_func

并在删除功能

    movl 12(%ebp), %edx # store prev pointer to edx
    movl 8(%ebp), %ebx # store current pointer aka node to be deleted at ebx
    movl NEXT(%ebx), %eax # temporarily store curr->next to eax
    movl %eax, NEXT(%edx) # prev->next=eax
    movl $0, (%ebx) # curr=NULL
    pushl %ebx # pushes params for func call
    call free_node

你有一般的想法,我在这里尝试做什么。当您正在使用NEXT走下列表时,基本上将prev指针推到堆栈上。但是,通过这种实现,我仍然不确定它是否会起作用,因为我不知道它是否可以递归到顶部或者它会在第一次删除时停止。我的第二种方法是将prev指针存储在寄存器中而不会压入堆栈。说%edi

    movl %ebx,%edi
    movl NEXT(%ebx), %ebx
    call remove_func

感谢您的帮助。很抱歉这个问题很长。 编辑:列表需要以相反的顺序删除。这是我试图实施的经典导弹指挥游戏。

1 个答案:

答案 0 :(得分:1)

我不确定你遇到了什么问题,但我有一个有趣的想法来实现这个,它利用调用堆栈作为堆栈数据结构,同时向前遍历,并使用它进行arg传递回来的路上,无需在任何地方复制任何东西。

假设:在释放列表节点时不需要修改它们。这是一个free-everything函数,所以我们可以设置HEAD = NULL,因此链接列表的任何其他用户(例如在信号处理程序或其他线程中)将看到它为空,而我们仍在通过释放它们的节点。

# gas AT&T syntax, x86 32-bit, SysV calling convention
# untested
.globl list_free
list_free:       # void list_free(struct node **phead)
    mov    4(%esp), %eax     # phead:   node**
    mov    (%eax), %edx      # p=head:  node*  (points to the first node)
    # p (pointer to old head) in %edx
    test   %edx,%edx            # check that head wasn't NULL to start
    jz     .Llist_was_empty     # free(NULL) is safe, but we would also try to dereference

    xor    %ecx,%ecx         # count = 0
    mov    %ecx, (%eax)      # *phead=NULL   (chop the list off at the head)

# loop over the list, pushing the address of each node on the stack
.Lforward_loop               # do {
    push   %edx              #  push p
    mov    NEXT(%edx), %edx  #  p=p->next
    inc    %ecx              #  count++
    test   %edx,%edx
    jnz   .Lforward_loop     # } while(p)

# walk back up the stack, using each pointer as an arg to free()
.Lbackward_loop              # do {
    call   free              #   free takes one arg, which is already on the stack
    add    $4, %esp          #   or just pop %edx, which will run faster on Intel CPUs: doesn't force a stack-sync uop
    dec    %ecx              # } while(--count);
    jnz    .Lbackward_loop

.Llist_was_empty:
    ret

我故意没有“保持简单”或其他任何东西,因为IDK实际上需要什么样的帮助。在考虑了你的问题之后,我基本上是为了自己的娱乐而写的。指令越少,理解的越少。 :)

我只需要三个寄存器,所以我没有保存/恢复任何寄存器。而且我不需要任何本地人,所以我也没有用ebp制作堆栈帧。

在64位代码中(AMD64 SysV调用约定,其中第一个arg进入%rdi),向后循环将是:

.Lbackward_loop              # do {
    pop    %rdi
    call   free              #   free(p)
    dec    %ecx              # } while(--count);
    jnz    .Lbackward_loop