size命令的sysv和berkeley格式差别

size命令使用说明

size命令用于显示二进制文件的段(节)大小,其功能类似于readelf -S,详细的说明如下:

用法:size [选项] [文件]
 显示二进制文件中节的大小
 没有给出输入文件,默认为 a.out
 The options are:
  -A|-B     --format={sysv|berkeley}  Select output style (default is berkeley)
  -o|-d|-x  --radix={8|10|16}         Display numbers in octal, decimal or hex
  -t        --totals                  Display the total sizes (Berkeley only)
            --common                  Display total size for *COM* syms
            --target=<bfdname>        Set the binary file format
            @<file>                   Read options from <file>
  -h        --help                    Display this information
  -v        --version                 Display the program's version

size:支持的目标: elf64-x86-64 elf32-i386 elf32-x86-64 a.out-i386-linux pei-i386  
pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big  
pe-x86-64 pe-i386 plugin srec symbolsrec verilog tekhex binary ihex

背景

size支持两种输出格式sysvberkeley,对同一个文件,看看执行效果有什么差异

[GMPY@11:41 tmp]$size test
   text    data     bss     dec     hex filename
   1810     584       8    2402     962 test
[GMPY@11:41 tmp]$size --format=sysv test
test  :
section              size      addr
...
.text                 640   4195616
...
.data                  16   6295624
.bss                    8   6295640
...
Total                2496

为了方便了解,这里补充以下3个重要段的功能:

段名 功能
BSS 存放程序中未初始化的全局变量的一块内存区域
DATA 存放程序中已初始化的全局变量的一块内存区域
TEXT/CODE 存放程序执行代码的一块内存区域

可以发现,test/data/bss段的大小不一致呀,我该相信哪一个?

源码

size命令是binutils软件包提供的子命令,在GNU的官网中找到其最新源码(2.32)

废话不多说,下载,解压,直接看源码(binutils/size.c)

static void
print_sizes (bfd *file)
{
  if (show_common)
    calculate_common_size (file);
  if (berkeley_format)
    print_berkeley_format (file);
  else
    print_sysv_format (file);
}

根据不同格式选择不同的打印方式,妥了,直接对比print_berkeley_formatprint_sysv_format

对比print_berkeley_format与print_sysv_format

berkeley格式

static void
print_berkeley_format (bfd *abfd)
{
  ...
  bsssize = 0;
  datasize = 0;
  textsize = 0;

  /*
   * 获取bss/data/text段大小
   * 找不到bfd_map_over_sections的定义,但猜测是一个宏
   * 功能:对每一个段调用berkeley_sum函数进行处理
   */
  bfd_map_over_sections (abfd, berkeley_sum, NULL);

  /* 打印信息头 */
  if (files_seen++ == 0)
    puts ((radix == octal) ? "   text\t   data\t    bss\t    oct\t    hex\tfilename" :
      "   text\t   data\t    bss\t    dec\t    hex\tfilename");

  /* 打印具体的数值 */
  rprint_number (7, textsize);
  putchar ('\t');
  rprint_number (7, datasize);
  putchar ('\t');
  rprint_number (7, bsssize);
  printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"),
      (unsigned long) total, (unsigned long) total);
  ...
}

sysv格式

static void
print_sysv_format (bfd *file)
{
  svi_total = 0;
  svi_maxvma = 0;
  svi_namelen = 0;

  /*
   * 获取bss/data/text段大小
   * 找不到bfd_map_over_sections的定义,但猜测是一个宏
   * 功能:对每一个段调用sysv_internal_printer函数进行处理
   */
  bfd_map_over_sections (file, sysv_internal_printer, NULL);
  if (show_common)
    {
      svi_total += common_size;
      sysv_one_line ("*COM*", common_size, 0);
    }
  ......
}

好吧,我们关注的是两者的数值差异,需要进一步对比子函数berkeley_sumsysv_internal_printer

对比berkeley_sum和sysv_internal_printer

berkeley格式

static void
berkeley_sum (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr sec,
          void *ignore ATTRIBUTE_UNUSED)
{
  flagword flags;
  bfd_size_type size;

  flags = bfd_get_section_flags (abfd, sec);
  if ((flags & SEC_ALLOC) == 0)
    return;

  size = bfd_get_section_size (sec);

  /* 根据不同段的属性,进行不同类别的累加 */
  if ((flags & SEC_CODE) != 0 || (flags & SEC_READONLY) != 0)
    /* text/code 段 */
    textsize += size;
  else if ((flags & SEC_HAS_CONTENTS) != 0)
    /* data 段 */
    datasize += size;
  else
    /* bss 段 */
    bsssize += size;
}

sysv格式:

static void
sysv_one_line (const char *name, bfd_size_type size, bfd_vma vma)
{
  printf ("%-*s   ", svi_namelen, name);
  rprint_number (svi_sizelen, size);
  printf ("   ");
  rprint_number (svi_vmalen, vma);
  printf ("\n");
}

static void
sysv_internal_printer (bfd *file ATTRIBUTE_UNUSED, sec_ptr sec,
               void *ignore ATTRIBUTE_UNUSED)
{
  bfd_size_type size = bfd_section_size (file, sec);

  if (   ! bfd_is_abs_section (sec)
      && ! bfd_is_com_section (sec)
      && ! bfd_is_und_section (sec))
    {
      svi_total += size;

      /* 分别打印出每一个段的信息 */
      sysv_one_line (bfd_section_name (file, sec),
             size,
             bfd_section_vma (file, sec));
    }
}

结论

实锤了,berkeley格式与sysv格式下的bss/data/text是不同的含义,其中

  • sysv是实打实的打印出每一个段的大小,等效于readelf -S
  • berkeley是统计的结果,把代码段和只读的段统计到text段,把有内容的段统计到data段,其他全归属bss段

在只需要知道分类的统计结果时用berkelay格式,在需要明细到每一个段时采用sysv格式

posted @ 2019-08-19 12:19 广漠飘羽 阅读(...) 评论(...) 编辑 收藏