1// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package symbol_inject
16
17import (
18	"debug/elf"
19	"fmt"
20	"io"
21)
22
23type mockableElfFile interface {
24	Symbols() ([]elf.Symbol, error)
25	Sections() []elf.SectionHeader
26	Type() elf.Type
27}
28
29var _ mockableElfFile = elfFileWrapper{}
30
31type elfFileWrapper struct {
32	*elf.File
33}
34
35func (f elfFileWrapper) Sections() []elf.SectionHeader {
36	ret := make([]elf.SectionHeader, len(f.File.Sections))
37	for i, section := range f.File.Sections {
38		ret[i] = section.SectionHeader
39	}
40
41	return ret
42}
43
44func (f elfFileWrapper) Type() elf.Type {
45	return f.File.Type
46}
47
48type mockElfFile struct {
49	symbols  []elf.Symbol
50	sections []elf.SectionHeader
51	t        elf.Type
52}
53
54func (f mockElfFile) Sections() []elf.SectionHeader  { return f.sections }
55func (f mockElfFile) Symbols() ([]elf.Symbol, error) { return f.symbols, nil }
56func (f mockElfFile) Type() elf.Type                 { return f.t }
57
58func elfSymbolsFromFile(r io.ReaderAt) (*File, error) {
59	elfFile, err := elf.NewFile(r)
60	if err != nil {
61		return nil, cantParseError{err}
62	}
63	return extractElfSymbols(elfFileWrapper{elfFile})
64}
65
66func extractElfSymbols(elfFile mockableElfFile) (*File, error) {
67	symbols, err := elfFile.Symbols()
68	if err != nil {
69		return nil, err
70	}
71
72	file := &File{}
73
74	for _, section := range elfFile.Sections() {
75		file.Sections = append(file.Sections, &Section{
76			Name:   section.Name,
77			Addr:   section.Addr,
78			Offset: section.Offset,
79			Size:   section.Size,
80		})
81	}
82
83	_ = elf.Section{}
84
85	for _, symbol := range symbols {
86		if elf.ST_TYPE(symbol.Info) != elf.STT_OBJECT {
87			continue
88		}
89		if symbol.Section == elf.SHN_UNDEF || symbol.Section >= elf.SHN_LORESERVE {
90			continue
91		}
92		if int(symbol.Section) >= len(file.Sections) {
93			return nil, fmt.Errorf("invalid section index %d", symbol.Section)
94		}
95
96		section := file.Sections[symbol.Section]
97
98		var addr uint64
99		switch elfFile.Type() {
100		case elf.ET_REL:
101			// "In relocatable files, st_value holds a section offset for a defined symbol.
102			// That is, st_value is an offset from the beginning of the section that st_shndx identifies."
103			addr = symbol.Value
104		case elf.ET_EXEC, elf.ET_DYN:
105			// "In executable and shared object files, st_value holds a virtual address. To make these
106			// files’ symbols more useful for the dynamic linker, the section offset (file interpretation)
107			// gives way to a virtual address (memory interpretation) for which the section number is
108			// irrelevant."
109			if symbol.Value < section.Addr {
110				return nil, fmt.Errorf("symbol starts before the start of its section")
111			}
112			addr = symbol.Value - section.Addr
113			if addr+symbol.Size > section.Size {
114				return nil, fmt.Errorf("symbol extends past the end of its section")
115			}
116		default:
117			return nil, fmt.Errorf("unsupported elf file type %d", elfFile.Type())
118		}
119
120		file.Symbols = append(file.Symbols, &Symbol{
121			Name:    symbol.Name,
122			Addr:    addr,
123			Size:    symbol.Size,
124			Section: section,
125		})
126	}
127
128	return file, nil
129}
130
131func dumpElfSymbols(r io.ReaderAt) error {
132	elfFile, err := elf.NewFile(r)
133	if err != nil {
134		return cantParseError{err}
135	}
136
137	symbols, err := elfFile.Symbols()
138	if err != nil {
139		return err
140	}
141
142	fmt.Println("mockElfFile{")
143	fmt.Printf("\tt: %#v,\n", elfFile.Type)
144
145	fmt.Println("\tsections: []elf.SectionHeader{")
146	for _, section := range elfFile.Sections {
147		fmt.Printf("\t\t%#v,\n", section.SectionHeader)
148	}
149	fmt.Println("\t},")
150
151	fmt.Println("\tsymbols: []elf.Symbol{")
152	for _, symbol := range symbols {
153		fmt.Printf("\t\t%#v,\n", symbol)
154	}
155	fmt.Println("\t},")
156
157	fmt.Println("}")
158
159	return nil
160}
161