Presence of PCLN Table in CGo binaries

385 views
Skip to first unread message

Ramon Rüttimann

unread,
Sep 16, 2022, 3:13:33 PM9/16/22
to golang-nuts
Hello fellow Gophers,

I am currently looking into what a built Go binary may contain and have found some confusing things around the `.gopclntab` ELF section, also known as the PCLN table.

At first, I assumed that the PCLN table is present in all binaries, no matter how they are built. I've done some tests with stripped, trimmed and CGo binaries and have found it to be present.
However, this article contains an interesting statement:

For ordinary unstripped Go binaries, this debugging information is in the .gopclntab and .gosymtab ELF sections of the binary, and can be read out with debug/elf/File.Section() and Section.Data(). Unfortunately, Go binaries that use cgo do not have these Go ELF sections. As mentioned in Building a better Go linker:

For “cgo” binaries, which may make arbitrary use of C libraries, the Go linker links all of the Go code into a single native object file and then invokes the system linker to produce the final binary.

This linkage obliterates .gopclntab and .gosymtab as separate ELF sections. I believe that their data is still there in the final binary, but I don't know how to extract them.

I have also found such binaries in the wild, like the `containerd` binary or some tools from Docker as well. I've tried to decipher how the containerd binary is being built (based on this Makefile), but I am not really familiar enough with Make to decipher this.

This S/O post has asked the same question, but the answer doesn't match what I've seen and also what that article claims.

Can someone shed some lights on how one can build CGo binaries without the `.pclntab` section? Also, what happens to stacktraces in such a case? 

Thanks!

Ian Lance Taylor

unread,
Sep 16, 2022, 3:36:53 PM9/16/22
to Ramon Rüttimann, golang-nuts
I believe that at least as of Go 1.19 the .gopclntab section exists
even if the binary is built with cgo, and the .gosymtab section is
empty in any case.

Ian

Ramon Rüttimann

unread,
Sep 17, 2022, 5:48:13 PM9/17/22
to golang-nuts
Sadly this does not seem to be the case, for example the nightly artifacts from containerd, which can be found here.
A `go version -m <binary>`  lists all deps & shows the Go version 1.19.1, but there is now PCLN table to be found... 
The binary seems to be created through the following go build command:

go build -gcflags=-trimpath=/home/runner/work/containerd/containerd/src -buildmode=pie -o bin/containerd -ldflags '-X github.com/containerd/containerd/version.Version=290ef2b -X github.com/containerd/containerd/version.Revision=290ef2b43ff1b824e56d0adaebf621e053de6e86 -X github.com/containerd/containerd/version.Package=github.com/containerd/containerd -s -w ' -tags "urfave_cli_no_docs" ./cmd/containerd

Local testing confirms that with `-buildmode=pie` ("Position Independent Executable") the PCLN table is not being added to the binary. Why? 
I'm not familiar with these executables, not sure how this would affect the PCLN.

Ian Lance Taylor

unread,
Sep 17, 2022, 10:51:43 PM9/17/22
to Ramon Rüttimann, golang-nuts
On Sat, Sep 17, 2022 at 2:48 PM 'Ramon Rüttimann' via golang-nuts
<golan...@googlegroups.com> wrote:
>
> Sadly this does not seem to be the case, for example the nightly artifacts from containerd, which can be found here.
> A `go version -m <binary>` lists all deps & shows the Go version 1.19.1, but there is now PCLN table to be found...
> The binary seems to be created through the following go build command:
>
> go build -gcflags=-trimpath=/home/runner/work/containerd/containerd/src -buildmode=pie -o bin/containerd -ldflags '-X github.com/containerd/containerd/version.Version=290ef2b -X github.com/containerd/containerd/version.Revision=290ef2b43ff1b824e56d0adaebf621e053de6e86 -X github.com/containerd/containerd/version.Package=github.com/containerd/containerd -s -w ' -tags "urfave_cli_no_docs" ./cmd/containerd
>
> Local testing confirms that with `-buildmode=pie` ("Position Independent Executable") the PCLN table is not being added to the binary. Why?
> I'm not familiar with these executables, not sure how this would affect the PCLN.

Interesting. I agree: with -buildmode=pie the .gopclntab section does
not appear in the final executable. It is getting folded into the
.data.rel.ro section. You can still find the information by looking
at the symbol table: the .gopclntab data is between runtime.pclntab
and runtime.epclntab.

Ian


> On Friday, September 16, 2022 at 9:36:53 PM UTC+2 Ian Lance Taylor wrote:
>>
>> On Fri, Sep 16, 2022 at 12:13 PM Ramon Rüttimann
>> <ramon.ru...@gmail.com> wrote:
>> >
>> > I am currently looking into what a built Go binary may contain and have found some confusing things around the `.gopclntab` ELF section, also known as the PCLN table.
>> >
>> > At first, I assumed that the PCLN table is present in all binaries, no matter how they are built. I've done some tests with stripped, trimmed and CGo binaries and have found it to be present.
>> > However, this article contains an interesting statement:
>> >
>> > For ordinary unstripped Go binaries, this debugging information is in the .gopclntab and .gosymtab ELF sections of the binary, and can be read out with debug/elf/File.Section() and Section.Data(). Unfortunately, Go binaries that use cgo do not have these Go ELF sections. As mentioned in Building a better Go linker:
>> >
>> > For “cgo” binaries, which may make arbitrary use of C libraries, the Go linker links all of the Go code into a single native object file and then invokes the system linker to produce the final binary.
>> >
>> > This linkage obliterates .gopclntab and .gosymtab as separate ELF sections. I believe that their data is still there in the final binary, but I don't know how to extract them.
>> >
>> > I have also found such binaries in the wild, like the `containerd` binary or some tools from Docker as well. I've tried to decipher how the containerd binary is being built (based on this Makefile), but I am not really familiar enough with Make to decipher this.
>> >
>> > This S/O post has asked the same question, but the answer doesn't match what I've seen and also what that article claims.
>> >
>> > Can someone shed some lights on how one can build CGo binaries without the `.pclntab` section? Also, what happens to stacktraces in such a case?
>>
>> I believe that at least as of Go 1.19 the .gopclntab section exists
>> even if the binary is built with cgo, and the .gosymtab section is
>> empty in any case.
>>
>> Ian
>
> --
> You received this message because you are subscribed to the Google Groups "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/a831b72f-6ecd-41e4-a5fd-e76b4b9fc7fbn%40googlegroups.com.

Ramon Rüttimann

unread,
Sep 18, 2022, 2:57:45 PM9/18/22
to Ian Lance Taylor, golang-nuts
Mh, this is interesting. When I build a test binary with buildmode=pie, I even get an ELF section called `.data.rel.ro.gopclntab`. 

However, in the containerd binary, this section does not exist.There also is no `.gosymtab` section, as that is easily wiped by the `ldflags`. 

 It is getting folded into the
.data.rel.ro section.  You can still find the information by looking
at the symbol table: the .gopclntab data is between runtime.pclntab
and runtime.epclntab.

I'm not sure I follow. 
If I understand correctly, this means that what I see in my test binary as `.data.rel.ro.gopclntab` is a subsection "within" the `.data.rel.ro` section.
Further, as the containerd binary has a `.data.rel.ro` section, at some offset within that data there should be a PCLN table? 

If so, two questions arise:
- How do I find that offset? By looking for the magic string for the PCLN table? 
- Why is that subsection not listed / present in the containerd binary? 

Thanks a lot, this is a very interesting topic! 
 
Ramon

PS: If you have some literature / documents on the Go specifics for this, I'd be curious to read them.

You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/oxseUnhRtXM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcUoC3-9X%3DRBYMt%3DSNgbkXG_%3DJ3hKM8j-QMc%3DLaKcEj_Dg%40mail.gmail.com.

Ian Lance Taylor

unread,
Sep 18, 2022, 9:55:34 PM9/18/22
to Ramon Rüttimann, golang-nuts
On Sun, Sep 18, 2022 at 11:57 AM Ramon Rüttimann
<ramon.r...@snyk.io> wrote:
>
> Mh, this is interesting. When I build a test binary with buildmode=pie, I even get an ELF section called `.data.rel.ro.gopclntab`.
>
> However, in the containerd binary, this section does not exist.There also is no `.gosymtab` section, as that is easily wiped by the `ldflags`.
>
> > It is getting folded into the
> > .data.rel.ro section. You can still find the information by looking
> > at the symbol table: the .gopclntab data is between runtime.pclntab
> > and runtime.epclntab.
>
>
> I'm not sure I follow.
> If I understand correctly, this means that what I see in my test binary as `.data.rel.ro.gopclntab` is a subsection "within" the `.data.rel.ro` section.
> Further, as the containerd binary has a `.data.rel.ro` section, at some offset within that data there should be a PCLN table?
>
> If so, two questions arise:
> - How do I find that offset? By looking for the magic string for the PCLN table?

Look in the symbol table for the runtime.pclntab and runtime.epclntab
symbols. The values of those symbols indicate the address of the
pclntab.

> - Why is that subsection not listed / present in the containerd binary?

Presumably because the system linker is merging all the .data.rel.ro.*
input sections into a single .data.rel.ro output section.

> PS: If you have some literature / documents on the Go specifics for this, I'd be curious to read them.

I don't know of any.

Ian
Reply all
Reply to author
Forward
0 new messages