From a0790e35c7b275ee3b8288cf5453c15390850458 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Fri, 12 Oct 2018 00:24:23 -0700 Subject: Rework how linux_bionic is built with LLD In order to simplify the wrapper function, and stop using a linker script, generate a set of flags to pass to LLD. Then run host_bionic_inject on the linked binary in order to verify the embedding, and give the wrapper function the address of the original entry point (_start). Bug: 31559095 Test: build host bionic with prebuilts/build-tools/build-prebuilts.sh Change-Id: I53e326050e0f9caa562c6cf6f76c4d0337bb6faf --- cmd/extract_linker/main.go | 69 ++++------- cmd/host_bionic_inject/Android.bp | 19 +++ cmd/host_bionic_inject/host_bionic_inject.go | 174 +++++++++++++++++++++++++++ 3 files changed, 214 insertions(+), 48 deletions(-) create mode 100644 cmd/host_bionic_inject/Android.bp create mode 100644 cmd/host_bionic_inject/host_bionic_inject.go (limited to 'cmd') diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go index 3f24ab2d..ea0bf4ee 100644 --- a/cmd/extract_linker/main.go +++ b/cmd/extract_linker/main.go @@ -13,7 +13,7 @@ // limitations under the License. // This tool extracts ELF LOAD segments from our linker binary, and produces an -// assembly file and linker script which will embed those segments as sections +// assembly file and linker flags which will embed those segments as sections // in another binary. package main @@ -26,38 +26,15 @@ import ( "io/ioutil" "log" "os" - "text/template" + "strings" ) -var linkerScriptTemplate = template.Must(template.New("linker_script").Parse(` -ENTRY(__dlwrap__start) -SECTIONS { - __dlwrap_original_start = _start; - /DISCARD/ : { *(.interp) } - -{{range .}} - . = {{ printf "0x%x" .Vaddr }}; - {{.Name}} : { KEEP(*({{.Name}})) } -{{end}} - - .text : { *(.text .text.*) } - .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } - .data : { *(.data .data.* .gnu.linkonce.d.*) } - .bss : { *(.dynbss) *(.bss .bss.* .gnu.linkonce.b.*) *(COMMON) } -} -`)) - -type LinkerSection struct { - Name string - Vaddr uint64 -} - func main() { var asmPath string - var scriptPath string + var flagsPath string flag.StringVar(&asmPath, "s", "", "Path to save the assembly file") - flag.StringVar(&scriptPath, "T", "", "Path to save the linker script") + flag.StringVar(&flagsPath, "f", "", "Path to save the linker flags") flag.Parse() f, err := os.Open(flag.Arg(0)) @@ -72,19 +49,21 @@ func main() { } asm := &bytes.Buffer{} - - fmt.Fprintln(asm, ".globl __dlwrap_linker_entry") - fmt.Fprintf(asm, ".set __dlwrap_linker_entry, 0x%x\n\n", ef.Entry) - baseLoadAddr := uint64(0x1000) - sections := []LinkerSection{} load := 0 + linkFlags := []string{} + + fmt.Fprintln(asm, ".globl __dlwrap_linker_offset") + fmt.Fprintf(asm, ".set __dlwrap_linker_offset, 0x%x\n", baseLoadAddr) + for _, prog := range ef.Progs { if prog.Type != elf.PT_LOAD { continue } sectionName := fmt.Sprintf(".linker.sect%d", load) + symName := fmt.Sprintf("__dlwrap_linker_sect%d", load) + flags := "" if prog.Flags&elf.PF_W != 0 { flags += "w" @@ -94,10 +73,12 @@ func main() { } fmt.Fprintf(asm, ".section %s, \"a%s\"\n", sectionName, flags) - if load == 0 { - fmt.Fprintln(asm, ".globl __dlwrap_linker_code_start") - fmt.Fprintln(asm, "__dlwrap_linker_code_start:") - } + fmt.Fprintf(asm, ".globl %s\n%s:\n\n", symName, symName) + + linkFlags = append(linkFlags, + fmt.Sprintf("-Wl,--undefined=%s", symName), + fmt.Sprintf("-Wl,--section-start=%s=0x%x", + sectionName, baseLoadAddr+prog.Vaddr)) buffer, _ := ioutil.ReadAll(prog.Open()) bytesToAsm(asm, buffer) @@ -113,11 +94,6 @@ func main() { } fmt.Fprintln(asm) - sections = append(sections, LinkerSection{ - Name: sectionName, - Vaddr: baseLoadAddr + prog.Vaddr, - }) - load += 1 } @@ -127,13 +103,10 @@ func main() { } } - if scriptPath != "" { - buf := &bytes.Buffer{} - if err := linkerScriptTemplate.Execute(buf, sections); err != nil { - log.Fatalf("Failed to create linker script: %v", err) - } - if err := ioutil.WriteFile(scriptPath, buf.Bytes(), 0777); err != nil { - log.Fatalf("Unable to write %q: %v", scriptPath, err) + if flagsPath != "" { + flags := strings.Join(linkFlags, " ") + if err := ioutil.WriteFile(flagsPath, []byte(flags), 0777); err != nil { + log.Fatalf("Unable to write %q: %v", flagsPath, err) } } } diff --git a/cmd/host_bionic_inject/Android.bp b/cmd/host_bionic_inject/Android.bp new file mode 100644 index 00000000..acce6838 --- /dev/null +++ b/cmd/host_bionic_inject/Android.bp @@ -0,0 +1,19 @@ +// 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. + +blueprint_go_binary { + name: "host_bionic_inject", + deps: ["soong-symbol_inject"], + srcs: ["host_bionic_inject.go"], +} diff --git a/cmd/host_bionic_inject/host_bionic_inject.go b/cmd/host_bionic_inject/host_bionic_inject.go new file mode 100644 index 00000000..0dabbba2 --- /dev/null +++ b/cmd/host_bionic_inject/host_bionic_inject.go @@ -0,0 +1,174 @@ +// 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. + +// Verifies a host bionic executable with an embedded linker, then injects +// the address of the _start function for the linker_wrapper to use. +package main + +import ( + "debug/elf" + "flag" + "fmt" + "io" + "os" + + "android/soong/symbol_inject" +) + +func main() { + var inputFile, linkerFile, outputFile string + + flag.StringVar(&inputFile, "i", "", "Input file") + flag.StringVar(&linkerFile, "l", "", "Linker file") + flag.StringVar(&outputFile, "o", "", "Output file") + flag.Parse() + + if inputFile == "" || linkerFile == "" || outputFile == "" || flag.NArg() != 0 { + flag.Usage() + os.Exit(1) + } + + r, err := os.Open(inputFile) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(2) + } + defer r.Close() + + file, err := symbol_inject.OpenFile(r) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(3) + } + + linker, err := elf.Open(linkerFile) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(4) + } + + start_addr, err := parseElf(r, linker) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(5) + } + + w, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(6) + } + defer w.Close() + + err = symbol_inject.InjectUint64Symbol(file, w, "__dlwrap_original_start", start_addr) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(7) + } +} + +// Check the ELF file, and return the address to the _start function +func parseElf(r io.ReaderAt, linker *elf.File) (uint64, error) { + file, err := elf.NewFile(r) + if err != nil { + return 0, err + } + + symbols, err := file.Symbols() + if err != nil { + return 0, err + } + + for _, prog := range file.Progs { + if prog.Type == elf.PT_INTERP { + return 0, fmt.Errorf("File should not have a PT_INTERP header") + } + } + + if dlwrap_start, err := findSymbol(symbols, "__dlwrap__start"); err != nil { + return 0, err + } else if dlwrap_start.Value != file.Entry { + return 0, fmt.Errorf("Expected file entry(0x%x) to point to __dlwrap_start(0x%x)", + file.Entry, dlwrap_start.Value) + } + + err = checkLinker(file, linker, symbols) + if err != nil { + return 0, err + } + + start, err := findSymbol(symbols, "_start") + if err != nil { + return 0, fmt.Errorf("Failed to find _start symbol") + } + return start.Value, nil +} + +func findSymbol(symbols []elf.Symbol, name string) (elf.Symbol, error) { + for _, sym := range symbols { + if sym.Name == name { + return sym, nil + } + } + return elf.Symbol{}, fmt.Errorf("Failed to find symbol %q", name) +} + +// Check that all of the PT_LOAD segments have been embedded properly +func checkLinker(file, linker *elf.File, fileSyms []elf.Symbol) error { + dlwrap_linker_offset, err := findSymbol(fileSyms, "__dlwrap_linker_offset") + if err != nil { + return err + } + + for i, lprog := range linker.Progs { + if lprog.Type != elf.PT_LOAD { + continue + } + + found := false + for j, prog := range file.Progs { + if prog.Type != elf.PT_LOAD { + continue + } + + if lprog.Vaddr+dlwrap_linker_offset.Value != prog.Vaddr { + continue + } + found = true + + if lprog.Memsz != prog.Memsz { + return fmt.Errorf("Linker prog %d (0x%x) memsz (0x%x) does not match (0x%x)", + i, lprog.Vaddr, lprog.Memsz, prog.Memsz) + } + + // The linker shouldn't be using BSS, since only one + // BSS section is supported per ELF file. + if prog.Memsz != prog.Filesz { + return fmt.Errorf("Embedded prog %d (0x%x) memsz (0x%x) does not match filesz (0x%x)", + j, prog.Vaddr, prog.Memsz, prog.Filesz) + } + + if lprog.Flags != prog.Flags { + return fmt.Errorf("Linker prog %d (0x%x) flags (%s) do not match (%s)", + i, lprog.Vaddr, lprog.Flags, prog.Flags) + } + } + if !found { + return fmt.Errorf("Linker prog %d (0x%x) not found at offset 0x%x", + i, lprog.Vaddr, dlwrap_linker_offset.Value) + } + } + + return nil +} -- cgit v1.2.3