• 链接脚本使用一例2---将二进制文件 如图片、MP3音乐、词典一类的东西作为目标文件中的一个段


    参考文章:

    《程序员的自我修养——链接、转载与库》 P68

    这里介绍两种方法,实现将将一张图片作为二进制可执行程序的一个段,其中第一种方法在我之前的博客中已经有所介绍,不过,那是采用的是交叉编译的方法,这次直接全部在PC机上完成;第二种方法是我在看上面的那本书的时候看到的,觉着也不错。

    环境介绍: Win7 + VirtualBox + Debian6 + gcc version 4.4.5 (Debian 4.4.5-8)

    第一种方法

    • 目录结构

    pengdl@debian:~/test/c/pic2$ tree -lh
    .
    |-- [ 135]  data.S
    |-- [ 811]  main.c
    |-- [  91]  Makefile
    `-- [134K]  peng.png

    0 directories, 4 files

    • data.S
    1: .section .piggydata,"a" 2: .globl input_data 3: input_data: 4: .incbin"/home/pengdl/test/c/pic2/peng.png" 5: .globl input_data_end 6: input_data_end:

    这里需要注意:

    这段代码是我直接从Linux内核中拷贝出来的,并做了修改,源文件如下(arch/arm/boot/compressed/piggy.S):

    1: .section .piggydata,#alloc 2: .globl input_data 3: input_data: 4: .incbin "arch/arm/boot/compressed/piggy.gz" 5: .globl input_data_end 6: input_data_end:

    但是上面的代码除了需要修改.incbin之外,第一行的段属性部分也需要修改,将"#alloc"修改为"a",意思是:这个段可以重定位。如果不加段属性"a",运行时会发生段错误:

       1: pengdl@debian:~/test/c/pic2$ ./main 
       2: input_data       = (nil)
       3: input_data_end   = 0x2187e
       4: size             = 0x2187e
       5:  
       6: Segmentation fault

    使用objdump工具可以看看为什么?

    先看看data.o:

       1: pengdl@debian:~/test/c/pic2$ objdump -ht data.o
       2:  
       3: data.o:     file format elf32-i386
       4:  
       5: Sections:
       6: Idx Name          Size      VMA       LMA       File off  Algn
       7:   0 .text         00000000  00000000  00000000  00000034  2**2
       8:                   CONTENTS, ALLOC, LOAD, READONLY, CODE
       9:   1 .data         00000000  00000000  00000000  00000034  2**2
      10:                   CONTENTS, ALLOC, LOAD, DATA
      11:   2 .bss          00000000  00000000  00000000  00000034  2**2
      12:                   ALLOC
      13:   3 .piggydata    0002187e  00000000  00000000  00000034  2**0
      14:                   CONTENTS, READONLY
      15: SYMBOL TABLE:
      16: 00000000 l    d  .text    00000000 .text
      17: 00000000 l    d  .data    00000000 .data
      18: 00000000 l    d  .bss    00000000 .bss
      19: 00000000 l    d  .piggydata    00000000 .piggydata
      20: 00000000 g       .piggydata    00000000 input_data
      21: 0002187e g       .piggydata    00000000 input_data_end

    再看看main:

       1: pengdl@debian:~/test/c/pic2$ objdump -ht main | grep input_data
       2: 0002187e g       .piggydata    00000000              input_data_end
       3: 00000000 g       .piggydata    00000000              input_data

    从data.o的objdump信息可以看到,它的.piggydata是不能重定位的,再看看main的信息,input_data的地址直接就是0,在main.c中会发生访问0地址的段错误!!从main的运行打印信息中也可以看到:input_data = (nil)

    把.piggydata的段属性重新设置为"a",再看看objdump信息:

       1: pengdl@debian:~/test/c/pic2$ objdump -ht data.o
       2:  
       3: data.o:     file format elf32-i386
       4:  
       5: Sections:
       6: Idx Name          Size      VMA       LMA       File off  Algn
       7:   0 .text         00000000  00000000  00000000  00000034  2**2
       8:                   CONTENTS, ALLOC, LOAD, READONLY, CODE
       9:   1 .data         00000000  00000000  00000000  00000034  2**2
      10:                   CONTENTS, ALLOC, LOAD, DATA
      11:   2 .bss          00000000  00000000  00000000  00000034  2**2
      12:                   ALLOC
      13:   3 .piggydata    0002187e  00000000  00000000  00000034  2**0
      14:                   
    CONTENTS, ALLOC, LOAD, READONLY, DATA
      15: SYMBOL TABLE:
      16: 00000000 l    d  .text    00000000 .text
      17: 00000000 l    d  .data    00000000 .data
      18: 00000000 l    d  .bss    00000000 .bss
      19: 00000000 l    d  .piggydata    00000000 .piggydata
      20: 00000000 g       .piggydata    00000000 input_data
      21: 0002187e g       .piggydata    00000000 input_data_end
     
       1: pengdl@debian:~/test/c/pic2$ objdump -ht main | grep input_data
       2: 08069e63 g       .piggydata    00000000              input_data_end
       3: 080485e5 g       .piggydata    00000000              input_data

    可以看到,这下信息就正常了。

    • main.c
       1: #include <stdio.h>
       2:  
       3: int main(int argc, const char *argv[])
       4: {
       5:         int i;
       6:        
       7:         extern const unsigned long input_data; 
       8:         extern const unsigned long input_data_end; 
       9:  
      10:         const unsigned char *start = (const unsigned char *)&input_data; 
      11:         const unsigned char *end   = (const unsigned char *)&input_data_end; 
      12:         unsigned long size = end - start;
      13:  
      14:         printf("input_data       = %p
    ", &input_data);
      15:         printf("input_data_end   = %p
    ", &input_data_end);
      16:         printf("size             = %#0x
    ", size);
      17:  
      18:  
      19:  
      20:         for(i=0; i<size; i++)
      21:         {
      22:                 if(i % 16 == 0)
      23:                 {
      24:                         printf("
    %0#x		",i);
      25:                 }
      26:                 printf("%3x ",start[i]);
      27:         }
      28:  
      29:         printf("
    ");
      30:  
      31:          
      32:         return 0;
      33: }

    需要注意的是:如何使用data.S中的标号。

    • Makefile
       1: main:main.o data.o
       2:     $(CC) $^ -o $@
       3:  
       4: data.o:data.S
       5:  
       6: main.c:main.o
       7:  
       8:  
       9: clean: 
      10:     $(RM) *.o main
    • peng.png  就是一张普通的文件,与图片的格式无关。

    下面是运行结果:

    pengdl@debian:~/test/c/pic2$ ./main
    input_data       = 0x80485e5    // 数值跟main的objdump信息是一致的
    input_data_end   = 0x8069e63
    size             = 0x2187e  //注意指针相减的含义

    00         89  50  4e  47   d   a  1a   a   0   0   0   d  49  48  44  52
    0x10          0   0   1  28   0   0   1  89   8   2   0   0   0  b6  25  39
    0x20          5   0   0   0   9  70  48  59  73   0   0   e  c4   0   0   e
    0x30         c4   1  95  2b   e  1b   0   0  20   0  49  44  41  54  78  da
    0x40         ec  bd  67  5f  23  59  b2  af  db  1f  eb  ee  b3  a7  bb   c
    0x50         de   a  79  21  bc  24  bc  40  de  23  ef  bd  f0  de  43  51
    0x60         a6  ed  9c  fb   1  ef  3f  56  64  2e  a5   4  d5  33  67  df
    0x70         17  73  5e  54  fe  9e  d1  24  22  11  a2  2b  1f  45  c4  b2
    0x80         3f  7d  fe  eb  4f  f0  ed  af  bf  7a  f8  e3  cf  af  bf  ff
    0x90         c1  e0  9c  f9  f2  db  ef  2f  df  7e  7d  7c  f9  fc  f0  e9
    0xa0          5  dc  3e  3e  5d  dd  dd  9f  5f  df  9c  5e  5e  9d  5c  5c
    0xb0         82  e3  cb  cb  a3  f3  ab  c3  b3  cb  83  b3  b3  fd  d3  53
    0xc0         a6  7d  7c  dc  3e  3c  68  1d  ec  b7  3a  6d  d0  de  ef  b4

    下面是用winhex看到的:

    image

    第二种方法

    使用objcopy工具

    目录结构:

    pengdl@debian:~/test/c/pic$ tree -lh
    .
    |-- [ 999]  main.c
    |-- [  77]  Makefile
    |-- [135K]  peng.o
    `-- [134K]  peng.png

    0 directories, 4 files

    • peng.o

    使用如下命令生成peng.o:

    objdcopy –I binary –O elf32-i386 –B i386 peng.png peng.o

    objdump –ht peng.o

    参数解释:

    -I --input-target <bfdname>      Assume input file is in format <bfdname>
       -O --output-target <bfdname>     Create an output file in format <bfdname>
       -B --binary-architecture <arch>  Set arch of output file, when input is binary

    -h, --[section-]headers  Display the contents of the section headers

    -t, --syms               Display the contents of the symbol table(s)

       1: pengdl@debian:~/test/c/pic$ objdump -ht peng.o
       2:  
       3: peng.o:     file format elf32-i386
       4:  
       5: Sections:
       6: Idx Name          Size      VMA       LMA       File off  Algn
       7:   0 .data         0002187e  00000000  00000000  00000034  2**0
       8:                  
    CONTENTS, ALLOC, LOAD, DATA
       9: SYMBOL TABLE:
      10: 00000000 l    d  .data    00000000 .data
      11: 00000000 g       .data    00000000 _binary_peng_png_start
      12: 0002187e g       .data    00000000 _binary_peng_png_end
      13: 0002187e g       *ABS*    00000000 _binary_peng_png_size

    符号“_binary_peng_png_start”、“_binary_peng_png_end”和“_binary_peng_png_size”分别表示该图片文件在内存中的起始地址、结束地址和大小。

    可以看到,图片数据被放到了.data段。

    • main.c
       1: #include <stdio.h>
       2:  
       3: int main(int argc, const char *argv[])
       4: {
       5:         int i;
       6:        
       7:         extern const unsigned long _binary_peng_png_start; 
       8:         extern const unsigned long _binary_peng_png_size; 
       9:         extern const unsigned long _binary_peng_png_end; 
      10:  
      11:         const unsigned char *start = (const unsigned char *)&_binary_peng_png_start; 
      12:         const unsigned long size   = (const unsigned long)&_binary_peng_png_size; 
      13:         const unsigned char *end   = (const unsigned char *)&_binary_peng_png_end; 
      14:  
      15:         printf("_binary_peng_png_start = %p
    ", &_binary_peng_png_start);
      16:         printf("_binary_peng_png_size  = %p
    ", &_binary_peng_png_size);
      17:         printf("_binary_peng_png_end   = %p
    ", &_binary_peng_png_end);
      18:  
      19:  
      20:  
      21:         for(i=0; i<size; i++)
      22:         {
      23:                 if(i % 16 == 0)
      24:                 {
      25:                         printf("
    %0#x		",i);
      26:                 }
      27:                 printf("%3x ",start[i]);
      28:         }
      29:  
      30:         printf("
    ");
      31:  
      32:          
      33:         return 0;
      34: }
    • Makefile
       1: main:main.o peng.o
       2:     $(CC) $^ -o $@
       3:  
       4: main.c:main.o
       5:  
       6:  
       7: clean: 
       8:     $(RM) *.o main

    下面是运行结果:

    pengdl@debian:~/test/c/pic$ ./main
    _binary_peng_png_start = 0x80496f4
    _binary_peng_png_size  = 0x2187e
    _binary_peng_png_end   = 0x806af72

    0         89  50  4e  47   d   a  1a   a   0   0   0   d  49  48  44  52
    0x10          0   0   1  28   0   0   1  89   8   2   0   0   0  b6  25  39
    0x20          5   0   0   0   9  70  48  59  73   0   0   e  c4   0   0   e
    0x30         c4   1  95  2b   e  1b   0   0  20   0  49  44  41  54  78  da
    0x40         ec  bd  67  5f  23  59  b2  af  db  1f  eb  ee  b3  a7  bb   c
    0x50         de   a  79  21  bc  24  bc  40  de  23  ef  bd  f0  de  43  51
    0x60         a6  ed  9c  fb   1  ef  3f  56  64  2e  a5   4  d5  33  67  df
    0x70         17  73  5e  54  fe  9e  d1  24  22  11  a2  2b  1f  45  c4  b2
    0x80         3f  7d  fe  eb  4f  f0  ed  af  bf  7a  f8  e3  cf  af  bf  ff
    0x90         c1  e0  9c  f9  f2  db  ef  2f  df  7e  7d  7c  f9  fc  f0  e9
    0xa0          5  dc  3e  3e  5d  dd  dd  9f  5f  df  9c  5e  5e  9d  5c  5c

    。。。。。。

    完!!!

  • 相关阅读:
    按输入行数,输出对应的倒等腰三角形星星
    按输入行、列数,输出对应的矩形星星
    运算程序,计算玩判断,Y继续,重复计算,N结束
    输入年 月 日 ,计算时该年的第几天
    for计算100以内的奇数和
    创建一个Android项目
    菜鸟大充电啦啦啦啦啦:百度上的安卓学习资料以及我的视频学习资料
    菜鸟大充电啦啦啦啦啦:eclipse SDK 是什么啊
    学习过程的记录:实验室电脑上的jdk环境变量
    WHAT is CPU负载?
  • 原文地址:https://www.cnblogs.com/pengdonglin137/p/3721505.html
Copyright © 2020-2023  润新知