运行时加载

动态链接库提供的API

函数声明

/* Open the shared object FILE and map it in; return a handle that can be
   passed to `dlsym' to get symbol values from it.  */
extern void *dlopen (const char *__file, int __mode) __THROWNL;

/* Unmap and close a shared object opened by `dlopen'.
   The handle cannot be used again after calling `dlclose'.  */
extern int dlclose (void *__handle) __THROWNL __nonnull ((1));

/* Find the run-time address in the shared object HANDLE refers to
   of the symbol called NAME.  */
extern void *dlsym (void *__restrict __handle,
        const char *__restrict __name) __THROW __nonnull ((2));

/* When any of the above functions fails, call this function
   to return a string describing the error.  Each call resets
   the error string so that a following call returns null.  */
extern char *dlerror (void) __THROW;

常量

/* The MODE argument to `dlopen' contains one of the following: */
#define RTLD_LAZY 0x00001 /* Lazy function call binding.  */
#define RTLD_NOW  0x00002 /* Immediate function call binding.  */
#define RTLD_BINDING_MASK   0x3 /* Mask of binding time value.  */
#define RTLD_NOLOAD 0x00004 /* Do not load the object.  */
#define RTLD_DEEPBIND 0x00008 /* Use deep binding.  */

/* If the following bit is set in the MODE argument to `dlopen',
   the symbols of the loaded object and its dependencies are made
   visible as if the object were linked directly into the program.  */
#define RTLD_GLOBAL 0x00100

/* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL.
   The implementation does this by default and so we can define the
   value to zero.  */
#define RTLD_LOCAL  0

/* Do not delete object when closed.  */
#define RTLD_NODELETE 0x01000

GNU Inline Assembler(内联汇编)

官方文档

基础内联汇编

语法

asm asm-qulifiers ( AssemblerInstructions)

在c语言中,关键字asm是GNU拓展的。(不使用GNU拓展的话就用__asm__,但需要添加一些编译选项。)

Qualifiers有两种,volatileinline,基础内联汇编代码默认都是volatile,显示添加没什么影响就看着好看。

inline的作用文档里说的目前看不懂。

AssemblerInstructions是一个字符串,里面是汇编代码,GCC不会解析这些字符串代表的指令,直接交给assmbler处理,字符串里面可以有多个指令,按照所使用的汇编语法去分割。

Extended Asm - Assembler Instructions with C Expression Operands

Extended asm可以把汇编代码和c代码联系在一起,使用冒号做分割符。

语法

asm asm-qualifiers (AssemblerTemplate
        :  OutputOperands
        [  : InputOperands
        [  : Clobbers] ])
asm asm-qualifiers (AssemblerTemplate
          : OutputOperands
          : InputOperands
          : Clobbers
          : GotoLabels)

Qualifiers

  • volatile

    禁止编译器优化

  • inline

    (暂时看不懂)

  • goto

    允许汇编代码跳到c的label

参数

Assembler Template

c编译器会做一些字符串的格式化

特殊的转义字符

%%:产生汇编代码中的一个%

%=%{%|、**%}**暂时用不到

输出操作符

格式是这样

[[asmSymbolicName]] constraint (cvariablename)

asmSymbolicName

可以用汇编里面定义的变量名(没用过),没有定义这个后面就会按顺序使用%0,%1这些占位符

constraint

先带一个prefix,再带一个additional constraints,用法挺复杂,常用的有

prefix:=表示overwrirte, +表示读写

additional constraints:r表示register,m表示memory

cvariablename

指定c变量

输入操作符

和输出操作符差不多,只是最后变量变成了表达式,也就是可以用一些函数

Clobbers

没用到

GotoLabels

没用到

函数的调用过程

了解自己系统的汇编代码

因为ABI很多,所以汇编代码得完全根据自己的机器来,我一开用《程序员的自我修养》里面的实例代码跑不通,后来想通了得自己看一下自己电脑上编译出来的汇编代码是什么。

先写一个测试程序

/*test.c*/
#include <stdio.h>
void foobar(int i)
{
  printf("foobar %d", i);
}
int main()
{
  asm volatile("nop");
  foobar(2);
  asm volatile("nop");
}

Untitled

nop做占位符可以很方便地找出关键代码,可以看到在我地机器上,main函数先往edi寄存器里写了一个2然后就调用foobar了,再来看看foobar里做了什么。

Untitled

一开始地三连击很熟悉,push,mov,sub,接下来从edi里读出值压入堆栈

所以很明显我的机器上,调用方调用函数之前只要把数值放入edi就行了

/*runso.c*/
#include <stdio.h>
#include <dlfcn.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  while (1)
  {
    void *handle;
    char *error;
    int i;
    long esp = 0;
    void *func;
    handle = dlopen("./dynamicLoad/bin/LibB.so", RTLD_NOW);
    if (handle == 0)
    {
      printf("Can't find LibB.so, use LibA.so.\n");
      handle = dlopen("./dynamicLoad/bin/LibA.so", RTLD_NOW);
      if (handle == 0)
      {
        printf("Can't find LibA.so,check out ./dynamicLoad/bin/ .");
        return -1;
      }
    }
    func = dlsym(handle, argv[1]);
    void (*func_void)() = func;
    asm volatile("mov %0,%%edi" ::"r"(2));
    func_void();
    dlclose(handle);
    sleep(2);
  }
}
/*Lib.c*/
#include <stdio.h>

void foobar(int i)
{
  printf("Printing from LibA.so %d\n", i);
  sleep(-1);
}

Untitled

Untitled