zoukankan      html  css  js  c++  java
  • ld 命令看内存布局 汇编级调试

    原文链接

    关于ld命令,网上资料不多,流传的有ld的中文手册,不过那手册不是给人看的,至少不是给新手看的,太难理解了。

    背景交待:

    1、将gcc与ld命令分开执行以生成可执行文件;

    2、回归经典,在屏幕上打印“Hello World!”;

    main.c内容:

    #include <stdio.h>

    int main(void)
    {
        printf("hello from %s(). \n", __func__);
        return 0;
    }

    编译生成main.o文件:

    [latelee@FightNow lib-test]$ gcc -c main.c 

    链接:

    [latelee@FightNow lib-test]$ ld main.o 
    ld: warning: cannot find entry symbol _start ; defaulting to 08048074 
    main.o: In function `main':
    main.c:(.text+0x21): undefined reference to `printf' 

    现在解决第一个warning,使用-e指定入口函数(地址):

    [latelee@FightNow lib-test]$ ld -e main main.o 
    main.o: In function `main':
    main.c:(.text+0x21): undefined reference to `printf'

    因为printf定义于C库中,因此需要使用C库:

    [latelee@FightNow lib-test]$ ld -e main main.o -lc 
    (默认为动态库)

    执行之,可惜出错了:

    [latelee@FightNow lib-test]$ ./a.out 
    -bash: ./a.out: /usr/lib/libc.so.1: bad ELF interpreter: 没有那个文件或目录

    它说“没有那个文件或目录”,查看一下:

    [latelee@FightNow lib-test]$ ls /usr/lib/ | grep "libc.so" 
    libc.so 

    的确没有,不过我们可以无中生有制作一个(网上有此说法,创建符号连接文件):

    [root@FightNow lib-test] # ln -s /usr/lib/libc.so  /usr/lib/libc.so.1 

    [root@FightNow lib-test] # ls /usr/lib/ | grep "libc.so" 
    libc.so
    libc.so.1
    [root@FightNow lib-test] # ls -l /usr/lib/ | grep "libc.so" 
    -rw-r--r--  1 root root      238 2008-07-17 libc.so
    lrwxrwxrwx  1 root root       16 03-24 14:31 libc.so.1 -> /usr/lib/libc.so

    再次执行:

    [root@FightNow lib-test] # ./a.out  
    bash: ./a.out: 权限不够

    还是不行,我都使用root来执行了还说权限不够,此路不通。

    前面默认是使用动态C库的(静态库与动态库同名时,优先使用动态库),下面使用C静态库再编译一次:

    [latelee@FightNow lib-test]$ ld -e main main.o /usr/lib/libc.a 
    /usr/lib/libc.a(syslog.o): In function `closelog': 
    (.text+0xcd): undefined reference to `_Unwind_Resume'
    /usr/lib/libc.a(syslog.o): In function `openlog': 
    (.text+0x3d2): undefined reference to `_Unwind_Resume'
    /usr/lib/libc.a(syslog.o): In function `__vsyslog_chk': 
    (.text+0x908): undefined reference to `_Unwind_Resume'
    /usr/lib/libc.a(syslog.o): In function `__vsyslog_chk': 
    (.text+0x91a): undefined reference to `_Unwind_Resume'
    .
    .
    .
    /usr/lib/libc.a(iofclose.o): In function `fclose': 
    (.text+0x1a7): undefined reference to `_Unwind_Resume'
    /usr/lib/libc.a(iofclose.o):(.eh_frame+0x166): undefined reference to `__gcc_personality_v0'
    /usr/lib/libc.a(ioftell.o): In function `ftell': 
    (.text+0x1ab): undefined reference to `_Unwind_Resume'
    /usr/lib/libc.a(ioftell.o):(.eh_frame+0xde): undefined reference to `__gcc_personality_v0' 
    /usr/lib/libc.a(iofwrite.o): In function `fwrite': 
    (.text+0x144): undefined reference to `_Unwind_Resume'
    /usr/lib/libc.a(iofwrite.o):(.eh_frame+0xde): undefined reference to `__gcc_personality_v0' 
    .
    .
    .

    这次出现的错误更多!我们的函数只使用了一个小小的printf,竟引用那么多的未定义函数,此路更不通。

    看来只要调用了库函数,恶梦将不断。下面引用《程序员的自我修养》中的例子,不使用printf库函数。

    打印字符串使用到write系统调用,退出程序使用到了exit系统调用。这里使用汇编代码仿照该系统调用。

    下面是完整的nomain.c代码:

    char* str = "hello world!\n";

    void myprint(void)
    {
    /*
    * write(int fd, const void *buf, size_t count);
    * 系统调用通过int 0x80中断实现
    * eax:4,write系统调用号
    * ebx:第一个参数,0,指定文件描述符为0(stdout)
    * ecx:第二个参数,字符串地址(?)
    * edx:第三个参数,字符串长度13
    */

     asm("movl $13, %%edx \n\t"
         "movl %0, %%ecx \n\t"
         "movl $0, %%ebx \n\t"
         "movl $4, %%eax \n\t"
         "int $0x80 \n\t"
         ::"r"(str):"edx""ecx""ebx");
    }

    void myexit(void)
    {
    /*
    * exit(code)
    * eax:1,exit系统调用号
    * ebx:退出码(exit code)
    */

     asm( "movl $42, %ebx \n\t"
          "movl $1, %eax \n\t"
          "int $0x80 \n\t");
    }


    void nomain(void)
    {
     myprint();
     myexit();
    }

    编译测试:

     $ gcc -c -fno-builtin main.c (可不需要-fno-builtin选项) 
     $ ld -static -e nomain main.o -o a.out 
     $ ./a.out 
     hello world!
     $ echo $? 
     42

    我的本意是想研究一下各个库之间的相互依赖关系,网上有资料说使用ld命令的--start-group和--end-group选项来解决,在U-Boot的Makefile中的确也看到了。不过自己没有顺利找到一个可以测试的例子。限于能力及时间,这个研究暂时搁置了。等以后对linux底层的东西更加了解时再来研究亦不晚。

    参考资料:

    1、如果想了解关于bin文件及汇编、连接的知识,请用google搜索“Making plain binary files using a C compiler”,作者是Cornelis Frank,写于2000年,年代虽久,经典依旧。

    2、《程序员的自我修养--链接、装载与库》,一本需要慢慢研读的好书。

  • 相关阅读:
    <阿里工程师的自我素养>读后感-技术人应该具备的一些基本素质
    Hbase的基本原理(与HIVE的区别、数据结构模型、拓扑结构、水平分区原理、场景)
    大数据技术体系 && NoSQL数据库的基本原理
    软件测试面试经验
    APP非功能测试
    手机APP测试(测试点、测试流程、功能测试)
    性能测试学习之路 (四)jmeter 脚本开发实战(JDBC &JMS &接口脚本 & 轻量级接口自动化测试框架)
    HTML 实战生成一张页面
    前端性能测试(H5性能测试)
    JAVA基础——设计模式之观察者模式
  • 原文地址:https://www.cnblogs.com/wangkangluo1/p/2566554.html
Copyright © 2011-2022 走看看