debug/elf: support zstd compression

Test cases added to debug/dwarf because that is where it matters in practice.

The new test binary line-gcc-zstd.elf built with

    gcc -g -no-pie -Wl,--compress-debug-sections=zstd line[12].c

using

    gcc (Debian 12.2.0-10) 12.2.0

with a development version of the GNU binutils.

Fixes #55107

Change-Id: I48507c96902e1f83a174e5647b5cc403d965b52b
Reviewed-on: https://go-review.googlesource.com/c/go/+/473256
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
This commit is contained in:
Ian Lance Taylor 2023-03-03 14:29:44 -08:00 committed by Gopher Robot
parent 73ee0fcf37
commit 48a1dcb927
9 changed files with 71 additions and 4 deletions

2
api/next/55107.txt Normal file
View file

@ -0,0 +1,2 @@
pkg debug/elf, const COMPRESS_ZSTD = 2 #55107
pkg debug/elf, const COMPRESS_ZSTD CompressionType #55107

View file

@ -78,6 +78,7 @@ var bootstrapDirs = []string{
"internal/types/errors",
"internal/unsafeheader",
"internal/xcoff",
"internal/zstd",
"math/big",
"math/bits",
"sort",

View file

@ -69,6 +69,12 @@ func TestReaderSeek(t *testing.T) {
{0x40117e, nil},
}
testRanges(t, "testdata/line-clang-dwarf5.elf", want)
want = []wantRange{
{0x401126, [][2]uint64{{0x401126, 0x40116a}}},
{0x40116a, [][2]uint64{{0x40116a, 0x401180}}},
}
testRanges(t, "testdata/line-gcc-zstd.elf", want)
}
func TestRangesSection(t *testing.T) {
@ -156,6 +162,15 @@ func TestReaderRanges(t *testing.T) {
{"f2", [][2]uint64{{0x401180, 0x401197}}},
},
},
{
"testdata/line-gcc-zstd.elf",
subprograms{
{"f2", nil},
{"main", [][2]uint64{{0x40114b, 0x40116a}}},
{"f1", [][2]uint64{{0x401126, 0x40114b}}},
{"f2", [][2]uint64{{0x40116a, 0x401180}}},
},
},
}
for _, test := range tests {

View file

@ -48,6 +48,44 @@ func TestLineELFGCC(t *testing.T) {
testLineTable(t, want, files, elfData(t, "testdata/line-gcc.elf"))
}
func TestLineELFGCCZstd(t *testing.T) {
// Generated by:
// # gcc --version | head -n1
// gcc (Debian 12.2.0-10) 12.2.0
// # gcc -g -no-pie -Wl,--compress-debug-sections=zstd line*.c
zfile1H := &LineFile{Name: "/home/iant/go/src/debug/dwarf/testdata/line1.h"}
zfile1C := &LineFile{Name: "/home/iant/go/src/debug/dwarf/testdata/line1.c"}
zfile2C := &LineFile{Name: "/home/iant/go/src/debug/dwarf/testdata/line2.c"}
// Line table based on readelf --debug-dump=rawline,decodedline
want := []LineEntry{
{Address: 0x401126, File: zfile1H, Line: 2, Column: 1, IsStmt: true},
{Address: 0x40112a, File: zfile1H, Line: 5, Column: 8, IsStmt: true},
{Address: 0x401131, File: zfile1H, Line: 5, Column: 2, IsStmt: true},
{Address: 0x401133, File: zfile1H, Line: 6, Column: 10, IsStmt: true, Discriminator: 3},
{Address: 0x40113d, File: zfile1H, Line: 5, Column: 22, IsStmt: true, Discriminator: 3},
{Address: 0x401141, File: zfile1H, Line: 5, Column: 15, IsStmt: true, Discriminator: 1},
{Address: 0x401147, File: zfile1H, Line: 7, Column: 1, IsStmt: true},
{Address: 0x40114b, File: zfile1C, Line: 6, Column: 1, IsStmt: true},
{Address: 0x40114f, File: zfile1C, Line: 7, Column: 2, IsStmt: true},
{Address: 0x401159, File: zfile1C, Line: 8, Column: 2, IsStmt: true},
{Address: 0x401168, File: zfile1C, Line: 9, Column: 1, IsStmt: true},
{Address: 0x40116a, EndSequence: true},
{Address: 0x40116a, File: zfile2C, Line: 4, Column: 1, IsStmt: true},
{Address: 0x40116e, File: zfile2C, Line: 5, Column: 2, IsStmt: true},
{Address: 0x40117d, File: zfile2C, Line: 6, Column: 1, IsStmt: true},
{Address: 0x401180, EndSequence: true},
}
files := [][]*LineFile{
{zfile1C, zfile1H, zfile1C},
{zfile2C, zfile2C},
}
testLineTable(t, want, files, elfData(t, "testdata/line-gcc-zstd.elf"))
}
func TestLineGCCWindows(t *testing.T) {
// Generated by:
// > gcc --version
@ -277,7 +315,7 @@ func testLineTable(t *testing.T, want []LineEntry, files [][]*LineFile, d *Data)
}
// Compare line tables.
if !compareLines(got, want) {
if !compareLines(t, got, want) {
t.Log("Line tables do not match. Got:")
dumpLines(t, got)
t.Log("Want:")
@ -312,8 +350,10 @@ func dumpFiles(t *testing.T, files []*LineFile) {
}
}
func compareLines(a, b []LineEntry) bool {
func compareLines(t *testing.T, a, b []LineEntry) bool {
t.Helper()
if len(a) != len(b) {
t.Errorf("len(a) == %d, len(b) == %d", len(a), len(b))
return false
}
@ -326,11 +366,13 @@ func compareLines(a, b []LineEntry) bool {
continue
}
if al.File.Name != bl.File.Name {
t.Errorf("%d: name %v != name %v", i, al.File.Name, bl.File.Name)
return false
}
al.File = nil
bl.File = nil
if al != bl {
t.Errorf("%d: %#v != %#v", i, al, bl)
return false
}
}

Binary file not shown.

View file

@ -728,6 +728,7 @@ type CompressionType int
const (
COMPRESS_ZLIB CompressionType = 1 /* ZLIB compression. */
COMPRESS_ZSTD CompressionType = 2 /* ZSTD compression. */
COMPRESS_LOOS CompressionType = 0x60000000 /* First OS-specific. */
COMPRESS_HIOS CompressionType = 0x6fffffff /* Last OS-specific. */
COMPRESS_LOPROC CompressionType = 0x70000000 /* First processor-specific type. */
@ -736,6 +737,7 @@ const (
var compressionStrings = []intName{
{1, "COMPRESS_ZLIB"},
{2, "COMPRESS_ZSTD"},
{0x60000000, "COMPRESS_LOOS"},
{0x6fffffff, "COMPRESS_HIOS"},
{0x70000000, "COMPRESS_LOPROC"},

View file

@ -38,7 +38,7 @@ var nameTests = []nameTest{
{R_SPARC_GOT22, "R_SPARC_GOT22"},
{ET_LOOS + 5, "ET_LOOS+5"},
{ProgFlag(0x50), "0x50"},
{COMPRESS_ZLIB + 1, "COMPRESS_ZLIB+1"},
{COMPRESS_ZLIB + 2, "COMPRESS_ZSTD+1"},
}
func TestNames(t *testing.T) {

View file

@ -23,6 +23,7 @@ import (
"errors"
"fmt"
"internal/saferio"
"internal/zstd"
"io"
"os"
"strings"
@ -164,6 +165,10 @@ func (s *Section) Open() io.ReadSeeker {
switch s.compressionType {
case COMPRESS_ZLIB:
zrd = zlib.NewReader
case COMPRESS_ZSTD:
zrd = func(r io.Reader) (io.ReadCloser, error) {
return io.NopCloser(zstd.NewReader(r)), nil
}
}
if zrd == nil {

View file

@ -251,7 +251,7 @@ var depsRules = `
< index/suffixarray;
# executable parsing
FMT, encoding/binary, compress/zlib, internal/saferio
FMT, encoding/binary, compress/zlib, internal/saferio, internal/zstd
< runtime/debug
< debug/dwarf
< debug/elf, debug/gosym, debug/macho, debug/pe, debug/plan9obj, internal/xcoff