blob: 07a260b2cd190eed0873c97ad0fd5c2abf4eb162 [file] [log] [blame]
Adam Langleyfd499932017-04-04 14:21:43 -07001// Copyright (c) 2017, Google Inc.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
Adam Langleyf64a6ee2017-05-17 13:05:50 -070015//go:generate peg delocate.peg
16
Adam Langleyfd499932017-04-04 14:21:43 -070017// delocate performs several transformations of textual assembly code. See
Adam Langleyf64a6ee2017-05-17 13:05:50 -070018// crypto/fipsmodule/FIPS.md for an overview.
Adam Langleyfd499932017-04-04 14:21:43 -070019package main
20
21import (
Adam Langleyf64a6ee2017-05-17 13:05:50 -070022 "errors"
Adam Langleyfd499932017-04-04 14:21:43 -070023 "flag"
24 "fmt"
Adam Langleyf64a6ee2017-05-17 13:05:50 -070025 "io/ioutil"
Adam Langleyfd499932017-04-04 14:21:43 -070026 "os"
Adam Langley2c673f12017-04-13 11:10:44 -070027 "sort"
David Benjamind0a40592017-04-06 23:55:17 -040028 "strconv"
Adam Langleyfd499932017-04-04 14:21:43 -070029 "strings"
Adam Langleyfd499932017-04-04 14:21:43 -070030)
31
Adam Langleyf64a6ee2017-05-17 13:05:50 -070032// inputFile represents a textual assembly file.
33type inputFile struct {
34 path string
35 // index is a unique identifer given to this file. It's used for
36 // mapping local symbols.
37 index int
38 // isArchive indicates that the input should be processed as an ar
39 // file.
40 isArchive bool
41 // contents contains the contents of the file.
42 contents string
43 // ast points to the head of the syntax tree.
44 ast *node32
45}
Adam Langleyfd499932017-04-04 14:21:43 -070046
Adam Langleyf64a6ee2017-05-17 13:05:50 -070047type stringWriter interface {
48 WriteString(string) (int, error)
49}
Adam Langleyfd499932017-04-04 14:21:43 -070050
Adam Langleyf64a6ee2017-05-17 13:05:50 -070051type processorType int
Adam Langleyfd499932017-04-04 14:21:43 -070052
Adam Langleyf64a6ee2017-05-17 13:05:50 -070053const (
54 ppc64le processorType = iota + 1
55 x86_64
56)
Adam Langleyfd499932017-04-04 14:21:43 -070057
Adam Langleyf64a6ee2017-05-17 13:05:50 -070058// delocation holds the state needed during a delocation operation.
59type delocation struct {
60 processor processorType
61 output stringWriter
Adam Langleya0eb4a82017-04-12 13:42:21 -070062
Adam Langleyf64a6ee2017-05-17 13:05:50 -070063 // symbols is the set of symbols defined in the module.
64 symbols map[string]struct{}
65 // localEntrySymbols is the set of symbols with .localentry directives.
66 localEntrySymbols map[string]struct{}
67 // redirectors maps from out-call symbol name to the name of a
68 // redirector function for that symbol. E.g. “memcpy” ->
69 // “bcm_redirector_memcpy”.
70 redirectors map[string]string
71 // bssAccessorsNeeded maps from a BSS symbol name to the symbol that
72 // should be used to reference it. E.g. “P384_data_storage” ->
73 // “P384_data_storage”.
74 bssAccessorsNeeded map[string]string
75 // tocLoaders is a set of symbol names for which TOC helper functions
76 // are required. (ppc64le only.)
77 tocLoaders map[string]struct{}
78 // gotExternalsNeeded is a set of symbol names for which we need
79 // “delta” symbols: symbols that contain the offset from their location
80 // to the memory in question.
81 gotExternalsNeeded map[string]struct{}
Adam Langleyfd499932017-04-04 14:21:43 -070082
Adam Langleyf64a6ee2017-05-17 13:05:50 -070083 currentInput inputFile
84}
Adam Langleyfd499932017-04-04 14:21:43 -070085
Adam Langleyf64a6ee2017-05-17 13:05:50 -070086func (d *delocation) contents(node *node32) string {
87 return d.currentInput.contents[node.begin:node.end]
88}
89
90// writeNode writes out an AST node.
91func (d *delocation) writeNode(node *node32) {
92 if _, err := d.output.WriteString(d.contents(node)); err != nil {
Adam Langleyfd499932017-04-04 14:21:43 -070093 panic(err)
94 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -070095}
Adam Langleyfd499932017-04-04 14:21:43 -070096
Adam Langleyf64a6ee2017-05-17 13:05:50 -070097func (d *delocation) writeCommentedNode(node *node32) {
98 line := d.contents(node)
99 if _, err := d.output.WriteString("# WAS " + strings.TrimSpace(line) + "\n"); err != nil {
100 panic(err)
Adam Langleyfd499932017-04-04 14:21:43 -0700101 }
102}
103
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700104func locateError(err error, with *node32, in inputFile) error {
105 posMap := translatePositions([]rune(in.contents), []int{int(with.begin)})
106 var line int
107 for _, pos := range posMap {
108 line = pos.line
Adam Langley08c9b842017-04-21 09:37:36 -0700109 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700110
111 return fmt.Errorf("error while processing %q on line %d: %q", in.contents[with.begin:with.end], line, err)
Adam Langley08c9b842017-04-21 09:37:36 -0700112}
113
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700114func (d *delocation) processInput(input inputFile) (err error) {
115 d.currentInput = input
Adam Langleyfd499932017-04-04 14:21:43 -0700116
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700117 var origStatement *node32
118 defer func() {
119 if err := recover(); err != nil {
120 panic(locateError(fmt.Errorf("%s", err), origStatement, input))
Adam Langleyfd499932017-04-04 14:21:43 -0700121 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700122 }()
Adam Langleyfd499932017-04-04 14:21:43 -0700123
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700124 for statement := input.ast.up; statement != nil; statement = statement.next {
125 assertNodeType(statement, ruleStatement)
126 origStatement = statement
Adam Langleyfd499932017-04-04 14:21:43 -0700127
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700128 node := skipWS(statement.up)
129 if node == nil {
130 d.writeNode(statement)
Adam Langleyfd499932017-04-04 14:21:43 -0700131 continue
132 }
133
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700134 switch node.pegRule {
135 case ruleGlobalDirective, ruleComment, ruleLocationDirective:
136 d.writeNode(statement)
137 case ruleDirective:
138 statement, err = d.processDirective(statement, node.up)
139 case ruleLabelContainingDirective:
140 statement, err = d.processLabelContainingDirective(statement, node.up)
141 case ruleLabel:
142 statement, err = d.processLabel(statement, node.up)
143 case ruleInstruction:
144 switch d.processor {
145 case x86_64:
146 statement, err = d.processIntelInstruction(statement, node.up)
147 case ppc64le:
148 statement, err = d.processPPCInstruction(statement, node.up)
149 default:
150 panic("unknown processor")
151 }
152 default:
153 panic(fmt.Sprintf("unknown top-level statement type %q", rul3s[node.pegRule]))
Adam Langleyfd499932017-04-04 14:21:43 -0700154 }
155
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700156 if err != nil {
157 return locateError(err, origStatement, input)
Adam Langleyfd499932017-04-04 14:21:43 -0700158 }
159 }
160
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700161 return nil
Adam Langleyfd499932017-04-04 14:21:43 -0700162}
163
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700164func (d *delocation) processDirective(statement, directive *node32) (*node32, error) {
165 assertNodeType(directive, ruleDirectiveName)
166 directiveName := d.contents(directive)
Adam Langleye2a701e2017-04-17 12:54:00 -0700167
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700168 var args []string
169 forEachPath(directive, func(arg *node32) {
170 // If the argument is a quoted string, use the raw contents.
171 // (Note that this doesn't unescape the string, but that's not
172 // needed so far.
173 if arg.up != nil {
174 arg = arg.up
175 assertNodeType(arg, ruleQuotedArg)
176 if arg.up == nil {
177 args = append(args, "")
178 return
179 }
180 arg = arg.up
181 assertNodeType(arg, ruleQuotedText)
182 }
183 args = append(args, d.contents(arg))
184 }, ruleArgs, ruleArg)
Adam Langleyc2dce9c2017-04-20 16:44:15 -0700185
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700186 switch directiveName {
187 case "comm", "lcomm":
188 if len(args) < 1 {
189 return nil, errors.New("comm directive has no arguments")
190 }
191 d.bssAccessorsNeeded[args[0]] = args[0]
192 d.writeNode(statement)
193
194 case "data":
195 // ASAN and some versions of MSAN are adding a .data section,
196 // and adding references to symbols within it to the code. We
197 // will have to work around this in the future.
198 return nil, errors.New(".data section found in module")
199
200 case "section":
201 section := args[0]
202
203 if section == ".data.rel.ro" {
204 // In a normal build, this is an indication of a
205 // problem but any references from the module to this
206 // section will result in a relocation and thus will
207 // break the integrity check. ASAN can generate these
208 // sections and so we will likely have to work around
209 // that in the future.
210 return nil, errors.New(".data.rel.ro section found in module")
211 }
212
213 sectionType, ok := sectionType(section)
214 if !ok {
215 // Unknown sections are permitted in order to be robust
216 // to different compiler modes.
217 d.writeNode(statement)
218 break
219 }
220
221 switch sectionType {
222 case ".rodata", ".text":
223 // Move .rodata to .text so it may be accessed without
224 // a relocation. GCC with -fmerge-constants will place
225 // strings into separate sections, so we move all
226 // sections named like .rodata. Also move .text.startup
227 // so the self-test function is also in the module.
228 d.writeCommentedNode(statement)
229 d.output.WriteString(".text\n")
230
231 case ".data":
232 // See above about .data
233 return nil, errors.New(".data section found in module")
234
235 case ".init_array", ".fini_array", ".ctors", ".dtors":
236 // init_array/ctors/dtors contains function
237 // pointers to constructor/destructor
238 // functions. These contain relocations, but
239 // they're in a different section anyway.
240 d.writeNode(statement)
241 break
242
243 case ".debug", ".note", ".toc":
244 d.writeNode(statement)
245 break
246
247 case ".bss":
248 d.writeNode(statement)
249 return d.handleBSS(statement)
250 }
251
252 default:
253 d.writeNode(statement)
Adam Langleyc2dce9c2017-04-20 16:44:15 -0700254 }
255
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700256 return statement, nil
Adam Langleyc2dce9c2017-04-20 16:44:15 -0700257}
258
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700259func (d *delocation) processLabelContainingDirective(statement, directive *node32) (*node32, error) {
260 // The symbols within directives need to be mapped so that local
261 // symbols in two different .s inputs don't collide.
262 changed := false
263 assertNodeType(directive, ruleLabelContainingDirectiveName)
264 name := d.contents(directive)
Adam Langleyc2dce9c2017-04-20 16:44:15 -0700265
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700266 node := directive.next
267 assertNodeType(node, ruleWS)
David Benjamin075875f2017-04-25 21:18:04 -0400268
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700269 node = node.next
270 assertNodeType(node, ruleSymbolArgs)
David Benjamin075875f2017-04-25 21:18:04 -0400271
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700272 var args []string
273 for node = skipWS(node.up); node != nil; node = skipWS(node.next) {
274 assertNodeType(node, ruleSymbolArg)
275 arg := node.up
276 var mapped string
277
278 for term := arg; term != nil; term = term.next {
279 if term.pegRule != ruleLocalSymbol {
280 mapped += d.contents(term)
David Benjamin075875f2017-04-25 21:18:04 -0400281 continue
282 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700283
284 oldSymbol := d.contents(term)
285 newSymbol := d.mapLocalSymbol(oldSymbol)
286 if newSymbol != oldSymbol {
287 changed = true
David Benjamin075875f2017-04-25 21:18:04 -0400288 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700289
290 mapped += newSymbol
David Benjamin075875f2017-04-25 21:18:04 -0400291 }
292
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700293 args = append(args, mapped)
294 }
295
296 if !changed {
297 d.writeNode(statement)
298 } else {
299 d.writeCommentedNode(statement)
300 d.output.WriteString("\t" + name + "\t" + strings.Join(args, ", ") + "\n")
301 }
302
303 if name == ".localentry" {
304 d.output.WriteString(localEntryName(args[0]) + ":\n")
305 }
306
307 return statement, nil
308}
309
310func (d *delocation) processLabel(statement, label *node32) (*node32, error) {
311 symbol := d.contents(label)
312
313 switch label.pegRule {
314 case ruleLocalLabel:
315 d.output.WriteString(symbol + ":\n")
316 case ruleLocalSymbol:
317 // symbols need to be mapped so that local symbols from two
318 // different .s inputs don't collide.
319 d.output.WriteString(d.mapLocalSymbol(symbol) + ":\n")
320 case ruleSymbolName:
321 d.output.WriteString(localTargetName(symbol) + ":\n")
322 d.writeNode(statement)
323 default:
324 return nil, fmt.Errorf("unknown label type %q", rul3s[label.pegRule])
325 }
326
327 return statement, nil
328}
329
330// instructionArgs collects all the arguments to an instruction.
331func instructionArgs(node *node32) (argNodes []*node32) {
332 for node = skipWS(node); node != nil; node = skipWS(node.next) {
333 assertNodeType(node, ruleInstructionArg)
334 argNodes = append(argNodes, node.up)
335 }
336
337 return argNodes
338}
339
340/* ppc64le
341
David Benjamin773ae912017-06-20 18:17:28 -0400342[PABI]: “64-Bit ELF V2 ABI Specification. Power Architecture.” March 21st,
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700343 2017
344
345(Also useful is “Power ISA Version 2.07 B”. Note that version three of that
346document is /not/ good as that's POWER9 specific.)
347
348ppc64le doesn't have IP-relative addressing and does a lot to work around this.
349Rather than reference a PLT and GOT direction, it has a single structure called
350the TOC (Table Of Contents). Within the TOC is the contents of .rodata, .data,
351.got, .plt, .bss, etc sections [PABI;3.3].
352
353A pointer to the TOC is maintained in r2 and the following pattern is used to
354load the address of an element into a register:
355
356 addis <address register>, 2, foo@toc@ha
357 addi <address register>, <address register>, foo@toc@l
358
359The “addis” instruction shifts a signed constant left 16 bits and adds the
360result to its second argument, saving the result in the first argument. The
361“addi” instruction does the same, but without shifting. Thus the “@toc@ha"
362suffix on a symbol means “the top 16 bits of the TOC offset” and “@toc@l” means
363“the bottom 16 bits of the offset”. However, note that both values are signed,
364thus offsets in the top half of a 64KB chunk will have an @ha value that's one
365greater than expected and a negative @l value.
366
367The TOC is specific to a “module” (basically an executable or shared object).
368This means that there's not a single TOC in a process and that r2 needs to
369change as control moves between modules. Thus functions have two entry points:
370the “global” entry point and the “local” entry point. Jumps from within the
371same module can use the local entry while jumps from other modules must use the
372global entry. The global entry establishes the correct value of r2 before
373running the function and the local entry skips that code.
374
375The global entry point for a function is defined by its label. The local entry
376is a power-of-two number of bytes from the global entry, set by the
377“.localentry” directive. (ppc64le instructions are always 32 bits, so an offset
378of 1 or 2 bytes is treated as an offset of zero.)
379
380In order to help the global entry code set r2 to point to the local TOC, r12 is
381set to the address of the global entry point when called [PABI;2.2.1.1]. Thus
382the global entry will typically use an addis+addi pair to add a known offset to
383r12 and store it in r2. For example:
384
385foo:
386 addis 2, 12, .TOC. - foo@ha
387 addi 2, 2, .TOC. - foo@l
388
389(It's worth noting that the '@' operator binds very loosely, so the 3rd
390arguments parse as (.TOC. - foo)@ha and (.TOC. - foo)@l.)
391
392When calling a function, the compiler doesn't know whether that function is in
393the same module or not. Thus it doesn't know whether r12 needs to be set nor
394whether r2 will be clobbered on return. Rather than always assume the worst,
395the linker fixes stuff up once it knows that a call is going out of module:
396
397Firstly, calling, say, memcpy (which we assume to be in a different module)
398won't actually jump directly to memcpy, or even a PLT resolution function.
399It'll call a synthesised function that:
400 a) saves r2 in the caller's stack frame
401 b) loads the address of memcpy@PLT into r12
402 c) jumps to r12.
403
404As this synthesised function loads memcpy@PLT, a call to memcpy from the
405compiled code just references “memcpy” directly, not “memcpy@PLT”.
406
407Since it jumps directly to memcpy@PLT, it can't restore r2 on return. Thus
408calls must be followed by a nop. If the call ends up going out-of-module, the
409linker will rewrite that nop to load r2 from the stack.
410
411Speaking of the stack, the stack pointer is kept in r1 and there's a 288-byte
412red-zone. The format of the stack frame is defined [PABI;2.2.2] and must be
413followed as called functions will write into their parent's stack frame. For
414example, the synthesised out-of-module trampolines will save r2 24 bytes into
415the caller's frame and all non-leaf functions save the return address 16 bytes
416into the caller's frame.
417
418A final point worth noting: some RISC ISAs have r0 wired to zero: all reads
419result in zero and all writes are discarded. POWER does something a little like
420that, but r0 is only special in certain argument positions for certain
421instructions. You just have to read the manual to know which they are.
422
423
424Delocation is easier than Intel because there's just TOC references, but it's
425also harder because there's no IP-relative addressing.
426
427Jumps are IP-relative however, and have a 24-bit immediate value. So we can
428jump to functions that set a register to the needed value. (r3 is the
429return-value register and so that's what is generally used here.) */
430
431// isPPC64LEAPair recognises an addis+addi pair that's adding the offset of
432// source to relative and writing the result to target.
433func (d *delocation) isPPC64LEAPair(statement *node32) (target, source, relative string, ok bool) {
434 instruction := skipWS(statement.up).up
435 assertNodeType(instruction, ruleInstructionName)
436 name1 := d.contents(instruction)
437 args1 := instructionArgs(instruction.next)
438
439 statement = statement.next
440 instruction = skipWS(statement.up).up
441 assertNodeType(instruction, ruleInstructionName)
442 name2 := d.contents(instruction)
443 args2 := instructionArgs(instruction.next)
444
445 if name1 != "addis" ||
446 len(args1) != 3 ||
447 name2 != "addi" ||
448 len(args2) != 3 {
449 return "", "", "", false
450 }
451
452 target = d.contents(args1[0])
453 relative = d.contents(args1[1])
454 source1 := d.contents(args1[2])
455 source2 := d.contents(args2[2])
456
457 if !strings.HasSuffix(source1, "@ha") ||
458 !strings.HasSuffix(source2, "@l") ||
459 source1[:len(source1)-3] != source2[:len(source2)-2] ||
460 d.contents(args2[0]) != target ||
461 d.contents(args2[1]) != target {
462 return "", "", "", false
463 }
464
465 source = source1[:len(source1)-3]
466 ok = true
467 return
468}
469
470// establishTOC writes the global entry prelude for a function. The standard
471// prelude involves relocations so this version moves the relocation outside
472// the integrity-checked area.
473func establishTOC(w stringWriter) {
474 w.WriteString("999:\n")
475 w.WriteString("\taddis 2, 12, .LBORINGSSL_external_toc-999b@ha\n")
476 w.WriteString("\taddi 2, 2, .LBORINGSSL_external_toc-999b@l\n")
477 w.WriteString("\tld 12, 0(2)\n")
478 w.WriteString("\tadd 2, 2, 12\n")
479}
480
481// loadTOCFuncName returns the name of a synthesized function that sets r3 to
Adam Langleycd334a52017-05-31 17:02:42 -0700482// the value of “symbol+offset”.
483func loadTOCFuncName(symbol, offset string) string {
David Benjamin055375e2017-06-06 16:03:53 -0400484 symbol = strings.Replace(symbol, ".", "_dot_", -1)
485 ret := ".Lbcm_loadtoc_" + symbol
486 if len(offset) != 0 {
487 offset = strings.Replace(offset, "+", "_plus_", -1)
488 offset = strings.Replace(offset, "-", "_minus_", -1)
489 ret += "_" + offset
Adam Langleycd334a52017-05-31 17:02:42 -0700490 }
Adam Langleycd334a52017-05-31 17:02:42 -0700491 return ret
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700492}
493
Adam Langleycd334a52017-05-31 17:02:42 -0700494func (d *delocation) loadFromTOC(w stringWriter, symbol, offset, dest string) wrapperFunc {
David Benjamin055375e2017-06-06 16:03:53 -0400495 d.tocLoaders[symbol+"\x00"+offset] = struct{}{}
Adam Langleycd334a52017-05-31 17:02:42 -0700496
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700497 return func(k func()) {
498 w.WriteString("\taddi 1, 1, -288\n") // Clear the red zone.
499 w.WriteString("\tmflr " + dest + "\n") // Stash the link register.
500 w.WriteString("\tstd " + dest + ", -8(1)\n")
501 // The TOC loader will use r3, so stash it if necessary.
502 if dest != "3" {
503 w.WriteString("\tstd 3, -16(1)\n")
David Benjamin075875f2017-04-25 21:18:04 -0400504 }
505
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700506 // Because loadTOCFuncName returns a “.L” name, we don't need a
507 // nop after this call.
Adam Langleycd334a52017-05-31 17:02:42 -0700508 w.WriteString("\tbl " + loadTOCFuncName(symbol, offset) + "\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700509
510 // Cycle registers around. We need r3 -> destReg, -8(1) ->
511 // lr and, optionally, -16(1) -> r3.
512 w.WriteString("\tstd 3, -24(1)\n")
513 w.WriteString("\tld 3, -8(1)\n")
514 w.WriteString("\tmtlr 3\n")
515 w.WriteString("\tld " + dest + ", -24(1)\n")
516 if dest != "3" {
517 w.WriteString("\tld 3, -16(1)\n")
David Benjamin075875f2017-04-25 21:18:04 -0400518 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700519 w.WriteString("\taddi 1, 1, 288\n")
520
521 k()
522 }
523}
524
David Benjamin055375e2017-06-06 16:03:53 -0400525func (d *delocation) gatherOffsets(symRef *node32, offsets string) (*node32, string) {
526 for symRef != nil && symRef.pegRule == ruleOffset {
527 offset := d.contents(symRef)
528 if offset[0] != '+' && offset[0] != '-' {
529 offset = "+" + offset
530 }
531 offsets = offsets + offset
532 symRef = symRef.next
533 }
534 return symRef, offsets
535}
536
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700537func (d *delocation) parseMemRef(memRef *node32) (symbol, offset, section string, didChange, symbolIsLocal bool, nextRef *node32) {
538 if memRef.pegRule != ruleSymbolRef {
539 return "", "", "", false, false, memRef
540 }
541
542 symRef := memRef.up
543 nextRef = memRef.next
544
David Benjamin055375e2017-06-06 16:03:53 -0400545 // (Offset* '+')?
546 symRef, offset = d.gatherOffsets(symRef, offset)
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700547
David Benjamin055375e2017-06-06 16:03:53 -0400548 // (LocalSymbol / SymbolName)
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700549 symbol = d.contents(symRef)
550 if symRef.pegRule == ruleLocalSymbol {
551 symbolIsLocal = true
552 mapped := d.mapLocalSymbol(symbol)
553 if mapped != symbol {
554 symbol = mapped
555 didChange = true
556 }
557 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700558 symRef = symRef.next
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700559
David Benjamin055375e2017-06-06 16:03:53 -0400560 // Offset*
561 symRef, offset = d.gatherOffsets(symRef, offset)
562
563 // ('@' Section / Offset*)?
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700564 if symRef != nil {
565 assertNodeType(symRef, ruleSection)
566 section = d.contents(symRef)
567 symRef = symRef.next
David Benjamin055375e2017-06-06 16:03:53 -0400568
569 symRef, offset = d.gatherOffsets(symRef, offset)
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700570 }
571
572 if symRef != nil {
573 panic(fmt.Sprintf("unexpected token in SymbolRef: %q", rul3s[symRef.pegRule]))
David Benjamin075875f2017-04-25 21:18:04 -0400574 }
575
576 return
577}
578
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700579func (d *delocation) processPPCInstruction(statement, instruction *node32) (*node32, error) {
580 assertNodeType(instruction, ruleInstructionName)
581 instructionName := d.contents(instruction)
582 isBranch := instructionName[0] == 'b'
Adam Langleyfd499932017-04-04 14:21:43 -0700583
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700584 argNodes := instructionArgs(instruction.next)
Adam Langleyfd499932017-04-04 14:21:43 -0700585
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700586 var wrappers wrapperStack
587 var args []string
588 changed := false
David Benjamind0a40592017-04-06 23:55:17 -0400589
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700590Args:
591 for i, arg := range argNodes {
592 fullArg := arg
593 isIndirect := false
David Benjamin91871012017-04-25 15:37:53 -0400594
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700595 if arg.pegRule == ruleIndirectionIndicator {
596 arg = arg.next
597 isIndirect = true
Adam Langleyc2dce9c2017-04-20 16:44:15 -0700598 }
599
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700600 switch arg.pegRule {
601 case ruleRegisterOrConstant, ruleLocalLabelRef:
602 args = append(args, d.contents(fullArg))
Adam Langley2e2a2262017-05-03 13:23:37 -0700603
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700604 case ruleTOCRefLow:
605 return nil, errors.New("Found low TOC reference outside preamble pattern")
David Benjamin91871012017-04-25 15:37:53 -0400606
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700607 case ruleTOCRefHigh:
608 target, _, relative, ok := d.isPPC64LEAPair(statement)
Adam Langleyb0d864e2017-04-20 16:48:24 -0700609 if !ok {
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700610 return nil, errors.New("Found high TOC reference outside preamble pattern")
Adam Langleyb0d864e2017-04-20 16:48:24 -0700611 }
612
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700613 if relative != "12" {
614 return nil, fmt.Errorf("preamble is relative to %q, not r12", relative)
615 }
Adam Langleyfd499932017-04-04 14:21:43 -0700616
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700617 if target != "2" {
618 return nil, fmt.Errorf("preamble is setting %q, not r2", target)
619 }
Adam Langleyfd499932017-04-04 14:21:43 -0700620
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700621 statement = statement.next
622 establishTOC(d.output)
623 instructionName = ""
624 changed = true
625 break Args
Adam Langleyb0d864e2017-04-20 16:48:24 -0700626
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700627 case ruleMemoryRef:
628 symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
629 changed = didChange
Adam Langleyb0d864e2017-04-20 16:48:24 -0700630
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700631 if len(symbol) > 0 {
632 if _, localEntrySymbol := d.localEntrySymbols[symbol]; localEntrySymbol && isBranch {
633 symbol = localEntryName(symbol)
634 changed = true
635 } else if _, knownSymbol := d.symbols[symbol]; knownSymbol {
636 symbol = localTargetName(symbol)
637 changed = true
638 } else if !symbolIsLocal && !isSynthesized(symbol) && len(section) == 0 {
639 changed = true
640 d.redirectors[symbol] = redirectorName(symbol)
641 symbol = redirectorName(symbol)
David Benjamincd60bf02017-06-20 20:01:06 -0400642 // TODO(davidben): This should sanity-check the next
643 // instruction is a nop and ideally remove it.
644 wrappers = append(wrappers, func(k func()) {
645 k()
646 // Like the linker's PLT stubs, redirector functions
647 // expect callers to restore r2.
648 d.output.WriteString("\tld 2, 24(1)\n")
649 })
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700650 }
651 }
Adam Langley947417a2017-04-18 10:35:18 -0700652
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700653 switch section {
654 case "":
655
656 case "tls":
657 // This section identifier just tells the
658 // assembler to use r13, the pointer to the
659 // thread-local data [PABI;3.7.3.3].
660
661 case "toc@ha":
662 // Delete toc@ha instructions. Per
663 // [PABI;3.6.3], the linker is allowed to erase
664 // toc@ha instructions. We take advantage of
665 // this by unconditionally erasing the toc@ha
666 // instructions and doing the full lookup when
667 // processing toc@l.
David Benjamin592af532017-05-30 14:35:53 -0400668 //
669 // Note that any offset here applies before @ha
670 // and @l. That is, 42+foo@toc@ha is
671 // #ha(42+foo-.TOC.), not 42+#ha(foo-.TOC.). Any
672 // corresponding toc@l references are required
673 // by the ABI to have the same offset. The
674 // offset will be incorporated in full when
675 // those are processed.
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700676 if instructionName != "addis" || len(argNodes) != 3 || i != 2 || args[1] != "2" {
677 return nil, errors.New("can't process toc@ha reference")
678 }
679 changed = true
680 instructionName = ""
681 break Args
682
683 case "toc@l":
684 // Per [PAB;3.6.3], this instruction must take
685 // as input a register which was the output of
686 // a toc@ha computation and compute the actual
687 // address of some symbol. The toc@ha
688 // computation was elided, so we ignore that
689 // input register and compute the address
690 // directly.
691 changed = true
692
693 // For all supported toc@l instructions, the
694 // destination register is the first argument.
695 destReg := args[0]
696
Adam Langleycd334a52017-05-31 17:02:42 -0700697 wrappers = append(wrappers, d.loadFromTOC(d.output, symbol, offset, destReg))
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700698 switch instructionName {
699 case "addi":
700 // The original instruction was:
David Benjamin592af532017-05-30 14:35:53 -0400701 // addi destReg, tocHaReg, offset+symbol@toc@l
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700702 instructionName = ""
Adam Langleycd334a52017-05-31 17:02:42 -0700703
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700704 case "ld", "lhz", "lwz":
705 // The original instruction was:
David Benjamin592af532017-05-30 14:35:53 -0400706 // l?? destReg, offset+symbol@toc@l(tocHaReg)
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700707 //
708 // We transform that into the
709 // equivalent dereference of destReg:
Adam Langleycd334a52017-05-31 17:02:42 -0700710 // l?? destReg, 0(destReg)
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700711 origInstructionName := instructionName
712 instructionName = ""
713
714 assertNodeType(memRef, ruleBaseIndexScale)
715 assertNodeType(memRef.up, ruleRegisterOrConstant)
716 if memRef.next != nil || memRef.up.next != nil {
717 return nil, errors.New("expected single register in BaseIndexScale for ld argument")
718 }
719
Adam Langleyff239452017-06-09 16:52:00 -0700720 baseReg := destReg
721 if baseReg == "0" {
722 // Register zero is special as the base register for a load.
723 // Avoid it by spilling and using r3 instead.
724 baseReg = "3"
725 wrappers = append(wrappers, func(k func()) {
David Benjamincd60bf02017-06-20 20:01:06 -0400726 d.output.WriteString("\taddi 1, 1, -288\n") // Clear the red zone.
Adam Langleyff239452017-06-09 16:52:00 -0700727 d.output.WriteString("\tstd " + baseReg + ", -8(1)\n")
728 d.output.WriteString("\tmr " + baseReg + ", " + destReg + "\n")
729 k()
730 d.output.WriteString("\tld " + baseReg + ", -8(1)\n")
David Benjamincd60bf02017-06-20 20:01:06 -0400731 d.output.WriteString("\taddi 1, 1, 288\n") // Clear the red zone.
Adam Langleyff239452017-06-09 16:52:00 -0700732 })
733 }
734
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700735 wrappers = append(wrappers, func(k func()) {
Adam Langleyff239452017-06-09 16:52:00 -0700736 d.output.WriteString("\t" + origInstructionName + " " + destReg + ", 0(" + baseReg + ")\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700737 })
738 default:
739 return nil, fmt.Errorf("can't process TOC argument to %q", instructionName)
Adam Langley947417a2017-04-18 10:35:18 -0700740 }
741
Adam Langleyb0d864e2017-04-20 16:48:24 -0700742 default:
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700743 return nil, fmt.Errorf("Unknown section type %q", section)
Adam Langleyfd499932017-04-04 14:21:43 -0700744 }
745
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700746 argStr := ""
747 if isIndirect {
748 argStr += "*"
749 }
750 argStr += symbol
751 if len(offset) > 0 {
752 argStr += offset
753 }
754 if len(section) > 0 {
755 argStr += "@"
756 argStr += section
757 }
758
759 for ; memRef != nil; memRef = memRef.next {
760 argStr += d.contents(memRef)
761 }
762
763 args = append(args, argStr)
764
Adam Langleyfd499932017-04-04 14:21:43 -0700765 default:
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700766 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
Adam Langleyfd499932017-04-04 14:21:43 -0700767 }
768 }
769
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700770 if changed {
771 d.writeCommentedNode(statement)
Adam Langleyfd499932017-04-04 14:21:43 -0700772
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700773 var replacement string
774 if len(instructionName) > 0 {
775 replacement = "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
776 }
777
778 wrappers.do(func() {
779 d.output.WriteString(replacement)
780 })
781 } else {
782 d.writeNode(statement)
783 }
784
785 return statement, nil
786}
787
788/* Intel */
789
790type instructionType int
791
792const (
793 instrPush instructionType = iota
794 instrMove
795 instrJump
796 instrConditionalMove
797 instrOther
798)
799
800func classifyInstruction(instr string, args []*node32) instructionType {
801 switch instr {
802 case "push", "pushq":
803 if len(args) == 1 {
804 return instrPush
805 }
806
807 case "mov", "movq", "vmovq":
808 if len(args) == 2 {
809 return instrMove
810 }
811
812 case "cmovneq", "cmoveq":
813 if len(args) == 2 {
814 return instrConditionalMove
815 }
816
817 case "call", "callq", "jmp", "jo", "jno", "js", "jns", "je", "jz", "jne", "jnz", "jb", "jnae", "jc", "jnb", "jae", "jnc", "jbe", "jna", "ja", "jnbe", "jl", "jnge", "jge", "jnl", "jle", "jng", "jg", "jnle", "jp", "jpe", "jnp", "jpo":
818 if len(args) == 1 {
819 return instrJump
820 }
821 }
822
823 return instrOther
824}
825
826func push(w stringWriter) wrapperFunc {
827 return func(k func()) {
828 w.WriteString("\tpushq %rax\n")
829 k()
830 w.WriteString("\txchg %rax, (%rsp)\n")
831 }
832}
833
834func (d *delocation) loadFromGOT(w stringWriter, destination, symbol, section string, redzoneCleared bool) wrapperFunc {
835 d.gotExternalsNeeded[symbol+"@"+section] = struct{}{}
836
837 return func(k func()) {
838 if !redzoneCleared {
839 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
840 }
841 w.WriteString("\tpushf\n")
842 w.WriteString(fmt.Sprintf("\tleaq %s_%s_external(%%rip), %s\n", symbol, section, destination))
843 w.WriteString(fmt.Sprintf("\taddq (%s), %s\n", destination, destination))
844 w.WriteString(fmt.Sprintf("\tmovq (%s), %s\n", destination, destination))
845 w.WriteString("\tpopf\n")
846 if !redzoneCleared {
847 w.WriteString("\tleaq\t128(%rsp), %rsp\n")
848 }
849 }
850}
851
852func saveRegister(w stringWriter) wrapperFunc {
853 return func(k func()) {
854 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
855 w.WriteString("\tpushq %rax\n")
856 k()
857 w.WriteString("\tpopq %rax\n")
858 w.WriteString("\tleaq 128(%rsp), %rsp\n")
859 }
860}
861
862func moveTo(w stringWriter, target string) wrapperFunc {
863 return func(k func()) {
864 k()
865 w.WriteString("\tmovq %rax, " + target + "\n")
866 }
867}
868
869func isValidLEATarget(reg string) bool {
870 return !strings.HasPrefix(reg, "%xmm") && !strings.HasPrefix(reg, "%ymm") && !strings.HasPrefix(reg, "%zmm")
871}
872
873func undoConditionalMove(w stringWriter, instr string) wrapperFunc {
874 var invertedCondition string
875
876 switch instr {
877 case "cmoveq":
878 invertedCondition = "ne"
879 case "cmovneq":
880 invertedCondition = "e"
881 default:
882 panic(fmt.Sprintf("don't know how to handle conditional move instruction %q", instr))
883 }
884
885 return func(k func()) {
886 w.WriteString("\tj" + invertedCondition + " 999f\n")
887 k()
888 w.WriteString("999:\n")
889 }
890}
891
892func (d *delocation) isRIPRelative(node *node32) bool {
893 return node != nil && node.pegRule == ruleBaseIndexScale && d.contents(node) == "(%rip)"
894}
895
896func (d *delocation) processIntelInstruction(statement, instruction *node32) (*node32, error) {
897 assertNodeType(instruction, ruleInstructionName)
898 instructionName := d.contents(instruction)
899
900 argNodes := instructionArgs(instruction.next)
901
902 var wrappers wrapperStack
903 var args []string
904 changed := false
905
906Args:
907 for i, arg := range argNodes {
908 fullArg := arg
909 isIndirect := false
910
911 if arg.pegRule == ruleIndirectionIndicator {
912 arg = arg.next
913 isIndirect = true
914 }
915
916 switch arg.pegRule {
917 case ruleRegisterOrConstant, ruleLocalLabelRef:
918 args = append(args, d.contents(fullArg))
919
920 case ruleMemoryRef:
921 symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
922 changed = didChange
923
924 if symbol == "OPENSSL_ia32cap_P" {
925 var ok bool
926 if section == "GOTPCREL" {
927 ok = instructionName == "movq"
928 } else if section == "" {
929 ok = instructionName == "leaq"
930 }
931
932 if !ok {
933 return nil, fmt.Errorf("instruction %q referenced OPENSSL_ia32cap_P in section %q, should be a movq from GOTPCREL or a direct leaq", instructionName, section)
934 }
935
936 if i != 0 || len(argNodes) != 2 || !d.isRIPRelative(memRef) || len(offset) > 0 {
937 return nil, fmt.Errorf("invalid OPENSSL_ia32cap_P reference in instruction %q", instructionName)
938 }
939
940 target := argNodes[1]
941 assertNodeType(target, ruleRegisterOrConstant)
942 reg := d.contents(target)
943
944 if !strings.HasPrefix(reg, "%r") {
945 return nil, fmt.Errorf("tried to load OPENSSL_ia32cap_P into %q, which is not a standard register.", reg)
946 }
947
948 changed = true
949 wrappers = append(wrappers, func(k func()) {
950 d.output.WriteString("\tleaq\t-128(%rsp), %rsp\n") // Clear the red zone.
951 d.output.WriteString("\tpushfq\n")
952 d.output.WriteString("\tleaq\tOPENSSL_ia32cap_addr_delta(%rip), " + reg + "\n")
953 d.output.WriteString("\taddq\t(" + reg + "), " + reg + "\n")
954 d.output.WriteString("\tpopfq\n")
955 d.output.WriteString("\tleaq\t128(%rsp), %rsp\n")
956 })
957
958 break Args
959 }
960
961 switch section {
962 case "":
963 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
964 symbol = localTargetName(symbol)
965 changed = true
966 }
967
968 case "PLT":
969 if classifyInstruction(instructionName, argNodes) != instrJump {
970 return nil, fmt.Errorf("Cannot rewrite PLT reference for non-jump instruction %q", instructionName)
971 }
972
973 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
974 symbol = localTargetName(symbol)
975 changed = true
976 } else if !symbolIsLocal && !isSynthesized(symbol) {
977 // Unknown symbol via PLT is an
978 // out-call from the module, e.g.
979 // memcpy.
980 d.redirectors[symbol+"@"+section] = redirectorName(symbol)
981 symbol = redirectorName(symbol)
982 }
983
984 changed = true
985
986 case "GOTPCREL":
987 if len(offset) > 0 {
988 return nil, errors.New("loading from GOT with offset is unsupported")
989 }
990 if i != 0 {
991 return nil, errors.New("GOT access must be source operand")
992 }
993 if !d.isRIPRelative(memRef) {
994 return nil, errors.New("GOT access must be IP-relative")
995 }
996
997 useGOT := false
998 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
999 symbol = localTargetName(symbol)
1000 changed = true
1001 } else if !isSynthesized(symbol) {
1002 useGOT = true
1003 }
1004
1005 // Reduce the instruction to movq symbol@GOTPCREL, targetReg.
1006 var targetReg string
1007 switch classifyInstruction(instructionName, argNodes) {
1008 case instrPush:
1009 wrappers = append(wrappers, push(d.output))
1010 targetReg = "%rax"
1011 case instrConditionalMove:
1012 wrappers = append(wrappers, undoConditionalMove(d.output, instructionName))
1013 fallthrough
1014 case instrMove:
1015 assertNodeType(argNodes[1], ruleRegisterOrConstant)
1016 targetReg = d.contents(argNodes[1])
1017 default:
1018 return nil, fmt.Errorf("Cannot rewrite GOTPCREL reference for instruction %q", instructionName)
1019 }
1020
1021 var redzoneCleared bool
1022 if !isValidLEATarget(targetReg) {
1023 // Sometimes the compiler will load from the GOT to an
1024 // XMM register, which is not a valid target of an LEA
1025 // instruction.
1026 wrappers = append(wrappers, saveRegister(d.output))
1027 wrappers = append(wrappers, moveTo(d.output, targetReg))
1028 targetReg = "%rax"
1029 redzoneCleared = true
1030 }
1031
1032 if useGOT {
1033 wrappers = append(wrappers, d.loadFromGOT(d.output, targetReg, symbol, section, redzoneCleared))
1034 } else {
1035 wrappers = append(wrappers, func(k func()) {
1036 d.output.WriteString(fmt.Sprintf("\tleaq\t%s(%%rip), %s\n", symbol, targetReg))
1037 })
1038 }
1039 changed = true
1040 break Args
1041
1042 default:
1043 return nil, fmt.Errorf("Unknown section type %q", section)
1044 }
1045
1046 if !changed && len(section) > 0 {
1047 panic("section was not handled")
1048 }
1049 section = ""
1050
1051 argStr := ""
1052 if isIndirect {
1053 argStr += "*"
1054 }
1055 argStr += symbol
1056 argStr += offset
1057
1058 for ; memRef != nil; memRef = memRef.next {
1059 argStr += d.contents(memRef)
1060 }
1061
1062 args = append(args, argStr)
1063
1064 default:
1065 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
1066 }
1067 }
1068
1069 if changed {
1070 d.writeCommentedNode(statement)
1071 replacement := "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
1072 wrappers.do(func() {
1073 d.output.WriteString(replacement)
1074 })
1075 } else {
1076 d.writeNode(statement)
1077 }
1078
1079 return statement, nil
1080}
1081
1082func (d *delocation) handleBSS(statement *node32) (*node32, error) {
1083 lastStatement := statement
1084 for statement = statement.next; statement != nil; lastStatement, statement = statement, statement.next {
1085 node := skipWS(statement.up)
1086 if node == nil {
1087 d.writeNode(statement)
1088 continue
1089 }
1090
1091 switch node.pegRule {
1092 case ruleGlobalDirective, ruleComment, ruleInstruction, ruleLocationDirective:
1093 d.writeNode(statement)
1094
1095 case ruleDirective:
1096 directive := node.up
1097 assertNodeType(directive, ruleDirectiveName)
1098 directiveName := d.contents(directive)
1099 if directiveName == "text" || directiveName == "section" || directiveName == "data" {
1100 return lastStatement, nil
1101 }
1102 d.writeNode(statement)
1103
1104 case ruleLabel:
1105 label := node.up
1106 d.writeNode(statement)
1107
1108 if label.pegRule != ruleLocalSymbol {
1109 symbol := d.contents(label)
1110 localSymbol := localTargetName(symbol)
1111 d.output.WriteString(fmt.Sprintf("\n%s:\n", localSymbol))
1112
1113 d.bssAccessorsNeeded[symbol] = localSymbol
1114 }
1115
1116 case ruleLabelContainingDirective:
1117 var err error
1118 statement, err = d.processLabelContainingDirective(statement, node.up)
1119 if err != nil {
1120 return nil, err
1121 }
1122
1123 default:
1124 return nil, fmt.Errorf("unknown BSS statement type %q in %q", rul3s[node.pegRule], d.contents(statement))
1125 }
1126 }
1127
1128 return lastStatement, nil
1129}
1130
1131func transform(w stringWriter, inputs []inputFile) error {
1132 // symbols contains all defined symbols.
1133 symbols := make(map[string]struct{})
1134 // localEntrySymbols contains all symbols with a .localentry directive.
1135 localEntrySymbols := make(map[string]struct{})
1136
1137 for _, input := range inputs {
1138 forEachPath(input.ast.up, func(node *node32) {
1139 symbol := input.contents[node.begin:node.end]
1140 if _, ok := symbols[symbol]; ok {
1141 panic(fmt.Sprintf("Duplicate symbol found: %q in %q", symbol, input.path))
1142 }
1143 symbols[symbol] = struct{}{}
1144 }, ruleStatement, ruleLabel, ruleSymbolName)
1145
1146 forEachPath(input.ast.up, func(node *node32) {
1147 node = node.up
1148 assertNodeType(node, ruleLabelContainingDirectiveName)
1149 directive := input.contents[node.begin:node.end]
1150 if directive != ".localentry" {
1151 return
1152 }
1153 // Extract the first argument.
1154 node = skipWS(node.next)
1155 assertNodeType(node, ruleSymbolArgs)
1156 node = node.up
1157 assertNodeType(node, ruleSymbolArg)
1158 symbol := input.contents[node.begin:node.end]
1159 if _, ok := localEntrySymbols[symbol]; ok {
1160 panic(fmt.Sprintf("Duplicate .localentry directive found: %q in %q", symbol, input.path))
1161 }
1162 localEntrySymbols[symbol] = struct{}{}
1163 }, ruleStatement, ruleLabelContainingDirective)
1164 }
1165
1166 processor := x86_64
1167 if len(inputs) > 0 {
1168 processor = detectProcessor(inputs[0])
1169 }
1170
1171 d := &delocation{
1172 symbols: symbols,
1173 localEntrySymbols: localEntrySymbols,
1174 processor: processor,
1175 output: w,
1176 redirectors: make(map[string]string),
1177 bssAccessorsNeeded: make(map[string]string),
1178 tocLoaders: make(map[string]struct{}),
1179 gotExternalsNeeded: make(map[string]struct{}),
1180 }
1181
1182 w.WriteString(".text\nBORINGSSL_bcm_text_start:\n")
1183
1184 for _, input := range inputs {
1185 if err := d.processInput(input); err != nil {
1186 return err
1187 }
1188 }
1189
1190 w.WriteString(".text\nBORINGSSL_bcm_text_end:\n")
1191
1192 // Emit redirector functions. Each is a single jump instruction.
Adam Langley2c673f12017-04-13 11:10:44 -07001193 var redirectorNames []string
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001194 for name := range d.redirectors {
Adam Langley2c673f12017-04-13 11:10:44 -07001195 redirectorNames = append(redirectorNames, name)
1196 }
1197 sort.Strings(redirectorNames)
1198
1199 for _, name := range redirectorNames {
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001200 redirector := d.redirectors[name]
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001201 if d.processor == ppc64le {
David Benjamincd60bf02017-06-20 20:01:06 -04001202 w.WriteString(".section \".toc\", \"aw\"\n")
1203 w.WriteString(".Lredirector_toc_" + name + ":\n")
1204 w.WriteString(".quad " + name + "\n")
1205 w.WriteString(".text\n")
1206 w.WriteString(".type " + redirector + ", @function\n")
1207 w.WriteString(redirector + ":\n")
1208 // |name| will clobber r2, so save it. This is matched by a restore in
1209 // redirector calls.
1210 w.WriteString("\tstd 2, 24(1)\n")
1211 // Load and call |name|'s global entry point.
1212 w.WriteString("\taddis 12, 2, .Lredirector_toc_" + name + "@toc@ha\n")
1213 w.WriteString("\tld 12, .Lredirector_toc_" + name + "@toc@l(12)\n")
1214 w.WriteString("\tmtctr 12\n")
1215 w.WriteString("\tbctr\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001216 } else {
David Benjamincd60bf02017-06-20 20:01:06 -04001217 w.WriteString(".type " + redirector + ", @function\n")
1218 w.WriteString(redirector + ":\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001219 w.WriteString("\tjmp\t" + name + "\n")
1220 }
Adam Langleyfd499932017-04-04 14:21:43 -07001221 }
1222
Adam Langley947417a2017-04-18 10:35:18 -07001223 var accessorNames []string
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001224 for accessor := range d.bssAccessorsNeeded {
Adam Langley947417a2017-04-18 10:35:18 -07001225 accessorNames = append(accessorNames, accessor)
1226 }
1227 sort.Strings(accessorNames)
1228
David Benjamind0a40592017-04-06 23:55:17 -04001229 // Emit BSS accessor functions. Each is a single LEA followed by RET.
Adam Langley947417a2017-04-18 10:35:18 -07001230 for _, name := range accessorNames {
David Benjamind0a40592017-04-06 23:55:17 -04001231 funcName := accessorName(name)
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001232 w.WriteString(".type " + funcName + ", @function\n")
1233 w.WriteString(funcName + ":\n")
1234 target := d.bssAccessorsNeeded[name]
1235
1236 if d.processor == ppc64le {
1237 w.WriteString("\taddis 3, 2, " + target + "@toc@ha\n")
1238 w.WriteString("\taddi 3, 3, " + target + "@toc@l\n")
1239 w.WriteString("\tblr\n")
1240 } else {
1241 w.WriteString("\tleaq\t" + target + "(%rip), %rax\n\tret\n")
1242 }
David Benjamind0a40592017-04-06 23:55:17 -04001243 }
1244
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001245 if d.processor == ppc64le {
1246 loadTOCNames := sortedSet(d.tocLoaders)
Adam Langleycd334a52017-05-31 17:02:42 -07001247 for _, symbolAndOffset := range loadTOCNames {
1248 parts := strings.SplitN(symbolAndOffset, "\x00", 2)
1249 symbol, offset := parts[0], parts[1]
1250
1251 funcName := loadTOCFuncName(symbol, offset)
1252 ref := symbol + offset
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001253
1254 w.WriteString(".type " + funcName[2:] + ", @function\n")
1255 w.WriteString(funcName[2:] + ":\n")
1256 w.WriteString(funcName + ":\n")
Adam Langleycd334a52017-05-31 17:02:42 -07001257 w.WriteString("\taddis 3, 2, " + ref + "@toc@ha\n")
1258 w.WriteString("\taddi 3, 3, " + ref + "@toc@l\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001259 w.WriteString("\tblr\n")
1260 }
1261
1262 w.WriteString(".LBORINGSSL_external_toc:\n")
1263 w.WriteString(".quad .TOC.-.LBORINGSSL_external_toc\n")
1264 } else {
1265 externalNames := sortedSet(d.gotExternalsNeeded)
1266 for _, name := range externalNames {
1267 parts := strings.SplitN(name, "@", 2)
1268 symbol, section := parts[0], parts[1]
1269 w.WriteString(".type " + symbol + "_" + section + "_external, @object\n")
1270 w.WriteString(".size " + symbol + "_" + section + "_external, 8\n")
1271 w.WriteString(symbol + "_" + section + "_external:\n")
1272 // Ideally this would be .quad foo@GOTPCREL, but clang's
1273 // assembler cannot emit a 64-bit GOTPCREL relocation. Instead,
1274 // we manually sign-extend the value, knowing that the GOT is
1275 // always at the end, thus foo@GOTPCREL has a positive value.
1276 w.WriteString("\t.long " + symbol + "@" + section + "\n")
1277 w.WriteString("\t.long 0\n")
1278 }
1279
1280 w.WriteString(".type OPENSSL_ia32cap_get, @function\n")
1281 w.WriteString("OPENSSL_ia32cap_get:\n")
1282 w.WriteString("\tleaq OPENSSL_ia32cap_P(%rip), %rax\n")
1283 w.WriteString("\tret\n")
1284
1285 w.WriteString(".extern OPENSSL_ia32cap_P\n")
1286 w.WriteString(".type OPENSSL_ia32cap_addr_delta, @object\n")
1287 w.WriteString(".size OPENSSL_ia32cap_addr_delta, 8\n")
1288 w.WriteString("OPENSSL_ia32cap_addr_delta:\n")
1289 w.WriteString(".quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta\n")
David Benjamin91871012017-04-25 15:37:53 -04001290 }
1291
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001292 w.WriteString(".type BORINGSSL_bcm_text_hash, @object\n")
1293 w.WriteString(".size BORINGSSL_bcm_text_hash, 64\n")
1294 w.WriteString("BORINGSSL_bcm_text_hash:\n")
David Benjamind0a40592017-04-06 23:55:17 -04001295 for _, b := range uninitHashValue {
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001296 w.WriteString(".byte 0x" + strconv.FormatUint(uint64(b), 16) + "\n")
David Benjamind0a40592017-04-06 23:55:17 -04001297 }
1298
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001299 return nil
Adam Langleyfd499932017-04-04 14:21:43 -07001300}
1301
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001302func parseInputs(inputs []inputFile) error {
1303 for i, input := range inputs {
1304 var contents string
Adam Langley5c38c052017-04-28 14:47:06 -07001305
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001306 if input.isArchive {
1307 arFile, err := os.Open(input.path)
1308 if err != nil {
1309 return err
1310 }
1311 defer arFile.Close()
Adam Langley947417a2017-04-18 10:35:18 -07001312
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001313 ar, err := ParseAR(arFile)
1314 if err != nil {
1315 return err
1316 }
1317
1318 if len(ar) != 1 {
1319 return fmt.Errorf("expected one file in archive, but found %d", len(ar))
1320 }
1321
1322 for _, c := range ar {
1323 contents = string(c)
1324 }
1325 } else {
1326 inBytes, err := ioutil.ReadFile(input.path)
1327 if err != nil {
1328 return err
1329 }
1330
1331 contents = string(inBytes)
Adam Langley947417a2017-04-18 10:35:18 -07001332 }
1333
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001334 asm := Asm{Buffer: contents, Pretty: true}
1335 asm.Init()
1336 if err := asm.Parse(); err != nil {
1337 return fmt.Errorf("error while parsing %q: %s", input.path, err)
1338 }
1339 ast := asm.AST()
1340
1341 inputs[i].contents = contents
1342 inputs[i].ast = ast
1343 }
1344
1345 return nil
1346}
1347
1348func main() {
1349 // The .a file, if given, is expected to be an archive of textual
1350 // assembly sources. That's odd, but CMake really wants to create
1351 // archive files so it's the only way that we can make it work.
1352 arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
1353 outFile := flag.String("o", "", "Path to output assembly")
1354
1355 flag.Parse()
1356
1357 if len(*outFile) == 0 {
1358 fmt.Fprintf(os.Stderr, "Must give argument to -o.\n")
1359 os.Exit(1)
1360 }
1361
1362 var inputs []inputFile
1363 if len(*arInput) > 0 {
1364 inputs = append(inputs, inputFile{
1365 path: *arInput,
1366 index: 0,
1367 isArchive: true,
1368 })
1369 }
1370
1371 for i, path := range flag.Args() {
1372 if len(path) == 0 {
Adam Langley947417a2017-04-18 10:35:18 -07001373 continue
1374 }
1375
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001376 inputs = append(inputs, inputFile{
1377 path: path,
1378 index: i + 1,
1379 })
1380 }
Adam Langley947417a2017-04-18 10:35:18 -07001381
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001382 if err := parseInputs(inputs); err != nil {
1383 fmt.Fprintf(os.Stderr, "%s\n", err)
1384 os.Exit(1)
1385 }
Adam Langley947417a2017-04-18 10:35:18 -07001386
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001387 out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
1388 if err != nil {
1389 panic(err)
1390 }
1391 defer out.Close()
1392
1393 if err := transform(out, inputs); err != nil {
1394 fmt.Fprintf(os.Stderr, "%s\n", err)
1395 os.Exit(1)
1396 }
1397}
1398
1399func forEachPath(node *node32, cb func(*node32), rules ...pegRule) {
1400 if node == nil {
1401 return
1402 }
1403
1404 if len(rules) == 0 {
1405 cb(node)
1406 return
1407 }
1408
1409 rule := rules[0]
1410 childRules := rules[1:]
1411
1412 for ; node != nil; node = node.next {
1413 if node.pegRule != rule {
Adam Langley947417a2017-04-18 10:35:18 -07001414 continue
1415 }
1416
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001417 if len(childRules) == 0 {
1418 cb(node)
1419 } else {
1420 forEachPath(node.up, cb, childRules...)
Adam Langley947417a2017-04-18 10:35:18 -07001421 }
1422 }
1423}
1424
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001425func skipNodes(node *node32, ruleToSkip pegRule) *node32 {
1426 for ; node != nil && node.pegRule == ruleToSkip; node = node.next {
1427 }
1428 return node
1429}
1430
1431func skipWS(node *node32) *node32 {
1432 return skipNodes(node, ruleWS)
1433}
1434
1435func assertNodeType(node *node32, expected pegRule) {
1436 if rule := node.pegRule; rule != expected {
1437 panic(fmt.Sprintf("node was %q, but wanted %q", rul3s[rule], rul3s[expected]))
1438 }
1439}
1440
1441type wrapperFunc func(func())
1442
1443type wrapperStack []wrapperFunc
1444
1445func (w *wrapperStack) do(baseCase func()) {
1446 if len(*w) == 0 {
1447 baseCase()
1448 return
1449 }
1450
1451 wrapper := (*w)[0]
1452 *w = (*w)[1:]
1453 wrapper(func() { w.do(baseCase) })
David Benjamind0a40592017-04-06 23:55:17 -04001454}
1455
Adam Langleyfd499932017-04-04 14:21:43 -07001456// localTargetName returns the name of the local target label for a global
1457// symbol named name.
1458func localTargetName(name string) string {
1459 return ".L" + name + "_local_target"
1460}
1461
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001462func localEntryName(name string) string {
1463 return ".L" + name + "_local_entry"
1464}
1465
1466func isSynthesized(symbol string) bool {
1467 return strings.HasSuffix(symbol, "_bss_get") ||
1468 symbol == "OPENSSL_ia32cap_get" ||
1469 strings.HasPrefix(symbol, "BORINGSSL_bcm_text_")
1470}
1471
1472func redirectorName(symbol string) string {
1473 return "bcm_redirector_" + symbol
1474}
1475
Adam Langleyb0d864e2017-04-20 16:48:24 -07001476// sectionType returns the type of a section. I.e. a section called “.text.foo”
1477// is a “.text” section.
1478func sectionType(section string) (string, bool) {
1479 if len(section) == 0 || section[0] != '.' {
1480 return "", false
1481 }
1482
1483 i := strings.Index(section[1:], ".")
1484 if i != -1 {
1485 section = section[:i+1]
1486 }
1487
1488 if strings.HasPrefix(section, ".debug_") {
1489 return ".debug", true
1490 }
1491
1492 return section, true
1493}
1494
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001495// accessorName returns the name of the accessor function for a BSS symbol
1496// named name.
1497func accessorName(name string) string {
1498 return name + "_bss_get"
1499}
Adam Langleyfd499932017-04-04 14:21:43 -07001500
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001501func (d *delocation) mapLocalSymbol(symbol string) string {
1502 if d.currentInput.index == 0 {
1503 return symbol
Adam Langleyfd499932017-04-04 14:21:43 -07001504 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001505 return symbol + "_BCM_" + strconv.Itoa(d.currentInput.index)
1506}
Adam Langleyfd499932017-04-04 14:21:43 -07001507
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001508func detectProcessor(input inputFile) processorType {
1509 for statement := input.ast.up; statement != nil; statement = statement.next {
1510 node := skipNodes(statement.up, ruleWS)
1511 if node == nil || node.pegRule != ruleInstruction {
Adam Langleyfd499932017-04-04 14:21:43 -07001512 continue
1513 }
1514
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001515 instruction := node.up
1516 instructionName := input.contents[instruction.begin:instruction.end]
Adam Langleyfd499932017-04-04 14:21:43 -07001517
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001518 switch instructionName {
1519 case "movq", "call", "leaq":
1520 return x86_64
1521 case "addis", "addi", "mflr":
1522 return ppc64le
Adam Langleyfd499932017-04-04 14:21:43 -07001523 }
1524 }
Adam Langley323f1eb2017-04-06 17:29:10 -07001525
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001526 panic("processed entire input and didn't recognise any instructions.")
Adam Langleyfd499932017-04-04 14:21:43 -07001527}
1528
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001529func sortedSet(m map[string]struct{}) []string {
1530 ret := make([]string, 0, len(m))
1531 for key := range m {
1532 ret = append(ret, key)
Adam Langleyfd499932017-04-04 14:21:43 -07001533 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001534 sort.Strings(ret)
1535 return ret
Adam Langleyfd499932017-04-04 14:21:43 -07001536}