Home | History | Annotate | Download | only in extract_linker
      1 // Copyright 2017 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 
     15 // This tool extracts ELF LOAD segments from our linker binary, and produces an
     16 // assembly file and linker flags which will embed those segments as sections
     17 // in another binary.
     18 package main
     19 
     20 import (
     21 	"bytes"
     22 	"debug/elf"
     23 	"flag"
     24 	"fmt"
     25 	"io"
     26 	"io/ioutil"
     27 	"log"
     28 	"os"
     29 	"strings"
     30 )
     31 
     32 func main() {
     33 	var asmPath string
     34 	var flagsPath string
     35 
     36 	flag.StringVar(&asmPath, "s", "", "Path to save the assembly file")
     37 	flag.StringVar(&flagsPath, "f", "", "Path to save the linker flags")
     38 	flag.Parse()
     39 
     40 	f, err := os.Open(flag.Arg(0))
     41 	if err != nil {
     42 		log.Fatalf("Error opening %q: %v", flag.Arg(0), err)
     43 	}
     44 	defer f.Close()
     45 
     46 	ef, err := elf.NewFile(f)
     47 	if err != nil {
     48 		log.Fatalf("Unable to read elf file: %v", err)
     49 	}
     50 
     51 	asm := &bytes.Buffer{}
     52 	baseLoadAddr := uint64(0x1000)
     53 	load := 0
     54 	linkFlags := []string{}
     55 
     56 	fmt.Fprintln(asm, ".globl __dlwrap_linker_offset")
     57 	fmt.Fprintf(asm, ".set __dlwrap_linker_offset, 0x%x\n", baseLoadAddr)
     58 
     59 	for _, prog := range ef.Progs {
     60 		if prog.Type != elf.PT_LOAD {
     61 			continue
     62 		}
     63 
     64 		sectionName := fmt.Sprintf(".linker.sect%d", load)
     65 		symName := fmt.Sprintf("__dlwrap_linker_sect%d", load)
     66 
     67 		flags := ""
     68 		if prog.Flags&elf.PF_W != 0 {
     69 			flags += "w"
     70 		}
     71 		if prog.Flags&elf.PF_X != 0 {
     72 			flags += "x"
     73 		}
     74 		fmt.Fprintf(asm, ".section %s, \"a%s\"\n", sectionName, flags)
     75 
     76 		fmt.Fprintf(asm, ".globl %s\n%s:\n\n", symName, symName)
     77 
     78 		linkFlags = append(linkFlags,
     79 			fmt.Sprintf("-Wl,--undefined=%s", symName),
     80 			fmt.Sprintf("-Wl,--section-start=%s=0x%x",
     81 				sectionName, baseLoadAddr+prog.Vaddr))
     82 
     83 		buffer, _ := ioutil.ReadAll(prog.Open())
     84 		bytesToAsm(asm, buffer)
     85 
     86 		// Fill in zeros for any BSS sections. It would be nice to keep
     87 		// this as a true BSS, but ld/gold isn't preserving those,
     88 		// instead combining the segments with the following segment,
     89 		// and BSS only exists at the end of a LOAD segment.  The
     90 		// linker doesn't use a lot of BSS, so this isn't a huge
     91 		// problem.
     92 		if prog.Memsz > prog.Filesz {
     93 			fmt.Fprintf(asm, ".fill 0x%x, 1, 0\n", prog.Memsz-prog.Filesz)
     94 		}
     95 		fmt.Fprintln(asm)
     96 
     97 		load += 1
     98 	}
     99 
    100 	if asmPath != "" {
    101 		if err := ioutil.WriteFile(asmPath, asm.Bytes(), 0777); err != nil {
    102 			log.Fatalf("Unable to write %q: %v", asmPath, err)
    103 		}
    104 	}
    105 
    106 	if flagsPath != "" {
    107 		flags := strings.Join(linkFlags, " ")
    108 		if err := ioutil.WriteFile(flagsPath, []byte(flags), 0777); err != nil {
    109 			log.Fatalf("Unable to write %q: %v", flagsPath, err)
    110 		}
    111 	}
    112 }
    113 
    114 func bytesToAsm(asm io.Writer, buf []byte) {
    115 	for i, b := range buf {
    116 		if i%64 == 0 {
    117 			if i != 0 {
    118 				fmt.Fprint(asm, "\n")
    119 			}
    120 			fmt.Fprint(asm, ".byte ")
    121 		} else {
    122 			fmt.Fprint(asm, ",")
    123 		}
    124 		fmt.Fprintf(asm, "%d", b)
    125 	}
    126 	fmt.Fprintln(asm)
    127 }
    128