Skip to content

all: binaries too big and growing #6853

Open

Description

@robpike
As an experiment, I build "hello, world" at the release points for go 1.0.
1.1, and 1.2. Here are the binary's sizes:

% ls -l x.1.?
-rwxr-xr-x  1 r  staff  1191952 Nov 30 10:25 x.1.0
-rwxr-xr-x  1 r  staff  1525936 Nov 30 10:20 x.1.1
-rwxr-xr-x  1 r  staff  2188576 Nov 30 10:18 x.1.2
% size x.1.?
__TEXT  __DATA  __OBJC  others  dec hex
880640  33682096    0   4112    34566848    20f72c0 x.1.0
1064960 94656   0   75952   1235568 12da70  x.1.1
1429504 147896  0   177440  1754840 1ac6d8  x.1.2
% 

A near-doubling of the binary size in two releases is a bug of a kind. I will hold on to
the files so they can be analyzed more, but am filing this issue to get the topic
registered. We need to develop a better understanding of the problem and how to address
it.

Marking this 1.3 (not maybe) because I consider it a priority.


A few months ago I exchanged mail with Russ about this topic regarding a different, much
larger binary. To avoid him having to redo the analysis, here is what he said at the
time:

====
i sent CL 13722046 to make the nm -S output a bit more useful.
for the toy binary i now get

  4a2280  1898528 D symtab
  26f3a0  1405936 D type.*
  671aa0  1058432 D pclntab
  3c6790   598056 D go.string.*
  4620c0    49600 D gcbss
  7a7c20    45496 B runtime.mheap
  46e280    21936 D gcdata
  7a29e0    21056 b bufferList
  1ed600    16480 T crypto/tls.(*Conn).clientHandshake
  79eb20    16064 b semtable
  1b3d90    14224 T net/http.init

that seems plausible to me. some notes:

symtab is the plan 9 symbol table. it in the binary but never referenced at run time. it
supports things like nm -S only. it needs to move into an unmapped section of the
binary, but it is only costing at most 8k at run time right now due to fragmentation and
it just wasn't worth the effort to try to move. the new linker will make this easier. of
course, moving it in the file doesn't shrink the file.

the thing named pclntab is a reencoding of the original pclntab and the parts of the
plan 9 symbol table that we did need at run time (mostly just a list of functions and
their names and addresses). as you can see, it is much smaller than the old form (the
symbol table dominates).

type.* is the reflect types and go.string.* is the static go string data. the *
indicates that i coalesced many symbols into one, to avoid useless individual names
bloating the symbol table. if we tried we could probably cut the reflect types by 2-4x.
it would mean packing the data a bit more compactly than an ordinary go data structure
would and then using unsafe to get it back out.

gcbss and gcdata are garbage collection bits for the bss and data segments. that's what
atom symbol did, and it's not clear whether it will last (probably not) and whether what
will replace it will be smaller. time will tell. i have a meeting with dmitriy, carl,
and keith next week to figure out what the plan is.

runtime.mheap, bufferList, and semtable are bss.

you're not seeing the gdb dwarf debug information here, because it's not a runtime
symbol.
 
g% otool -l $(which toy) | egrep '^  segname|filesize'
  segname __PAGEZERO
 filesize 0
  segname __TEXT
 filesize 7811072
  segname __DATA
 filesize 126560
  segname __LINKEDIT
 filesize 921772
  segname __DWARF
 filesize 2886943
g% 

there's another 3 MB. you can build with -ldflags -w to get rid of that at least.
if you read the full otool -l output you will find

Load command 6
     cmd LC_SYMTAB
 cmdsize 24
  symoff 10825728
   nsyms 22559
  stroff 11186924
 strsize 560576

looks like another 1 MB or so (560576+11186924-10825728 or 22559*16+560576) for the
mach-o symbol table.

when we do the new linker we can make recording this kind of information in a useful
form a priority.

Activity

  1. robpike commented on Nov 30, 2013

    @robpike
    ContributorAuthor

    Comment 1:

    Note: the binaries were build on amd64 10.7.5 (Lion), with  gcc -version
    i686-apple-darwin11-llvm-gcc-4.2
    For the record, I couldn't do this experiment on 10.9 with Xcode 5 because the older
    releases wouldn't build due to gcc/clang skew.
  2. robpike commented on Nov 30, 2013

    @robpike
    ContributorAuthor

    Comment 2:

    Labels changed: added priority-later, removed priority-triage.

  3. gopherbot commented on Dec 2, 2013

    @gopherbot
    Contributor

    Comment 3 by jlourenco27:

    Just for added reference, this are the size on go 1.1.2 vs 1.2 with OS X 10.9 and Xcode
    5 (darwin gcc llvm 5.0 x86_64):
    $ ls -l *1.*
    -rwxr-xr-x  1 j      staff  1525984  2 Dez 21:44 hello_1.1.2
    -rwxr-xr-x  1 j      staff  2192672  2 Dez 21:40 hello_1.2
    $ size *1.*
    __TEXT  __DATA  __OBJC  others  dec hex
    1064960 94720   0   76000   1235680 12dae0  hello_1.1.2
    1433600 147896  0   177440  1758936 1ad6d8  hello_1.2
  4. randall77 commented on Dec 3, 2013

    @randall77
    Contributor

    Comment 4:

    This issue was updated by revision f238049.

    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/35940047
  5. rsc commented on Dec 4, 2013

    @rsc
    Contributor

    Comment 5:

    Labels changed: added release-go1.3.

  6. rsc commented on Dec 4, 2013

    @rsc
    Contributor

    Comment 6:

    Labels changed: removed go1.3.

  7. rsc commented on Dec 4, 2013

    @rsc
    Contributor

    Comment 7:

    Labels changed: added repo-main.

  8. zephyr commented on Dec 7, 2013

    @zephyr

    Comment 8:

    In this context, please reevaluate if the golang binaries can be changed to work with
    UPX (ultimate packer for executables).¹²
    For a small amount of computing power, upx can reduce the size of a binary down to a
    quarter of its original size. You can authenticate this using a existing example
    ›fixer‹ programm for golang binaries on linux/amd64.³
    While this approach doesn't fix the root of the problem – only the symptoms – it
    would be nice to have this possibility always on hand.
    For technical background of this problem (PT_LOAD[0].p_offset==0), please look at the
    UPX bugtracker⁴.
    ¹ http://upx.sourceforge.net/
    ² https://en.wikipedia.org/wiki/UPX
    ³ https://github.com/pwaller/goupx
    ⁴ http://sourceforge.net/p/upx/bugs/195/
  9. minux commented on Dec 7, 2013

    @minux
    Member

    Comment 9:

    re #8, I don't think it's Go's problem. upx should be made more flexible to handle this.
  10. leo-liu commented on Dec 8, 2013

    @leo-liu

    Comment 10:

    minux:
    We may figure out why the binaries are growing fast first (lager runtime, optimization,
    etc.), before we claim that it isn't Go's problem.
  11. minux commented on Dec 8, 2013

    @minux
    Member

    Comment 11:

    re #10, my #9 reply is to #8, which is about an entirely different problem.
    i'm not saying that the ever-growing binaries is not our problem, only that i
    don't believe that upx not accepting our binaries is our problem.
    it's clear that upx isn't able to handle all possible and correct ELF files (i.e.
    if the kernel can execute our binaries just fine, it's upx's problem to not be
    able to compress them).
  12. robpike commented on Feb 19, 2014

    @robpike
    ContributorAuthor

    Comment 12:

    More detail. The Plan 9 symbol table is about to be deleted. Here is a reference point,
    adding one new entry to the list above:
    $ ls -l *1.*
    -rwxr-xr-x  1 j      staff  1525984  2 Dez 21:44 hello_1.1.2
    -rwxr-xr-x  1 j      staff  2192672  2 Dez 21:40 hello_1.2
    -rwxr-xr-x  1 j      staff  2474512 Feb 18 20:27 hello_1.2.x
    $ size *1.*
    __TEXT  __DATA  __OBJC  others  dec hex
    1064960 94720   0   76000   1235680 12dae0  hello_1.1.2
    1433600 147896  0   177440  1758936 1ad6d8  hello_1.2
    1699840 160984  0   188944  2049768 1f46e8     hello_1.2.x
    Text has grown substantially, as has data. At least some of this is due to new
    annotations for the garbage collector.
  13. robpike commented on Feb 19, 2014

    @robpike
    ContributorAuthor

    Comment 13:

    More detail. The Plan 9 symbol table is about to be deleted. Here is a reference point,
    adding one new entry to the list above:
    $ ls -l *1.*
    -rwxr-xr-x  1 r  staff  1191952 Nov 30 10:25 x.1.0
    -rwxr-xr-x  1 r  staff  1525936 Nov 30 10:20 x.1.1
    -rwxr-xr-x  1 r  staff  2188576 Nov 30 10:18 x.1.2
    -rwxr-xr-x  1 r  staff  2474512 Feb 18 20:27 hello_1.2.x
    $ size *1.*
    __TEXT  __DATA  __OBJC  others  dec hex
    880640  33682096     0  4112    34566848     20f72c0    x.1.0
    1064960 94656   0   75952   1235568 12da70  x.1.1
    1429504 147896  0   177440  1754840 1ac6d8  x.1.2
    1699840 160984  0   188944  2049768 1f46e8     hello_1.2.x
    Text has grown substantially, as has data. At least some of this is due to new
    annotations for the garbage collector.
  14. robpike commented on Feb 19, 2014

    @robpike
    ContributorAuthor

    Comment 14:

    More detail. The Plan 9 symbol table is about to be deleted. Here is a reference point,
    adding one new entry to the list above:
    $ ls -l *1.*
    -rwxr-xr-x  1 r  staff  1191952 Nov 30 10:25 x.1.0
    -rwxr-xr-x  1 r  staff  1525936 Nov 30 10:20 x.1.1
    -rwxr-xr-x  1 r  staff  2188576 Nov 30 10:18 x.1.2
    -rwxr-xr-x  1 r  staff  2474512 Feb 18 20:27 hello_1.2.x
    $ size *1.*
    __TEXT  __DATA  __OBJC  others  dec hex
    880640  33682096     0  4112    34566848     20f72c0    x.1.0
    1064960 94656   0   75952   1235568 12da70  x.1.1
    1429504 147896  0   177440  1754840 1ac6d8  x.1.2
    1699840 160984  0   188944  2049768 1f46e8     x.1.2.x
    Text has grown substantially, as has data. At least some of this is due to new
    annotations for the garbage collector.
  15. rsc commented on Feb 19, 2014

    @rsc
    Contributor

    Comment 15:

    This issue was updated by revision 964f6d3.

    Nothing reads the Plan 9 symbol table anymore.
    The last holdout was 'go tool nm', but since being rewritten in Go
    it uses the standard symbol table for the binary format
    (ELF, Mach-O, PE) instead.
    Removing the Plan 9 symbol table saves ~15% disk space
    on most binaries.
    Two supporting changes included in this CL:
    debug/gosym: use Go 1.2 pclntab to synthesize func-only
    symbol table when there is no Plan 9 symbol table
    debug/elf, debug/macho, debug/pe: ignore final EOF from ReadAt
    LGTM=r
    R=r, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/65740045
  16. 239 remaining items

  17. gopherbot commented on Feb 12, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/744820 mentions this issue: cmd/link: handle runtime.type based on size, not GOOS

  18. Splizard commented on Feb 20, 2026

    @Splizard
    Contributor

    #77222 may be relevant here.

    There's certainly an opportunity to optimize DCE by not disabling it when reflect.TypeFor[T]().Methods() is known at compile time. Is Go open to contributions on this?

  19. added a commit that references this issue on Mar 26, 2026
  20. gopherbot commented on Jun 8, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/788140 mentions this issue: cmd/link: deduplicate ELF symbol string table entries

  21. gopherbot commented on Jun 15, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/790740 mentions this issue: cmd/compile: elide zero-offset promoted method wrappers

  22. gopherbot commented on Jun 15, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/790741 mentions this issue: cmd/compile: share non-zero promoted method wrappers

  23. gopherbot commented on Jul 26, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/806120 mentions this issue: cmd/compile/internal/ssagen: order frame slots by access density

  24. gopherbot commented on Aug 25, 2026

    @gopherbot
    Contributor

    Change https://go.dev/cl/820200 mentions this issue: cmd/link, runtime: deduplicate inline function metadata

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsFixThe path to resolution is known, but the work has not been done.binary-sizeumbrella

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions