1
2
3
4
5
16 package elf
17
18 import (
19 "bytes"
20 "compress/zlib"
21 "debug/dwarf"
22 "encoding/binary"
23 "errors"
24 "fmt"
25 "internal/saferio"
26 "internal/zstd"
27 "io"
28 "os"
29 "strings"
30 )
31
32
33
34
35
36
37 const (
38 seekStart int = 0
39 seekCurrent int = 1
40 seekEnd int = 2
41 )
42
43
44
45
48
49
50 type FileHeader struct {
51 Class Class
52 Data Data
53 Version Version
54 OSABI OSABI
55 ABIVersion uint8
56 ByteOrder binary.ByteOrder
57 Type Type
58 Machine Machine
59 Entry uint64
60 }
61
62
63 type File struct {
64 FileHeader
65 Sections []*Section
66 Progs []*Prog
67 closer io.Closer
68 gnuNeed []verneed
69 gnuVersym []byte
70 }
71
72
73 type SectionHeader struct {
74 Name string
75 Type SectionType
76 Flags SectionFlag
77 Addr uint64
78 Offset uint64
79 Size uint64
80 Link uint32
81 Info uint32
82 Addralign uint64
83 Entsize uint64
84
85
86
87
88
89 FileSize uint64
90 }
91
92
93 type Section struct {
94 SectionHeader
95
96
97
98
99
100
101
102
103
104
105
106 io.ReaderAt
107 sr *io.SectionReader
108
109 compressionType CompressionType
110 compressionOffset int64
111 }
112
113
114
115
116
117
118 func (s *Section) Data() ([]byte, error) {
119 return saferio.ReadData(s.Open(), s.Size)
120 }
121
122
123
124 func (f *File) stringTable(link uint32) ([]byte, error) {
125 if link <= 0 || link >= uint32(len(f.Sections)) {
126 return nil, errors.New("section has invalid string table link")
127 }
128 return f.Sections[link].Data()
129 }
130
131
132
133
134
135
136
137 func (s *Section) Open() io.ReadSeeker {
138 if s.Type == SHT_NOBITS {
139 return io.NewSectionReader(&nobitsSectionReader{}, 0, int64(s.Size))
140 }
141
142 var zrd func(io.Reader) (io.ReadCloser, error)
143 if s.Flags&SHF_COMPRESSED == 0 {
144
145 if !strings.HasPrefix(s.Name, ".zdebug") {
146 return io.NewSectionReader(s.sr, 0, 1<<63-1)
147 }
148
149 b := make([]byte, 12)
150 n, _ := s.sr.ReadAt(b, 0)
151 if n != 12 || string(b[:4]) != "ZLIB" {
152 return io.NewSectionReader(s.sr, 0, 1<<63-1)
153 }
154
155 s.compressionOffset = 12
156 s.compressionType = COMPRESS_ZLIB
157 s.Size = binary.BigEndian.Uint64(b[4:12])
158 zrd = zlib.NewReader
159
160 } else if s.Flags&SHF_ALLOC != 0 {
161 return errorReader{&FormatError{int64(s.Offset),
162 "SHF_COMPRESSED applies only to non-allocable sections", s.compressionType}}
163 }
164
165 switch s.compressionType {
166 case COMPRESS_ZLIB:
167 zrd = zlib.NewReader
168 case COMPRESS_ZSTD:
169 zrd = func(r io.Reader) (io.ReadCloser, error) {
170 return io.NopCloser(zstd.NewReader(r)), nil
171 }
172 }
173
174 if zrd == nil {
175 return errorReader{&FormatError{int64(s.Offset), "unknown compression type", s.compressionType}}
176 }
177
178 return &readSeekerFromReader{
179 reset: func() (io.Reader, error) {
180 fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.FileSize)-s.compressionOffset)
181 return zrd(fr)
182 },
183 size: int64(s.Size),
184 }
185 }
186
187
188 type ProgHeader struct {
189 Type ProgType
190 Flags ProgFlag
191 Off uint64
192 Vaddr uint64
193 Paddr uint64
194 Filesz uint64
195 Memsz uint64
196 Align uint64
197 }
198
199
200 type Prog struct {
201 ProgHeader
202
203
204
205
206
207
208
209 io.ReaderAt
210 sr *io.SectionReader
211 }
212
213
214 func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) }
215
216
217 type Symbol struct {
218 Name string
219 Info, Other byte
220 Section SectionIndex
221 Value, Size uint64
222
223
224
225 Version string
226 Library string
227 }
228
229
232
233 type FormatError struct {
234 off int64
235 msg string
236 val any
237 }
238
239 func (e *FormatError) Error() string {
240 msg := e.msg
241 if e.val != nil {
242 msg += fmt.Sprintf(" '%v' ", e.val)
243 }
244 msg += fmt.Sprintf("in record at byte %#x", e.off)
245 return msg
246 }
247
248
249 func Open(name string) (*File, error) {
250 f, err := os.Open(name)
251 if err != nil {
252 return nil, err
253 }
254 ff, err := NewFile(f)
255 if err != nil {
256 f.Close()
257 return nil, err
258 }
259 ff.closer = f
260 return ff, nil
261 }
262
263
264
265
266 func (f *File) Close() error {
267 var err error
268 if f.closer != nil {
269 err = f.closer.Close()
270 f.closer = nil
271 }
272 return err
273 }
274
275
276
277 func (f *File) SectionByType(typ SectionType) *Section {
278 for _, s := range f.Sections {
279 if s.Type == typ {
280 return s
281 }
282 }
283 return nil
284 }
285
286
287
288 func NewFile(r io.ReaderAt) (*File, error) {
289 sr := io.NewSectionReader(r, 0, 1<<63-1)
290
291 var ident [16]uint8
292 if _, err := r.ReadAt(ident[0:], 0); err != nil {
293 return nil, err
294 }
295 if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' {
296 return nil, &FormatError{0, "bad magic number", ident[0:4]}
297 }
298
299 f := new(File)
300 f.Class = Class(ident[EI_CLASS])
301 switch f.Class {
302 case ELFCLASS32:
303 case ELFCLASS64:
304
305 default:
306 return nil, &FormatError{0, "unknown ELF class", f.Class}
307 }
308
309 f.Data = Data(ident[EI_DATA])
310 switch f.Data {
311 case ELFDATA2LSB:
312 f.ByteOrder = binary.LittleEndian
313 case ELFDATA2MSB:
314 f.ByteOrder = binary.BigEndian
315 default:
316 return nil, &FormatError{0, "unknown ELF data encoding", f.Data}
317 }
318
319 f.Version = Version(ident[EI_VERSION])
320 if f.Version != EV_CURRENT {
321 return nil, &FormatError{0, "unknown ELF version", f.Version}
322 }
323
324 f.OSABI = OSABI(ident[EI_OSABI])
325 f.ABIVersion = ident[EI_ABIVERSION]
326
327
328 var phoff int64
329 var phentsize, phnum int
330 var shoff int64
331 var shentsize, shnum, shstrndx int
332 switch f.Class {
333 case ELFCLASS32:
334 hdr := new(Header32)
335 sr.Seek(0, seekStart)
336 if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
337 return nil, err
338 }
339 f.Type = Type(hdr.Type)
340 f.Machine = Machine(hdr.Machine)
341 f.Entry = uint64(hdr.Entry)
342 if v := Version(hdr.Version); v != f.Version {
343 return nil, &FormatError{0, "mismatched ELF version", v}
344 }
345 phoff = int64(hdr.Phoff)
346 phentsize = int(hdr.Phentsize)
347 phnum = int(hdr.Phnum)
348 shoff = int64(hdr.Shoff)
349 shentsize = int(hdr.Shentsize)
350 shnum = int(hdr.Shnum)
351 shstrndx = int(hdr.Shstrndx)
352 case ELFCLASS64:
353 hdr := new(Header64)
354 sr.Seek(0, seekStart)
355 if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
356 return nil, err
357 }
358 f.Type = Type(hdr.Type)
359 f.Machine = Machine(hdr.Machine)
360 f.Entry = hdr.Entry
361 if v := Version(hdr.Version); v != f.Version {
362 return nil, &FormatError{0, "mismatched ELF version", v}
363 }
364 phoff = int64(hdr.Phoff)
365 phentsize = int(hdr.Phentsize)
366 phnum = int(hdr.Phnum)
367 shoff = int64(hdr.Shoff)
368 shentsize = int(hdr.Shentsize)
369 shnum = int(hdr.Shnum)
370 shstrndx = int(hdr.Shstrndx)
371 }
372
373 if shoff < 0 {
374 return nil, &FormatError{0, "invalid shoff", shoff}
375 }
376 if phoff < 0 {
377 return nil, &FormatError{0, "invalid phoff", phoff}
378 }
379
380 if shoff == 0 && shnum != 0 {
381 return nil, &FormatError{0, "invalid ELF shnum for shoff=0", shnum}
382 }
383
384 if shnum > 0 && shstrndx >= shnum {
385 return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx}
386 }
387
388 var wantPhentsize, wantShentsize int
389 switch f.Class {
390 case ELFCLASS32:
391 wantPhentsize = 8 * 4
392 wantShentsize = 10 * 4
393 case ELFCLASS64:
394 wantPhentsize = 2*4 + 6*8
395 wantShentsize = 4*4 + 6*8
396 }
397 if phnum > 0 && phentsize < wantPhentsize {
398 return nil, &FormatError{0, "invalid ELF phentsize", phentsize}
399 }
400
401
402 f.Progs = make([]*Prog, phnum)
403 for i := 0; i < phnum; i++ {
404 off := phoff + int64(i)*int64(phentsize)
405 sr.Seek(off, seekStart)
406 p := new(Prog)
407 switch f.Class {
408 case ELFCLASS32:
409 ph := new(Prog32)
410 if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
411 return nil, err
412 }
413 p.ProgHeader = ProgHeader{
414 Type: ProgType(ph.Type),
415 Flags: ProgFlag(ph.Flags),
416 Off: uint64(ph.Off),
417 Vaddr: uint64(ph.Vaddr),
418 Paddr: uint64(ph.Paddr),
419 Filesz: uint64(ph.Filesz),
420 Memsz: uint64(ph.Memsz),
421 Align: uint64(ph.Align),
422 }
423 case ELFCLASS64:
424 ph := new(Prog64)
425 if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
426 return nil, err
427 }
428 p.ProgHeader = ProgHeader{
429 Type: ProgType(ph.Type),
430 Flags: ProgFlag(ph.Flags),
431 Off: ph.Off,
432 Vaddr: ph.Vaddr,
433 Paddr: ph.Paddr,
434 Filesz: ph.Filesz,
435 Memsz: ph.Memsz,
436 Align: ph.Align,
437 }
438 }
439 if int64(p.Off) < 0 {
440 return nil, &FormatError{off, "invalid program header offset", p.Off}
441 }
442 if int64(p.Filesz) < 0 {
443 return nil, &FormatError{off, "invalid program header file size", p.Filesz}
444 }
445 p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
446 p.ReaderAt = p.sr
447 f.Progs[i] = p
448 }
449
450
451
452
453
454 if shoff > 0 && shnum == 0 {
455 var typ, link uint32
456 sr.Seek(shoff, seekStart)
457 switch f.Class {
458 case ELFCLASS32:
459 sh := new(Section32)
460 if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
461 return nil, err
462 }
463 shnum = int(sh.Size)
464 typ = sh.Type
465 link = sh.Link
466 case ELFCLASS64:
467 sh := new(Section64)
468 if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
469 return nil, err
470 }
471 shnum = int(sh.Size)
472 typ = sh.Type
473 link = sh.Link
474 }
475 if SectionType(typ) != SHT_NULL {
476 return nil, &FormatError{shoff, "invalid type of the initial section", SectionType(typ)}
477 }
478
479 if shnum < int(SHN_LORESERVE) {
480 return nil, &FormatError{shoff, "invalid ELF shnum contained in sh_size", shnum}
481 }
482
483
484
485
486
487
488 if shstrndx == int(SHN_XINDEX) {
489 shstrndx = int(link)
490 if shstrndx < int(SHN_LORESERVE) {
491 return nil, &FormatError{shoff, "invalid ELF shstrndx contained in sh_link", shstrndx}
492 }
493 }
494 }
495
496 if shnum > 0 && shentsize < wantShentsize {
497 return nil, &FormatError{0, "invalid ELF shentsize", shentsize}
498 }
499
500
501 c := saferio.SliceCap((*Section)(nil), uint64(shnum))
502 if c < 0 {
503 return nil, &FormatError{0, "too many sections", shnum}
504 }
505 f.Sections = make([]*Section, 0, c)
506 names := make([]uint32, 0, c)
507 for i := 0; i < shnum; i++ {
508 off := shoff + int64(i)*int64(shentsize)
509 sr.Seek(off, seekStart)
510 s := new(Section)
511 switch f.Class {
512 case ELFCLASS32:
513 sh := new(Section32)
514 if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
515 return nil, err
516 }
517 names = append(names, sh.Name)
518 s.SectionHeader = SectionHeader{
519 Type: SectionType(sh.Type),
520 Flags: SectionFlag(sh.Flags),
521 Addr: uint64(sh.Addr),
522 Offset: uint64(sh.Off),
523 FileSize: uint64(sh.Size),
524 Link: sh.Link,
525 Info: sh.Info,
526 Addralign: uint64(sh.Addralign),
527 Entsize: uint64(sh.Entsize),
528 }
529 case ELFCLASS64:
530 sh := new(Section64)
531 if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
532 return nil, err
533 }
534 names = append(names, sh.Name)
535 s.SectionHeader = SectionHeader{
536 Type: SectionType(sh.Type),
537 Flags: SectionFlag(sh.Flags),
538 Offset: sh.Off,
539 FileSize: sh.Size,
540 Addr: sh.Addr,
541 Link: sh.Link,
542 Info: sh.Info,
543 Addralign: sh.Addralign,
544 Entsize: sh.Entsize,
545 }
546 }
547 if int64(s.Offset) < 0 {
548 return nil, &FormatError{off, "invalid section offset", int64(s.Offset)}
549 }
550 if int64(s.FileSize) < 0 {
551 return nil, &FormatError{off, "invalid section size", int64(s.FileSize)}
552 }
553 s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))
554
555 if s.Flags&SHF_COMPRESSED == 0 {
556 s.ReaderAt = s.sr
557 s.Size = s.FileSize
558 } else {
559
560 switch f.Class {
561 case ELFCLASS32:
562 ch := new(Chdr32)
563 if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
564 return nil, err
565 }
566 s.compressionType = CompressionType(ch.Type)
567 s.Size = uint64(ch.Size)
568 s.Addralign = uint64(ch.Addralign)
569 s.compressionOffset = int64(binary.Size(ch))
570 case ELFCLASS64:
571 ch := new(Chdr64)
572 if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
573 return nil, err
574 }
575 s.compressionType = CompressionType(ch.Type)
576 s.Size = ch.Size
577 s.Addralign = ch.Addralign
578 s.compressionOffset = int64(binary.Size(ch))
579 }
580 }
581
582 f.Sections = append(f.Sections, s)
583 }
584
585 if len(f.Sections) == 0 {
586 return f, nil
587 }
588
589
590 if shstrndx == 0 {
591
592
593 return f, nil
594 }
595 shstr := f.Sections[shstrndx]
596 if shstr.Type != SHT_STRTAB {
597 return nil, &FormatError{shoff + int64(shstrndx*shentsize), "invalid ELF section name string table type", shstr.Type}
598 }
599 shstrtab, err := shstr.Data()
600 if err != nil {
601 return nil, err
602 }
603 for i, s := range f.Sections {
604 var ok bool
605 s.Name, ok = getString(shstrtab, int(names[i]))
606 if !ok {
607 return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]}
608 }
609 }
610
611 return f, nil
612 }
613
614
615
616 func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) {
617 switch f.Class {
618 case ELFCLASS64:
619 return f.getSymbols64(typ)
620
621 case ELFCLASS32:
622 return f.getSymbols32(typ)
623 }
624
625 return nil, nil, errors.New("not implemented")
626 }
627
628
629
630 var ErrNoSymbols = errors.New("no symbol section")
631
632 func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {
633 symtabSection := f.SectionByType(typ)
634 if symtabSection == nil {
635 return nil, nil, ErrNoSymbols
636 }
637
638 data, err := symtabSection.Data()
639 if err != nil {
640 return nil, nil, fmt.Errorf("cannot load symbol section: %w", err)
641 }
642 symtab := bytes.NewReader(data)
643 if symtab.Len()%Sym32Size != 0 {
644 return nil, nil, errors.New("length of symbol section is not a multiple of SymSize")
645 }
646
647 strdata, err := f.stringTable(symtabSection.Link)
648 if err != nil {
649 return nil, nil, fmt.Errorf("cannot load string table section: %w", err)
650 }
651
652
653 var skip [Sym32Size]byte
654 symtab.Read(skip[:])
655
656 symbols := make([]Symbol, symtab.Len()/Sym32Size)
657
658 i := 0
659 var sym Sym32
660 for symtab.Len() > 0 {
661 binary.Read(symtab, f.ByteOrder, &sym)
662 str, _ := getString(strdata, int(sym.Name))
663 symbols[i].Name = str
664 symbols[i].Info = sym.Info
665 symbols[i].Other = sym.Other
666 symbols[i].Section = SectionIndex(sym.Shndx)
667 symbols[i].Value = uint64(sym.Value)
668 symbols[i].Size = uint64(sym.Size)
669 i++
670 }
671
672 return symbols, strdata, nil
673 }
674
675 func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
676 symtabSection := f.SectionByType(typ)
677 if symtabSection == nil {
678 return nil, nil, ErrNoSymbols
679 }
680
681 data, err := symtabSection.Data()
682 if err != nil {
683 return nil, nil, fmt.Errorf("cannot load symbol section: %w", err)
684 }
685 symtab := bytes.NewReader(data)
686 if symtab.Len()%Sym64Size != 0 {
687 return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
688 }
689
690 strdata, err := f.stringTable(symtabSection.Link)
691 if err != nil {
692 return nil, nil, fmt.Errorf("cannot load string table section: %w", err)
693 }
694
695
696 var skip [Sym64Size]byte
697 symtab.Read(skip[:])
698
699 symbols := make([]Symbol, symtab.Len()/Sym64Size)
700
701 i := 0
702 var sym Sym64
703 for symtab.Len() > 0 {
704 binary.Read(symtab, f.ByteOrder, &sym)
705 str, _ := getString(strdata, int(sym.Name))
706 symbols[i].Name = str
707 symbols[i].Info = sym.Info
708 symbols[i].Other = sym.Other
709 symbols[i].Section = SectionIndex(sym.Shndx)
710 symbols[i].Value = sym.Value
711 symbols[i].Size = sym.Size
712 i++
713 }
714
715 return symbols, strdata, nil
716 }
717
718
719 func getString(section []byte, start int) (string, bool) {
720 if start < 0 || start >= len(section) {
721 return "", false
722 }
723
724 for end := start; end < len(section); end++ {
725 if section[end] == 0 {
726 return string(section[start:end]), true
727 }
728 }
729 return "", false
730 }
731
732
733
734 func (f *File) Section(name string) *Section {
735 for _, s := range f.Sections {
736 if s.Name == name {
737 return s
738 }
739 }
740 return nil
741 }
742
743
744
745 func (f *File) applyRelocations(dst []byte, rels []byte) error {
746 switch {
747 case f.Class == ELFCLASS64 && f.Machine == EM_X86_64:
748 return f.applyRelocationsAMD64(dst, rels)
749 case f.Class == ELFCLASS32 && f.Machine == EM_386:
750 return f.applyRelocations386(dst, rels)
751 case f.Class == ELFCLASS32 && f.Machine == EM_ARM:
752 return f.applyRelocationsARM(dst, rels)
753 case f.Class == ELFCLASS64 && f.Machine == EM_AARCH64:
754 return f.applyRelocationsARM64(dst, rels)
755 case f.Class == ELFCLASS32 && f.Machine == EM_PPC:
756 return f.applyRelocationsPPC(dst, rels)
757 case f.Class == ELFCLASS64 && f.Machine == EM_PPC64:
758 return f.applyRelocationsPPC64(dst, rels)
759 case f.Class == ELFCLASS32 && f.Machine == EM_MIPS:
760 return f.applyRelocationsMIPS(dst, rels)
761 case f.Class == ELFCLASS64 && f.Machine == EM_MIPS:
762 return f.applyRelocationsMIPS64(dst, rels)
763 case f.Class == ELFCLASS64 && f.Machine == EM_LOONGARCH:
764 return f.applyRelocationsLOONG64(dst, rels)
765 case f.Class == ELFCLASS64 && f.Machine == EM_RISCV:
766 return f.applyRelocationsRISCV64(dst, rels)
767 case f.Class == ELFCLASS64 && f.Machine == EM_S390:
768 return f.applyRelocationss390x(dst, rels)
769 case f.Class == ELFCLASS64 && f.Machine == EM_SPARCV9:
770 return f.applyRelocationsSPARC64(dst, rels)
771 default:
772 return errors.New("applyRelocations: not implemented")
773 }
774 }
775
776
777
778
779
780
781
782 func canApplyRelocation(sym *Symbol) bool {
783 return sym.Section != SHN_UNDEF && sym.Section < SHN_LORESERVE
784 }
785
786 func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
787
788 if len(rels)%24 != 0 {
789 return errors.New("length of relocation section is not a multiple of 24")
790 }
791
792 symbols, _, err := f.getSymbols(SHT_SYMTAB)
793 if err != nil {
794 return err
795 }
796
797 b := bytes.NewReader(rels)
798 var rela Rela64
799
800 for b.Len() > 0 {
801 binary.Read(b, f.ByteOrder, &rela)
802 symNo := rela.Info >> 32
803 t := R_X86_64(rela.Info & 0xffff)
804
805 if symNo == 0 || symNo > uint64(len(symbols)) {
806 continue
807 }
808 sym := &symbols[symNo-1]
809 if !canApplyRelocation(sym) {
810 continue
811 }
812
813
814
815
816
817 switch t {
818 case R_X86_64_64:
819 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
820 continue
821 }
822 val64 := sym.Value + uint64(rela.Addend)
823 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
824 case R_X86_64_32:
825 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
826 continue
827 }
828 val32 := uint32(sym.Value) + uint32(rela.Addend)
829 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
830 }
831 }
832
833 return nil
834 }
835
836 func (f *File) applyRelocations386(dst []byte, rels []byte) error {
837
838 if len(rels)%8 != 0 {
839 return errors.New("length of relocation section is not a multiple of 8")
840 }
841
842 symbols, _, err := f.getSymbols(SHT_SYMTAB)
843 if err != nil {
844 return err
845 }
846
847 b := bytes.NewReader(rels)
848 var rel Rel32
849
850 for b.Len() > 0 {
851 binary.Read(b, f.ByteOrder, &rel)
852 symNo := rel.Info >> 8
853 t := R_386(rel.Info & 0xff)
854
855 if symNo == 0 || symNo > uint32(len(symbols)) {
856 continue
857 }
858 sym := &symbols[symNo-1]
859
860 if t == R_386_32 {
861 if rel.Off+4 >= uint32(len(dst)) {
862 continue
863 }
864 val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
865 val += uint32(sym.Value)
866 f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
867 }
868 }
869
870 return nil
871 }
872
873 func (f *File) applyRelocationsARM(dst []byte, rels []byte) error {
874
875 if len(rels)%8 != 0 {
876 return errors.New("length of relocation section is not a multiple of 8")
877 }
878
879 symbols, _, err := f.getSymbols(SHT_SYMTAB)
880 if err != nil {
881 return err
882 }
883
884 b := bytes.NewReader(rels)
885 var rel Rel32
886
887 for b.Len() > 0 {
888 binary.Read(b, f.ByteOrder, &rel)
889 symNo := rel.Info >> 8
890 t := R_ARM(rel.Info & 0xff)
891
892 if symNo == 0 || symNo > uint32(len(symbols)) {
893 continue
894 }
895 sym := &symbols[symNo-1]
896
897 switch t {
898 case R_ARM_ABS32:
899 if rel.Off+4 >= uint32(len(dst)) {
900 continue
901 }
902 val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
903 val += uint32(sym.Value)
904 f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
905 }
906 }
907
908 return nil
909 }
910
911 func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
912
913 if len(rels)%24 != 0 {
914 return errors.New("length of relocation section is not a multiple of 24")
915 }
916
917 symbols, _, err := f.getSymbols(SHT_SYMTAB)
918 if err != nil {
919 return err
920 }
921
922 b := bytes.NewReader(rels)
923 var rela Rela64
924
925 for b.Len() > 0 {
926 binary.Read(b, f.ByteOrder, &rela)
927 symNo := rela.Info >> 32
928 t := R_AARCH64(rela.Info & 0xffff)
929
930 if symNo == 0 || symNo > uint64(len(symbols)) {
931 continue
932 }
933 sym := &symbols[symNo-1]
934 if !canApplyRelocation(sym) {
935 continue
936 }
937
938
939
940
941
942 switch t {
943 case R_AARCH64_ABS64:
944 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
945 continue
946 }
947 val64 := sym.Value + uint64(rela.Addend)
948 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
949 case R_AARCH64_ABS32:
950 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
951 continue
952 }
953 val32 := uint32(sym.Value) + uint32(rela.Addend)
954 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
955 }
956 }
957
958 return nil
959 }
960
961 func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {
962
963 if len(rels)%12 != 0 {
964 return errors.New("length of relocation section is not a multiple of 12")
965 }
966
967 symbols, _, err := f.getSymbols(SHT_SYMTAB)
968 if err != nil {
969 return err
970 }
971
972 b := bytes.NewReader(rels)
973 var rela Rela32
974
975 for b.Len() > 0 {
976 binary.Read(b, f.ByteOrder, &rela)
977 symNo := rela.Info >> 8
978 t := R_PPC(rela.Info & 0xff)
979
980 if symNo == 0 || symNo > uint32(len(symbols)) {
981 continue
982 }
983 sym := &symbols[symNo-1]
984 if !canApplyRelocation(sym) {
985 continue
986 }
987
988 switch t {
989 case R_PPC_ADDR32:
990 if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 {
991 continue
992 }
993 val32 := uint32(sym.Value) + uint32(rela.Addend)
994 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
995 }
996 }
997
998 return nil
999 }
1000
1001 func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
1002
1003 if len(rels)%24 != 0 {
1004 return errors.New("length of relocation section is not a multiple of 24")
1005 }
1006
1007 symbols, _, err := f.getSymbols(SHT_SYMTAB)
1008 if err != nil {
1009 return err
1010 }
1011
1012 b := bytes.NewReader(rels)
1013 var rela Rela64
1014
1015 for b.Len() > 0 {
1016 binary.Read(b, f.ByteOrder, &rela)
1017 symNo := rela.Info >> 32
1018 t := R_PPC64(rela.Info & 0xffff)
1019
1020 if symNo == 0 || symNo > uint64(len(symbols)) {
1021 continue
1022 }
1023 sym := &symbols[symNo-1]
1024 if !canApplyRelocation(sym) {
1025 continue
1026 }
1027
1028 switch t {
1029 case R_PPC64_ADDR64:
1030 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
1031 continue
1032 }
1033 val64 := sym.Value + uint64(rela.Addend)
1034 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
1035 case R_PPC64_ADDR32:
1036 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
1037 continue
1038 }
1039 val32 := uint32(sym.Value) + uint32(rela.Addend)
1040 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
1041 }
1042 }
1043
1044 return nil
1045 }
1046
1047 func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error {
1048
1049 if len(rels)%8 != 0 {
1050 return errors.New("length of relocation section is not a multiple of 8")
1051 }
1052
1053 symbols, _, err := f.getSymbols(SHT_SYMTAB)
1054 if err != nil {
1055 return err
1056 }
1057
1058 b := bytes.NewReader(rels)
1059 var rel Rel32
1060
1061 for b.Len() > 0 {
1062 binary.Read(b, f.ByteOrder, &rel)
1063 symNo := rel.Info >> 8
1064 t := R_MIPS(rel.Info & 0xff)
1065
1066 if symNo == 0 || symNo > uint32(len(symbols)) {
1067 continue
1068 }
1069 sym := &symbols[symNo-1]
1070
1071 switch t {
1072 case R_MIPS_32:
1073 if rel.Off+4 >= uint32(len(dst)) {
1074 continue
1075 }
1076 val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
1077 val += uint32(sym.Value)
1078 f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
1079 }
1080 }
1081
1082 return nil
1083 }
1084
1085 func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {
1086
1087 if len(rels)%24 != 0 {
1088 return errors.New("length of relocation section is not a multiple of 24")
1089 }
1090
1091 symbols, _, err := f.getSymbols(SHT_SYMTAB)
1092 if err != nil {
1093 return err
1094 }
1095
1096 b := bytes.NewReader(rels)
1097 var rela Rela64
1098
1099 for b.Len() > 0 {
1100 binary.Read(b, f.ByteOrder, &rela)
1101 var symNo uint64
1102 var t R_MIPS
1103 if f.ByteOrder == binary.BigEndian {
1104 symNo = rela.Info >> 32
1105 t = R_MIPS(rela.Info & 0xff)
1106 } else {
1107 symNo = rela.Info & 0xffffffff
1108 t = R_MIPS(rela.Info >> 56)
1109 }
1110
1111 if symNo == 0 || symNo > uint64(len(symbols)) {
1112 continue
1113 }
1114 sym := &symbols[symNo-1]
1115 if !canApplyRelocation(sym) {
1116 continue
1117 }
1118
1119 switch t {
1120 case R_MIPS_64:
1121 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
1122 continue
1123 }
1124 val64 := sym.Value + uint64(rela.Addend)
1125 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
1126 case R_MIPS_32:
1127 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
1128 continue
1129 }
1130 val32 := uint32(sym.Value) + uint32(rela.Addend)
1131 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
1132 }
1133 }
1134
1135 return nil
1136 }
1137
1138 func (f *File) applyRelocationsLOONG64(dst []byte, rels []byte) error {
1139
1140 if len(rels)%24 != 0 {
1141 return errors.New("length of relocation section is not a multiple of 24")
1142 }
1143
1144 symbols, _, err := f.getSymbols(SHT_SYMTAB)
1145 if err != nil {
1146 return err
1147 }
1148
1149 b := bytes.NewReader(rels)
1150 var rela Rela64
1151
1152 for b.Len() > 0 {
1153 binary.Read(b, f.ByteOrder, &rela)
1154 var symNo uint64
1155 var t R_LARCH
1156 symNo = rela.Info >> 32
1157 t = R_LARCH(rela.Info & 0xffff)
1158
1159 if symNo == 0 || symNo > uint64(len(symbols)) {
1160 continue
1161 }
1162 sym := &symbols[symNo-1]
1163 if !canApplyRelocation(sym) {
1164 continue
1165 }
1166
1167 switch t {
1168 case R_LARCH_64:
1169 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
1170 continue
1171 }
1172 val64 := sym.Value + uint64(rela.Addend)
1173 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
1174 case R_LARCH_32:
1175 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
1176 continue
1177 }
1178 val32 := uint32(sym.Value) + uint32(rela.Addend)
1179 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
1180 }
1181 }
1182
1183 return nil
1184 }
1185
1186 func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {
1187
1188 if len(rels)%24 != 0 {
1189 return errors.New("length of relocation section is not a multiple of 24")
1190 }
1191
1192 symbols, _, err := f.getSymbols(SHT_SYMTAB)
1193 if err != nil {
1194 return err
1195 }
1196
1197 b := bytes.NewReader(rels)
1198 var rela Rela64
1199
1200 for b.Len() > 0 {
1201 binary.Read(b, f.ByteOrder, &rela)
1202 symNo := rela.Info >> 32
1203 t := R_RISCV(rela.Info & 0xffff)
1204
1205 if symNo == 0 || symNo > uint64(len(symbols)) {
1206 continue
1207 }
1208 sym := &symbols[symNo-1]
1209 if !canApplyRelocation(sym) {
1210 continue
1211 }
1212
1213 switch t {
1214 case R_RISCV_64:
1215 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
1216 continue
1217 }
1218 val64 := sym.Value + uint64(rela.Addend)
1219 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
1220 case R_RISCV_32:
1221 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
1222 continue
1223 }
1224 val32 := uint32(sym.Value) + uint32(rela.Addend)
1225 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
1226 }
1227 }
1228
1229 return nil
1230 }
1231
1232 func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {
1233
1234 if len(rels)%24 != 0 {
1235 return errors.New("length of relocation section is not a multiple of 24")
1236 }
1237
1238 symbols, _, err := f.getSymbols(SHT_SYMTAB)
1239 if err != nil {
1240 return err
1241 }
1242
1243 b := bytes.NewReader(rels)
1244 var rela Rela64
1245
1246 for b.Len() > 0 {
1247 binary.Read(b, f.ByteOrder, &rela)
1248 symNo := rela.Info >> 32
1249 t := R_390(rela.Info & 0xffff)
1250
1251 if symNo == 0 || symNo > uint64(len(symbols)) {
1252 continue
1253 }
1254 sym := &symbols[symNo-1]
1255 if !canApplyRelocation(sym) {
1256 continue
1257 }
1258
1259 switch t {
1260 case R_390_64:
1261 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
1262 continue
1263 }
1264 val64 := sym.Value + uint64(rela.Addend)
1265 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
1266 case R_390_32:
1267 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
1268 continue
1269 }
1270 val32 := uint32(sym.Value) + uint32(rela.Addend)
1271 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
1272 }
1273 }
1274
1275 return nil
1276 }
1277
1278 func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {
1279
1280 if len(rels)%24 != 0 {
1281 return errors.New("length of relocation section is not a multiple of 24")
1282 }
1283
1284 symbols, _, err := f.getSymbols(SHT_SYMTAB)
1285 if err != nil {
1286 return err
1287 }
1288
1289 b := bytes.NewReader(rels)
1290 var rela Rela64
1291
1292 for b.Len() > 0 {
1293 binary.Read(b, f.ByteOrder, &rela)
1294 symNo := rela.Info >> 32
1295 t := R_SPARC(rela.Info & 0xff)
1296
1297 if symNo == 0 || symNo > uint64(len(symbols)) {
1298 continue
1299 }
1300 sym := &symbols[symNo-1]
1301 if !canApplyRelocation(sym) {
1302 continue
1303 }
1304
1305 switch t {
1306 case R_SPARC_64, R_SPARC_UA64:
1307 if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
1308 continue
1309 }
1310 val64 := sym.Value + uint64(rela.Addend)
1311 f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64)
1312 case R_SPARC_32, R_SPARC_UA32:
1313 if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
1314 continue
1315 }
1316 val32 := uint32(sym.Value) + uint32(rela.Addend)
1317 f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32)
1318 }
1319 }
1320
1321 return nil
1322 }
1323
1324 func (f *File) DWARF() (*dwarf.Data, error) {
1325 dwarfSuffix := func(s *Section) string {
1326 switch {
1327 case strings.HasPrefix(s.Name, ".debug_"):
1328 return s.Name[7:]
1329 case strings.HasPrefix(s.Name, ".zdebug_"):
1330 return s.Name[8:]
1331 default:
1332 return ""
1333 }
1334
1335 }
1336
1337
1338 sectionData := func(i int, s *Section) ([]byte, error) {
1339 b, err := s.Data()
1340 if err != nil && uint64(len(b)) < s.Size {
1341 return nil, err
1342 }
1343
1344 if f.Type == ET_EXEC {
1345
1346
1347
1348 return b, nil
1349 }
1350
1351 for _, r := range f.Sections {
1352 if r.Type != SHT_RELA && r.Type != SHT_REL {
1353 continue
1354 }
1355 if int(r.Info) != i {
1356 continue
1357 }
1358 rd, err := r.Data()
1359 if err != nil {
1360 return nil, err
1361 }
1362 err = f.applyRelocations(b, rd)
1363 if err != nil {
1364 return nil, err
1365 }
1366 }
1367 return b, nil
1368 }
1369
1370
1371
1372 var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
1373 for i, s := range f.Sections {
1374 suffix := dwarfSuffix(s)
1375 if suffix == "" {
1376 continue
1377 }
1378 if _, ok := dat[suffix]; !ok {
1379 continue
1380 }
1381 b, err := sectionData(i, s)
1382 if err != nil {
1383 return nil, err
1384 }
1385 dat[suffix] = b
1386 }
1387
1388 d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
1389 if err != nil {
1390 return nil, err
1391 }
1392
1393
1394 for i, s := range f.Sections {
1395 suffix := dwarfSuffix(s)
1396 if suffix == "" {
1397 continue
1398 }
1399 if _, ok := dat[suffix]; ok {
1400
1401 continue
1402 }
1403
1404 b, err := sectionData(i, s)
1405 if err != nil {
1406 return nil, err
1407 }
1408
1409 if suffix == "types" {
1410 if err := d.AddTypes(fmt.Sprintf("types-%d", i), b); err != nil {
1411 return nil, err
1412 }
1413 } else {
1414 if err := d.AddSection(".debug_"+suffix, b); err != nil {
1415 return nil, err
1416 }
1417 }
1418 }
1419
1420 return d, nil
1421 }
1422
1423
1424
1425
1426
1427
1428
1429 func (f *File) Symbols() ([]Symbol, error) {
1430 sym, _, err := f.getSymbols(SHT_SYMTAB)
1431 return sym, err
1432 }
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443 func (f *File) DynamicSymbols() ([]Symbol, error) {
1444 sym, str, err := f.getSymbols(SHT_DYNSYM)
1445 if err != nil {
1446 return nil, err
1447 }
1448 if f.gnuVersionInit(str) {
1449 for i := range sym {
1450 sym[i].Library, sym[i].Version = f.gnuVersion(i)
1451 }
1452 }
1453 return sym, nil
1454 }
1455
1456 type ImportedSymbol struct {
1457 Name string
1458 Version string
1459 Library string
1460 }
1461
1462
1463
1464
1465
1466 func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
1467 sym, str, err := f.getSymbols(SHT_DYNSYM)
1468 if err != nil {
1469 return nil, err
1470 }
1471 f.gnuVersionInit(str)
1472 var all []ImportedSymbol
1473 for i, s := range sym {
1474 if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {
1475 all = append(all, ImportedSymbol{Name: s.Name})
1476 sym := &all[len(all)-1]
1477 sym.Library, sym.Version = f.gnuVersion(i)
1478 }
1479 }
1480 return all, nil
1481 }
1482
1483 type verneed struct {
1484 File string
1485 Name string
1486 }
1487
1488
1489
1490 func (f *File) gnuVersionInit(str []byte) bool {
1491 if f.gnuNeed != nil {
1492
1493 return true
1494 }
1495
1496
1497 vn := f.SectionByType(SHT_GNU_VERNEED)
1498 if vn == nil {
1499 return false
1500 }
1501 d, _ := vn.Data()
1502
1503 var need []verneed
1504 i := 0
1505 for {
1506 if i+16 > len(d) {
1507 break
1508 }
1509 vers := f.ByteOrder.Uint16(d[i : i+2])
1510 if vers != 1 {
1511 break
1512 }
1513 cnt := f.ByteOrder.Uint16(d[i+2 : i+4])
1514 fileoff := f.ByteOrder.Uint32(d[i+4 : i+8])
1515 aux := f.ByteOrder.Uint32(d[i+8 : i+12])
1516 next := f.ByteOrder.Uint32(d[i+12 : i+16])
1517 file, _ := getString(str, int(fileoff))
1518
1519 var name string
1520 j := i + int(aux)
1521 for c := 0; c < int(cnt); c++ {
1522 if j+16 > len(d) {
1523 break
1524 }
1525
1526
1527 other := f.ByteOrder.Uint16(d[j+6 : j+8])
1528 nameoff := f.ByteOrder.Uint32(d[j+8 : j+12])
1529 next := f.ByteOrder.Uint32(d[j+12 : j+16])
1530 name, _ = getString(str, int(nameoff))
1531 ndx := int(other)
1532 if ndx >= len(need) {
1533 a := make([]verneed, 2*(ndx+1))
1534 copy(a, need)
1535 need = a
1536 }
1537
1538 need[ndx] = verneed{file, name}
1539 if next == 0 {
1540 break
1541 }
1542 j += int(next)
1543 }
1544
1545 if next == 0 {
1546 break
1547 }
1548 i += int(next)
1549 }
1550
1551
1552 vs := f.SectionByType(SHT_GNU_VERSYM)
1553 if vs == nil {
1554 return false
1555 }
1556 d, _ = vs.Data()
1557
1558 f.gnuNeed = need
1559 f.gnuVersym = d
1560 return true
1561 }
1562
1563
1564
1565 func (f *File) gnuVersion(i int) (library string, version string) {
1566
1567 i = (i + 1) * 2
1568 if i >= len(f.gnuVersym) {
1569 return
1570 }
1571 s := f.gnuVersym[i:]
1572 if len(s) < 2 {
1573 return
1574 }
1575 j := int(f.ByteOrder.Uint16(s))
1576 if j < 2 || j >= len(f.gnuNeed) {
1577 return
1578 }
1579 n := &f.gnuNeed[j]
1580 return n.File, n.Name
1581 }
1582
1583
1584
1585
1586 func (f *File) ImportedLibraries() ([]string, error) {
1587 return f.DynString(DT_NEEDED)
1588 }
1589
1590
1591
1592
1593
1594
1595 func (f *File) DynString(tag DynTag) ([]string, error) {
1596 switch tag {
1597 case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH:
1598 default:
1599 return nil, fmt.Errorf("non-string-valued tag %v", tag)
1600 }
1601 ds := f.SectionByType(SHT_DYNAMIC)
1602 if ds == nil {
1603
1604 return nil, nil
1605 }
1606 d, err := ds.Data()
1607 if err != nil {
1608 return nil, err
1609 }
1610 str, err := f.stringTable(ds.Link)
1611 if err != nil {
1612 return nil, err
1613 }
1614 var all []string
1615 for len(d) > 0 {
1616 var t DynTag
1617 var v uint64
1618 switch f.Class {
1619 case ELFCLASS32:
1620 t = DynTag(f.ByteOrder.Uint32(d[0:4]))
1621 v = uint64(f.ByteOrder.Uint32(d[4:8]))
1622 d = d[8:]
1623 case ELFCLASS64:
1624 t = DynTag(f.ByteOrder.Uint64(d[0:8]))
1625 v = f.ByteOrder.Uint64(d[8:16])
1626 d = d[16:]
1627 }
1628 if t == tag {
1629 s, ok := getString(str, int(v))
1630 if ok {
1631 all = append(all, s)
1632 }
1633 }
1634 }
1635 return all, nil
1636 }
1637
1638
1639
1640 func (f *File) DynValue(tag DynTag) ([]uint64, error) {
1641 ds := f.SectionByType(SHT_DYNAMIC)
1642 if ds == nil {
1643 return nil, nil
1644 }
1645 d, err := ds.Data()
1646 if err != nil {
1647 return nil, err
1648 }
1649
1650
1651 var vals []uint64
1652 for len(d) > 0 {
1653 var t DynTag
1654 var v uint64
1655 switch f.Class {
1656 case ELFCLASS32:
1657 t = DynTag(f.ByteOrder.Uint32(d[0:4]))
1658 v = uint64(f.ByteOrder.Uint32(d[4:8]))
1659 d = d[8:]
1660 case ELFCLASS64:
1661 t = DynTag(f.ByteOrder.Uint64(d[0:8]))
1662 v = f.ByteOrder.Uint64(d[8:16])
1663 d = d[16:]
1664 }
1665 if t == tag {
1666 vals = append(vals, v)
1667 }
1668 }
1669 return vals, nil
1670 }
1671
1672 type nobitsSectionReader struct{}
1673
1674 func (*nobitsSectionReader) ReadAt(p []byte, off int64) (n int, err error) {
1675 return 0, errors.New("unexpected read from SHT_NOBITS section")
1676 }
1677
View as plain text