blob: b4371fc6a310b727eb6cf620ae66dcb75b42cf12 [file] [log] [blame]
Robert Sloan8ff03552017-06-14 12:40:58 -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
15//go:generate peg delocate.peg
16
17// delocate performs several transformations of textual assembly code. See
18// crypto/fipsmodule/FIPS.md for an overview.
19package main
20
21import (
22 "errors"
23 "flag"
24 "fmt"
25 "io/ioutil"
26 "os"
27 "sort"
28 "strconv"
29 "strings"
30)
31
32// 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}
46
47type stringWriter interface {
48 WriteString(string) (int, error)
49}
50
51type processorType int
52
53const (
54 ppc64le processorType = iota + 1
55 x86_64
56)
57
58// delocation holds the state needed during a delocation operation.
59type delocation struct {
60 processor processorType
61 output stringWriter
62
63 // 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{}
82
83 currentInput inputFile
84}
85
86func (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 {
93 panic(err)
94 }
95}
96
97func (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)
101 }
102}
103
104func 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
109 }
110
111 return fmt.Errorf("error while processing %q on line %d: %q", in.contents[with.begin:with.end], line, err)
112}
113
114func (d *delocation) processInput(input inputFile) (err error) {
115 d.currentInput = input
116
117 var origStatement *node32
118 defer func() {
119 if err := recover(); err != nil {
120 panic(locateError(fmt.Errorf("%s", err), origStatement, input))
121 }
122 }()
123
124 for statement := input.ast.up; statement != nil; statement = statement.next {
125 assertNodeType(statement, ruleStatement)
126 origStatement = statement
127
128 node := skipWS(statement.up)
129 if node == nil {
130 d.writeNode(statement)
131 continue
132 }
133
134 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]))
154 }
155
156 if err != nil {
157 return locateError(err, origStatement, input)
158 }
159 }
160
161 return nil
162}
163
164func (d *delocation) processDirective(statement, directive *node32) (*node32, error) {
165 assertNodeType(directive, ruleDirectiveName)
166 directiveName := d.contents(directive)
167
168 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)
185
186 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)
254 }
255
256 return statement, nil
257}
258
259func (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)
265
266 node := directive.next
267 assertNodeType(node, ruleWS)
268
269 node = node.next
270 assertNodeType(node, ruleSymbolArgs)
271
272 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)
281 continue
282 }
283
284 oldSymbol := d.contents(term)
285 newSymbol := d.mapLocalSymbol(oldSymbol)
286 if newSymbol != oldSymbol {
287 changed = true
288 }
289
290 mapped += newSymbol
291 }
292
293 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
Robert Sloane56da3e2017-06-26 08:26:42 -0700342[PABI]: “64-Bit ELF V2 ABI Specification. Power Architecture.” March 21st,
Robert Sloan8ff03552017-06-14 12:40:58 -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
482// the value of “symbol+offset”.
483func loadTOCFuncName(symbol, offset string) string {
484 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
490 }
491 return ret
492}
493
494func (d *delocation) loadFromTOC(w stringWriter, symbol, offset, dest string) wrapperFunc {
495 d.tocLoaders[symbol+"\x00"+offset] = struct{}{}
496
497 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")
504 }
505
506 // Because loadTOCFuncName returns a “.L” name, we don't need a
507 // nop after this call.
508 w.WriteString("\tbl " + loadTOCFuncName(symbol, offset) + "\n")
509
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")
518 }
519 w.WriteString("\taddi 1, 1, 288\n")
520
521 k()
522 }
523}
524
525func (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
537func (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
545 // (Offset* '+')?
546 symRef, offset = d.gatherOffsets(symRef, offset)
547
548 // (LocalSymbol / SymbolName)
549 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 }
558 symRef = symRef.next
559
560 // Offset*
561 symRef, offset = d.gatherOffsets(symRef, offset)
562
563 // ('@' Section / Offset*)?
564 if symRef != nil {
565 assertNodeType(symRef, ruleSection)
566 section = d.contents(symRef)
567 symRef = symRef.next
568
569 symRef, offset = d.gatherOffsets(symRef, offset)
570 }
571
572 if symRef != nil {
573 panic(fmt.Sprintf("unexpected token in SymbolRef: %q", rul3s[symRef.pegRule]))
574 }
575
576 return
577}
578
579func (d *delocation) processPPCInstruction(statement, instruction *node32) (*node32, error) {
580 assertNodeType(instruction, ruleInstructionName)
581 instructionName := d.contents(instruction)
582 isBranch := instructionName[0] == 'b'
583
584 argNodes := instructionArgs(instruction.next)
585
586 var wrappers wrapperStack
587 var args []string
588 changed := false
589
590Args:
591 for i, arg := range argNodes {
592 fullArg := arg
593 isIndirect := false
594
595 if arg.pegRule == ruleIndirectionIndicator {
596 arg = arg.next
597 isIndirect = true
598 }
599
600 switch arg.pegRule {
601 case ruleRegisterOrConstant, ruleLocalLabelRef:
602 args = append(args, d.contents(fullArg))
603
604 case ruleTOCRefLow:
605 return nil, errors.New("Found low TOC reference outside preamble pattern")
606
607 case ruleTOCRefHigh:
608 target, _, relative, ok := d.isPPC64LEAPair(statement)
609 if !ok {
610 return nil, errors.New("Found high TOC reference outside preamble pattern")
611 }
612
613 if relative != "12" {
614 return nil, fmt.Errorf("preamble is relative to %q, not r12", relative)
615 }
616
617 if target != "2" {
618 return nil, fmt.Errorf("preamble is setting %q, not r2", target)
619 }
620
621 statement = statement.next
622 establishTOC(d.output)
623 instructionName = ""
624 changed = true
625 break Args
626
627 case ruleMemoryRef:
628 symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
629 changed = didChange
630
631 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)
Robert Sloane56da3e2017-06-26 08:26:42 -0700642 // 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 })
Robert Sloan8ff03552017-06-14 12:40:58 -0700650 }
651 }
652
653 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.
668 //
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.
676 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
697 wrappers = append(wrappers, d.loadFromTOC(d.output, symbol, offset, destReg))
698 switch instructionName {
699 case "addi":
700 // The original instruction was:
701 // addi destReg, tocHaReg, offset+symbol@toc@l
702 instructionName = ""
703
704 case "ld", "lhz", "lwz":
705 // The original instruction was:
706 // l?? destReg, offset+symbol@toc@l(tocHaReg)
707 //
708 // We transform that into the
709 // equivalent dereference of destReg:
710 // l?? destReg, 0(destReg)
711 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
720 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()) {
Robert Sloane56da3e2017-06-26 08:26:42 -0700726 d.output.WriteString("\taddi 1, 1, -288\n") // Clear the red zone.
Robert Sloan8ff03552017-06-14 12:40:58 -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")
Robert Sloane56da3e2017-06-26 08:26:42 -0700731 d.output.WriteString("\taddi 1, 1, 288\n") // Clear the red zone.
Robert Sloan8ff03552017-06-14 12:40:58 -0700732 })
733 }
734
735 wrappers = append(wrappers, func(k func()) {
736 d.output.WriteString("\t" + origInstructionName + " " + destReg + ", 0(" + baseReg + ")\n")
737 })
738 default:
739 return nil, fmt.Errorf("can't process TOC argument to %q", instructionName)
740 }
741
742 default:
743 return nil, fmt.Errorf("Unknown section type %q", section)
744 }
745
746 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
765 default:
766 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
767 }
768 }
769
770 if changed {
771 d.writeCommentedNode(statement)
772
773 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
Robert Sloan0da43952018-01-03 15:13:14 -0800795 // instrTransformingMove is essentially a move, but it performs some
796 // transformation of the data during the process.
797 instrTransformingMove
Robert Sloan8ff03552017-06-14 12:40:58 -0700798 instrJump
799 instrConditionalMove
800 instrOther
801)
802
803func classifyInstruction(instr string, args []*node32) instructionType {
804 switch instr {
805 case "push", "pushq":
806 if len(args) == 1 {
807 return instrPush
808 }
809
Robert Sloan2e9e66a2017-09-25 09:08:29 -0700810 case "mov", "movq", "vmovq", "movsd", "vmovsd":
Robert Sloan8ff03552017-06-14 12:40:58 -0700811 if len(args) == 2 {
812 return instrMove
813 }
814
815 case "cmovneq", "cmoveq":
816 if len(args) == 2 {
817 return instrConditionalMove
818 }
819
820 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":
821 if len(args) == 1 {
822 return instrJump
823 }
Robert Sloan0da43952018-01-03 15:13:14 -0800824
825 case "vpbroadcastq":
826 if len(args) == 2 {
827 return instrTransformingMove
828 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700829 }
830
831 return instrOther
832}
833
834func push(w stringWriter) wrapperFunc {
835 return func(k func()) {
836 w.WriteString("\tpushq %rax\n")
837 k()
838 w.WriteString("\txchg %rax, (%rsp)\n")
839 }
840}
841
842func (d *delocation) loadFromGOT(w stringWriter, destination, symbol, section string, redzoneCleared bool) wrapperFunc {
843 d.gotExternalsNeeded[symbol+"@"+section] = struct{}{}
844
845 return func(k func()) {
846 if !redzoneCleared {
847 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
848 }
849 w.WriteString("\tpushf\n")
850 w.WriteString(fmt.Sprintf("\tleaq %s_%s_external(%%rip), %s\n", symbol, section, destination))
851 w.WriteString(fmt.Sprintf("\taddq (%s), %s\n", destination, destination))
852 w.WriteString(fmt.Sprintf("\tmovq (%s), %s\n", destination, destination))
853 w.WriteString("\tpopf\n")
854 if !redzoneCleared {
855 w.WriteString("\tleaq\t128(%rsp), %rsp\n")
856 }
857 }
858}
859
860func saveRegister(w stringWriter) wrapperFunc {
861 return func(k func()) {
862 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
863 w.WriteString("\tpushq %rax\n")
864 k()
865 w.WriteString("\tpopq %rax\n")
866 w.WriteString("\tleaq 128(%rsp), %rsp\n")
867 }
868}
869
Robert Sloan2e9e66a2017-09-25 09:08:29 -0700870func moveTo(w stringWriter, target string, isAVX bool) wrapperFunc {
Robert Sloan8ff03552017-06-14 12:40:58 -0700871 return func(k func()) {
872 k()
Robert Sloan2e9e66a2017-09-25 09:08:29 -0700873 prefix := ""
874 if isAVX {
875 prefix = "v"
876 }
877 w.WriteString("\t" + prefix + "movq %rax, " + target + "\n")
Robert Sloan8ff03552017-06-14 12:40:58 -0700878 }
879}
880
Robert Sloan0da43952018-01-03 15:13:14 -0800881func finalTransform(w stringWriter, transformInstruction, reg string) wrapperFunc {
882 return func(k func()) {
883 k()
884 w.WriteString("\t" + transformInstruction + " " + reg + ", " + reg + "\n")
885 }
886}
887
Robert Sloan8ff03552017-06-14 12:40:58 -0700888func isValidLEATarget(reg string) bool {
889 return !strings.HasPrefix(reg, "%xmm") && !strings.HasPrefix(reg, "%ymm") && !strings.HasPrefix(reg, "%zmm")
890}
891
892func undoConditionalMove(w stringWriter, instr string) wrapperFunc {
893 var invertedCondition string
894
895 switch instr {
896 case "cmoveq":
897 invertedCondition = "ne"
898 case "cmovneq":
899 invertedCondition = "e"
900 default:
901 panic(fmt.Sprintf("don't know how to handle conditional move instruction %q", instr))
902 }
903
904 return func(k func()) {
905 w.WriteString("\tj" + invertedCondition + " 999f\n")
906 k()
907 w.WriteString("999:\n")
908 }
909}
910
911func (d *delocation) isRIPRelative(node *node32) bool {
912 return node != nil && node.pegRule == ruleBaseIndexScale && d.contents(node) == "(%rip)"
913}
914
915func (d *delocation) processIntelInstruction(statement, instruction *node32) (*node32, error) {
916 assertNodeType(instruction, ruleInstructionName)
917 instructionName := d.contents(instruction)
918
919 argNodes := instructionArgs(instruction.next)
920
921 var wrappers wrapperStack
922 var args []string
923 changed := false
924
925Args:
926 for i, arg := range argNodes {
927 fullArg := arg
928 isIndirect := false
929
930 if arg.pegRule == ruleIndirectionIndicator {
931 arg = arg.next
932 isIndirect = true
933 }
934
935 switch arg.pegRule {
936 case ruleRegisterOrConstant, ruleLocalLabelRef:
937 args = append(args, d.contents(fullArg))
938
939 case ruleMemoryRef:
940 symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
941 changed = didChange
942
943 if symbol == "OPENSSL_ia32cap_P" {
944 var ok bool
945 if section == "GOTPCREL" {
946 ok = instructionName == "movq"
947 } else if section == "" {
948 ok = instructionName == "leaq"
949 }
950
951 if !ok {
952 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)
953 }
954
955 if i != 0 || len(argNodes) != 2 || !d.isRIPRelative(memRef) || len(offset) > 0 {
956 return nil, fmt.Errorf("invalid OPENSSL_ia32cap_P reference in instruction %q", instructionName)
957 }
958
959 target := argNodes[1]
960 assertNodeType(target, ruleRegisterOrConstant)
961 reg := d.contents(target)
962
963 if !strings.HasPrefix(reg, "%r") {
964 return nil, fmt.Errorf("tried to load OPENSSL_ia32cap_P into %q, which is not a standard register.", reg)
965 }
966
967 changed = true
968 wrappers = append(wrappers, func(k func()) {
969 d.output.WriteString("\tleaq\t-128(%rsp), %rsp\n") // Clear the red zone.
970 d.output.WriteString("\tpushfq\n")
971 d.output.WriteString("\tleaq\tOPENSSL_ia32cap_addr_delta(%rip), " + reg + "\n")
972 d.output.WriteString("\taddq\t(" + reg + "), " + reg + "\n")
973 d.output.WriteString("\tpopfq\n")
974 d.output.WriteString("\tleaq\t128(%rsp), %rsp\n")
975 })
976
977 break Args
978 }
979
980 switch section {
981 case "":
982 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
983 symbol = localTargetName(symbol)
984 changed = true
985 }
986
987 case "PLT":
988 if classifyInstruction(instructionName, argNodes) != instrJump {
989 return nil, fmt.Errorf("Cannot rewrite PLT reference for non-jump instruction %q", instructionName)
990 }
991
992 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
993 symbol = localTargetName(symbol)
994 changed = true
995 } else if !symbolIsLocal && !isSynthesized(symbol) {
996 // Unknown symbol via PLT is an
997 // out-call from the module, e.g.
998 // memcpy.
999 d.redirectors[symbol+"@"+section] = redirectorName(symbol)
1000 symbol = redirectorName(symbol)
1001 }
1002
1003 changed = true
1004
1005 case "GOTPCREL":
1006 if len(offset) > 0 {
1007 return nil, errors.New("loading from GOT with offset is unsupported")
1008 }
1009 if i != 0 {
1010 return nil, errors.New("GOT access must be source operand")
1011 }
1012 if !d.isRIPRelative(memRef) {
1013 return nil, errors.New("GOT access must be IP-relative")
1014 }
1015
1016 useGOT := false
1017 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
1018 symbol = localTargetName(symbol)
1019 changed = true
1020 } else if !isSynthesized(symbol) {
1021 useGOT = true
1022 }
1023
1024 // Reduce the instruction to movq symbol@GOTPCREL, targetReg.
1025 var targetReg string
1026 switch classifyInstruction(instructionName, argNodes) {
1027 case instrPush:
1028 wrappers = append(wrappers, push(d.output))
1029 targetReg = "%rax"
1030 case instrConditionalMove:
1031 wrappers = append(wrappers, undoConditionalMove(d.output, instructionName))
1032 fallthrough
1033 case instrMove:
1034 assertNodeType(argNodes[1], ruleRegisterOrConstant)
1035 targetReg = d.contents(argNodes[1])
Robert Sloan0da43952018-01-03 15:13:14 -08001036 case instrTransformingMove:
1037 assertNodeType(argNodes[1], ruleRegisterOrConstant)
1038 targetReg = d.contents(argNodes[1])
1039 wrappers = append(wrappers, finalTransform(d.output, instructionName, targetReg))
1040 if isValidLEATarget(targetReg) {
Robert Sloan978112c2018-01-22 12:53:01 -08001041 return nil, errors.New("Currently transforming moves are assumed to target XMM registers. Otherwise we'll pop %rax before reading it to do the transform.")
Robert Sloan0da43952018-01-03 15:13:14 -08001042 }
Robert Sloan8ff03552017-06-14 12:40:58 -07001043 default:
1044 return nil, fmt.Errorf("Cannot rewrite GOTPCREL reference for instruction %q", instructionName)
1045 }
1046
1047 var redzoneCleared bool
1048 if !isValidLEATarget(targetReg) {
1049 // Sometimes the compiler will load from the GOT to an
1050 // XMM register, which is not a valid target of an LEA
1051 // instruction.
1052 wrappers = append(wrappers, saveRegister(d.output))
Robert Sloan2e9e66a2017-09-25 09:08:29 -07001053 isAVX := strings.HasPrefix(instructionName, "v")
1054 wrappers = append(wrappers, moveTo(d.output, targetReg, isAVX))
Robert Sloan8ff03552017-06-14 12:40:58 -07001055 targetReg = "%rax"
1056 redzoneCleared = true
1057 }
1058
1059 if useGOT {
1060 wrappers = append(wrappers, d.loadFromGOT(d.output, targetReg, symbol, section, redzoneCleared))
1061 } else {
1062 wrappers = append(wrappers, func(k func()) {
1063 d.output.WriteString(fmt.Sprintf("\tleaq\t%s(%%rip), %s\n", symbol, targetReg))
1064 })
1065 }
1066 changed = true
1067 break Args
1068
1069 default:
1070 return nil, fmt.Errorf("Unknown section type %q", section)
1071 }
1072
1073 if !changed && len(section) > 0 {
1074 panic("section was not handled")
1075 }
1076 section = ""
1077
1078 argStr := ""
1079 if isIndirect {
1080 argStr += "*"
1081 }
1082 argStr += symbol
1083 argStr += offset
1084
1085 for ; memRef != nil; memRef = memRef.next {
1086 argStr += d.contents(memRef)
1087 }
1088
1089 args = append(args, argStr)
1090
1091 default:
1092 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
1093 }
1094 }
1095
1096 if changed {
1097 d.writeCommentedNode(statement)
1098 replacement := "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
1099 wrappers.do(func() {
1100 d.output.WriteString(replacement)
1101 })
1102 } else {
1103 d.writeNode(statement)
1104 }
1105
1106 return statement, nil
1107}
1108
1109func (d *delocation) handleBSS(statement *node32) (*node32, error) {
1110 lastStatement := statement
1111 for statement = statement.next; statement != nil; lastStatement, statement = statement, statement.next {
1112 node := skipWS(statement.up)
1113 if node == nil {
1114 d.writeNode(statement)
1115 continue
1116 }
1117
1118 switch node.pegRule {
1119 case ruleGlobalDirective, ruleComment, ruleInstruction, ruleLocationDirective:
1120 d.writeNode(statement)
1121
1122 case ruleDirective:
1123 directive := node.up
1124 assertNodeType(directive, ruleDirectiveName)
1125 directiveName := d.contents(directive)
1126 if directiveName == "text" || directiveName == "section" || directiveName == "data" {
1127 return lastStatement, nil
1128 }
1129 d.writeNode(statement)
1130
1131 case ruleLabel:
1132 label := node.up
1133 d.writeNode(statement)
1134
1135 if label.pegRule != ruleLocalSymbol {
1136 symbol := d.contents(label)
1137 localSymbol := localTargetName(symbol)
1138 d.output.WriteString(fmt.Sprintf("\n%s:\n", localSymbol))
1139
1140 d.bssAccessorsNeeded[symbol] = localSymbol
1141 }
1142
1143 case ruleLabelContainingDirective:
1144 var err error
1145 statement, err = d.processLabelContainingDirective(statement, node.up)
1146 if err != nil {
1147 return nil, err
1148 }
1149
1150 default:
1151 return nil, fmt.Errorf("unknown BSS statement type %q in %q", rul3s[node.pegRule], d.contents(statement))
1152 }
1153 }
1154
1155 return lastStatement, nil
1156}
1157
1158func transform(w stringWriter, inputs []inputFile) error {
1159 // symbols contains all defined symbols.
1160 symbols := make(map[string]struct{})
1161 // localEntrySymbols contains all symbols with a .localentry directive.
1162 localEntrySymbols := make(map[string]struct{})
1163
1164 for _, input := range inputs {
1165 forEachPath(input.ast.up, func(node *node32) {
1166 symbol := input.contents[node.begin:node.end]
1167 if _, ok := symbols[symbol]; ok {
1168 panic(fmt.Sprintf("Duplicate symbol found: %q in %q", symbol, input.path))
1169 }
1170 symbols[symbol] = struct{}{}
1171 }, ruleStatement, ruleLabel, ruleSymbolName)
1172
1173 forEachPath(input.ast.up, func(node *node32) {
1174 node = node.up
1175 assertNodeType(node, ruleLabelContainingDirectiveName)
1176 directive := input.contents[node.begin:node.end]
1177 if directive != ".localentry" {
1178 return
1179 }
1180 // Extract the first argument.
1181 node = skipWS(node.next)
1182 assertNodeType(node, ruleSymbolArgs)
1183 node = node.up
1184 assertNodeType(node, ruleSymbolArg)
1185 symbol := input.contents[node.begin:node.end]
1186 if _, ok := localEntrySymbols[symbol]; ok {
1187 panic(fmt.Sprintf("Duplicate .localentry directive found: %q in %q", symbol, input.path))
1188 }
1189 localEntrySymbols[symbol] = struct{}{}
1190 }, ruleStatement, ruleLabelContainingDirective)
1191 }
1192
1193 processor := x86_64
1194 if len(inputs) > 0 {
1195 processor = detectProcessor(inputs[0])
1196 }
1197
1198 d := &delocation{
1199 symbols: symbols,
1200 localEntrySymbols: localEntrySymbols,
1201 processor: processor,
1202 output: w,
1203 redirectors: make(map[string]string),
1204 bssAccessorsNeeded: make(map[string]string),
1205 tocLoaders: make(map[string]struct{}),
1206 gotExternalsNeeded: make(map[string]struct{}),
1207 }
1208
1209 w.WriteString(".text\nBORINGSSL_bcm_text_start:\n")
1210
1211 for _, input := range inputs {
1212 if err := d.processInput(input); err != nil {
1213 return err
1214 }
1215 }
1216
1217 w.WriteString(".text\nBORINGSSL_bcm_text_end:\n")
1218
1219 // Emit redirector functions. Each is a single jump instruction.
1220 var redirectorNames []string
1221 for name := range d.redirectors {
1222 redirectorNames = append(redirectorNames, name)
1223 }
1224 sort.Strings(redirectorNames)
1225
1226 for _, name := range redirectorNames {
1227 redirector := d.redirectors[name]
Robert Sloan8ff03552017-06-14 12:40:58 -07001228 if d.processor == ppc64le {
Robert Sloane56da3e2017-06-26 08:26:42 -07001229 w.WriteString(".section \".toc\", \"aw\"\n")
1230 w.WriteString(".Lredirector_toc_" + name + ":\n")
1231 w.WriteString(".quad " + name + "\n")
1232 w.WriteString(".text\n")
1233 w.WriteString(".type " + redirector + ", @function\n")
1234 w.WriteString(redirector + ":\n")
1235 // |name| will clobber r2, so save it. This is matched by a restore in
1236 // redirector calls.
1237 w.WriteString("\tstd 2, 24(1)\n")
1238 // Load and call |name|'s global entry point.
1239 w.WriteString("\taddis 12, 2, .Lredirector_toc_" + name + "@toc@ha\n")
1240 w.WriteString("\tld 12, .Lredirector_toc_" + name + "@toc@l(12)\n")
1241 w.WriteString("\tmtctr 12\n")
1242 w.WriteString("\tbctr\n")
Robert Sloan8ff03552017-06-14 12:40:58 -07001243 } else {
Robert Sloane56da3e2017-06-26 08:26:42 -07001244 w.WriteString(".type " + redirector + ", @function\n")
1245 w.WriteString(redirector + ":\n")
Robert Sloan8ff03552017-06-14 12:40:58 -07001246 w.WriteString("\tjmp\t" + name + "\n")
1247 }
1248 }
1249
1250 var accessorNames []string
1251 for accessor := range d.bssAccessorsNeeded {
1252 accessorNames = append(accessorNames, accessor)
1253 }
1254 sort.Strings(accessorNames)
1255
1256 // Emit BSS accessor functions. Each is a single LEA followed by RET.
1257 for _, name := range accessorNames {
1258 funcName := accessorName(name)
1259 w.WriteString(".type " + funcName + ", @function\n")
1260 w.WriteString(funcName + ":\n")
1261 target := d.bssAccessorsNeeded[name]
1262
1263 if d.processor == ppc64le {
1264 w.WriteString("\taddis 3, 2, " + target + "@toc@ha\n")
1265 w.WriteString("\taddi 3, 3, " + target + "@toc@l\n")
1266 w.WriteString("\tblr\n")
1267 } else {
1268 w.WriteString("\tleaq\t" + target + "(%rip), %rax\n\tret\n")
1269 }
1270 }
1271
1272 if d.processor == ppc64le {
1273 loadTOCNames := sortedSet(d.tocLoaders)
1274 for _, symbolAndOffset := range loadTOCNames {
1275 parts := strings.SplitN(symbolAndOffset, "\x00", 2)
1276 symbol, offset := parts[0], parts[1]
1277
1278 funcName := loadTOCFuncName(symbol, offset)
1279 ref := symbol + offset
1280
1281 w.WriteString(".type " + funcName[2:] + ", @function\n")
1282 w.WriteString(funcName[2:] + ":\n")
1283 w.WriteString(funcName + ":\n")
1284 w.WriteString("\taddis 3, 2, " + ref + "@toc@ha\n")
1285 w.WriteString("\taddi 3, 3, " + ref + "@toc@l\n")
1286 w.WriteString("\tblr\n")
1287 }
1288
1289 w.WriteString(".LBORINGSSL_external_toc:\n")
1290 w.WriteString(".quad .TOC.-.LBORINGSSL_external_toc\n")
1291 } else {
1292 externalNames := sortedSet(d.gotExternalsNeeded)
1293 for _, name := range externalNames {
1294 parts := strings.SplitN(name, "@", 2)
1295 symbol, section := parts[0], parts[1]
1296 w.WriteString(".type " + symbol + "_" + section + "_external, @object\n")
1297 w.WriteString(".size " + symbol + "_" + section + "_external, 8\n")
1298 w.WriteString(symbol + "_" + section + "_external:\n")
1299 // Ideally this would be .quad foo@GOTPCREL, but clang's
1300 // assembler cannot emit a 64-bit GOTPCREL relocation. Instead,
1301 // we manually sign-extend the value, knowing that the GOT is
1302 // always at the end, thus foo@GOTPCREL has a positive value.
1303 w.WriteString("\t.long " + symbol + "@" + section + "\n")
1304 w.WriteString("\t.long 0\n")
1305 }
1306
1307 w.WriteString(".type OPENSSL_ia32cap_get, @function\n")
1308 w.WriteString("OPENSSL_ia32cap_get:\n")
1309 w.WriteString("\tleaq OPENSSL_ia32cap_P(%rip), %rax\n")
1310 w.WriteString("\tret\n")
1311
1312 w.WriteString(".extern OPENSSL_ia32cap_P\n")
1313 w.WriteString(".type OPENSSL_ia32cap_addr_delta, @object\n")
1314 w.WriteString(".size OPENSSL_ia32cap_addr_delta, 8\n")
1315 w.WriteString("OPENSSL_ia32cap_addr_delta:\n")
1316 w.WriteString(".quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta\n")
1317 }
1318
1319 w.WriteString(".type BORINGSSL_bcm_text_hash, @object\n")
1320 w.WriteString(".size BORINGSSL_bcm_text_hash, 64\n")
1321 w.WriteString("BORINGSSL_bcm_text_hash:\n")
1322 for _, b := range uninitHashValue {
1323 w.WriteString(".byte 0x" + strconv.FormatUint(uint64(b), 16) + "\n")
1324 }
1325
1326 return nil
1327}
1328
1329func parseInputs(inputs []inputFile) error {
1330 for i, input := range inputs {
1331 var contents string
1332
1333 if input.isArchive {
1334 arFile, err := os.Open(input.path)
1335 if err != nil {
1336 return err
1337 }
1338 defer arFile.Close()
1339
1340 ar, err := ParseAR(arFile)
1341 if err != nil {
1342 return err
1343 }
1344
1345 if len(ar) != 1 {
1346 return fmt.Errorf("expected one file in archive, but found %d", len(ar))
1347 }
1348
1349 for _, c := range ar {
1350 contents = string(c)
1351 }
1352 } else {
1353 inBytes, err := ioutil.ReadFile(input.path)
1354 if err != nil {
1355 return err
1356 }
1357
1358 contents = string(inBytes)
1359 }
1360
1361 asm := Asm{Buffer: contents, Pretty: true}
1362 asm.Init()
1363 if err := asm.Parse(); err != nil {
1364 return fmt.Errorf("error while parsing %q: %s", input.path, err)
1365 }
1366 ast := asm.AST()
1367
1368 inputs[i].contents = contents
1369 inputs[i].ast = ast
1370 }
1371
1372 return nil
1373}
1374
1375func main() {
1376 // The .a file, if given, is expected to be an archive of textual
1377 // assembly sources. That's odd, but CMake really wants to create
1378 // archive files so it's the only way that we can make it work.
1379 arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
1380 outFile := flag.String("o", "", "Path to output assembly")
1381
1382 flag.Parse()
1383
1384 if len(*outFile) == 0 {
1385 fmt.Fprintf(os.Stderr, "Must give argument to -o.\n")
1386 os.Exit(1)
1387 }
1388
1389 var inputs []inputFile
1390 if len(*arInput) > 0 {
1391 inputs = append(inputs, inputFile{
1392 path: *arInput,
1393 index: 0,
1394 isArchive: true,
1395 })
1396 }
1397
1398 for i, path := range flag.Args() {
1399 if len(path) == 0 {
1400 continue
1401 }
1402
1403 inputs = append(inputs, inputFile{
1404 path: path,
1405 index: i + 1,
1406 })
1407 }
1408
1409 if err := parseInputs(inputs); err != nil {
1410 fmt.Fprintf(os.Stderr, "%s\n", err)
1411 os.Exit(1)
1412 }
1413
1414 out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
1415 if err != nil {
1416 panic(err)
1417 }
1418 defer out.Close()
1419
1420 if err := transform(out, inputs); err != nil {
1421 fmt.Fprintf(os.Stderr, "%s\n", err)
1422 os.Exit(1)
1423 }
1424}
1425
1426func forEachPath(node *node32, cb func(*node32), rules ...pegRule) {
1427 if node == nil {
1428 return
1429 }
1430
1431 if len(rules) == 0 {
1432 cb(node)
1433 return
1434 }
1435
1436 rule := rules[0]
1437 childRules := rules[1:]
1438
1439 for ; node != nil; node = node.next {
1440 if node.pegRule != rule {
1441 continue
1442 }
1443
1444 if len(childRules) == 0 {
1445 cb(node)
1446 } else {
1447 forEachPath(node.up, cb, childRules...)
1448 }
1449 }
1450}
1451
1452func skipNodes(node *node32, ruleToSkip pegRule) *node32 {
1453 for ; node != nil && node.pegRule == ruleToSkip; node = node.next {
1454 }
1455 return node
1456}
1457
1458func skipWS(node *node32) *node32 {
1459 return skipNodes(node, ruleWS)
1460}
1461
1462func assertNodeType(node *node32, expected pegRule) {
1463 if rule := node.pegRule; rule != expected {
1464 panic(fmt.Sprintf("node was %q, but wanted %q", rul3s[rule], rul3s[expected]))
1465 }
1466}
1467
1468type wrapperFunc func(func())
1469
1470type wrapperStack []wrapperFunc
1471
1472func (w *wrapperStack) do(baseCase func()) {
1473 if len(*w) == 0 {
1474 baseCase()
1475 return
1476 }
1477
1478 wrapper := (*w)[0]
1479 *w = (*w)[1:]
1480 wrapper(func() { w.do(baseCase) })
1481}
1482
1483// localTargetName returns the name of the local target label for a global
1484// symbol named name.
1485func localTargetName(name string) string {
1486 return ".L" + name + "_local_target"
1487}
1488
1489func localEntryName(name string) string {
1490 return ".L" + name + "_local_entry"
1491}
1492
1493func isSynthesized(symbol string) bool {
1494 return strings.HasSuffix(symbol, "_bss_get") ||
1495 symbol == "OPENSSL_ia32cap_get" ||
1496 strings.HasPrefix(symbol, "BORINGSSL_bcm_text_")
1497}
1498
1499func redirectorName(symbol string) string {
1500 return "bcm_redirector_" + symbol
1501}
1502
1503// sectionType returns the type of a section. I.e. a section called “.text.foo”
1504// is a “.text” section.
1505func sectionType(section string) (string, bool) {
1506 if len(section) == 0 || section[0] != '.' {
1507 return "", false
1508 }
1509
1510 i := strings.Index(section[1:], ".")
1511 if i != -1 {
1512 section = section[:i+1]
1513 }
1514
1515 if strings.HasPrefix(section, ".debug_") {
1516 return ".debug", true
1517 }
1518
1519 return section, true
1520}
1521
1522// accessorName returns the name of the accessor function for a BSS symbol
1523// named name.
1524func accessorName(name string) string {
1525 return name + "_bss_get"
1526}
1527
1528func (d *delocation) mapLocalSymbol(symbol string) string {
1529 if d.currentInput.index == 0 {
1530 return symbol
1531 }
1532 return symbol + "_BCM_" + strconv.Itoa(d.currentInput.index)
1533}
1534
1535func detectProcessor(input inputFile) processorType {
1536 for statement := input.ast.up; statement != nil; statement = statement.next {
1537 node := skipNodes(statement.up, ruleWS)
1538 if node == nil || node.pegRule != ruleInstruction {
1539 continue
1540 }
1541
1542 instruction := node.up
1543 instructionName := input.contents[instruction.begin:instruction.end]
1544
1545 switch instructionName {
1546 case "movq", "call", "leaq":
1547 return x86_64
1548 case "addis", "addi", "mflr":
1549 return ppc64le
1550 }
1551 }
1552
1553 panic("processed entire input and didn't recognise any instructions.")
1554}
1555
1556func sortedSet(m map[string]struct{}) []string {
1557 ret := make([]string, 0, len(m))
1558 for key := range m {
1559 ret = append(ret, key)
1560 }
1561 sort.Strings(ret)
1562 return ret
1563}