Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly Segfault在一个简单的malloc和free实现中_Assembly_Memory Management_X86_Malloc - Fatal编程技术网

Assembly Segfault在一个简单的malloc和free实现中

Assembly Segfault在一个简单的malloc和free实现中,assembly,memory-management,x86,malloc,Assembly,Memory Management,X86,Malloc,TLDR 我用一个自定义逻辑超越了malloc和free。然而,该程序是segfaulting。我有什么遗漏吗?最终,我能够用一个简单的malloc和free调用来测试它,但没有使用任何stdio.h特性。为什么会这样 完整版本 我使用LD_PRELOAD编写了一个32位汇编程序来覆盖malloc和free # PURPOSE: Program to replace malloc and free using LD_PRELOAD # # NOT

TLDR

我用一个自定义逻辑超越了
malloc
free
。然而,该程序是
segfault
ing。我有什么遗漏吗?最终,我能够用一个简单的
malloc
free
调用来测试它,但没有使用任何
stdio.h
特性。为什么会这样

完整版本

我使用
LD_PRELOAD
编写了一个32位汇编程序来覆盖
malloc
free

        # PURPOSE:  Program to replace malloc and free using LD_PRELOAD
        #
        # NOTES:    The programs using these routines will ask for a certain
        #           size of memory. We actually use more than that size, but we
        #           put it at the beginning, before the pointer we hand back.
        #           We add a Size field and an Available/Unavailable marker. So
        #           the memory looks like this:
        #
        #           #########################################################
        #           #Available Marker#Size of Memory#Actual Memory Locations#
        #           #########################################################
        #                                            ^Returned pointer points
        #                                             here
        #
        #           The pointer we return only points to the actual locations
        #           requested to make it easier for the calling program. It
        #           also allows us to change our structure without the calling
        #           program having to change at all.
        #
        #           The _alloc-lib-test.c program can test it:
        #           Dump the output and read it using hexdump.


        .section .data

# Global variables

# This points to the beginning of the memory we are managing

heap_begin:
        .long 0

# This points to one location past the memory we are managing

current_break:
        .long 0

# Structure information

        .equ HEADER_SIZE, 8       # Size of space for memory region header
        .equ HDR_AVAIL_OFFSET, 0  # Location of the "Available" flag in the
                                  # header
        .equ HDR_SIZE_OFFSET, 4   # Location of the size field in the header

# Constants

        .equ UNAVAILABLE, 0  # This is the number we will use to mark space
                             # that has been given out
        .equ AVAILABLE, 1    # This is the number we will use to mark space
                             # that has been returned, and is available for
                             # giving
        .equ SYS_BRK, 45     # break system call

        .equ LINUX_SYSCALL, 0x80

        .section .text

# Functions

# allocate_init starts

        # PURPOSE:  Call this function to initialize the functions
        #           (specifically, this sets heap_begin and current_break).
        #
        # PARAMS:   None
        #
        # RETURN:   None

        .globl allocate_init
        .type allocate_init,@function

allocate_init:

        pushl %ebp                  # Standard function stuff
        movl  %esp, %ebp

        # If the brk system call is called with 0 in %ebx, it returns the last
        # valid usable address

        movl  $SYS_BRK, %eax        # Find out where the break is
        movl  $0, %ebx
        int   $LINUX_SYSCALL
        incl  %eax                  # %eax now has the last valid address, and
                                    # we want the memory location after that
        movl  %eax, current_break   # Store the current break
        movl  %eax, heap_begin      # Store the current break as our first
                                    # address. This will cause the allocate
                                    # function to get more memory from Linux
                                    # the first time it is run
        movl  %ebp, %esp            # Exit the function
        popl  %ebp
        ret

# allocate_init ends

# allocate starts

        # PURPOSE:  This function is used to grab a section of memory. It
        #           checks to see if there are any free blocks, and, if not,
        #           it asks Linux for a new one.
        #
        # PARAMS:   This function has one parameter - the size of the memory
        #           block we want to allocate
        #
        # RETURN:   This function returns the address of the allocated memory
        #           in %eax. If there is no memory available, it will return 0
        #           in %eax
        #
        #           %ecx - size of the requested memory (first/only parameter)
        #           %eax - current memory region being examined
        #           %ebx - current break position
        #           %edx - size of current memory region
        #
        # We scan through each memory region starting with heap_begin. We look
        # at the size of each one, and if it has been allocated. If it's big
        # enough for the requested size, and its available, it grabs that one.
        # If it does not find a region large enough, it asks Linux for more
        # memory. In that case, it moves current_break up

        .globl malloc
        .type malloc,@function

        # Stack position of the memory size to allocate

        .equ ST_MEM_SIZE, 8

malloc:

        pushl %ebp                     # Standard function stuff
        movl  %esp, %ebp

        movl  $0, %edi
        movl  heap_begin, %eax         # If heap_begin is 0, call allocate_init
        cmpl  %eax, %edi
        je    heap_empty
        jne   heap_nonempty

heap_empty:

        call allocate_init

heap_nonempty:

        movl  ST_MEM_SIZE(%ebp), %ecx  # %ecx will hold the size we are looking
                                       # for (which is the first and only
                                       # parameter)

        movl  heap_begin, %eax         # %eax will hold the current
                                       # search location

        movl  current_break, %ebx      # %ebx will hold the current break

alloc_loop_begin:                      # Here we iterate through each memory
                                       # region

        cmpl  %ebx, %eax               # Need more memory if these are equal
        je    move_to_break

        # Grab the size of this memory

        movl  HDR_SIZE_OFFSET(%eax), %edx

        # If the space is unavailable, go to the next one

        cmpl  $UNAVAILABLE, HDR_AVAIL_OFFSET(%eax)
        je    next_location

        cmpl  %edx, %ecx               # If the space is available, compare the
        jle   allocate_here            # size to the needed size. If its big
                                       # enough, go to allocate_here

next_location:

        addl  $HEADER_SIZE, %eax       # The total size of the memory region
        addl  %edx, %eax               # is the sum of the size that was
                                       # requested when this block was created
                                       # (currently stored in %edx), plus
                                       # another 8 bytes for the header (4 for
                                       # the Available/Unavailable flag, and 4
                                       # for the Size of the region). So,
                                       # adding %edx and $8 to %eax will get
                                       # the address of the next memory region

        jmp   alloc_loop_begin         # Go look at the next location

allocate_here:                         # If we've made it here, that means that
                                       # the region header of the region to
                                       # allocate is in %eax

        movl  $UNAVAILABLE, HDR_AVAIL_OFFSET(%eax)  # Mark space as unavailable

        addl  $HEADER_SIZE, %eax       # Move %eax past the header to the
                                       # usable memory (since that's what we
                                       # return)

        movl  %ebp, %esp               # Return from the function
        popl  %ebp
        ret

# If we've made it here, that means that we have exhausted all addressable
# memory, and we need to ask for more. %ebx holds the current endpoint of the
# data, and %ecx holds its size

move_to_break:                    # We need to increase %ebx to where we _want_
                                  # memory to end, so we add space for the
        addl  $HEADER_SIZE, %ebx  # headers structure and add space to the
                                  # break for the data requested
        addl  %ecx, %ebx


        # Now its time to ask Linux for more memory

        pushl %eax                # Save needed registers
        pushl %ecx
        pushl %ebx

        movl  $SYS_BRK, %eax      # Reset the break (%ebx has the requested
                                  # break point)
        int   $LINUX_SYSCALL

# Under normal conditions, this should return the new break in %eax, which will
# be either 0 if it fails, or it will be equal to or larger than we asked for.
# We don't care in this program where it actually sets the break, so as long as
# %eax isn't 0, we don't care what it is

        cmpl  $0, %eax            # Check for error conditions
        je    error

        popl  %ebx                # Restore saved registers
        popl  %ecx
        popl  %eax

        # Set this memory as unavailable, since we're about to give it away

        movl  $UNAVAILABLE, HDR_AVAIL_OFFSET(%eax)

        # Set the size of the memory

        movl  %ecx, HDR_SIZE_OFFSET(%eax)

        # Move %eax to the actual start of usable memory.
        # %eax now holds the return value

        addl  $HEADER_SIZE, %eax

        movl  %ebx, current_break  # Save the new break

        movl  %ebp, %esp           # Return the function
        popl  %ebp
        ret

error:

        movl  $0, %eax             # On error, we return zero
        movl  %ebp, %esp
        popl  %ebp
        ret

# allocate ends

# deallocate starts

        # PURPOSE:  The purpose of this function is to give back a region of
        #           memory to the pool after we're done using it.
        #
        # PARAMS:   The only parameter is the address of the memory we want to
        #           return to the memory pool.
        #
        # RETURN:   There is no return value
        #
        # If you remember, we actually hand the program the start of the memory
        # that they can use, which is 8 storage locations after the actual
        # start of the memory region. All we have to do is go back 8 locations
        # and mark that memory as available, so that the allocate function
        # knows it can use it.

        .globl free
        .type free,@function

        # Stack position of the memory region to free

        .equ ST_MEMORY_SEG, 4

free:

        # Since the function is so simple, we don't need any of the fancy
        # function stuff

        # Get the address of the memory to free (normally this is 8(%ebp), but
        # since we didn't push %ebp or move %esp to %ebp, we can just do
        # 4(%esp))

        movl  ST_MEMORY_SEG(%esp), %eax

        # Get the pointer to the real beginning of the memory

        subl  $HEADER_SIZE, %eax

        # Mark it as available

        movl  $AVAILABLE, HDR_AVAIL_OFFSET(%eax)

        ret

# deallocate ends
这是在64位系统中交叉编译的,如下所示:

as --32 alloc-lib.asm -o alloc-lib.o
ld --shared "-melf_i386" alloc-lib.o -o alloc-lib.so
但是,当我在32位系统上使用以下方法测试它时,它只会产生
segfault

LD_PRELOAD=./alloc-lib.so ls
我决定编写一个简单的C程序来分配一个整数1来测试它,但甚至不能进行
printf
函数调用。它
segfault
s

最后,我修改了C程序,使用
putchar
将字节从新内存位置转储到文件中,并使用
hextump
验证值<代码>putchar
API
在没有SEGFULT的情况下工作

#include <stdlib.h>
#include <stdio.h>

int main(){
    int *ptra;
    char *cptra;
    ptra = malloc(4);
    *ptra = 1;
    cptra = ptra;
    putchar(*cptra);
    putchar(*(cptra + 1));
    putchar(*(cptra + 2));
    putchar(*(cptra + 3));
    free(ptra);
}
#包括
#包括
int main(){
int*ptra;
char*cptra;
ptra=malloc(4);
*ptra=1;
cptra=ptra;
putchar(*cptra);
putchar(*(cptra+1));
putchar(*(cptra+2));
putchar(*(cptra+3));
免费(ptra);
}
  • 有人知道为什么使用
    ls
    printf
    函数调用?假设这可能是因为我没有实现
    realloc
    等,那么该程序不会只使用标准的
    realloc
  • 上述测试用例是否合理?我只是使用新的
    malloc
    free
    分配一个整数,将其转储到一个文件中,并使用
    hextump
    验证它

  • 这是本书中的练习。

    您不仅需要覆盖
    malloc
    free
    ,还需要覆盖所有其他内存分配函数。请进一步注意,stdio在内部使用这些函数进行缓冲区管理,因此除非完全忽略stdio,否则无法解决此问题。告诉我你在为什么操作系统和libc编程,我可以试着找出这些是什么函数。好的,谢谢你,@fuz。我正在使用
    Arch-Linux
    运行一个
    32位chroot
    环境,使用
    ldd(GNU-libc)2.12
    。只要你说我需要这些额外的函数,那么我认为现在这应该是一个足够的答案(你不需要调试并告诉我函数名,以后会保存)。在这种情况下,替代测试用例可以吗?知道程序没有问题真的很有帮助。我在任何地方都看不到替代测试用例。您可以做的一件简单的事情是将对
    putchar
    的调用替换为对
    write
    的调用(有关如何调用
    write
    ,请参阅手册)。由于
    write
    是一个系统调用,它根本不涉及任何内存分配,您不应该遇到问题。是的,但是
    printf
    调用
    malloc
    和其他内存管理函数,如
    realloc
    ,您不包装它们,从而导致观察到的问题。是的,它会
    realloc
    访问
    malloc
    的内部数据结构。如果您传递一个指向未由库的
    malloc
    分配的内存的指针,则行为未定义,您的程序可能会崩溃。为什么您希望libc的
    realloc
    能够处理您从自己的代码中分配的块?