Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1136133
  • 博文数量: 646
  • 博客积分: 288
  • 博客等级: 二等列兵
  • 技术积分: 5375
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-08 14:33
个人简介

为了技术,我不会停下学习的脚步,我相信我还能走二十年。

文章分类

全部博文(646)

文章存档

2014年(8)

2013年(134)

2012年(504)

分类: LINUX

2014-01-15 22:32:42

零.C语言入口函数

从第一天学习C语言开始,我们的脑子里就深深烙下这样一个概念:C语言程序总是从main()函数开始执行,main()函数结束,程序也就结束了.在平时的练习中貌似这没有问题,但事实真的是这样吗?测试一下,

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>

  4. int enter(void)
  5. {
  6.     printf("the start function!\n");

  7.     return 0;
  8. }
编译:
打印处一页错误,其中有这样一条:
crt1.o:In function `__start':(.text+0x20): undefined reference to `main'

字面翻译就是:在函数_start中,main没有定义. __start函数是什么?main还要定义?crtl.o又是什么?带着疑问,我打开了百度.在其中搜到这样一份伪代码:

section .text:
    __start:
    
     :
     init stack;
     init heap;
     open stdin;
     open stdout;
     open stderr;
     :
     push argv;
     push argc;
     call _main; (调用 main)
     :
     destory heap;
     close stdin;
     close stdout;
     close stderr;
     :
     call __exit;
终于找到了,原来main就躲在crt1.o中,再看__start函数,"start"?不是开始的意思吗?难道每一个C程序就是从这开始的吗?

原来,对于“crt1.o”这个文件,其中crt是“C runtime library”的缩写,其含义是“C运时库”,C运行时库除了给我们提供必要的库函数调用(如memcpy、printf、malloc等)之外,它提供的另一个最重要的功能是为应用程序 添加启动函数。C运行时库启动函数的主要功能为 进行程序的初始化 ,对全局变量进行赋初值 ,加载用户程序的入口函数


soga,那这是不是意味着我们可以修改所要加载的入口函数呢?原来还有这样一条命令:

gcc *.c -e <函数名> -nostartfiles

其中, *.c 为程序的源文件,-e为修改函数的入口地址,e == entrance ,后面的 <函数名> 就指明要替换的函数,-nostartfiles选项的作用是 通知编译器不自动加入启动函数以及别的库级别的初始化 ,这样就不会调用到crt1.o中的_start函数!测试一下,

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>

  4. int enter(void)
  5. {
  6.     printf("the start function!\n");

  7.     return 0;
  8. }
编译通过,运行:
the start function!
段错误 (核心已转储)

成功运行enter函数,但提示了段错误.
原来,在编译的时候加上了-nostartfiles这个选项的同时,使得最后的return语句不能正常执行。解决方案是,把程序最后的return语句改成exit()函数,因为return是将控制权交给调用函数,此时我们已经没有调用__start这个函数了,自然就没有enter的调用函数,然而exit()函数是将控制权交给内核,所以能够成功退出.

运行结果:
the start function!

一.LD_PRELOAD
在将LD_PRELOAD之前,我们先来了解一下两种链接方式
静态链接:由链接器在 链接时 将库的内容加入到可执行程序中.
动态链接:在可执行文件 装载时或运行时(注意与静态链接的区别) ,由操作系统的装载程序加载库.

通俗地讲,一种是无论程序运行与否,都加载库(静态库);另一种是只有执行到那一部分代码时,才加载库(动态库).

在Linux的动态链接库的世界中,LD_PRELOAD就是这样一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义 在程序运行前 优先加载的动态链接库 .这个功能主要就是 用来有选择性的载入不同动态链接库中的相同函数 .通过这个环境变量,我们可以 在主程序和其动态链接库的中间加载别的动态链接库 ,甚至覆盖正常的函数库.
说了这么多,是不是有点模糊?举个例子,
首先先编写一个“strcmp”函数,就将它命名为passwd.c

点击(此处)折叠或打开

  1. #include
  2.  
  3. int strcmp(const char *str1,const char *str2)
  4. {
  5.     puts(str1);
  6.     puts(str2);
  7.     return 0;
  8. }
  执行下面的语句:
gcc passwd.c -shared -o passwd.so -fPIC

其中,-shared选项的意思是将passwd.c编译链接成为一个动态库.*.so在Linux中为共享库——shared object,用于动态链接,和Windows下的dll差不多. 
在shell中执行程序时,shell会提供一组环境变量.其中export的作用就是新增一个环境变量,供后续执行的程序使用.export的效力仅及于该此登陆操作.
对于-fPIC,-f后面跟一些编译选项,PIC是其中一种,表示生成位置无关代码(Position Independent Code),位置无关码就是可以在进程的任意内存位置执行的目标码动态链接库必须使用.
然后,我们就用passwd.so这个库来进行优先替换:
export LD_PRELOAD=./passwd.so  

我们知道,strcmp()的功能是比较两个字符串,但是在这里我们用passwd.so来代替系统的动态库,测试一下,

点击(此处)折叠或打开

  1. #include <unistd.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>

  4. int main(int argc, char *argv[])
  5. {
  6.     char *str = "abcdef";
  7.     char pass[10];
  8.    
  9.     printf("Please input the passwd:\n");
  10.         gets(pass);
  11. if( strcmp(pass,str) == 0 )
  12. {
  13.     printf("Login succeed!\n");
  14. }
  15. else
  16. {
  17.     printf("Passwd incorrect!\n");
  18.     exit(0);
  19. }

  20. return 0;
  21. }
运行结果:
Please input the passwd:
123456
123456
abcdef
Login succeed!
就这样,strcmp()成功被我们替换了...

                                                                     参考博文---------------------http://blog.sina.com.cn/s/blog_76a864e20101ehuz.html
阅读(1730) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~