From c60fa42b25af882609ad7947e6648fbf96e1ebf6 Mon Sep 17 00:00:00 2001 From: Brennan Vincent Date: Wed, 18 Sep 2024 13:46:15 -0400 Subject: [PATCH 1/3] Look at .tdata when computing TLS image size On x86, finding thread-local variables requires us to know the size of the per-thread TLS image. This is the .tdata section followed by the .tbss section. Previously, we were only taking into account the .tbss section, which was not caught earlier because I only tested with programs where .tdata was empty on x86. --- interpreter/customlabels/customlabels.go | 26 +++++++++++++++++++++++- libpf/pfelf/file.go | 21 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/interpreter/customlabels/customlabels.go b/interpreter/customlabels/customlabels.go index ace5dda5..cc32f7a7 100644 --- a/interpreter/customlabels/customlabels.go +++ b/interpreter/customlabels/customlabels.go @@ -31,6 +31,13 @@ type data struct { var _ interpreter.Data = &data{} +func roundUp(multiple uint64, value uint64) uint64 { + if multiple == 0 { + return value + } + return (value + multiple - 1) / multiple * multiple +} + func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interpreter.Data, error) { ef, err := info.GetELF() if err != nil { @@ -75,11 +82,28 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete if ef.Machine == elf.EM_AARCH64 { tlsAddr = libpf.Address(tlsSym.Address + 16) } else if ef.Machine == elf.EM_X86_64 { + // Symbol addresses are relative to the start of the + // thread-local storage image, but the thread pointer points to the _end_ + // of the image. So we need to find the size of the image in order to know where the + // beginning is. + // + // The image is just .tdata followed by .tbss, but we also have to respect the alignment. tbss, err := ef.Tbss() if err != nil { return nil, err } - tlsAddr = libpf.Address(int64(tlsSym.Address) - int64(tbss.Size)) + tdata, err := ef.Tdata() + var tdataSize uint64 + if err != nil { + // No Tdata is ok, it's the same as size 0 + if err != pfelf.ErrNoTdata { + return nil, err + } + } else { + tdataSize = tdata.Size + } + imageSize := roundUp(tbss.Addralign, tdataSize) + tbss.Size + tlsAddr = libpf.Address(int64(tlsSym.Address) - int64(imageSize)) } else { return nil, fmt.Errorf("unrecognized machine: %s", ef.Machine.String()) } diff --git a/libpf/pfelf/file.go b/libpf/pfelf/file.go index 5ab3f97a..d69ace3d 100644 --- a/libpf/pfelf/file.go +++ b/libpf/pfelf/file.go @@ -57,6 +57,12 @@ var ErrSymbolNotFound = errors.New("symbol not found") // ErrNotELF is returned when the file is not an ELF var ErrNotELF = errors.New("not an ELF file") +// ErrNoTbss is returned when the tbss section cannot be found +var ErrNoTbss = errors.New("no thread-local uninitialized data section (tbss)") + +// ErrNoTdata is returned when the tdata section cannot be found +var ErrNoTdata = errors.New("no thread-local initialized data section (tdata)") + // File represents an open ELF file type File struct { // closer is called internally when resources for this File are to be released @@ -426,7 +432,20 @@ func (f *File) Tbss() (*Section, error) { return &sec, nil } } - return nil, errors.New("no thread-local uninitialized data section (tbss)") + return nil, ErrNoTbss +} + +// Tdata gets the thread-local initialized data section +func (f *File) Tdata() (*Section, error) { + if err := f.LoadSections(); err != nil { + return nil, err + } + for _, sec := range f.Sections { + if sec.Type == elf.SHT_PROGBITS && sec.Flags&elf.SHF_TLS != 0 { + return &sec, nil + } + } + return nil, ErrNoTdata } // ReadVirtualMemory reads bytes from given virtual address From 362df1229a2eda98c882e76920fa9287f4fb938f Mon Sep 17 00:00:00 2001 From: Brennan Vincent Date: Wed, 18 Sep 2024 14:03:16 -0400 Subject: [PATCH 2/3] appease various linters --- interpreter/customlabels/customlabels.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interpreter/customlabels/customlabels.go b/interpreter/customlabels/customlabels.go index cc32f7a7..2cb7a984 100644 --- a/interpreter/customlabels/customlabels.go +++ b/interpreter/customlabels/customlabels.go @@ -31,7 +31,7 @@ type data struct { var _ interpreter.Data = &data{} -func roundUp(multiple uint64, value uint64) uint64 { +func roundUp(multiple, value uint64) uint64 { if multiple == 0 { return value } @@ -87,7 +87,8 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete // of the image. So we need to find the size of the image in order to know where the // beginning is. // - // The image is just .tdata followed by .tbss, but we also have to respect the alignment. + // The image is just .tdata followed by .tbss, + // but we also have to respect the alignment. tbss, err := ef.Tbss() if err != nil { return nil, err From cf9610ebb8c63ec0804822abd2e1f2576212ba65 Mon Sep 17 00:00:00 2001 From: Brennan Vincent Date: Wed, 18 Sep 2024 14:09:07 -0400 Subject: [PATCH 3/3] oops --- interpreter/customlabels/customlabels.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interpreter/customlabels/customlabels.go b/interpreter/customlabels/customlabels.go index 2cb7a984..e77c9f44 100644 --- a/interpreter/customlabels/customlabels.go +++ b/interpreter/customlabels/customlabels.go @@ -88,7 +88,7 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete // beginning is. // // The image is just .tdata followed by .tbss, - // but we also have to respect the alignment. + // but we also have to respect the alignment. tbss, err := ef.Tbss() if err != nil { return nil, err