4722|0

222

帖子

2

TA的资源

一粒金砂(高级)

楼主
 

软件版本自描述 [复制链接]

本帖最后由 lzwml 于 2016-6-10 17:58 编辑

#软件版本自描述


通常在windows下某程序的运行版本能在菜单栏的“About xxx”能看到具体描述,Linux下查阅程序版本方式也有一套俗成的方法,程序名后加“-V”。对于一个平台查阅本平台应用程序的版本信息没什么难度,而现实中有或许会遇到同事拿着一个可执行程序要你确定版本的情况。


这种情况通常出现在产品开发初期小批量试生产阶段,程序最终版本未定型,试生产过程经常有版本变动,存在批量生产的设备程序版本不一致情况。还有一种情况是研发人员特意给某几台设备下载不同程序,被下载差异程序的设备通常是在之前小批量试生产测试中发现软、硬缺陷记录,研发人员测试修改后的软件是否已经修复软件缺陷或绕过硬件缺陷。


一个软件版本自描述应该包括:软件版本号,编译日期,甚至可包括简略的Change log。下面我介绍两种我曾经用过的软件版本自描述信息添加、查阅方法,该方法能在不运行目标平台机器码的情况下阅读。


首先介绍简略介绍在linux平台下可执行程序格式——ELF文件格式。处理器可执行的编码是架构所识别的机器码,该编码构成的文件叫做Bin文件(raw binary),ELF文件是Bin文件前加入一个“自描述头”——ELF头,


对软件版本自描述只关心ELF头描述如下信息:
* Class(说明该文件是ELF格式)
* Type:文件类型(可执行程序、动态链接库、模块)
* Machine:平台
* Section:有多少个节


```
root@UbuntuServer:~$ readelf -h /bin/ls
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8049d60
  Start of program headers:          52 (bytes into file)
  Start of section headers:          95164 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         29
  Section header string table index: 28


root@UbuntuServer:~$ readelf -h /lib/libreadline.so.6.1                  
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0xbff0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          203780 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         27
  Section header string table index: 26


root@UbuntuServer:~$ readelf -h /lib/modules/2.6.35-22-generic/kernel/sound/i2c/other/snd-ak4113.ko
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              REL (Relocatable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x0
  Start of program headers:          0 (bytes into file)
  Start of section headers:          7568 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         31
  Section header string table index: 28
```


上面的声卡驱动snd-ak4113.ko文件里有31个section(Number of section headers:         31),具体这31个section是什么打开看一下,从0~30共31个section


```
root@UbuntuServer:~$ readelf -S /lib/modules/2.6.35-22-generic/kernel/sound/i2c/other/snd-ak4113.ko
There are 31 section headers, starting at offset 0x1d90:


Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .note.gnu.build-i NOTE            00000000 000034 000024 00   A  0   0  4
  [ 2] .text             PROGBITS        00000000 000060 000c68 00  AX  0   0 16
...
  [16] .modinfo          PROGBITS        00000000 000e60 00011b 00   A  0   0 32
...
  [30] .strtab           STRTAB          00000000 002948 0007ba 00      0   0  1
```
31个section中有一个比较特别,同时也是.ko文件特有的段,.modinfo,该段记录该模块的版权信息、作者、依赖等,总之驱动编写者想上面描述都可以。可用modinfo查阅
```
root@UbuntuServer:raw$ modinfo /lib/modules/2.6.35-22-generic/kernel/sound/i2c/other/snd-ak4113.ko
filename:       /lib/modules/2.6.35-22-generic/kernel/sound/i2c/other/snd-ak4113.ko
license:        GPL
description:    AK4113 IEC958 (S/PDIF) receiver by Asahi Kasei
author:         Pavel Hofman <pavel.hofman@ivitera.com>
srcversion:     B3EC7090715816C9FAF1DFC
depends:        snd-pcm,snd
vermagic:       2.6.35-22-generic SMP mod_unload modversions 686
```
modinfo是怎么工作的呢?首先返回readelf -S查阅到的.modinfo section偏移位置和大小以16进制表示分别是0xe60、0x11b,该section内容完全以ASCII码写入,实际上modinfo的工作就是在ELF文头里找是否存在.modinfo这个section,如果存在则将其所有内容打印出来,下面是用hexdump以文本方式打印.modinfo的效果。看是不是与modinfo工具输出的一致。
```
root@UbuntuServer:cifs$ hexdump -C -n 283 -s 3680 /lib/modules/2.6.35-22-generic/kernel/sound/i2c/other/snd-ak4113.ko
00000e60  6c 69 63 65 6e 73 65 3d  47 50 4c 00 00 00 00 00  |license=GPL.....|
00000e70  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000e80  64 65 73 63 72 69 70 74  69 6f 6e 3d 41 4b 34 31  |description=AK41|
00000e90  31 33 20 49 45 43 39 35  38 20 28 53 2f 50 44 49  |13 IEC958 (S/PDI|
00000ea0  46 29 20 72 65 63 65 69  76 65 72 20 62 79 20 41  |F) receiver by A|
00000eb0  73 61 68 69 20 4b 61 73  65 69 00 00 00 00 00 00  |sahi Kasei......|
00000ec0  61 75 74 68 6f 72 3d 50  61 76 65 6c 20 48 6f 66  |author=Pavel Hof|
00000ed0  6d 61 6e 20 3c 70 61 76  65 6c 2e 68 6f 66 6d 61  |man
00000ee0  6e 40 69 76 69 74 65 72  61 2e 63 6f 6d 3e 00 00  |n@ivitera.com>..|
00000ef0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000f00  73 72 63 76 65 72 73 69  6f 6e 3d 42 33 45 43 37  |srcversion=B3EC7|
00000f10  30 39 30 37 31 35 38 31  36 43 39 46 41 46 31 44  |090715816C9FAF1D|
00000f20  46 43 00 64 65 70 65 6e  64 73 3d 73 6e 64 2d 70  |FC.depends=snd-p|
00000f30  63 6d 2c 73 6e 64 00 00  00 00 00 00 00 00 00 00  |cm,snd..........|
00000f40  76 65 72 6d 61 67 69 63  3d 32 2e 36 2e 33 35 2d  |vermagic=2.6.35-|
00000f50  32 32 2d 67 65 6e 65 72  69 63 20 53 4d 50 20 6d  |22-generic SMP m|
00000f60  6f 64 5f 75 6e 6c 6f 61  64 20 6d 6f 64 76 65 72  |od_unload modver|
00000f70  73 69 6f 6e 73 20 36 38  36 20 00                 |sions 686 .|
```


铺垫已经说完,可以看到ELF文件的头的读取是与平台无关的,某些section还能以ASCII格式存储。我所想要的软件版本自描述就从这些section入手,只需ELF头里加入自己附加的section,用该section描述版本自描述信息。


# 做自己的section
建立一个.proginfo的段,通过宏PROG_INFO定义段内所有变量的变量名与变量值,变量名所有变量均为字符型数据,变量名前附加前缀__pi_。
```
#define __stringify(x...)        #x


#define PROG_INFO(name,desc) \
        volatile char   __pi_##name[] __attribute__ ((unused,section (".proginfo"))) = __stringify(name) "=" desc;


#ifdef BUILD_DATE
PROG_INFO(build_date, BUILD_DATE "\n");
#endif


PROG_INFO(author, "I'm author\n");
PROG_INFO(version, "1.2.3\n");
PROG_INFO(change,
        "\n\t"
        "1. Test bug\n\t"
        "2. fix xxx\n\t"
        "3. add xxx\n\t"
        "4. add xxx\n\t"
        "5. add xxx\n");
```


最终在段内看到的变量名像这样
```
root@UbuntuServer:raw$ readelf  -s describe.elf | grep __pi_
    48: 0804a040    19 OBJECT  GLOBAL DEFAULT   24 __pi_author
    54: 0804a053    15 OBJECT  GLOBAL DEFAULT   24 __pi_version
    60: 0804a080    70 OBJECT  GLOBAL DEFAULT   24 __pi_change
    62: 0804a020    32 OBJECT  GLOBAL DEFAULT   24 __pi_build_date
```
上面有个神奇的变量__pi_build_date,它的值是BUILD_DATE,而BUILD_DATE并不是一个固定值,每次编译后Makefile调用系统时间BUILD_DATA赋值给它,BUILD_DATE在编译C文件时对所有C文件可见。为什么特别要对BUILD_DATE动态处理呢?其中之一是照顾爱偷懒的研发人员,另外通常软件的修订内容change log和version长期不会变动;修改微乎其微的部分没达到写入change log要求;或者出于原始产品有严重缺陷,但一直没对客户承认,在修订后此类型也不便于写入change log。这些修订细节只能保存在公司内部版本控制服务器上,通过编译时间来追踪。


```
NOWTIME="$(shell date "+%Y-%m-%d_%H:%M:%S")"
CC_FLAGS    = -std=gnu99  -DBUILD_DATE=\"$(NOWTIME)\"
```


看看我们的.proginfo段有没有添加到ELF头呢?
```
root@UbuntuServer:raw$ readelf  -S describe.elf  
There are 30 section headers, starting at offset 0x122c:


Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
...
  [24] .proginfo         PROGBITS        0804a020 001020 0000a6 00  WA  0   0 32
...
  [29] .strtab           STRTAB          00000000 001b1c 00021f 00      0   0  1
```
显然已经添加进去了,接着就把它读出来呗,我提供一个脚本——pgi来读取.proginfo内容,如下成功读出。
```
root@UbuntuServer:raw$ ./pgi  describe.elf
166+0 records in
166+0 records out
166 bytes (166 B) copied, 0.000839059 s, 198 kB/s
==========
build_date=2016-06-10_11:42:13
author=I'm author
version=1.2.3
change=
        1. Test bug
        2. fix xxx
        3. add xxx
        4. add xxx
        5. add xxx
==========
```


其实modinfo也可以读取我们自己添加的section,看前那么对modinfo原理点的解释,它只是找ELF文件里是否存在.modnifo section,那么将.proginfo改成.modinfo,modinfo工具就可以读出里面的内容。
```
root@UbuntuServer:raw$ modinfo   describe.elf            
filename:       describe.elf
build_date:     2016-06-10_11:59:56


author:         I'm author


version:        1.2.3


change:         
        1. Test bug
        2. fix xxx
        3. add xxx
        4. add xxx
        5. add xxx
```
# 还有Bin文件呢
ELF格式以及说完,那么BIN文件又怎样做呢?它可是赤裸裸的机器码,不可能在前段加入ASCII内容。


我曾经参与开发过某机架式系统,机框内插入8-10种类型板卡,上百块板卡与网管服务器通过TCP连接。最初,在做该系统时板卡只有一种类型,产品在需要升级时候服务器通过TCP将Bin文件下发到板卡即可。随着公司的发展为了让客户体验系统扩容的便利,客户只需安装一个网管即可与公司现有业务板通信,那么板卡升级维护就成了问题。


网管下发升级包给业务板可能存在程序类型错误,本来A的程序给B板卡升级,两板卡属于不同业务板(不同处理器架构),按理说被升级对象应该具备检测升级包是否适应本平台的容错功能,处理器内升级程序和业务程序分离,即使升级程序下载错误,系统重启调用新程序会因为Bin的架构不兼容导致错误,系统弹出硬件中断终止程序,最后再重新执行升级程序等待下一次升级。


上面的应对措施寄托于研发人员对处理器平台的熟悉才能办到。不是开玩笑,有些公司研发人员对处理器的了解仅限于点亮LED灯,拿着开发板提供商的例程稀里糊涂的开发产品,遇到与底层相关缺陷根本无法处理,这种公司没有什么研发能力,从市场上讨来几个二次开发的集成模块组合在一起就是新产品。现实就是这样。


为了解决板卡升级程序张冠李戴的问题还有一套是靠人工解决的方法,升级程序文件名命名规则,人总会犯错,这种方法只能用于产品生产阶段,升级阶段搞不好一样把设备烧成砖头。


# 升级包也有头
与ELF文件一样,升级包也对Bin文件加上一个头,描述升级包所适应的板卡,网管检查相应字段检测决定是否对板卡升级,符合升级条件则取出升级包头部内容,将Bin格式传输给板卡。这种方法不寄托于操作人员的素质,也不依靠被升级板卡容错设计。


升级头:
* 文件类型:该文件是一个升级包文件
* 大小:升级包头部的大小,从文件头开始偏移该大小则是Bin文件内容
* 升级对象:该升级包适应的板卡
* 时间:升级包的打包时间
* 自描述信息:若干字节,描述该升级包的内容












describe.rar

3.21 KB, 下载次数: 2

ELF头加入section

upp.rar

201.62 KB, 下载次数: 3

bin文件添加升级头

点赞 关注

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表