C 正在修改链接器脚本以使.text部分可写,错误

C 正在修改链接器脚本以使.text部分可写,错误,c,linux,gcc,linker,x86-64,C,Linux,Gcc,Linker,X86 64,我正在尝试使.text部分可为C程序编写。我查看了中提供的选项,并专注于修改链接器脚本以实现这一点 为此,我使用 内存{rwx(wx):原点=0x400000,长度=256K} 并在章节中添加文本: .text : { *(.text.unlikely .text.*_unlikely) *(.text.exit .text.exit.*) *(.text.startup .text.startup.*) *(.text.hot .text.hot.*) *

我正在尝试使.text部分可为
C
程序编写。我查看了中提供的选项,并专注于修改链接器脚本以实现这一点

  • 为此,我使用
内存{rwx(wx):原点=0x400000,长度=256K}

并在章节中添加文本:

.text           :
  {
 *(.text.unlikely .text.*_unlikely)
 *(.text.exit .text.exit.*)
 *(.text.startup .text.startup.*)
 *(.text.hot .text.hot.*)
 *(.text .stub .text.* .gnu.linkonce.t.*)
 /* .gnu.warning sections are handled specially by elf32.em.  */
 *(.gnu.warning)
} >rwx
在使用
gcc
标志
-T
编译代码并将我的链接器文件作为参数时,我遇到了一个错误:

error: no memory region specified for loadable section '.interp'
我只是试图更改.text区域的内存权限。在Ubuntu x86_64体系结构上工作

有更好的方法吗? 非常感谢您的帮助

谢谢


在Linux中,您可以使用
mprotect()
从运行时代码启用/禁用文本节写保护;请参阅中的“注释”部分

这是一个真实的例子。然而,首先要注意的是:

我认为这只是一个概念证明的实现,而不是我在现实世界应用中使用过的东西。在某种类型的高性能库中使用它可能看起来很吸引人,但根据我的经验,更改库的API(或范例/方法)通常会产生更好的结果,也会减少难以调试的bug

考虑以下六个文件:


foo1.c

int foo1(const int a, const int b) { return a*a - 2*a*b + b*b; }
int foo2(const int a, const int b) { return a*a + b*b; }
#ifndef   FOO_H
#define   FOO_H

extern int foo1(const int a, const int b);

extern int foo2(const int a, const int b);
#endif /* FOO_H */
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#include <string.h>
#include <stdio.h>
#include "foo.h"

int text_copy(const void *const target,
              const void *const source,
              const size_t      length)
{
    const long  page = sysconf(_SC_PAGESIZE);
    void       *start = (char *)target - ((long)target % page);
    size_t      bytes = length + (size_t)((long)target % page);

    /* Verify sane page size. */
    if (page < 1L)
        return errno = ENOTSUP;

    /* Although length should not need to be a multiple of page size,
     * adjust it up if need be. */
    if (bytes % (size_t)page)
        bytes = bytes + (size_t)page - (bytes % (size_t)page);

    /* Disable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_WRITE | PROT_EXEC))
        return errno;

    /* Copy code.
     * Note: if the target code is being executed, we're in trouble;
     *       this offers no atomicity guarantees, so other threads may
     *       end up executing some combination of old/new code.
    */
    memcpy((void *)target, (const void *)source, length);

    /* Re-enable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_EXEC))
        return errno;

    /* Success. */
    return 0;
}

int main(void)
{
    printf("foo1(): %d bytes at %p\n", foo1_SIZE, foo1_ADDR);
    printf("foo2(): %d bytes at %p\n", foo2_SIZE, foo2_ADDR);

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    if (foo2_SIZE < foo1_SIZE) {
        printf("Replacing foo1() with foo2(): ");
        if (text_copy(foo1_ADDR, foo2_ADDR, foo2_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    } else {
        printf("Replacing foo2() with foo1(): ");
        if (text_copy(foo2_ADDR, foo1_ADDR, foo1_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    }

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    return 0;
}
#!/bin/bash

addr_prefix=""
addr_suffix="_ADDR"

size_prefix=""
size_suffix="_SIZE"

export LANG=C
export LC_ALL=C

nm -S "$@" | while read addr size kind name dummy ; do
    [ -n "$addr" ] || continue
    [ -n "$size" ] || continue
    [ -z "$dummy" ] || continue
    [ "$kind" = "T" ] || continue
    [ "$name" != "${name#[A-Za-z]}" ] || continue
    printf '#define %s ((void *)0x%sL)\n' "$addr_prefix$name$addr_suffix" "$addr"
    printf '#define %s %d\n' "$size_prefix$name$size_suffix" "0x$size"
done || exit $?
gcc -W -Wall -O3 main.o foo1.o foo2.o -o example

foo2.c

int foo1(const int a, const int b) { return a*a - 2*a*b + b*b; }
int foo2(const int a, const int b) { return a*a + b*b; }
#ifndef   FOO_H
#define   FOO_H

extern int foo1(const int a, const int b);

extern int foo2(const int a, const int b);
#endif /* FOO_H */
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#include <string.h>
#include <stdio.h>
#include "foo.h"

int text_copy(const void *const target,
              const void *const source,
              const size_t      length)
{
    const long  page = sysconf(_SC_PAGESIZE);
    void       *start = (char *)target - ((long)target % page);
    size_t      bytes = length + (size_t)((long)target % page);

    /* Verify sane page size. */
    if (page < 1L)
        return errno = ENOTSUP;

    /* Although length should not need to be a multiple of page size,
     * adjust it up if need be. */
    if (bytes % (size_t)page)
        bytes = bytes + (size_t)page - (bytes % (size_t)page);

    /* Disable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_WRITE | PROT_EXEC))
        return errno;

    /* Copy code.
     * Note: if the target code is being executed, we're in trouble;
     *       this offers no atomicity guarantees, so other threads may
     *       end up executing some combination of old/new code.
    */
    memcpy((void *)target, (const void *)source, length);

    /* Re-enable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_EXEC))
        return errno;

    /* Success. */
    return 0;
}

int main(void)
{
    printf("foo1(): %d bytes at %p\n", foo1_SIZE, foo1_ADDR);
    printf("foo2(): %d bytes at %p\n", foo2_SIZE, foo2_ADDR);

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    if (foo2_SIZE < foo1_SIZE) {
        printf("Replacing foo1() with foo2(): ");
        if (text_copy(foo1_ADDR, foo2_ADDR, foo2_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    } else {
        printf("Replacing foo2() with foo1(): ");
        if (text_copy(foo2_ADDR, foo1_ADDR, foo1_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    }

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    return 0;
}
#!/bin/bash

addr_prefix=""
addr_suffix="_ADDR"

size_prefix=""
size_suffix="_SIZE"

export LANG=C
export LC_ALL=C

nm -S "$@" | while read addr size kind name dummy ; do
    [ -n "$addr" ] || continue
    [ -n "$size" ] || continue
    [ -z "$dummy" ] || continue
    [ "$kind" = "T" ] || continue
    [ "$name" != "${name#[A-Za-z]}" ] || continue
    printf '#define %s ((void *)0x%sL)\n' "$addr_prefix$name$addr_suffix" "$addr"
    printf '#define %s %d\n' "$size_prefix$name$size_suffix" "0x$size"
done || exit $?
gcc -W -Wall -O3 main.o foo1.o foo2.o -o example

foo.h.标题

int foo1(const int a, const int b) { return a*a - 2*a*b + b*b; }
int foo2(const int a, const int b) { return a*a + b*b; }
#ifndef   FOO_H
#define   FOO_H

extern int foo1(const int a, const int b);

extern int foo2(const int a, const int b);
#endif /* FOO_H */
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#include <string.h>
#include <stdio.h>
#include "foo.h"

int text_copy(const void *const target,
              const void *const source,
              const size_t      length)
{
    const long  page = sysconf(_SC_PAGESIZE);
    void       *start = (char *)target - ((long)target % page);
    size_t      bytes = length + (size_t)((long)target % page);

    /* Verify sane page size. */
    if (page < 1L)
        return errno = ENOTSUP;

    /* Although length should not need to be a multiple of page size,
     * adjust it up if need be. */
    if (bytes % (size_t)page)
        bytes = bytes + (size_t)page - (bytes % (size_t)page);

    /* Disable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_WRITE | PROT_EXEC))
        return errno;

    /* Copy code.
     * Note: if the target code is being executed, we're in trouble;
     *       this offers no atomicity guarantees, so other threads may
     *       end up executing some combination of old/new code.
    */
    memcpy((void *)target, (const void *)source, length);

    /* Re-enable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_EXEC))
        return errno;

    /* Success. */
    return 0;
}

int main(void)
{
    printf("foo1(): %d bytes at %p\n", foo1_SIZE, foo1_ADDR);
    printf("foo2(): %d bytes at %p\n", foo2_SIZE, foo2_ADDR);

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    if (foo2_SIZE < foo1_SIZE) {
        printf("Replacing foo1() with foo2(): ");
        if (text_copy(foo1_ADDR, foo2_ADDR, foo2_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    } else {
        printf("Replacing foo2() with foo1(): ");
        if (text_copy(foo2_ADDR, foo1_ADDR, foo1_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    }

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    return 0;
}
#!/bin/bash

addr_prefix=""
addr_suffix="_ADDR"

size_prefix=""
size_suffix="_SIZE"

export LANG=C
export LC_ALL=C

nm -S "$@" | while read addr size kind name dummy ; do
    [ -n "$addr" ] || continue
    [ -n "$size" ] || continue
    [ -z "$dummy" ] || continue
    [ "$kind" = "T" ] || continue
    [ "$name" != "${name#[A-Za-z]}" ] || continue
    printf '#define %s ((void *)0x%sL)\n' "$addr_prefix$name$addr_suffix" "$addr"
    printf '#define %s %d\n' "$size_prefix$name$size_suffix" "0x$size"
done || exit $?
gcc -W -Wall -O3 main.o foo1.o foo2.o -o example

foo.h.footer

int foo1(const int a, const int b) { return a*a - 2*a*b + b*b; }
int foo2(const int a, const int b) { return a*a + b*b; }
#ifndef   FOO_H
#define   FOO_H

extern int foo1(const int a, const int b);

extern int foo2(const int a, const int b);
#endif /* FOO_H */
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#include <string.h>
#include <stdio.h>
#include "foo.h"

int text_copy(const void *const target,
              const void *const source,
              const size_t      length)
{
    const long  page = sysconf(_SC_PAGESIZE);
    void       *start = (char *)target - ((long)target % page);
    size_t      bytes = length + (size_t)((long)target % page);

    /* Verify sane page size. */
    if (page < 1L)
        return errno = ENOTSUP;

    /* Although length should not need to be a multiple of page size,
     * adjust it up if need be. */
    if (bytes % (size_t)page)
        bytes = bytes + (size_t)page - (bytes % (size_t)page);

    /* Disable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_WRITE | PROT_EXEC))
        return errno;

    /* Copy code.
     * Note: if the target code is being executed, we're in trouble;
     *       this offers no atomicity guarantees, so other threads may
     *       end up executing some combination of old/new code.
    */
    memcpy((void *)target, (const void *)source, length);

    /* Re-enable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_EXEC))
        return errno;

    /* Success. */
    return 0;
}

int main(void)
{
    printf("foo1(): %d bytes at %p\n", foo1_SIZE, foo1_ADDR);
    printf("foo2(): %d bytes at %p\n", foo2_SIZE, foo2_ADDR);

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    if (foo2_SIZE < foo1_SIZE) {
        printf("Replacing foo1() with foo2(): ");
        if (text_copy(foo1_ADDR, foo2_ADDR, foo2_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    } else {
        printf("Replacing foo2() with foo1(): ");
        if (text_copy(foo2_ADDR, foo1_ADDR, foo1_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    }

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    return 0;
}
#!/bin/bash

addr_prefix=""
addr_suffix="_ADDR"

size_prefix=""
size_suffix="_SIZE"

export LANG=C
export LC_ALL=C

nm -S "$@" | while read addr size kind name dummy ; do
    [ -n "$addr" ] || continue
    [ -n "$size" ] || continue
    [ -z "$dummy" ] || continue
    [ "$kind" = "T" ] || continue
    [ "$name" != "${name#[A-Za-z]}" ] || continue
    printf '#define %s ((void *)0x%sL)\n' "$addr_prefix$name$addr_suffix" "$addr"
    printf '#define %s %d\n' "$size_prefix$name$size_suffix" "0x$size"
done || exit $?
gcc -W -Wall -O3 main.o foo1.o foo2.o -o example

main.c

int foo1(const int a, const int b) { return a*a - 2*a*b + b*b; }
int foo2(const int a, const int b) { return a*a + b*b; }
#ifndef   FOO_H
#define   FOO_H

extern int foo1(const int a, const int b);

extern int foo2(const int a, const int b);
#endif /* FOO_H */
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#include <string.h>
#include <stdio.h>
#include "foo.h"

int text_copy(const void *const target,
              const void *const source,
              const size_t      length)
{
    const long  page = sysconf(_SC_PAGESIZE);
    void       *start = (char *)target - ((long)target % page);
    size_t      bytes = length + (size_t)((long)target % page);

    /* Verify sane page size. */
    if (page < 1L)
        return errno = ENOTSUP;

    /* Although length should not need to be a multiple of page size,
     * adjust it up if need be. */
    if (bytes % (size_t)page)
        bytes = bytes + (size_t)page - (bytes % (size_t)page);

    /* Disable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_WRITE | PROT_EXEC))
        return errno;

    /* Copy code.
     * Note: if the target code is being executed, we're in trouble;
     *       this offers no atomicity guarantees, so other threads may
     *       end up executing some combination of old/new code.
    */
    memcpy((void *)target, (const void *)source, length);

    /* Re-enable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_EXEC))
        return errno;

    /* Success. */
    return 0;
}

int main(void)
{
    printf("foo1(): %d bytes at %p\n", foo1_SIZE, foo1_ADDR);
    printf("foo2(): %d bytes at %p\n", foo2_SIZE, foo2_ADDR);

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    if (foo2_SIZE < foo1_SIZE) {
        printf("Replacing foo1() with foo2(): ");
        if (text_copy(foo1_ADDR, foo2_ADDR, foo2_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    } else {
        printf("Replacing foo2() with foo1(): ");
        if (text_copy(foo2_ADDR, foo1_ADDR, foo1_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    }

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    return 0;
}
#!/bin/bash

addr_prefix=""
addr_suffix="_ADDR"

size_prefix=""
size_suffix="_SIZE"

export LANG=C
export LC_ALL=C

nm -S "$@" | while read addr size kind name dummy ; do
    [ -n "$addr" ] || continue
    [ -n "$size" ] || continue
    [ -z "$dummy" ] || continue
    [ "$kind" = "T" ] || continue
    [ "$name" != "${name#[A-Za-z]}" ] || continue
    printf '#define %s ((void *)0x%sL)\n' "$addr_prefix$name$addr_suffix" "$addr"
    printf '#define %s %d\n' "$size_prefix$name$size_suffix" "0x$size"
done || exit $?
gcc -W -Wall -O3 main.o foo1.o foo2.o -o example
记住使用
chmodu+x./function info.bash使其可执行


首先,使用有效大小但无效地址编译源:

gcc -W -Wall -O3 -c foo1.c
gcc -W -Wall -O3 -c foo2.c
( cat foo.h.header ; ./function-info.bash foo1.o foo2.o ; cat foo.h.footer) > foo.h
gcc -W -Wall -O3 -c main.c
( cat foo.h.header ; ./function-info.bash example ; cat foo.h.footer) > foo.h
大小正确,但地址不正确,因为代码尚未链接。相对于最终二进制文件,对象文件内容通常在链接时重新定位。因此,链接源以获取示例可执行文件,example

int foo1(const int a, const int b) { return a*a - 2*a*b + b*b; }
int foo2(const int a, const int b) { return a*a + b*b; }
#ifndef   FOO_H
#define   FOO_H

extern int foo1(const int a, const int b);

extern int foo2(const int a, const int b);
#endif /* FOO_H */
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

#include <string.h>
#include <stdio.h>
#include "foo.h"

int text_copy(const void *const target,
              const void *const source,
              const size_t      length)
{
    const long  page = sysconf(_SC_PAGESIZE);
    void       *start = (char *)target - ((long)target % page);
    size_t      bytes = length + (size_t)((long)target % page);

    /* Verify sane page size. */
    if (page < 1L)
        return errno = ENOTSUP;

    /* Although length should not need to be a multiple of page size,
     * adjust it up if need be. */
    if (bytes % (size_t)page)
        bytes = bytes + (size_t)page - (bytes % (size_t)page);

    /* Disable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_WRITE | PROT_EXEC))
        return errno;

    /* Copy code.
     * Note: if the target code is being executed, we're in trouble;
     *       this offers no atomicity guarantees, so other threads may
     *       end up executing some combination of old/new code.
    */
    memcpy((void *)target, (const void *)source, length);

    /* Re-enable write protect on target pages. */
    if (mprotect(start, bytes, PROT_READ | PROT_EXEC))
        return errno;

    /* Success. */
    return 0;
}

int main(void)
{
    printf("foo1(): %d bytes at %p\n", foo1_SIZE, foo1_ADDR);
    printf("foo2(): %d bytes at %p\n", foo2_SIZE, foo2_ADDR);

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    if (foo2_SIZE < foo1_SIZE) {
        printf("Replacing foo1() with foo2(): ");
        if (text_copy(foo1_ADDR, foo2_ADDR, foo2_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    } else {
        printf("Replacing foo2() with foo1(): ");
        if (text_copy(foo2_ADDR, foo1_ADDR, foo1_SIZE)) {
            printf("%s.\n", strerror(errno));
            return 1;
        }
        printf("Done.\n");
    }

    printf("foo1(3, 5): %d\n", foo1(3, 5));
    printf("foo2(3, 5): %d\n", foo2(3, 5));

    return 0;
}
#!/bin/bash

addr_prefix=""
addr_suffix="_ADDR"

size_prefix=""
size_suffix="_SIZE"

export LANG=C
export LC_ALL=C

nm -S "$@" | while read addr size kind name dummy ; do
    [ -n "$addr" ] || continue
    [ -n "$size" ] || continue
    [ -z "$dummy" ] || continue
    [ "$kind" = "T" ] || continue
    [ "$name" != "${name#[A-Za-z]}" ] || continue
    printf '#define %s ((void *)0x%sL)\n' "$addr_prefix$name$addr_suffix" "$addr"
    printf '#define %s %d\n' "$size_prefix$name$size_suffix" "0x$size"
done || exit $?
gcc -W -Wall -O3 main.o foo1.o foo2.o -o example
提取正确的(大小和)地址:

gcc -W -Wall -O3 -c foo1.c
gcc -W -Wall -O3 -c foo2.c
( cat foo.h.header ; ./function-info.bash foo1.o foo2.o ; cat foo.h.footer) > foo.h
gcc -W -Wall -O3 -c main.c
( cat foo.h.header ; ./function-info.bash example ; cat foo.h.footer) > foo.h
重新编译和链接

gcc -W -Wall -O3 -c main.c
gcc -W -Wall -O3 foo1.o foo2.o main.o -o example
并验证这些常量现在是否匹配:

mv -f foo.h foo.h.used
( cat foo.h.header ; ./function-info.bash example ; cat foo.h.footer) > foo.h
cmp -s foo.h foo.h.used && echo "Done." || echo "Recompile and relink."
由于高度优化(
-O3
),利用常数的代码可能会改变大小,需要再次重新编译重新链接。如果最后一行输出“重新编译和重新链接”,只需重复最后两个步骤,即五行

(请注意,由于foo1.c和foo2.c不使用foo.h中的常量,因此它们显然不需要重新编译。)

在x86_64(GCC-4.6.3-1ubuntu5)上,运行
/example
输出

foo1(): 21 bytes at 0x400820
foo2(): 10 bytes at 0x400840
foo1(3, 5): 4
foo2(3, 5): 34
Replacing foo1() with foo2(): Done.
foo1(3, 5): 34
foo2(3, 5): 34
这表明
foo1()
函数确实被替换了。请注意,较长的函数总是替换为较短的函数,因为我们不能覆盖两个函数之外的任何代码

您可以修改这两个函数来验证这一点;只需记住重复整个过程(以便在
main()
中使用正确的_SIZE和_ADDR常量)

下面是为上述内容生成的
foo.h
,仅供参考:

#ifndef   FOO_H
#define   FOO_H

extern int foo1(const int a, const int b);

extern int foo2(const int a, const int b);
#define foo1_ADDR ((void *)0x0000000000400820L)
#define foo1_SIZE 21
#define foo2_ADDR ((void *)0x0000000000400840L)
#define foo2_SIZE 10
#define main_ADDR ((void *)0x0000000000400610L)
#define main_SIZE 291
#define text_copy_ADDR ((void *)0x0000000000400850L)
#define text_copy_SIZE 226
#endif /* FOO_H */
您可能希望使用更智能的scriptlet,例如使用
awk
的scriptlet,它使用
nm-S
获取所有函数名、地址和大小,并且在头文件中仅替换现有定义的值,以生成头文件。我会使用一个Makefile和一些助手脚本

进一步说明:

  • 功能代码按原样复制,不进行重新定位等操作。(这意味着如果替换函数的机器代码包含绝对跳转,则在原始代码中继续执行。选择这些示例函数是因为它们不太可能包含绝对跳转。运行
    objdump-d foo1.o foo2.o
    从程序集进行验证。)

    如果您使用该示例只是为了研究如何在正在运行的流程中修改可执行代码,那么这与此无关。但是,如果在本示例的基础上构建运行时函数替换方案,则可能需要为替换的代码使用位置无关的代码(请参阅,以了解体系结构的相关选项)或自行重新定位

  • 如果另一个线程或信号处理程序执行被修改的代码,您将遇到严重的麻烦。您会得到未定义的结果。不幸的是,有些库会启动额外的线程,这可能不会阻止所有可能的信号,因此在修改可能由信号处理程序运行的代码时要格外小心

  • 不要假设编译器以特定的方式编译代码或使用特定的组织。我的示例使用单独的编译单元,以避免编译器可能在类似函数之间共享代码的情况

    此外,它还直接检查最终的可执行二进制文件,以获得要修改的大小和地址,从而修改整个函数实现。所有的验证都应该在目标文件或最终可执行文件上完成,并进行反汇编,而不仅仅是查看C代码

  • 将任何依赖于地址和大小常量的代码放入一个单独的编译单元,可以更容易、更快地重新编译和重新链接二进制文件。(您只需重新编译直接使用常量的代码,甚至可以对该代码使用较少的优化,以消除额外的重新编译重新链接周期,而不会影响总体代码质量。)

  • 在my
    main.c
    中,提供给
    mprotect()
    的地址和长度都是页面对齐的(基于用户参数)。文件上说只有地址是必须的。由于保护是页面粒度的,因此确保长度是页面大小的倍数不会造成伤害

  • 您可以读取和解析
    /proc/self/maps
    (这是内核生成的伪文件;有关更多信息,请参阅
    /proc/[pid]/maps
    部分)以获取现有映射及其对当前进程的保护

在任何情况下,如果你有任何问题,我很乐意尝试和cl