参考文章:

《程序员的自我修养——链接、转载与库》 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\n", &input_data);

  15:         printf("input_data_end   = %p\n", &input_data_end);

  16:         printf("size             = %#0x\n", size);

  17:  

  18:  

  19:  

  20:         for(i=0; i<size; i++)

  21:         {

  22:                 if(i % 16 == 0)

  23:                 {

  24:                         printf("\n%0#x\t\t",i);

  25:                 }

  26:                 printf("%3x ",start[i]);

  27:         }

  28:  

  29:         printf("\n");

  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看到的:

第二种方法

使用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\n", &_binary_peng_png_start);

  16:         printf("_binary_peng_png_size  = %p\n", &_binary_peng_png_size);

  17:         printf("_binary_peng_png_end   = %p\n", &_binary_peng_png_end);

  18:  

  19:  

  20:  

  21:         for(i=0; i<size; i++)

  22:         {

  23:                 if(i % 16 == 0)

  24:                 {

  25:                         printf("\n%0#x\t\t",i);

  26:                 }

  27:                 printf("%3x ",start[i]);

  28:         }

  29:  

  30:         printf("\n");

  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

。。。。。。

完!!!

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

  1. 链接脚本(Linker Script)应用实例(一)使用copy table将函数载入到RAM中运行

    将函数载入到RAM中运行需要以下三个步骤: (1)用编译器命令#pragma section "<section name>" <user functions&g ...

  2. 四、u-boot 链接脚本

    4.1 C语言中的段 编译器在编译程序的时候,将程序中的所有的元素分成了一些组成部分,各部分构成一个段,所以说段是可执行程序的组成部分. 代码段:代码段就是程序中的可执行部分,直观理解代码段就是函数堆 ...

  3. GNU linker script,ld script,GNU链接脚本

    https://blog.csdn.net/itxiebo/article/details/50937412 https://blog.csdn.net/itxiebo/article/details ...

  4. [转]Linux下的lds链接脚本详解

    转载自:http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.shtml     一. 概论 每一个链接过程都由链接脚本(lin ...

  5. Linux下的lds链接脚本简介

    转载:http://hubingforever.blog.163.com/blog/static/171040579201192472552886/   一. 概论 每一个链接过程都由链接脚本(lin ...

  6. makefile使用.lds链接脚本以及 $@ ,$^, $,< 解析

    先来分析一个简单的.lds链接脚本 例1,假如现在有head.c init.c nand.c main.c这4个文件: 1.1 首先创建链接脚本nand.lds: SECTIONS { firtst ...

  7. [转]Linux下的链接脚本基础

    [转]http://linux.chinaunix.net/techdoc/beginner/2009/08/12/1129972.shtml 1. 前言 (1)每一个链接过程都由链接脚本(linke ...

  8. Linux下的lds链接脚本详解【转】

    转自:http://www.cnblogs.com/li-hao/p/4107964.html 转载自:http://linux.chinaunix.net/techdoc/beginner/2009 ...

  9. makefile使用.lds链接脚本以及 $@ ,$^, $,< 解析【转】

    转自:http://www.cnblogs.com/lifexy/p/7089873.html 先来分析一个简单的.lds链接脚本 例1,假如现在有head.c init.c nand.c main. ...

随机推荐

  1. Javascript&Html-延迟调用和间歇调用

    Javascript&Html-延迟调用和间歇调用 Javascript 是一种单线程语言,所有的javascript任务都会放到一个任务列表中,这些javascript任务会按照插入到列表中 ...

  2. MVC中的过滤器/拦截器怎么写

    创建一个AuthenticateFilterAttribute(即过滤器/拦截器) 引用System.Web.Mvc; public class AuthenticateFilterAttribute ...

  3. Qualcomm 專業名詞

    APSS Application processor subsystem software BAM Bus Access Manager blsp BAM low speed peripheral T ...

  4. 用Python和Pygame写游戏-从入门到精通(py2exe篇)

    这次不是直接讲解下去,而是谈一下如何把我们写的游戏做成一个exe文件,这样一来,用户不需要安装python就可以玩了.扫清了游戏发布一大障碍啊! perl,python,java等编程语言,非常好用, ...

  5. C#图解教程学习笔记——接口

    一.接口概念接口是指定一组函数成员而不实现它们的引用类型.所以只能类和结构来实现接口. 二.声明接口1. 接口声明不能包含:数据成员.静态成员,只能包含以下类型的非静态成员函数:方法.属性.事件.索引 ...

  6. RadioGroup动态添加RadioButton,并且获得事件

    由于有许多的RadioButton是动态的,不是固定的一些,所以需要在代码中,动态的添加到RadioGroup中,下面是我的实现方法. 1.添加RadioButton到RadioGroup中 Radi ...

  7. 第十三届北航程序设计竞赛决赛网络同步赛 B题 校赛签到(建树 + 打标记)

    题目链接  校赛签到 对每个操作之间建立关系. 比较正常的是前$3$种操作,若第$i$个操作属于前$3$种,那么就从操作$i-1$向$i$连一条有向边. 比较特殊的是第$4$种操作,若第$i$个操作属 ...

  8. Java 实现多线程切换等待唤醒交替打印奇偶数

    引言 在日常工作生活中,可能会有用时几个人或是很多人干同一件事,在java编程中,同样也会出现类似的情况,多个线程干同样一个活儿,比如火车站买票系统不能多个人买一到的是同一张票,当某个窗口(线程)在卖 ...

  9. hdu 3062 Party 2-SAT

    题目链接:HDU - 3062 有n对夫妻被邀请参加一个聚会,因为场地的问题,每对夫妻中只有1人可以列席.在2n 个人中,某些人之间有着很大的矛盾(当然夫妻之间是没有矛盾的),有矛盾的2个人是不会同时 ...

  10. Windows 远程桌面文件传输的方法

    实现电脑的远程连接以后,很多时候会需要进行主机间的文件传输,这个时候就可以用系统自带的远程连接里的磁盘映射来完成,详细如下: 远程桌面程序内置了映射磁盘的功能,通过这个功能可以实现远程登录服务器时自动 ...