blob: a4799290479edb132718e66bdc4b7dd16bbbe5ea [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
342[PABI]: “64-bit ELF v2 ABI Specification. Power Architechture.” March 21st,
343 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)
642 }
643 }
644
645 switch section {
646 case "":
647
648 case "tls":
649 // This section identifier just tells the
650 // assembler to use r13, the pointer to the
651 // thread-local data [PABI;3.7.3.3].
652
653 case "toc@ha":
654 // Delete toc@ha instructions. Per
655 // [PABI;3.6.3], the linker is allowed to erase
656 // toc@ha instructions. We take advantage of
657 // this by unconditionally erasing the toc@ha
658 // instructions and doing the full lookup when
659 // processing toc@l.
660 //
661 // Note that any offset here applies before @ha
662 // and @l. That is, 42+foo@toc@ha is
663 // #ha(42+foo-.TOC.), not 42+#ha(foo-.TOC.). Any
664 // corresponding toc@l references are required
665 // by the ABI to have the same offset. The
666 // offset will be incorporated in full when
667 // those are processed.
668 if instructionName != "addis" || len(argNodes) != 3 || i != 2 || args[1] != "2" {
669 return nil, errors.New("can't process toc@ha reference")
670 }
671 changed = true
672 instructionName = ""
673 break Args
674
675 case "toc@l":
676 // Per [PAB;3.6.3], this instruction must take
677 // as input a register which was the output of
678 // a toc@ha computation and compute the actual
679 // address of some symbol. The toc@ha
680 // computation was elided, so we ignore that
681 // input register and compute the address
682 // directly.
683 changed = true
684
685 // For all supported toc@l instructions, the
686 // destination register is the first argument.
687 destReg := args[0]
688
689 wrappers = append(wrappers, d.loadFromTOC(d.output, symbol, offset, destReg))
690 switch instructionName {
691 case "addi":
692 // The original instruction was:
693 // addi destReg, tocHaReg, offset+symbol@toc@l
694 instructionName = ""
695
696 case "ld", "lhz", "lwz":
697 // The original instruction was:
698 // l?? destReg, offset+symbol@toc@l(tocHaReg)
699 //
700 // We transform that into the
701 // equivalent dereference of destReg:
702 // l?? destReg, 0(destReg)
703 origInstructionName := instructionName
704 instructionName = ""
705
706 assertNodeType(memRef, ruleBaseIndexScale)
707 assertNodeType(memRef.up, ruleRegisterOrConstant)
708 if memRef.next != nil || memRef.up.next != nil {
709 return nil, errors.New("expected single register in BaseIndexScale for ld argument")
710 }
711
712 baseReg := destReg
713 if baseReg == "0" {
714 // Register zero is special as the base register for a load.
715 // Avoid it by spilling and using r3 instead.
716 baseReg = "3"
717 wrappers = append(wrappers, func(k func()) {
718 d.output.WriteString("\taddi 1, 1, -288\n") // Clear the red zone.
719 d.output.WriteString("\tstd " + baseReg + ", -8(1)\n")
720 d.output.WriteString("\tmr " + baseReg + ", " + destReg + "\n")
721 k()
722 d.output.WriteString("\tld " + baseReg + ", -8(1)\n")
723 d.output.WriteString("\taddi 1, 1, 288\n") // Clear the red zone.
724 })
725 }
726
727 wrappers = append(wrappers, func(k func()) {
728 d.output.WriteString("\t" + origInstructionName + " " + destReg + ", 0(" + baseReg + ")\n")
729 })
730 default:
731 return nil, fmt.Errorf("can't process TOC argument to %q", instructionName)
732 }
733
734 default:
735 return nil, fmt.Errorf("Unknown section type %q", section)
736 }
737
738 argStr := ""
739 if isIndirect {
740 argStr += "*"
741 }
742 argStr += symbol
743 if len(offset) > 0 {
744 argStr += offset
745 }
746 if len(section) > 0 {
747 argStr += "@"
748 argStr += section
749 }
750
751 for ; memRef != nil; memRef = memRef.next {
752 argStr += d.contents(memRef)
753 }
754
755 args = append(args, argStr)
756
757 default:
758 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
759 }
760 }
761
762 if changed {
763 d.writeCommentedNode(statement)
764
765 var replacement string
766 if len(instructionName) > 0 {
767 replacement = "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
768 }
769
770 wrappers.do(func() {
771 d.output.WriteString(replacement)
772 })
773 } else {
774 d.writeNode(statement)
775 }
776
777 return statement, nil
778}
779
780/* Intel */
781
782type instructionType int
783
784const (
785 instrPush instructionType = iota
786 instrMove
787 instrJump
788 instrConditionalMove
789 instrOther
790)
791
792func classifyInstruction(instr string, args []*node32) instructionType {
793 switch instr {
794 case "push", "pushq":
795 if len(args) == 1 {
796 return instrPush
797 }
798
799 case "mov", "movq", "vmovq":
800 if len(args) == 2 {
801 return instrMove
802 }
803
804 case "cmovneq", "cmoveq":
805 if len(args) == 2 {
806 return instrConditionalMove
807 }
808
809 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":
810 if len(args) == 1 {
811 return instrJump
812 }
813 }
814
815 return instrOther
816}
817
818func push(w stringWriter) wrapperFunc {
819 return func(k func()) {
820 w.WriteString("\tpushq %rax\n")
821 k()
822 w.WriteString("\txchg %rax, (%rsp)\n")
823 }
824}
825
826func (d *delocation) loadFromGOT(w stringWriter, destination, symbol, section string, redzoneCleared bool) wrapperFunc {
827 d.gotExternalsNeeded[symbol+"@"+section] = struct{}{}
828
829 return func(k func()) {
830 if !redzoneCleared {
831 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
832 }
833 w.WriteString("\tpushf\n")
834 w.WriteString(fmt.Sprintf("\tleaq %s_%s_external(%%rip), %s\n", symbol, section, destination))
835 w.WriteString(fmt.Sprintf("\taddq (%s), %s\n", destination, destination))
836 w.WriteString(fmt.Sprintf("\tmovq (%s), %s\n", destination, destination))
837 w.WriteString("\tpopf\n")
838 if !redzoneCleared {
839 w.WriteString("\tleaq\t128(%rsp), %rsp\n")
840 }
841 }
842}
843
844func saveRegister(w stringWriter) wrapperFunc {
845 return func(k func()) {
846 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
847 w.WriteString("\tpushq %rax\n")
848 k()
849 w.WriteString("\tpopq %rax\n")
850 w.WriteString("\tleaq 128(%rsp), %rsp\n")
851 }
852}
853
854func moveTo(w stringWriter, target string) wrapperFunc {
855 return func(k func()) {
856 k()
857 w.WriteString("\tmovq %rax, " + target + "\n")
858 }
859}
860
861func isValidLEATarget(reg string) bool {
862 return !strings.HasPrefix(reg, "%xmm") && !strings.HasPrefix(reg, "%ymm") && !strings.HasPrefix(reg, "%zmm")
863}
864
865func undoConditionalMove(w stringWriter, instr string) wrapperFunc {
866 var invertedCondition string
867
868 switch instr {
869 case "cmoveq":
870 invertedCondition = "ne"
871 case "cmovneq":
872 invertedCondition = "e"
873 default:
874 panic(fmt.Sprintf("don't know how to handle conditional move instruction %q", instr))
875 }
876
877 return func(k func()) {
878 w.WriteString("\tj" + invertedCondition + " 999f\n")
879 k()
880 w.WriteString("999:\n")
881 }
882}
883
884func (d *delocation) isRIPRelative(node *node32) bool {
885 return node != nil && node.pegRule == ruleBaseIndexScale && d.contents(node) == "(%rip)"
886}
887
888func (d *delocation) processIntelInstruction(statement, instruction *node32) (*node32, error) {
889 assertNodeType(instruction, ruleInstructionName)
890 instructionName := d.contents(instruction)
891
892 argNodes := instructionArgs(instruction.next)
893
894 var wrappers wrapperStack
895 var args []string
896 changed := false
897
898Args:
899 for i, arg := range argNodes {
900 fullArg := arg
901 isIndirect := false
902
903 if arg.pegRule == ruleIndirectionIndicator {
904 arg = arg.next
905 isIndirect = true
906 }
907
908 switch arg.pegRule {
909 case ruleRegisterOrConstant, ruleLocalLabelRef:
910 args = append(args, d.contents(fullArg))
911
912 case ruleMemoryRef:
913 symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
914 changed = didChange
915
916 if symbol == "OPENSSL_ia32cap_P" {
917 var ok bool
918 if section == "GOTPCREL" {
919 ok = instructionName == "movq"
920 } else if section == "" {
921 ok = instructionName == "leaq"
922 }
923
924 if !ok {
925 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)
926 }
927
928 if i != 0 || len(argNodes) != 2 || !d.isRIPRelative(memRef) || len(offset) > 0 {
929 return nil, fmt.Errorf("invalid OPENSSL_ia32cap_P reference in instruction %q", instructionName)
930 }
931
932 target := argNodes[1]
933 assertNodeType(target, ruleRegisterOrConstant)
934 reg := d.contents(target)
935
936 if !strings.HasPrefix(reg, "%r") {
937 return nil, fmt.Errorf("tried to load OPENSSL_ia32cap_P into %q, which is not a standard register.", reg)
938 }
939
940 changed = true
941 wrappers = append(wrappers, func(k func()) {
942 d.output.WriteString("\tleaq\t-128(%rsp), %rsp\n") // Clear the red zone.
943 d.output.WriteString("\tpushfq\n")
944 d.output.WriteString("\tleaq\tOPENSSL_ia32cap_addr_delta(%rip), " + reg + "\n")
945 d.output.WriteString("\taddq\t(" + reg + "), " + reg + "\n")
946 d.output.WriteString("\tpopfq\n")
947 d.output.WriteString("\tleaq\t128(%rsp), %rsp\n")
948 })
949
950 break Args
951 }
952
953 switch section {
954 case "":
955 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
956 symbol = localTargetName(symbol)
957 changed = true
958 }
959
960 case "PLT":
961 if classifyInstruction(instructionName, argNodes) != instrJump {
962 return nil, fmt.Errorf("Cannot rewrite PLT reference for non-jump instruction %q", instructionName)
963 }
964
965 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
966 symbol = localTargetName(symbol)
967 changed = true
968 } else if !symbolIsLocal && !isSynthesized(symbol) {
969 // Unknown symbol via PLT is an
970 // out-call from the module, e.g.
971 // memcpy.
972 d.redirectors[symbol+"@"+section] = redirectorName(symbol)
973 symbol = redirectorName(symbol)
974 }
975
976 changed = true
977
978 case "GOTPCREL":
979 if len(offset) > 0 {
980 return nil, errors.New("loading from GOT with offset is unsupported")
981 }
982 if i != 0 {
983 return nil, errors.New("GOT access must be source operand")
984 }
985 if !d.isRIPRelative(memRef) {
986 return nil, errors.New("GOT access must be IP-relative")
987 }
988
989 useGOT := false
990 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
991 symbol = localTargetName(symbol)
992 changed = true
993 } else if !isSynthesized(symbol) {
994 useGOT = true
995 }
996
997 // Reduce the instruction to movq symbol@GOTPCREL, targetReg.
998 var targetReg string
999 switch classifyInstruction(instructionName, argNodes) {
1000 case instrPush:
1001 wrappers = append(wrappers, push(d.output))
1002 targetReg = "%rax"
1003 case instrConditionalMove:
1004 wrappers = append(wrappers, undoConditionalMove(d.output, instructionName))
1005 fallthrough
1006 case instrMove:
1007 assertNodeType(argNodes[1], ruleRegisterOrConstant)
1008 targetReg = d.contents(argNodes[1])
1009 default:
1010 return nil, fmt.Errorf("Cannot rewrite GOTPCREL reference for instruction %q", instructionName)
1011 }
1012
1013 var redzoneCleared bool
1014 if !isValidLEATarget(targetReg) {
1015 // Sometimes the compiler will load from the GOT to an
1016 // XMM register, which is not a valid target of an LEA
1017 // instruction.
1018 wrappers = append(wrappers, saveRegister(d.output))
1019 wrappers = append(wrappers, moveTo(d.output, targetReg))
1020 targetReg = "%rax"
1021 redzoneCleared = true
1022 }
1023
1024 if useGOT {
1025 wrappers = append(wrappers, d.loadFromGOT(d.output, targetReg, symbol, section, redzoneCleared))
1026 } else {
1027 wrappers = append(wrappers, func(k func()) {
1028 d.output.WriteString(fmt.Sprintf("\tleaq\t%s(%%rip), %s\n", symbol, targetReg))
1029 })
1030 }
1031 changed = true
1032 break Args
1033
1034 default:
1035 return nil, fmt.Errorf("Unknown section type %q", section)
1036 }
1037
1038 if !changed && len(section) > 0 {
1039 panic("section was not handled")
1040 }
1041 section = ""
1042
1043 argStr := ""
1044 if isIndirect {
1045 argStr += "*"
1046 }
1047 argStr += symbol
1048 argStr += offset
1049
1050 for ; memRef != nil; memRef = memRef.next {
1051 argStr += d.contents(memRef)
1052 }
1053
1054 args = append(args, argStr)
1055
1056 default:
1057 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
1058 }
1059 }
1060
1061 if changed {
1062 d.writeCommentedNode(statement)
1063 replacement := "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
1064 wrappers.do(func() {
1065 d.output.WriteString(replacement)
1066 })
1067 } else {
1068 d.writeNode(statement)
1069 }
1070
1071 return statement, nil
1072}
1073
1074func (d *delocation) handleBSS(statement *node32) (*node32, error) {
1075 lastStatement := statement
1076 for statement = statement.next; statement != nil; lastStatement, statement = statement, statement.next {
1077 node := skipWS(statement.up)
1078 if node == nil {
1079 d.writeNode(statement)
1080 continue
1081 }
1082
1083 switch node.pegRule {
1084 case ruleGlobalDirective, ruleComment, ruleInstruction, ruleLocationDirective:
1085 d.writeNode(statement)
1086
1087 case ruleDirective:
1088 directive := node.up
1089 assertNodeType(directive, ruleDirectiveName)
1090 directiveName := d.contents(directive)
1091 if directiveName == "text" || directiveName == "section" || directiveName == "data" {
1092 return lastStatement, nil
1093 }
1094 d.writeNode(statement)
1095
1096 case ruleLabel:
1097 label := node.up
1098 d.writeNode(statement)
1099
1100 if label.pegRule != ruleLocalSymbol {
1101 symbol := d.contents(label)
1102 localSymbol := localTargetName(symbol)
1103 d.output.WriteString(fmt.Sprintf("\n%s:\n", localSymbol))
1104
1105 d.bssAccessorsNeeded[symbol] = localSymbol
1106 }
1107
1108 case ruleLabelContainingDirective:
1109 var err error
1110 statement, err = d.processLabelContainingDirective(statement, node.up)
1111 if err != nil {
1112 return nil, err
1113 }
1114
1115 default:
1116 return nil, fmt.Errorf("unknown BSS statement type %q in %q", rul3s[node.pegRule], d.contents(statement))
1117 }
1118 }
1119
1120 return lastStatement, nil
1121}
1122
1123func transform(w stringWriter, inputs []inputFile) error {
1124 // symbols contains all defined symbols.
1125 symbols := make(map[string]struct{})
1126 // localEntrySymbols contains all symbols with a .localentry directive.
1127 localEntrySymbols := make(map[string]struct{})
1128
1129 for _, input := range inputs {
1130 forEachPath(input.ast.up, func(node *node32) {
1131 symbol := input.contents[node.begin:node.end]
1132 if _, ok := symbols[symbol]; ok {
1133 panic(fmt.Sprintf("Duplicate symbol found: %q in %q", symbol, input.path))
1134 }
1135 symbols[symbol] = struct{}{}
1136 }, ruleStatement, ruleLabel, ruleSymbolName)
1137
1138 forEachPath(input.ast.up, func(node *node32) {
1139 node = node.up
1140 assertNodeType(node, ruleLabelContainingDirectiveName)
1141 directive := input.contents[node.begin:node.end]
1142 if directive != ".localentry" {
1143 return
1144 }
1145 // Extract the first argument.
1146 node = skipWS(node.next)
1147 assertNodeType(node, ruleSymbolArgs)
1148 node = node.up
1149 assertNodeType(node, ruleSymbolArg)
1150 symbol := input.contents[node.begin:node.end]
1151 if _, ok := localEntrySymbols[symbol]; ok {
1152 panic(fmt.Sprintf("Duplicate .localentry directive found: %q in %q", symbol, input.path))
1153 }
1154 localEntrySymbols[symbol] = struct{}{}
1155 }, ruleStatement, ruleLabelContainingDirective)
1156 }
1157
1158 processor := x86_64
1159 if len(inputs) > 0 {
1160 processor = detectProcessor(inputs[0])
1161 }
1162
1163 d := &delocation{
1164 symbols: symbols,
1165 localEntrySymbols: localEntrySymbols,
1166 processor: processor,
1167 output: w,
1168 redirectors: make(map[string]string),
1169 bssAccessorsNeeded: make(map[string]string),
1170 tocLoaders: make(map[string]struct{}),
1171 gotExternalsNeeded: make(map[string]struct{}),
1172 }
1173
1174 w.WriteString(".text\nBORINGSSL_bcm_text_start:\n")
1175
1176 for _, input := range inputs {
1177 if err := d.processInput(input); err != nil {
1178 return err
1179 }
1180 }
1181
1182 w.WriteString(".text\nBORINGSSL_bcm_text_end:\n")
1183
1184 // Emit redirector functions. Each is a single jump instruction.
1185 var redirectorNames []string
1186 for name := range d.redirectors {
1187 redirectorNames = append(redirectorNames, name)
1188 }
1189 sort.Strings(redirectorNames)
1190
1191 for _, name := range redirectorNames {
1192 redirector := d.redirectors[name]
1193 w.WriteString(".type " + redirector + ", @function\n")
1194 w.WriteString(redirector + ":\n")
1195 if d.processor == ppc64le {
1196 w.WriteString("\tmflr 0\n")
1197 w.WriteString("\tstd 0,16(1)\n")
1198 w.WriteString("\tstdu 1,-32(1)\n")
1199 w.WriteString("\tbl\t" + name + "\n")
1200 w.WriteString("\tnop\n")
1201 w.WriteString("\taddi 1,1,32\n")
1202 w.WriteString("\tld 0,16(1)\n")
1203 w.WriteString("\tmtlr 0\n")
1204 w.WriteString("\tblr\n")
1205 } else {
1206 w.WriteString("\tjmp\t" + name + "\n")
1207 }
1208 }
1209
1210 var accessorNames []string
1211 for accessor := range d.bssAccessorsNeeded {
1212 accessorNames = append(accessorNames, accessor)
1213 }
1214 sort.Strings(accessorNames)
1215
1216 // Emit BSS accessor functions. Each is a single LEA followed by RET.
1217 for _, name := range accessorNames {
1218 funcName := accessorName(name)
1219 w.WriteString(".type " + funcName + ", @function\n")
1220 w.WriteString(funcName + ":\n")
1221 target := d.bssAccessorsNeeded[name]
1222
1223 if d.processor == ppc64le {
1224 w.WriteString("\taddis 3, 2, " + target + "@toc@ha\n")
1225 w.WriteString("\taddi 3, 3, " + target + "@toc@l\n")
1226 w.WriteString("\tblr\n")
1227 } else {
1228 w.WriteString("\tleaq\t" + target + "(%rip), %rax\n\tret\n")
1229 }
1230 }
1231
1232 if d.processor == ppc64le {
1233 loadTOCNames := sortedSet(d.tocLoaders)
1234 for _, symbolAndOffset := range loadTOCNames {
1235 parts := strings.SplitN(symbolAndOffset, "\x00", 2)
1236 symbol, offset := parts[0], parts[1]
1237
1238 funcName := loadTOCFuncName(symbol, offset)
1239 ref := symbol + offset
1240
1241 w.WriteString(".type " + funcName[2:] + ", @function\n")
1242 w.WriteString(funcName[2:] + ":\n")
1243 w.WriteString(funcName + ":\n")
1244 w.WriteString("\taddis 3, 2, " + ref + "@toc@ha\n")
1245 w.WriteString("\taddi 3, 3, " + ref + "@toc@l\n")
1246 w.WriteString("\tblr\n")
1247 }
1248
1249 w.WriteString(".LBORINGSSL_external_toc:\n")
1250 w.WriteString(".quad .TOC.-.LBORINGSSL_external_toc\n")
1251 } else {
1252 externalNames := sortedSet(d.gotExternalsNeeded)
1253 for _, name := range externalNames {
1254 parts := strings.SplitN(name, "@", 2)
1255 symbol, section := parts[0], parts[1]
1256 w.WriteString(".type " + symbol + "_" + section + "_external, @object\n")
1257 w.WriteString(".size " + symbol + "_" + section + "_external, 8\n")
1258 w.WriteString(symbol + "_" + section + "_external:\n")
1259 // Ideally this would be .quad foo@GOTPCREL, but clang's
1260 // assembler cannot emit a 64-bit GOTPCREL relocation. Instead,
1261 // we manually sign-extend the value, knowing that the GOT is
1262 // always at the end, thus foo@GOTPCREL has a positive value.
1263 w.WriteString("\t.long " + symbol + "@" + section + "\n")
1264 w.WriteString("\t.long 0\n")
1265 }
1266
1267 w.WriteString(".type OPENSSL_ia32cap_get, @function\n")
1268 w.WriteString("OPENSSL_ia32cap_get:\n")
1269 w.WriteString("\tleaq OPENSSL_ia32cap_P(%rip), %rax\n")
1270 w.WriteString("\tret\n")
1271
1272 w.WriteString(".extern OPENSSL_ia32cap_P\n")
1273 w.WriteString(".type OPENSSL_ia32cap_addr_delta, @object\n")
1274 w.WriteString(".size OPENSSL_ia32cap_addr_delta, 8\n")
1275 w.WriteString("OPENSSL_ia32cap_addr_delta:\n")
1276 w.WriteString(".quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta\n")
1277 }
1278
1279 w.WriteString(".type BORINGSSL_bcm_text_hash, @object\n")
1280 w.WriteString(".size BORINGSSL_bcm_text_hash, 64\n")
1281 w.WriteString("BORINGSSL_bcm_text_hash:\n")
1282 for _, b := range uninitHashValue {
1283 w.WriteString(".byte 0x" + strconv.FormatUint(uint64(b), 16) + "\n")
1284 }
1285
1286 return nil
1287}
1288
1289func parseInputs(inputs []inputFile) error {
1290 for i, input := range inputs {
1291 var contents string
1292
1293 if input.isArchive {
1294 arFile, err := os.Open(input.path)
1295 if err != nil {
1296 return err
1297 }
1298 defer arFile.Close()
1299
1300 ar, err := ParseAR(arFile)
1301 if err != nil {
1302 return err
1303 }
1304
1305 if len(ar) != 1 {
1306 return fmt.Errorf("expected one file in archive, but found %d", len(ar))
1307 }
1308
1309 for _, c := range ar {
1310 contents = string(c)
1311 }
1312 } else {
1313 inBytes, err := ioutil.ReadFile(input.path)
1314 if err != nil {
1315 return err
1316 }
1317
1318 contents = string(inBytes)
1319 }
1320
1321 asm := Asm{Buffer: contents, Pretty: true}
1322 asm.Init()
1323 if err := asm.Parse(); err != nil {
1324 return fmt.Errorf("error while parsing %q: %s", input.path, err)
1325 }
1326 ast := asm.AST()
1327
1328 inputs[i].contents = contents
1329 inputs[i].ast = ast
1330 }
1331
1332 return nil
1333}
1334
1335func main() {
1336 // The .a file, if given, is expected to be an archive of textual
1337 // assembly sources. That's odd, but CMake really wants to create
1338 // archive files so it's the only way that we can make it work.
1339 arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
1340 outFile := flag.String("o", "", "Path to output assembly")
1341
1342 flag.Parse()
1343
1344 if len(*outFile) == 0 {
1345 fmt.Fprintf(os.Stderr, "Must give argument to -o.\n")
1346 os.Exit(1)
1347 }
1348
1349 var inputs []inputFile
1350 if len(*arInput) > 0 {
1351 inputs = append(inputs, inputFile{
1352 path: *arInput,
1353 index: 0,
1354 isArchive: true,
1355 })
1356 }
1357
1358 for i, path := range flag.Args() {
1359 if len(path) == 0 {
1360 continue
1361 }
1362
1363 inputs = append(inputs, inputFile{
1364 path: path,
1365 index: i + 1,
1366 })
1367 }
1368
1369 if err := parseInputs(inputs); err != nil {
1370 fmt.Fprintf(os.Stderr, "%s\n", err)
1371 os.Exit(1)
1372 }
1373
1374 out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
1375 if err != nil {
1376 panic(err)
1377 }
1378 defer out.Close()
1379
1380 if err := transform(out, inputs); err != nil {
1381 fmt.Fprintf(os.Stderr, "%s\n", err)
1382 os.Exit(1)
1383 }
1384}
1385
1386func forEachPath(node *node32, cb func(*node32), rules ...pegRule) {
1387 if node == nil {
1388 return
1389 }
1390
1391 if len(rules) == 0 {
1392 cb(node)
1393 return
1394 }
1395
1396 rule := rules[0]
1397 childRules := rules[1:]
1398
1399 for ; node != nil; node = node.next {
1400 if node.pegRule != rule {
1401 continue
1402 }
1403
1404 if len(childRules) == 0 {
1405 cb(node)
1406 } else {
1407 forEachPath(node.up, cb, childRules...)
1408 }
1409 }
1410}
1411
1412func skipNodes(node *node32, ruleToSkip pegRule) *node32 {
1413 for ; node != nil && node.pegRule == ruleToSkip; node = node.next {
1414 }
1415 return node
1416}
1417
1418func skipWS(node *node32) *node32 {
1419 return skipNodes(node, ruleWS)
1420}
1421
1422func assertNodeType(node *node32, expected pegRule) {
1423 if rule := node.pegRule; rule != expected {
1424 panic(fmt.Sprintf("node was %q, but wanted %q", rul3s[rule], rul3s[expected]))
1425 }
1426}
1427
1428type wrapperFunc func(func())
1429
1430type wrapperStack []wrapperFunc
1431
1432func (w *wrapperStack) do(baseCase func()) {
1433 if len(*w) == 0 {
1434 baseCase()
1435 return
1436 }
1437
1438 wrapper := (*w)[0]
1439 *w = (*w)[1:]
1440 wrapper(func() { w.do(baseCase) })
1441}
1442
1443// localTargetName returns the name of the local target label for a global
1444// symbol named name.
1445func localTargetName(name string) string {
1446 return ".L" + name + "_local_target"
1447}
1448
1449func localEntryName(name string) string {
1450 return ".L" + name + "_local_entry"
1451}
1452
1453func isSynthesized(symbol string) bool {
1454 return strings.HasSuffix(symbol, "_bss_get") ||
1455 symbol == "OPENSSL_ia32cap_get" ||
1456 strings.HasPrefix(symbol, "BORINGSSL_bcm_text_")
1457}
1458
1459func redirectorName(symbol string) string {
1460 return "bcm_redirector_" + symbol
1461}
1462
1463// sectionType returns the type of a section. I.e. a section called “.text.foo”
1464// is a “.text” section.
1465func sectionType(section string) (string, bool) {
1466 if len(section) == 0 || section[0] != '.' {
1467 return "", false
1468 }
1469
1470 i := strings.Index(section[1:], ".")
1471 if i != -1 {
1472 section = section[:i+1]
1473 }
1474
1475 if strings.HasPrefix(section, ".debug_") {
1476 return ".debug", true
1477 }
1478
1479 return section, true
1480}
1481
1482// accessorName returns the name of the accessor function for a BSS symbol
1483// named name.
1484func accessorName(name string) string {
1485 return name + "_bss_get"
1486}
1487
1488func (d *delocation) mapLocalSymbol(symbol string) string {
1489 if d.currentInput.index == 0 {
1490 return symbol
1491 }
1492 return symbol + "_BCM_" + strconv.Itoa(d.currentInput.index)
1493}
1494
1495func detectProcessor(input inputFile) processorType {
1496 for statement := input.ast.up; statement != nil; statement = statement.next {
1497 node := skipNodes(statement.up, ruleWS)
1498 if node == nil || node.pegRule != ruleInstruction {
1499 continue
1500 }
1501
1502 instruction := node.up
1503 instructionName := input.contents[instruction.begin:instruction.end]
1504
1505 switch instructionName {
1506 case "movq", "call", "leaq":
1507 return x86_64
1508 case "addis", "addi", "mflr":
1509 return ppc64le
1510 }
1511 }
1512
1513 panic("processed entire input and didn't recognise any instructions.")
1514}
1515
1516func sortedSet(m map[string]struct{}) []string {
1517 ret := make([]string, 0, len(m))
1518 for key := range m {
1519 ret = append(ret, key)
1520 }
1521 sort.Strings(ret)
1522 return ret
1523}