Skip to content

Commit 7648387

Browse files
committed
debug/elf: transparently decompress compressed sections
This adds support for compressed ELF sections. This compression is treated as a framing issue and hence the package APIs all transparently decompress compressed sections. This requires some subtlety for (*Section).Open, which returns an io.ReadSeeker: since the decompressed data comes from an io.Reader, this commit introduces a Reader-to-ReadSeeker adapter that is efficient for common uses of Seek and does what it can otherwise. Fixes #11773. Change-Id: Ic0cb7255a85cadf4c1d15fb563d5a2e89dbd3c36 Reviewed-on: https://go-review.googlesource.com/17341 Reviewed-by: Russ Cox <[email protected]> Run-TryBot: Austin Clements <[email protected]>
1 parent e1544d3 commit 7648387

File tree

6 files changed

+424
-77
lines changed

6 files changed

+424
-77
lines changed

src/debug/elf/elf.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ const (
411411
SHF_OS_NONCONFORMING SectionFlag = 0x100 /* OS-specific processing required. */
412412
SHF_GROUP SectionFlag = 0x200 /* Member of section group. */
413413
SHF_TLS SectionFlag = 0x400 /* Section contains TLS data. */
414+
SHF_COMPRESSED SectionFlag = 0x800 /* Section is compressed. */
414415
SHF_MASKOS SectionFlag = 0x0ff00000 /* OS-specific semantics. */
415416
SHF_MASKPROC SectionFlag = 0xf0000000 /* Processor-specific semantics. */
416417
)
@@ -426,11 +427,34 @@ var shfStrings = []intName{
426427
{0x100, "SHF_OS_NONCONFORMING"},
427428
{0x200, "SHF_GROUP"},
428429
{0x400, "SHF_TLS"},
430+
{0x800, "SHF_COMPRESSED"},
429431
}
430432

431433
func (i SectionFlag) String() string { return flagName(uint32(i), shfStrings, false) }
432434
func (i SectionFlag) GoString() string { return flagName(uint32(i), shfStrings, true) }
433435

436+
// Section compression type.
437+
type CompressionType int
438+
439+
const (
440+
COMPRESS_ZLIB CompressionType = 1 /* ZLIB compression. */
441+
COMPRESS_LOOS CompressionType = 0x60000000 /* First OS-specific. */
442+
COMPRESS_HIOS CompressionType = 0x6fffffff /* Last OS-specific. */
443+
COMPRESS_LOPROC CompressionType = 0x70000000 /* First processor-specific type. */
444+
COMPRESS_HIPROC CompressionType = 0x7fffffff /* Last processor-specific type. */
445+
)
446+
447+
var compressionStrings = []intName{
448+
{0, "COMPRESS_ZLIB"},
449+
{0x60000000, "COMPRESS_LOOS"},
450+
{0x6fffffff, "COMPRESS_HIOS"},
451+
{0x70000000, "COMPRESS_LOPROC"},
452+
{0x7fffffff, "COMPRESS_HIPROC"},
453+
}
454+
455+
func (i CompressionType) String() string { return stringName(uint32(i), compressionStrings, false) }
456+
func (i CompressionType) GoString() string { return stringName(uint32(i), compressionStrings, true) }
457+
434458
// Prog.Type
435459
type ProgType int
436460

@@ -1878,6 +1902,13 @@ type Dyn32 struct {
18781902
Val uint32 /* Integer/Address value. */
18791903
}
18801904

1905+
// ELF32 Compression header.
1906+
type Chdr32 struct {
1907+
Type uint32
1908+
Size uint32
1909+
Addralign uint32
1910+
}
1911+
18811912
/*
18821913
* Relocation entries.
18831914
*/
@@ -1972,6 +2003,14 @@ type Dyn64 struct {
19722003
Val uint64 /* Integer/address value */
19732004
}
19742005

2006+
// ELF64 Compression header.
2007+
type Chdr64 struct {
2008+
Type uint32
2009+
Reserved uint32
2010+
Size uint64
2011+
Addralign uint64
2012+
}
2013+
19752014
/*
19762015
* Relocation entries.
19772016
*/

src/debug/elf/file.go

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ type SectionHeader struct {
5858
Info uint32
5959
Addralign uint64
6060
Entsize uint64
61+
62+
// FileSize is the size of this section in the file in bytes.
63+
// If a section is compressed, FileSize is the size of the
64+
// compressed data, while Size (above) is the size of the
65+
// uncompressed data.
66+
FileSize uint64
6167
}
6268

6369
// A Section represents a single section in an ELF file.
@@ -70,17 +76,23 @@ type Section struct {
7076
// If a client wants Read and Seek it must use
7177
// Open() to avoid fighting over the seek offset
7278
// with other clients.
79+
//
80+
// ReaderAt may be nil if the section is not easily available
81+
// in a random-access form. For example, a compressed section
82+
// may have a nil ReaderAt.
7383
io.ReaderAt
7484
sr *io.SectionReader
85+
86+
compressionType CompressionType
87+
compressionOffset int64
7588
}
7689

7790
// Data reads and returns the contents of the ELF section.
91+
// Even if the section is stored compressed in the ELF file,
92+
// Data returns uncompressed data.
7893
func (s *Section) Data() ([]byte, error) {
79-
dat := make([]byte, s.sr.Size())
80-
n, err := s.sr.ReadAt(dat, 0)
81-
if n == len(dat) {
82-
err = nil
83-
}
94+
dat := make([]byte, s.Size)
95+
n, err := io.ReadFull(s.Open(), dat)
8496
return dat[0:n], err
8597
}
8698

@@ -94,7 +106,24 @@ func (f *File) stringTable(link uint32) ([]byte, error) {
94106
}
95107

96108
// Open returns a new ReadSeeker reading the ELF section.
97-
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
109+
// Even if the section is stored compressed in the ELF file,
110+
// the ReadSeeker reads uncompressed data.
111+
func (s *Section) Open() io.ReadSeeker {
112+
if s.Flags&SHF_COMPRESSED == 0 {
113+
return io.NewSectionReader(s.sr, 0, 1<<63-1)
114+
}
115+
if s.compressionType == COMPRESS_ZLIB {
116+
return &readSeekerFromReader{
117+
reset: func() (io.Reader, error) {
118+
fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.FileSize)-s.compressionOffset)
119+
return zlib.NewReader(fr)
120+
},
121+
size: int64(s.Size),
122+
}
123+
}
124+
err := &FormatError{int64(s.Offset), "unknown compression type", s.compressionType}
125+
return errorReader{err}
126+
}
98127

99128
// A ProgHeader represents a single ELF program header.
100129
type ProgHeader struct {
@@ -344,7 +373,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
344373
Flags: SectionFlag(sh.Flags),
345374
Addr: uint64(sh.Addr),
346375
Offset: uint64(sh.Off),
347-
Size: uint64(sh.Size),
376+
FileSize: uint64(sh.Size),
348377
Link: uint32(sh.Link),
349378
Info: uint32(sh.Info),
350379
Addralign: uint64(sh.Addralign),
@@ -360,16 +389,43 @@ func NewFile(r io.ReaderAt) (*File, error) {
360389
Type: SectionType(sh.Type),
361390
Flags: SectionFlag(sh.Flags),
362391
Offset: uint64(sh.Off),
363-
Size: uint64(sh.Size),
392+
FileSize: uint64(sh.Size),
364393
Addr: uint64(sh.Addr),
365394
Link: uint32(sh.Link),
366395
Info: uint32(sh.Info),
367396
Addralign: uint64(sh.Addralign),
368397
Entsize: uint64(sh.Entsize),
369398
}
370399
}
371-
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size))
372-
s.ReaderAt = s.sr
400+
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))
401+
402+
if s.Flags&SHF_COMPRESSED == 0 {
403+
s.ReaderAt = s.sr
404+
s.Size = s.FileSize
405+
} else {
406+
// Read the compression header.
407+
switch f.Class {
408+
case ELFCLASS32:
409+
ch := new(Chdr32)
410+
if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
411+
return nil, err
412+
}
413+
s.compressionType = CompressionType(ch.Type)
414+
s.Size = uint64(ch.Size)
415+
s.Addralign = uint64(ch.Addralign)
416+
s.compressionOffset = int64(binary.Size(ch))
417+
case ELFCLASS64:
418+
ch := new(Chdr64)
419+
if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
420+
return nil, err
421+
}
422+
s.compressionType = CompressionType(ch.Type)
423+
s.Size = ch.Size
424+
s.Addralign = ch.Addralign
425+
s.compressionOffset = int64(binary.Size(ch))
426+
}
427+
}
428+
373429
f.Sections[i] = s
374430
}
375431

0 commit comments

Comments
 (0)