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 main
16
17import (
18	"debug/elf"
19	"fmt"
20	"testing"
21)
22
23// prog is a shortcut to fill out a elf.Prog structure
24func prog(flags elf.ProgFlag, offset, addr, filesz, memsz uint64) *elf.Prog {
25	return &elf.Prog{
26		ProgHeader: elf.ProgHeader{
27			Type:   elf.PT_LOAD,
28			Flags:  flags,
29			Off:    offset,
30			Vaddr:  addr,
31			Paddr:  addr,
32			Filesz: filesz,
33			Memsz:  memsz,
34		},
35	}
36}
37
38// linkerGold returns an example elf.File from a linker binary that was linked
39// with gold.
40func linkerGold() *elf.File {
41	return &elf.File{
42		Progs: []*elf.Prog{
43			prog(elf.PF_R|elf.PF_X, 0, 0, 0xd0fac, 0xd0fac),
44			prog(elf.PF_R|elf.PF_W, 0xd1050, 0xd2050, 0x6890, 0xd88c),
45		},
46	}
47}
48
49// fileGold returns an example elf binary with a properly embedded linker. The
50// embedded linker was the one returned by linkerGold.
51func fileGold() *elf.File {
52	return &elf.File{
53		Progs: []*elf.Prog{
54			prog(elf.PF_R, 0, 0, 0x2e0, 0x2e0),
55			prog(elf.PF_R|elf.PF_X, 0x1000, 0x1000, 0xd0fac, 0xd0fac),
56			prog(elf.PF_R|elf.PF_W, 0xd2050, 0xd3050, 0xd88c, 0xd88c),
57			prog(elf.PF_R, 0xe0000, 0xe1000, 0x10e4, 0x10e4),
58			prog(elf.PF_R|elf.PF_X, 0xe2000, 0xe3000, 0x1360, 0x1360),
59			prog(elf.PF_R|elf.PF_W, 0xe4000, 0xe5000, 0x1358, 0x1358),
60		},
61	}
62}
63
64// linkerLld returns an example elf.File from a linker binary that was linked
65// with lld.
66func linkerLld() *elf.File {
67	return &elf.File{
68		Progs: []*elf.Prog{
69			prog(elf.PF_R, 0, 0, 0x3c944, 0x3c944),
70			prog(elf.PF_R|elf.PF_X, 0x3d000, 0x3d000, 0x946fa, 0x946fa),
71			prog(elf.PF_R|elf.PF_W, 0xd2000, 0xd2000, 0x7450, 0xf778),
72		},
73	}
74}
75
76// fileGold returns an example elf binary with a properly embedded linker. The
77// embedded linker was the one returned by linkerLld.
78func fileLld() *elf.File {
79	return &elf.File{
80		Progs: []*elf.Prog{
81			prog(elf.PF_R, 0, 0, 0x3d944, 0x3d944),
82			prog(elf.PF_R|elf.PF_X, 0x3e000, 0x3e000, 0x946fa, 0x946fa),
83			prog(elf.PF_R|elf.PF_W, 0xd3000, 0xd3000, 0xf778, 0xf778),
84			prog(elf.PF_R, 0xe3000, 0xe3000, 0x10e4, 0x10e4),
85			prog(elf.PF_R|elf.PF_X, 0xe5000, 0xe5000, 0x1360, 0x1360),
86			prog(elf.PF_R|elf.PF_W, 0xe7000, 0xe7000, 0x1358, 0x1358),
87		},
88	}
89}
90
91// linkerOffset returns the symbol representing the linker offset used by both
92// fileGold and fileLld
93func linkerOffset() []elf.Symbol {
94	return []elf.Symbol{
95		elf.Symbol{
96			Name:  "__dlwrap_linker_offset",
97			Value: 0x1000,
98		},
99	}
100}
101
102func TestCheckLinker(t *testing.T) {
103	cases := []struct {
104		name   string
105		err    error
106		file   func() *elf.File
107		linker func() *elf.File
108	}{
109		{
110			name:   "good gold-linked linker",
111			file:   fileGold,
112			linker: linkerGold,
113		},
114		{
115			name:   "good lld-linked linker",
116			file:   fileLld,
117			linker: linkerLld,
118		},
119		{
120			name: "truncated RO section",
121			err:  fmt.Errorf("Linker prog 0 (0x0) not fully present (0x3d944 > 0x3d943)"),
122			file: func() *elf.File {
123				f := fileLld()
124				f.Progs[0].Filesz -= 1
125				f.Progs[0].Memsz -= 1
126				return f
127			},
128			linker: linkerLld,
129		},
130	}
131
132	for _, tc := range cases {
133		t.Run(tc.name, func(t *testing.T) {
134			err := checkLinker(tc.file(), tc.linker(), linkerOffset())
135			if tc.err == nil {
136				if err != nil {
137					t.Fatalf("No error expected, but got: %v", err)
138				}
139			} else if err == nil {
140				t.Fatalf("Returned no error, but wanted: %v", tc.err)
141			} else if err.Error() != tc.err.Error() {
142				t.Fatalf("Different error found:\nwant: %v\n got: %v", tc.err, err)
143			}
144		})
145	}
146}
147