简体   繁体   中英

how does .bss segment allocates and deallocates the memory for the C program?

I am trying to understand the logic behind the .bss memory allocation and deallocation

I have few cases which I have tried to understand the same.

I concluded that it is incrementing in the chunks of 8 bytes if I declare any variable in the global scope of the code because of the linker script of mine is having the alignment of 8 bytes

but when I define the variable inside the same scope it is deallocating the memory from the .bss

but exactly what is the logical calculations behind the deallocation is which I do not understand?

case1 :

#include <stdio.h>

int a;
int b;

int main(void){
        return 0;
}

this code snippet generates the below sizes for the different segments

   text    data     bss     dec     hex filename
   1418     544      16    1978     7ba a.out

now if I initialize one int variable like this

case 2:

#include <stdio.h>

int a = 1;
int b;

int main(void){
        return 0;
}

this gives me the following size table

   text    data     bss     dec     hex filename
   1418     548      12    1978     7ba a.out

now if I initialize one char before the int variable like this

case 3:

#include <stdio.h>

char a = 1;
int b;

int main(void){
        return 0;
}

it is showing

text    data     bss     dec     hex filename
1418     545      12    1978     7ba a.out

this is the nm -n output specifically for the case 3

0000000000004000 D __data_start
0000000000004000 W data_start
0000000000004008 D __dso_handle
0000000000004010 D a
0000000000004011 B __bss_start
0000000000004011 D _edata
0000000000004014 b completed.8060
0000000000004018 D __TMC_END__
0000000000004018 B b
0000000000004020 B _end

now if I initialize one int variable like this

case 4:

#include <stdio.h>

int a = 1;
char b;

int main(void){
        return 0;
}

it is showing

text    data     bss     dec     hex filename
1418     548      4    1978     7ba a.out

now if I initialize one int variable like this

case 5:

#include <stdio.h>

char a = 1;
char b;

int main(void){
        return 0;
}

it is showing

text    data     bss     dec     hex filename
1418     545      7    1978     7ba a.out

anyone please help me to understand this ?

Symbols of a given type have a natural alignment that the linker must honor (eg int must be 4 byte aligned). Within a given section (eg .bss ), the linker has some leeway to reorder the symbols so that the alignment of a given symbol doesn't cause excess padding. If we have:

char a;
int b;

Without the reordering, the load map would look like:

0000 a
0004 b

And, the section length would be 8.

With reordering:

0000 b
0000 a

And, the section length would be 5

Note that the size of the sections is aligned upward to some multiple (usually 8 bytes). So, for example, if a section has only 1 byte of data, the section size will be reported as 8.

But, there are exceptions.

One of the ways to see this more clearly is to simplify the data:

  1. We can elide any libraries or startup code to reduce the executable size.

  2. We can use addresses relative to the start of the first data section (eg start of .data )

  3. Combine results from all cases into a single table

I've created a [perl] script to do that. It has your original test cases and some additional ones.

Of particular note is case 6 ...

Below is the output of the script. The final table [and discussion] is in the FINAL section below.


Case 1:

Source:

int a;
int b;
int _start(void) { return 0; }

Command: nm -n xfile

0000 B b
0000 B __bss_start
0000 B _edata
0004 B a
0008 B _end

Command: size xfile

   text    data     bss     dec     hex filename
     50       0       8      58      3a xfile

Case 2:

Source:

int a = 1;
int b;
int _start(void) { return 0; }

Command: nm -n xfile

0000 D a
0004 B b
0004 B __bss_start
0004 D _edata
0008 B _end

Command: size xfile

   text    data     bss     dec     hex filename
     50       4       4      58      3a xfile

Case 3:

Source:

char a = 1;
int b;
int _start(void) { return 0; }

Command: nm -n xfile

0000 D a
0001 B __bss_start
0001 D _edata
0004 B b
0008 B _end

Command: size xfile

   text    data     bss     dec     hex filename
     50       1       4      55      37 xfile

Case 4:

Source:

int a = 1;
char b;
int _start(void) { return 0; }

Command: nm -n xfile

0000 D a
0004 B b
0004 B __bss_start
0004 D _edata
0008 B _end

Command: size xfile

   text    data     bss     dec     hex filename
     50       4       4      58      3a xfile

Case 5:

Source:

char a = 1;
char b;
int _start(void) { return 0; }

Command: nm -n xfile

0000 D a
0001 B b
0001 B __bss_start
0001 D _edata
0008 B _end

Command: size xfile

   text    data     bss     dec     hex filename
     50       1       7      58      3a xfile

Case 6:

Source:

char a;
int b;
char c = 1;
int d = 1;
int _start(void) { return 0; }

Command: nm -n xfile

0000 D d
0004 D c
0005 B __bss_start
0005 D _edata
0008 B b
000C B a
0010 B _end

Command: size xfile

   text    data     bss     dec     hex filename
     50       5       8      63      3f xfile

FINAL:

CASE    DATA    BSS     DEFS
1       0       8       int a; int b;
                        00/B/b 00/B/__bss_start 00/B/_edata 04/B/a 08/B/_end
2       4       4       int a = 1; int b;
                        00/D/a 04/B/b 04/B/__bss_start 04/D/_edata 08/B/_end
3       1       4       char a = 1; int b;
                        00/D/a 01/B/__bss_start 01/D/_edata 04/B/b 08/B/_end
4       4       4       int a = 1; char b;
                        00/D/a 04/B/b 04/B/__bss_start 04/D/_edata 08/B/_end
5       1       7       char a = 1; char b;
                        00/D/a 01/B/b 01/B/__bss_start 01/D/_edata 08/B/_end
6       5       8       char a; int b; char c = 1; int d = 1;
                        00/D/d 04/D/c 05/B/__bss_start 05/D/_edata 08/B/b 0C/B/a 10/B/_end

Note that in case 6, the .data length is 5. And, the starting address of .bss is [also] 5. But, the lowest .bss address actually used is 8 (for b )


Here is the script source:

#!/usr/bin/perl
# bssgen -- generate .bss data
#
# options:
#   "-c" -- use .o instead of executable
#   "-O" -- optimzation level (DEFAULT: 2)
#   "-v" -- output to stdout (DEFAULT: off)

master(@ARGV);
exit(0);

sub master
{
    my(@argv) = @_;

    $opt_c = 0;
    $opt_O = 2;

    $pubfile = "bssgen.txt";
    printf("output to %s\n",$pubfile);
    $xfpub = xfopen(">$pubfile","master");

    optget(\@argv,
        ["c",1],
        ["n",1],
        ["O",1]);

    my($xfsrc) = "bssgen::DATA";
    $xfsrc = \*$xfsrc;

    while ($bf = <$xfsrc>) {
        chomp($bf);

        if ($bf =~ /^\s*$/) {
            dotest();
        }
        else {
            push(@defs,$bf);
        }
    }

    dotest()
        if (@defs > 0);

    final();

    $xfpub = xfclose($xfpub);
}

sub final
{

    prtsct("FINAL:");

    prtsep();

    @prtcol_tabs = (0,8,16,24);

    prtcol("%s","CASE");
    prtcol("%s","DATA");
    prtcol("%s","BSS");
    prtcol("%s","DEFS");
    prtcol();

    $caseno = 0;
    foreach $tst (@tstlist) {
        ++$caseno;

        prtcol("%d",$caseno);
        prtcol("%d",$tst->{tst_data});
        prtcol("%d",$tst->{tst_bss});
        prtcol("%s",$tst->{tst_defs});
        prtcol();

        prtcol("%s");
        prtcol("%s");
        prtcol("%s");

        my($buf);
        my($nmlhs) = $tst->{tst_nm};
        my($base);
        foreach $nm (@$nmlhs) {
            $buf .= " "
                if (defined($buf));
            $buf .= sprintf("%2.2X/%s/%s",
                $nm->{addr},$nm->{typ},$nm->{sym});
        }
        prtcol("%s",$buf);
        prtcol();
    }

    prtsep();
}

sub dotest
{

    prtsct("Case %d:",++$caseno);

    my($root) = "xfile";
    my($sfile) = $root . ".c";
    my($ofile) = $root . ".o";
    my($xfile) = $root;
    my($run) = $root;

    local($xfdst) = xfopen(">$sfile","dotest/sfile");

    prtpub("Source:\n");
    prtsep();
    foreach $bf (@defs) {
        prtcode("%s\n",$bf);
    }
    prtcode("int %s(void) { return 0; }\n",$opt_c ? "main" : "_start");
    prtsep();

    $xfdst = xfclose($xfdst);

    my(@cflags);
    push(@cflags,"-O")
        if ($opt_O);
    push(@cflags,"-c");
    doexec("cc",@cflags,$sfile);

    unless ($opt_c) {
        doexec("ld","-o",$xfile,$ofile);
        $run = $xfile;
    }
    else {
        $run = $ofile;
    }

    my(@nm);
    push(@nm,"-n");
    @nmrhs = grab(0,"nm",@nm,$run);
    my($base);
    my(@nmlhs);
    prtsep();
    foreach $nm (@nmrhs) {
        chomp($nm);

        my($addr,$typ,$sym) = split(" ",$nm);
        next if ($typ eq "T");

        $addr = hex("0x$addr");
        $base //= $addr;

        $nm = {};
        $nm->{addr} = $addr - $base;
        $nm->{typ} = $typ;
        $nm->{sym} = $sym;

        prtpub("%4.4X %s %s\n",$nm->{addr},$nm->{typ},$nm->{sym});

        push(@nmlhs,$nm);
    }
    prtsep();

    my(@siz) = grab(1,"size",$run);
    my($txt,$data,$bss) = split(" ",$siz[1]);

    my($tst) = {};
    $tst->{tst_defs} = join(" ",@defs);
    $tst->{tst_data} = $data + 0;
    $tst->{tst_bss} = $bss + 0;;
    $tst->{tst_nm} = \@nmlhs;
    push(@tstlist,$tst);

    unless ($opt_k) {
        unlink($sfile);
        unlink($ofile);
        unlink($xfile);
    }

    undef(@defs);
}

sub xfopen
{
    my($file,$who) = @_;
    my($xf);

    open($xf,$file) or
        die("xfopen: unable to open '$file' -- $!\n");

    $xf;
}

sub xfclose
{
    my($xf) = @_;

    close($xf);
    undef($xf);

    $xf;
}

sub grab
{
    my($vflg,@argv) = @_;
    my($cmd,@cmd);

    $cmd = $argv[0];

    prtpub("\n");
    $cmd = join(" ",@argv);
    prtpub("Command: `%s`\n",$cmd);

    @cmd = (`$cmd`);

    prtsep()
        if ($vflg);
    foreach $cmd (@cmd) {
        chomp($cmd);
        prtpub("%s\n",$cmd)
            if ($vflg);
    }
    prtsep()
        if ($vflg);

    @cmd;
}

sub doexec
{

    $_ = join(" ",@_);
    system($_);

    $_ = $? >> 8;
    exit($_) if ($_);
}

sub prtsct
{
    my($fmt,$arg) = @_;

    prtpub("\n");
    prtpub("%s\n","-" x 8);

    $fmt = "**" . $fmt . "**\n";

    prtpub($fmt,$arg);

    prtpub("\n");
}

sub prtsep
{

    prtpub("```\n");
}

sub prtcol
{
    my($fmt,$val) = @_;
    my($tabto);

    {
        unless (defined($fmt)) {
            prtpub("%s\n",$prtcol_buf);
            undef($prtcol_buf);
            undef($prtcol_idx);
            last;
        }

        $tabto = $prtcol_tabs[$prtcol_idx++];
        while (length($prtcol_buf) < $tabto) {
            $prtcol_buf .= " ";
        }

        $fmt = sprintf($fmt,$val);
        $prtcol_buf .= $fmt;
    }
}

sub prtpub
{
    my($fmt);

    $fmt = shift(@_);
    printf($xfpub $fmt,@_);
    printf($fmt,@_)
        if ($opt_v);
}

sub prtcode
{
    my($fmt);

    $fmt = shift(@_);
    printf($xfdst $fmt,@_);
    prtpub($fmt,@_);
}

sub optget
{
    my($argv,@opts) = @_;
    my($arg,$opt);
    my($sym,$val);

    @opts = sort({ $b->[0] cmp $a->[0]} @opts);

    while (@$argv > 0) {
        $arg = $argv->[0];
        last unless ($arg =~ s/^-//);

        shift(@$argv);

        foreach $opt (@opts) {
            my($sym,$dft) = @$opt;

            if ($arg =~ /^$sym(.*)$/) {
                $val = $1;

                {
                    last if ($val =~ s/^=//);
                    last if ($val ne "");
                    $val = $dft;
                }

                ${"opt_" . $sym} = $val;

                last;
            }
        }
    }

    foreach $opt (@opts) {
        my($sym,$dft) = @$opt;
        $sym = "opt_" . $sym;
        ###printf("optget: DEBUG_CAE %s %s\n",$sym,$$sym);
    }
}

package bssgen;
__DATA__
int a;
int b;

int a = 1;
int b;

char a = 1;
int b;

int a = 1;
char b;

char a = 1;
char b;

char a;
int b;
char c = 1;
int d = 1;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM