blob: 6d56f928043c3a47705da5154a76b7e66e5131af [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
Adam Langleye314e1c2017-09-18 09:29:23 -0700807 case "mov", "movq", "vmovq", "movsd", "vmovsd":
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700808 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
Adam Langley32c5b8d2017-09-18 09:59:52 -0700862func moveTo(w stringWriter, target string, isAVX bool) wrapperFunc {
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700863 return func(k func()) {
864 k()
Adam Langley32c5b8d2017-09-18 09:59:52 -0700865 prefix := ""
866 if isAVX {
867 prefix = "v"
868 }
869 w.WriteString("\t" + prefix + "movq %rax, " + target + "\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -0700870 }
871}
872
873func isValidLEATarget(reg string) bool {
874 return !strings.HasPrefix(reg, "%xmm") && !strings.HasPrefix(reg, "%ymm") && !strings.HasPrefix(reg, "%zmm")
875}
876
877func undoConditionalMove(w stringWriter, instr string) wrapperFunc {
878 var invertedCondition string
879
880 switch instr {
881 case "cmoveq":
882 invertedCondition = "ne"
883 case "cmovneq":
884 invertedCondition = "e"
885 default:
886 panic(fmt.Sprintf("don't know how to handle conditional move instruction %q", instr))
887 }
888
889 return func(k func()) {
890 w.WriteString("\tj" + invertedCondition + " 999f\n")
891 k()
892 w.WriteString("999:\n")
893 }
894}
895
896func (d *delocation) isRIPRelative(node *node32) bool {
897 return node != nil && node.pegRule == ruleBaseIndexScale && d.contents(node) == "(%rip)"
898}
899
900func (d *delocation) processIntelInstruction(statement, instruction *node32) (*node32, error) {
901 assertNodeType(instruction, ruleInstructionName)
902 instructionName := d.contents(instruction)
903
904 argNodes := instructionArgs(instruction.next)
905
906 var wrappers wrapperStack
907 var args []string
908 changed := false
909
910Args:
911 for i, arg := range argNodes {
912 fullArg := arg
913 isIndirect := false
914
915 if arg.pegRule == ruleIndirectionIndicator {
916 arg = arg.next
917 isIndirect = true
918 }
919
920 switch arg.pegRule {
921 case ruleRegisterOrConstant, ruleLocalLabelRef:
922 args = append(args, d.contents(fullArg))
923
924 case ruleMemoryRef:
925 symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
926 changed = didChange
927
928 if symbol == "OPENSSL_ia32cap_P" {
929 var ok bool
930 if section == "GOTPCREL" {
931 ok = instructionName == "movq"
932 } else if section == "" {
933 ok = instructionName == "leaq"
934 }
935
936 if !ok {
937 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)
938 }
939
940 if i != 0 || len(argNodes) != 2 || !d.isRIPRelative(memRef) || len(offset) > 0 {
941 return nil, fmt.Errorf("invalid OPENSSL_ia32cap_P reference in instruction %q", instructionName)
942 }
943
944 target := argNodes[1]
945 assertNodeType(target, ruleRegisterOrConstant)
946 reg := d.contents(target)
947
948 if !strings.HasPrefix(reg, "%r") {
949 return nil, fmt.Errorf("tried to load OPENSSL_ia32cap_P into %q, which is not a standard register.", reg)
950 }
951
952 changed = true
953 wrappers = append(wrappers, func(k func()) {
954 d.output.WriteString("\tleaq\t-128(%rsp), %rsp\n") // Clear the red zone.
955 d.output.WriteString("\tpushfq\n")
956 d.output.WriteString("\tleaq\tOPENSSL_ia32cap_addr_delta(%rip), " + reg + "\n")
957 d.output.WriteString("\taddq\t(" + reg + "), " + reg + "\n")
958 d.output.WriteString("\tpopfq\n")
959 d.output.WriteString("\tleaq\t128(%rsp), %rsp\n")
960 })
961
962 break Args
963 }
964
965 switch section {
966 case "":
967 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
968 symbol = localTargetName(symbol)
969 changed = true
970 }
971
972 case "PLT":
973 if classifyInstruction(instructionName, argNodes) != instrJump {
974 return nil, fmt.Errorf("Cannot rewrite PLT reference for non-jump instruction %q", instructionName)
975 }
976
977 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
978 symbol = localTargetName(symbol)
979 changed = true
980 } else if !symbolIsLocal && !isSynthesized(symbol) {
981 // Unknown symbol via PLT is an
982 // out-call from the module, e.g.
983 // memcpy.
984 d.redirectors[symbol+"@"+section] = redirectorName(symbol)
985 symbol = redirectorName(symbol)
986 }
987
988 changed = true
989
990 case "GOTPCREL":
991 if len(offset) > 0 {
992 return nil, errors.New("loading from GOT with offset is unsupported")
993 }
994 if i != 0 {
995 return nil, errors.New("GOT access must be source operand")
996 }
997 if !d.isRIPRelative(memRef) {
998 return nil, errors.New("GOT access must be IP-relative")
999 }
1000
1001 useGOT := false
1002 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
1003 symbol = localTargetName(symbol)
1004 changed = true
1005 } else if !isSynthesized(symbol) {
1006 useGOT = true
1007 }
1008
1009 // Reduce the instruction to movq symbol@GOTPCREL, targetReg.
1010 var targetReg string
1011 switch classifyInstruction(instructionName, argNodes) {
1012 case instrPush:
1013 wrappers = append(wrappers, push(d.output))
1014 targetReg = "%rax"
1015 case instrConditionalMove:
1016 wrappers = append(wrappers, undoConditionalMove(d.output, instructionName))
1017 fallthrough
1018 case instrMove:
1019 assertNodeType(argNodes[1], ruleRegisterOrConstant)
1020 targetReg = d.contents(argNodes[1])
1021 default:
1022 return nil, fmt.Errorf("Cannot rewrite GOTPCREL reference for instruction %q", instructionName)
1023 }
1024
1025 var redzoneCleared bool
1026 if !isValidLEATarget(targetReg) {
1027 // Sometimes the compiler will load from the GOT to an
1028 // XMM register, which is not a valid target of an LEA
1029 // instruction.
1030 wrappers = append(wrappers, saveRegister(d.output))
Adam Langley32c5b8d2017-09-18 09:59:52 -07001031 isAVX := strings.HasPrefix(instructionName, "v")
1032 wrappers = append(wrappers, moveTo(d.output, targetReg, isAVX))
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001033 targetReg = "%rax"
1034 redzoneCleared = true
1035 }
1036
1037 if useGOT {
1038 wrappers = append(wrappers, d.loadFromGOT(d.output, targetReg, symbol, section, redzoneCleared))
1039 } else {
1040 wrappers = append(wrappers, func(k func()) {
1041 d.output.WriteString(fmt.Sprintf("\tleaq\t%s(%%rip), %s\n", symbol, targetReg))
1042 })
1043 }
1044 changed = true
1045 break Args
1046
1047 default:
1048 return nil, fmt.Errorf("Unknown section type %q", section)
1049 }
1050
1051 if !changed && len(section) > 0 {
1052 panic("section was not handled")
1053 }
1054 section = ""
1055
1056 argStr := ""
1057 if isIndirect {
1058 argStr += "*"
1059 }
1060 argStr += symbol
1061 argStr += offset
1062
1063 for ; memRef != nil; memRef = memRef.next {
1064 argStr += d.contents(memRef)
1065 }
1066
1067 args = append(args, argStr)
1068
1069 default:
1070 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
1071 }
1072 }
1073
1074 if changed {
1075 d.writeCommentedNode(statement)
1076 replacement := "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
1077 wrappers.do(func() {
1078 d.output.WriteString(replacement)
1079 })
1080 } else {
1081 d.writeNode(statement)
1082 }
1083
1084 return statement, nil
1085}
1086
1087func (d *delocation) handleBSS(statement *node32) (*node32, error) {
1088 lastStatement := statement
1089 for statement = statement.next; statement != nil; lastStatement, statement = statement, statement.next {
1090 node := skipWS(statement.up)
1091 if node == nil {
1092 d.writeNode(statement)
1093 continue
1094 }
1095
1096 switch node.pegRule {
1097 case ruleGlobalDirective, ruleComment, ruleInstruction, ruleLocationDirective:
1098 d.writeNode(statement)
1099
1100 case ruleDirective:
1101 directive := node.up
1102 assertNodeType(directive, ruleDirectiveName)
1103 directiveName := d.contents(directive)
1104 if directiveName == "text" || directiveName == "section" || directiveName == "data" {
1105 return lastStatement, nil
1106 }
1107 d.writeNode(statement)
1108
1109 case ruleLabel:
1110 label := node.up
1111 d.writeNode(statement)
1112
1113 if label.pegRule != ruleLocalSymbol {
1114 symbol := d.contents(label)
1115 localSymbol := localTargetName(symbol)
1116 d.output.WriteString(fmt.Sprintf("\n%s:\n", localSymbol))
1117
1118 d.bssAccessorsNeeded[symbol] = localSymbol
1119 }
1120
1121 case ruleLabelContainingDirective:
1122 var err error
1123 statement, err = d.processLabelContainingDirective(statement, node.up)
1124 if err != nil {
1125 return nil, err
1126 }
1127
1128 default:
1129 return nil, fmt.Errorf("unknown BSS statement type %q in %q", rul3s[node.pegRule], d.contents(statement))
1130 }
1131 }
1132
1133 return lastStatement, nil
1134}
1135
1136func transform(w stringWriter, inputs []inputFile) error {
1137 // symbols contains all defined symbols.
1138 symbols := make(map[string]struct{})
1139 // localEntrySymbols contains all symbols with a .localentry directive.
1140 localEntrySymbols := make(map[string]struct{})
1141
1142 for _, input := range inputs {
1143 forEachPath(input.ast.up, func(node *node32) {
1144 symbol := input.contents[node.begin:node.end]
1145 if _, ok := symbols[symbol]; ok {
1146 panic(fmt.Sprintf("Duplicate symbol found: %q in %q", symbol, input.path))
1147 }
1148 symbols[symbol] = struct{}{}
1149 }, ruleStatement, ruleLabel, ruleSymbolName)
1150
1151 forEachPath(input.ast.up, func(node *node32) {
1152 node = node.up
1153 assertNodeType(node, ruleLabelContainingDirectiveName)
1154 directive := input.contents[node.begin:node.end]
1155 if directive != ".localentry" {
1156 return
1157 }
1158 // Extract the first argument.
1159 node = skipWS(node.next)
1160 assertNodeType(node, ruleSymbolArgs)
1161 node = node.up
1162 assertNodeType(node, ruleSymbolArg)
1163 symbol := input.contents[node.begin:node.end]
1164 if _, ok := localEntrySymbols[symbol]; ok {
1165 panic(fmt.Sprintf("Duplicate .localentry directive found: %q in %q", symbol, input.path))
1166 }
1167 localEntrySymbols[symbol] = struct{}{}
1168 }, ruleStatement, ruleLabelContainingDirective)
1169 }
1170
1171 processor := x86_64
1172 if len(inputs) > 0 {
1173 processor = detectProcessor(inputs[0])
1174 }
1175
1176 d := &delocation{
1177 symbols: symbols,
1178 localEntrySymbols: localEntrySymbols,
1179 processor: processor,
1180 output: w,
1181 redirectors: make(map[string]string),
1182 bssAccessorsNeeded: make(map[string]string),
1183 tocLoaders: make(map[string]struct{}),
1184 gotExternalsNeeded: make(map[string]struct{}),
1185 }
1186
1187 w.WriteString(".text\nBORINGSSL_bcm_text_start:\n")
1188
1189 for _, input := range inputs {
1190 if err := d.processInput(input); err != nil {
1191 return err
1192 }
1193 }
1194
1195 w.WriteString(".text\nBORINGSSL_bcm_text_end:\n")
1196
1197 // Emit redirector functions. Each is a single jump instruction.
Adam Langley2c673f12017-04-13 11:10:44 -07001198 var redirectorNames []string
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001199 for name := range d.redirectors {
Adam Langley2c673f12017-04-13 11:10:44 -07001200 redirectorNames = append(redirectorNames, name)
1201 }
1202 sort.Strings(redirectorNames)
1203
1204 for _, name := range redirectorNames {
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001205 redirector := d.redirectors[name]
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001206 if d.processor == ppc64le {
David Benjamincd60bf02017-06-20 20:01:06 -04001207 w.WriteString(".section \".toc\", \"aw\"\n")
1208 w.WriteString(".Lredirector_toc_" + name + ":\n")
1209 w.WriteString(".quad " + name + "\n")
1210 w.WriteString(".text\n")
1211 w.WriteString(".type " + redirector + ", @function\n")
1212 w.WriteString(redirector + ":\n")
1213 // |name| will clobber r2, so save it. This is matched by a restore in
1214 // redirector calls.
1215 w.WriteString("\tstd 2, 24(1)\n")
1216 // Load and call |name|'s global entry point.
1217 w.WriteString("\taddis 12, 2, .Lredirector_toc_" + name + "@toc@ha\n")
1218 w.WriteString("\tld 12, .Lredirector_toc_" + name + "@toc@l(12)\n")
1219 w.WriteString("\tmtctr 12\n")
1220 w.WriteString("\tbctr\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001221 } else {
David Benjamincd60bf02017-06-20 20:01:06 -04001222 w.WriteString(".type " + redirector + ", @function\n")
1223 w.WriteString(redirector + ":\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001224 w.WriteString("\tjmp\t" + name + "\n")
1225 }
Adam Langleyfd499932017-04-04 14:21:43 -07001226 }
1227
Adam Langley947417a2017-04-18 10:35:18 -07001228 var accessorNames []string
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001229 for accessor := range d.bssAccessorsNeeded {
Adam Langley947417a2017-04-18 10:35:18 -07001230 accessorNames = append(accessorNames, accessor)
1231 }
1232 sort.Strings(accessorNames)
1233
David Benjamind0a40592017-04-06 23:55:17 -04001234 // Emit BSS accessor functions. Each is a single LEA followed by RET.
Adam Langley947417a2017-04-18 10:35:18 -07001235 for _, name := range accessorNames {
David Benjamind0a40592017-04-06 23:55:17 -04001236 funcName := accessorName(name)
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001237 w.WriteString(".type " + funcName + ", @function\n")
1238 w.WriteString(funcName + ":\n")
1239 target := d.bssAccessorsNeeded[name]
1240
1241 if d.processor == ppc64le {
1242 w.WriteString("\taddis 3, 2, " + target + "@toc@ha\n")
1243 w.WriteString("\taddi 3, 3, " + target + "@toc@l\n")
1244 w.WriteString("\tblr\n")
1245 } else {
1246 w.WriteString("\tleaq\t" + target + "(%rip), %rax\n\tret\n")
1247 }
David Benjamind0a40592017-04-06 23:55:17 -04001248 }
1249
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001250 if d.processor == ppc64le {
1251 loadTOCNames := sortedSet(d.tocLoaders)
Adam Langleycd334a52017-05-31 17:02:42 -07001252 for _, symbolAndOffset := range loadTOCNames {
1253 parts := strings.SplitN(symbolAndOffset, "\x00", 2)
1254 symbol, offset := parts[0], parts[1]
1255
1256 funcName := loadTOCFuncName(symbol, offset)
1257 ref := symbol + offset
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001258
1259 w.WriteString(".type " + funcName[2:] + ", @function\n")
1260 w.WriteString(funcName[2:] + ":\n")
1261 w.WriteString(funcName + ":\n")
Adam Langleycd334a52017-05-31 17:02:42 -07001262 w.WriteString("\taddis 3, 2, " + ref + "@toc@ha\n")
1263 w.WriteString("\taddi 3, 3, " + ref + "@toc@l\n")
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001264 w.WriteString("\tblr\n")
1265 }
1266
1267 w.WriteString(".LBORINGSSL_external_toc:\n")
1268 w.WriteString(".quad .TOC.-.LBORINGSSL_external_toc\n")
1269 } else {
1270 externalNames := sortedSet(d.gotExternalsNeeded)
1271 for _, name := range externalNames {
1272 parts := strings.SplitN(name, "@", 2)
1273 symbol, section := parts[0], parts[1]
1274 w.WriteString(".type " + symbol + "_" + section + "_external, @object\n")
1275 w.WriteString(".size " + symbol + "_" + section + "_external, 8\n")
1276 w.WriteString(symbol + "_" + section + "_external:\n")
1277 // Ideally this would be .quad foo@GOTPCREL, but clang's
1278 // assembler cannot emit a 64-bit GOTPCREL relocation. Instead,
1279 // we manually sign-extend the value, knowing that the GOT is
1280 // always at the end, thus foo@GOTPCREL has a positive value.
1281 w.WriteString("\t.long " + symbol + "@" + section + "\n")
1282 w.WriteString("\t.long 0\n")
1283 }
1284
1285 w.WriteString(".type OPENSSL_ia32cap_get, @function\n")
1286 w.WriteString("OPENSSL_ia32cap_get:\n")
1287 w.WriteString("\tleaq OPENSSL_ia32cap_P(%rip), %rax\n")
1288 w.WriteString("\tret\n")
1289
1290 w.WriteString(".extern OPENSSL_ia32cap_P\n")
1291 w.WriteString(".type OPENSSL_ia32cap_addr_delta, @object\n")
1292 w.WriteString(".size OPENSSL_ia32cap_addr_delta, 8\n")
1293 w.WriteString("OPENSSL_ia32cap_addr_delta:\n")
1294 w.WriteString(".quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta\n")
David Benjamin91871012017-04-25 15:37:53 -04001295 }
1296
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001297 w.WriteString(".type BORINGSSL_bcm_text_hash, @object\n")
1298 w.WriteString(".size BORINGSSL_bcm_text_hash, 64\n")
1299 w.WriteString("BORINGSSL_bcm_text_hash:\n")
David Benjamind0a40592017-04-06 23:55:17 -04001300 for _, b := range uninitHashValue {
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001301 w.WriteString(".byte 0x" + strconv.FormatUint(uint64(b), 16) + "\n")
David Benjamind0a40592017-04-06 23:55:17 -04001302 }
1303
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001304 return nil
Adam Langleyfd499932017-04-04 14:21:43 -07001305}
1306
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001307func parseInputs(inputs []inputFile) error {
1308 for i, input := range inputs {
1309 var contents string
Adam Langley5c38c052017-04-28 14:47:06 -07001310
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001311 if input.isArchive {
1312 arFile, err := os.Open(input.path)
1313 if err != nil {
1314 return err
1315 }
1316 defer arFile.Close()
Adam Langley947417a2017-04-18 10:35:18 -07001317
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001318 ar, err := ParseAR(arFile)
1319 if err != nil {
1320 return err
1321 }
1322
1323 if len(ar) != 1 {
1324 return fmt.Errorf("expected one file in archive, but found %d", len(ar))
1325 }
1326
1327 for _, c := range ar {
1328 contents = string(c)
1329 }
1330 } else {
1331 inBytes, err := ioutil.ReadFile(input.path)
1332 if err != nil {
1333 return err
1334 }
1335
1336 contents = string(inBytes)
Adam Langley947417a2017-04-18 10:35:18 -07001337 }
1338
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001339 asm := Asm{Buffer: contents, Pretty: true}
1340 asm.Init()
1341 if err := asm.Parse(); err != nil {
1342 return fmt.Errorf("error while parsing %q: %s", input.path, err)
1343 }
1344 ast := asm.AST()
1345
1346 inputs[i].contents = contents
1347 inputs[i].ast = ast
1348 }
1349
1350 return nil
1351}
1352
1353func main() {
1354 // The .a file, if given, is expected to be an archive of textual
1355 // assembly sources. That's odd, but CMake really wants to create
1356 // archive files so it's the only way that we can make it work.
1357 arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
1358 outFile := flag.String("o", "", "Path to output assembly")
1359
1360 flag.Parse()
1361
1362 if len(*outFile) == 0 {
1363 fmt.Fprintf(os.Stderr, "Must give argument to -o.\n")
1364 os.Exit(1)
1365 }
1366
1367 var inputs []inputFile
1368 if len(*arInput) > 0 {
1369 inputs = append(inputs, inputFile{
1370 path: *arInput,
1371 index: 0,
1372 isArchive: true,
1373 })
1374 }
1375
1376 for i, path := range flag.Args() {
1377 if len(path) == 0 {
Adam Langley947417a2017-04-18 10:35:18 -07001378 continue
1379 }
1380
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001381 inputs = append(inputs, inputFile{
1382 path: path,
1383 index: i + 1,
1384 })
1385 }
Adam Langley947417a2017-04-18 10:35:18 -07001386
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001387 if err := parseInputs(inputs); err != nil {
1388 fmt.Fprintf(os.Stderr, "%s\n", err)
1389 os.Exit(1)
1390 }
Adam Langley947417a2017-04-18 10:35:18 -07001391
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001392 out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
1393 if err != nil {
1394 panic(err)
1395 }
1396 defer out.Close()
1397
1398 if err := transform(out, inputs); err != nil {
1399 fmt.Fprintf(os.Stderr, "%s\n", err)
1400 os.Exit(1)
1401 }
1402}
1403
1404func forEachPath(node *node32, cb func(*node32), rules ...pegRule) {
1405 if node == nil {
1406 return
1407 }
1408
1409 if len(rules) == 0 {
1410 cb(node)
1411 return
1412 }
1413
1414 rule := rules[0]
1415 childRules := rules[1:]
1416
1417 for ; node != nil; node = node.next {
1418 if node.pegRule != rule {
Adam Langley947417a2017-04-18 10:35:18 -07001419 continue
1420 }
1421
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001422 if len(childRules) == 0 {
1423 cb(node)
1424 } else {
1425 forEachPath(node.up, cb, childRules...)
Adam Langley947417a2017-04-18 10:35:18 -07001426 }
1427 }
1428}
1429
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001430func skipNodes(node *node32, ruleToSkip pegRule) *node32 {
1431 for ; node != nil && node.pegRule == ruleToSkip; node = node.next {
1432 }
1433 return node
1434}
1435
1436func skipWS(node *node32) *node32 {
1437 return skipNodes(node, ruleWS)
1438}
1439
1440func assertNodeType(node *node32, expected pegRule) {
1441 if rule := node.pegRule; rule != expected {
1442 panic(fmt.Sprintf("node was %q, but wanted %q", rul3s[rule], rul3s[expected]))
1443 }
1444}
1445
1446type wrapperFunc func(func())
1447
1448type wrapperStack []wrapperFunc
1449
1450func (w *wrapperStack) do(baseCase func()) {
1451 if len(*w) == 0 {
1452 baseCase()
1453 return
1454 }
1455
1456 wrapper := (*w)[0]
1457 *w = (*w)[1:]
1458 wrapper(func() { w.do(baseCase) })
David Benjamind0a40592017-04-06 23:55:17 -04001459}
1460
Adam Langleyfd499932017-04-04 14:21:43 -07001461// localTargetName returns the name of the local target label for a global
1462// symbol named name.
1463func localTargetName(name string) string {
1464 return ".L" + name + "_local_target"
1465}
1466
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001467func localEntryName(name string) string {
1468 return ".L" + name + "_local_entry"
1469}
1470
1471func isSynthesized(symbol string) bool {
1472 return strings.HasSuffix(symbol, "_bss_get") ||
1473 symbol == "OPENSSL_ia32cap_get" ||
1474 strings.HasPrefix(symbol, "BORINGSSL_bcm_text_")
1475}
1476
1477func redirectorName(symbol string) string {
1478 return "bcm_redirector_" + symbol
1479}
1480
Adam Langleyb0d864e2017-04-20 16:48:24 -07001481// sectionType returns the type of a section. I.e. a section called “.text.foo”
1482// is a “.text” section.
1483func sectionType(section string) (string, bool) {
1484 if len(section) == 0 || section[0] != '.' {
1485 return "", false
1486 }
1487
1488 i := strings.Index(section[1:], ".")
1489 if i != -1 {
1490 section = section[:i+1]
1491 }
1492
1493 if strings.HasPrefix(section, ".debug_") {
1494 return ".debug", true
1495 }
1496
1497 return section, true
1498}
1499
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001500// accessorName returns the name of the accessor function for a BSS symbol
1501// named name.
1502func accessorName(name string) string {
1503 return name + "_bss_get"
1504}
Adam Langleyfd499932017-04-04 14:21:43 -07001505
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001506func (d *delocation) mapLocalSymbol(symbol string) string {
1507 if d.currentInput.index == 0 {
1508 return symbol
Adam Langleyfd499932017-04-04 14:21:43 -07001509 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001510 return symbol + "_BCM_" + strconv.Itoa(d.currentInput.index)
1511}
Adam Langleyfd499932017-04-04 14:21:43 -07001512
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001513func detectProcessor(input inputFile) processorType {
1514 for statement := input.ast.up; statement != nil; statement = statement.next {
1515 node := skipNodes(statement.up, ruleWS)
1516 if node == nil || node.pegRule != ruleInstruction {
Adam Langleyfd499932017-04-04 14:21:43 -07001517 continue
1518 }
1519
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001520 instruction := node.up
1521 instructionName := input.contents[instruction.begin:instruction.end]
Adam Langleyfd499932017-04-04 14:21:43 -07001522
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001523 switch instructionName {
1524 case "movq", "call", "leaq":
1525 return x86_64
1526 case "addis", "addi", "mflr":
1527 return ppc64le
Adam Langleyfd499932017-04-04 14:21:43 -07001528 }
1529 }
Adam Langley323f1eb2017-04-06 17:29:10 -07001530
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001531 panic("processed entire input and didn't recognise any instructions.")
Adam Langleyfd499932017-04-04 14:21:43 -07001532}
1533
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001534func sortedSet(m map[string]struct{}) []string {
1535 ret := make([]string, 0, len(m))
1536 for key := range m {
1537 ret = append(ret, key)
Adam Langleyfd499932017-04-04 14:21:43 -07001538 }
Adam Langleyf64a6ee2017-05-17 13:05:50 -07001539 sort.Strings(ret)
1540 return ret
Adam Langleyfd499932017-04-04 14:21:43 -07001541}