// Copyright 2018 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package main import ( "debug/elf" "fmt" "testing" ) // prog is a shortcut to fill out a elf.Prog structure func prog(flags elf.ProgFlag, offset, addr, filesz, memsz uint64) *elf.Prog { return &elf.Prog{ ProgHeader: elf.ProgHeader{ Type: elf.PT_LOAD, Flags: flags, Off: offset, Vaddr: addr, Paddr: addr, Filesz: filesz, Memsz: memsz, }, } } // linkerGold returns an example elf.File from a linker binary that was linked // with gold. func linkerGold() *elf.File { return &elf.File{ Progs: []*elf.Prog{ prog(elf.PF_R|elf.PF_X, 0, 0, 0xd0fac, 0xd0fac), prog(elf.PF_R|elf.PF_W, 0xd1050, 0xd2050, 0x6890, 0xd88c), }, } } // fileGold returns an example elf binary with a properly embedded linker. The // embedded linker was the one returned by linkerGold. func fileGold() *elf.File { return &elf.File{ Progs: []*elf.Prog{ prog(elf.PF_R, 0, 0, 0x2e0, 0x2e0), prog(elf.PF_R|elf.PF_X, 0x1000, 0x1000, 0xd0fac, 0xd0fac), prog(elf.PF_R|elf.PF_W, 0xd2050, 0xd3050, 0xd88c, 0xd88c), prog(elf.PF_R, 0xe0000, 0xe1000, 0x10e4, 0x10e4), prog(elf.PF_R|elf.PF_X, 0xe2000, 0xe3000, 0x1360, 0x1360), prog(elf.PF_R|elf.PF_W, 0xe4000, 0xe5000, 0x1358, 0x1358), }, } } // linkerLld returns an example elf.File from a linker binary that was linked // with lld. func linkerLld() *elf.File { return &elf.File{ Progs: []*elf.Prog{ prog(elf.PF_R, 0, 0, 0x3c944, 0x3c944), prog(elf.PF_R|elf.PF_X, 0x3d000, 0x3d000, 0x946fa, 0x946fa), prog(elf.PF_R|elf.PF_W, 0xd2000, 0xd2000, 0x7450, 0xf778), }, } } // fileGold returns an example elf binary with a properly embedded linker. The // embedded linker was the one returned by linkerLld. func fileLld() *elf.File { return &elf.File{ Progs: []*elf.Prog{ prog(elf.PF_R, 0, 0, 0x3d944, 0x3d944), prog(elf.PF_R|elf.PF_X, 0x3e000, 0x3e000, 0x946fa, 0x946fa), prog(elf.PF_R|elf.PF_W, 0xd3000, 0xd3000, 0xf778, 0xf778), prog(elf.PF_R, 0xe3000, 0xe3000, 0x10e4, 0x10e4), prog(elf.PF_R|elf.PF_X, 0xe5000, 0xe5000, 0x1360, 0x1360), prog(elf.PF_R|elf.PF_W, 0xe7000, 0xe7000, 0x1358, 0x1358), }, } } // linkerOffset returns the symbol representing the linker offset used by both // fileGold and fileLld func linkerOffset() []elf.Symbol { return []elf.Symbol{ elf.Symbol{ Name: "__dlwrap_linker_offset", Value: 0x1000, }, } } func TestCheckLinker(t *testing.T) { cases := []struct { name string err error file func() *elf.File linker func() *elf.File }{ { name: "good gold-linked linker", file: fileGold, linker: linkerGold, }, { name: "good lld-linked linker", file: fileLld, linker: linkerLld, }, { name: "truncated RO section", err: fmt.Errorf("Linker prog 0 (0x0) not fully present (0x3d944 > 0x3d943)"), file: func() *elf.File { f := fileLld() f.Progs[0].Filesz -= 1 f.Progs[0].Memsz -= 1 return f }, linker: linkerLld, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { err := checkLinker(tc.file(), tc.linker(), linkerOffset()) if tc.err == nil { if err != nil { t.Fatalf("No error expected, but got: %v", err) } } else if err == nil { t.Fatalf("Returned no error, but wanted: %v", tc.err) } else if err.Error() != tc.err.Error() { t.Fatalf("Different error found:\nwant: %v\n got: %v", tc.err, err) } }) } }