您尚未登录。

#1 2021-04-20 16:39:52

matrikslee
会员
注册时间: 2017-04-21
帖子: 264
个人网站

《程序员的自我修养:链接、装载与库》演示程序运行错误

我最近在学习《程序员的自我修养:链接、装载与库》这本书,在7.7运行时装载的演示程序处遇到了问题(我都有点怀疑我是不是买了盗版书了,书里真的好多错漏点),涉及到gcc的嵌入式汇编和函数调用平栈相关,有点头晕,搞了许久没有办法,到论坛来问问看是否有大手子帮忙解答。
下面是书里原始的程序代码,

#include <stdio.h>
#include <dlfcn.h>

#define SETUP_STACK \
i = 2; \
while(++i < argc-1)  { \
    switch(argv[i][0]) { \
    case 'i': \
        asm volatile("push %0" :: \
        "r"(atoi(&argv[i][1])) ); \
        esp += 4; \
        break; \
    case 'd': \
        atof(&argv[i][1]); \
        asm volatile("subl $8, %esp\n" \
        "fstpl (%esp)" ); \
        esp += 8; \
        break; \
     case 's': \
        asm volatile("push %0":: \
        "r"(&argv[i][1]) ); \
        esp += 4; \
        break; \
     default: \
        printf("error argument type"); \
        goto exit_runso; \
     } \
} \

#define RESTORE_STACK \
     asm volatile("add %0, %%esp":: "r"(esp))

int main(int argc, char *argv[])
{
    void *handle;
    char *error;
    int i;
    int esp = 0;
    void *func;

    handle = dlopen(argv[1], RTLD_NOW);
    if(handle == 0) {
        printf("Can't findlibrary: %s\n", argv[1]);
        return -1;
    }

    func = dlsym(handle, argv[2]);
    if( (error = dlerror()) != NULL) {
        printf("Find symbol %s error: %s\n", argv[2], error);
        goto exit_runso;
    }

    switch(argv[argc-1][0]) {
    case 'i':
    {
        int (*func_int)() = func;
        SETUP_STACK;
        int ret = func_int();
        RESTORE_STACK;
        printf("ret = %d\n", ret);
        break;
    }
    case 'd':
    {
        int (*func_double)() = func;
        SETUP_STACK;
        double ret = func_double();
        RESTORE_STACK;
        printf("ret = %f\n", ret);
        break;
    }
    case 's':
    {
        int (*func_str)() = func;
        SETUP_STACK;
        char *ret = func_str();
        RESTORE_STACK;
        printf("ret = %s\n", ret);
        break;
    }
    case 'v':
    {
        void (*func_void)() = func;
        SETUP_STACK;
        func_void();
        RESTORE_STACK;
        printf("ret = void\n");
        break;
    }
    }

exit_runso:
    dlclose(handle);
}

一开始我把代码手敲到电脑上,然后遇到了编译错误,发现gcc内嵌汇编的寄存器写法是两个百分号,然后还有unused value warning,
然后我修了内嵌汇编的编译错误,忽视unused value warning,然后运行返回double值的case:./runso /lib32/libm-2.16.so sin d2.0 d
然后程序就crash掉了,用gdb debug加上objdump反汇编,结果发现下面的问题:
1.  atof(&argv[\i][1]); 的调用因为没有用到返回值被gcc优化去掉了,所以根本就没有将argv[\i][1]转成double数传给sin调用,

然后我把setup_stack宏里面的case 'd'部分代码按照我自己的理解改成了如下情况修了问题1:

case 'd':
        asm volatile("subl $8, %%esp\n"
        "fstpl (%%esp)"::
        "f"(atof(&argv[i][1])) );
        esp += 8;
        break;

然后遇到了下面的问题2:
2.  在执行了SETUP_STACK里面case 'd'部分的内嵌汇编代码之后,变量i,esp,func_double的值都跑飞了,i变成了一个非常大的数字(预期值是4),esp变成了9(预期值是8),func_table这个函数指针的值变成了0x5(预期值应该是外部模块libm的sin函数在当前elf的got地址)

最近编辑记录 matrikslee (2021-04-20 16:42:51)

离线

#2 2021-04-24 12:36:29

xtricman
エクス·トリクマン
注册时间: 2012-12-26
帖子: 1,227

Re: 《程序员的自我修养:链接、装载与库》演示程序运行错误

因为作者写的就有很多错漏点


反社会,精神极其不稳定,随时可能炸碎身边所有人

离线

页脚