blob: 593abec3852385c7918fbd60e26e70eabb194118 [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
Robert Sloan8ff03552017-06-14 12:40:58 -070015// delocate performs several transformations of textual assembly code. See
16// crypto/fipsmodule/FIPS.md for an overview.
17package main
18
19import (
20 "errors"
21 "flag"
22 "fmt"
23 "io/ioutil"
24 "os"
25 "sort"
26 "strconv"
27 "strings"
Robert Sloanf573be72018-09-17 15:29:11 -070028
Robert Sloanf068def2018-10-10 18:45:40 -070029 "boringssl.googlesource.com/boringssl/util/ar"
Robert Sloanf573be72018-09-17 15:29:11 -070030 "boringssl.googlesource.com/boringssl/util/fipstools/fipscommon"
Robert Sloan8ff03552017-06-14 12:40:58 -070031)
32
33// inputFile represents a textual assembly file.
34type inputFile struct {
35 path string
36 // index is a unique identifer given to this file. It's used for
37 // mapping local symbols.
38 index int
39 // isArchive indicates that the input should be processed as an ar
40 // file.
41 isArchive bool
42 // contents contains the contents of the file.
43 contents string
44 // ast points to the head of the syntax tree.
45 ast *node32
46}
47
48type stringWriter interface {
49 WriteString(string) (int, error)
50}
51
52type processorType int
53
54const (
55 ppc64le processorType = iota + 1
56 x86_64
57)
58
59// delocation holds the state needed during a delocation operation.
60type delocation struct {
61 processor processorType
62 output stringWriter
63
64 // symbols is the set of symbols defined in the module.
65 symbols map[string]struct{}
66 // localEntrySymbols is the set of symbols with .localentry directives.
67 localEntrySymbols map[string]struct{}
68 // redirectors maps from out-call symbol name to the name of a
69 // redirector function for that symbol. E.g. “memcpy” ->
70 // “bcm_redirector_memcpy”.
71 redirectors map[string]string
72 // bssAccessorsNeeded maps from a BSS symbol name to the symbol that
73 // should be used to reference it. E.g. “P384_data_storage” ->
74 // “P384_data_storage”.
75 bssAccessorsNeeded map[string]string
76 // tocLoaders is a set of symbol names for which TOC helper functions
77 // are required. (ppc64le only.)
78 tocLoaders map[string]struct{}
79 // gotExternalsNeeded is a set of symbol names for which we need
80 // “delta” symbols: symbols that contain the offset from their location
81 // to the memory in question.
82 gotExternalsNeeded map[string]struct{}
83
84 currentInput inputFile
85}
86
87func (d *delocation) contents(node *node32) string {
88 return d.currentInput.contents[node.begin:node.end]
89}
90
91// writeNode writes out an AST node.
92func (d *delocation) writeNode(node *node32) {
93 if _, err := d.output.WriteString(d.contents(node)); err != nil {
94 panic(err)
95 }
96}
97
98func (d *delocation) writeCommentedNode(node *node32) {
99 line := d.contents(node)
100 if _, err := d.output.WriteString("# WAS " + strings.TrimSpace(line) + "\n"); err != nil {
101 panic(err)
102 }
103}
104
105func locateError(err error, with *node32, in inputFile) error {
106 posMap := translatePositions([]rune(in.contents), []int{int(with.begin)})
107 var line int
108 for _, pos := range posMap {
109 line = pos.line
110 }
111
112 return fmt.Errorf("error while processing %q on line %d: %q", in.contents[with.begin:with.end], line, err)
113}
114
115func (d *delocation) processInput(input inputFile) (err error) {
116 d.currentInput = input
117
118 var origStatement *node32
119 defer func() {
120 if err := recover(); err != nil {
121 panic(locateError(fmt.Errorf("%s", err), origStatement, input))
122 }
123 }()
124
125 for statement := input.ast.up; statement != nil; statement = statement.next {
126 assertNodeType(statement, ruleStatement)
127 origStatement = statement
128
129 node := skipWS(statement.up)
130 if node == nil {
131 d.writeNode(statement)
132 continue
133 }
134
135 switch node.pegRule {
136 case ruleGlobalDirective, ruleComment, ruleLocationDirective:
137 d.writeNode(statement)
138 case ruleDirective:
139 statement, err = d.processDirective(statement, node.up)
140 case ruleLabelContainingDirective:
141 statement, err = d.processLabelContainingDirective(statement, node.up)
142 case ruleLabel:
143 statement, err = d.processLabel(statement, node.up)
144 case ruleInstruction:
145 switch d.processor {
146 case x86_64:
147 statement, err = d.processIntelInstruction(statement, node.up)
148 case ppc64le:
149 statement, err = d.processPPCInstruction(statement, node.up)
150 default:
151 panic("unknown processor")
152 }
153 default:
154 panic(fmt.Sprintf("unknown top-level statement type %q", rul3s[node.pegRule]))
155 }
156
157 if err != nil {
158 return locateError(err, origStatement, input)
159 }
160 }
161
162 return nil
163}
164
165func (d *delocation) processDirective(statement, directive *node32) (*node32, error) {
166 assertNodeType(directive, ruleDirectiveName)
167 directiveName := d.contents(directive)
168
169 var args []string
170 forEachPath(directive, func(arg *node32) {
171 // If the argument is a quoted string, use the raw contents.
172 // (Note that this doesn't unescape the string, but that's not
173 // needed so far.
174 if arg.up != nil {
175 arg = arg.up
176 assertNodeType(arg, ruleQuotedArg)
177 if arg.up == nil {
178 args = append(args, "")
179 return
180 }
181 arg = arg.up
182 assertNodeType(arg, ruleQuotedText)
183 }
184 args = append(args, d.contents(arg))
185 }, ruleArgs, ruleArg)
186
187 switch directiveName {
188 case "comm", "lcomm":
189 if len(args) < 1 {
190 return nil, errors.New("comm directive has no arguments")
191 }
192 d.bssAccessorsNeeded[args[0]] = args[0]
193 d.writeNode(statement)
194
195 case "data":
196 // ASAN and some versions of MSAN are adding a .data section,
197 // and adding references to symbols within it to the code. We
198 // will have to work around this in the future.
199 return nil, errors.New(".data section found in module")
200
201 case "section":
202 section := args[0]
203
204 if section == ".data.rel.ro" {
205 // In a normal build, this is an indication of a
206 // problem but any references from the module to this
207 // section will result in a relocation and thus will
208 // break the integrity check. ASAN can generate these
209 // sections and so we will likely have to work around
210 // that in the future.
211 return nil, errors.New(".data.rel.ro section found in module")
212 }
213
214 sectionType, ok := sectionType(section)
215 if !ok {
216 // Unknown sections are permitted in order to be robust
217 // to different compiler modes.
218 d.writeNode(statement)
219 break
220 }
221
222 switch sectionType {
223 case ".rodata", ".text":
224 // Move .rodata to .text so it may be accessed without
225 // a relocation. GCC with -fmerge-constants will place
226 // strings into separate sections, so we move all
227 // sections named like .rodata. Also move .text.startup
228 // so the self-test function is also in the module.
229 d.writeCommentedNode(statement)
230 d.output.WriteString(".text\n")
231
232 case ".data":
233 // See above about .data
234 return nil, errors.New(".data section found in module")
235
236 case ".init_array", ".fini_array", ".ctors", ".dtors":
237 // init_array/ctors/dtors contains function
238 // pointers to constructor/destructor
239 // functions. These contain relocations, but
240 // they're in a different section anyway.
241 d.writeNode(statement)
242 break
243
244 case ".debug", ".note", ".toc":
245 d.writeNode(statement)
246 break
247
248 case ".bss":
249 d.writeNode(statement)
250 return d.handleBSS(statement)
251 }
252
253 default:
254 d.writeNode(statement)
255 }
256
257 return statement, nil
258}
259
260func (d *delocation) processLabelContainingDirective(statement, directive *node32) (*node32, error) {
261 // The symbols within directives need to be mapped so that local
262 // symbols in two different .s inputs don't collide.
263 changed := false
264 assertNodeType(directive, ruleLabelContainingDirectiveName)
265 name := d.contents(directive)
266
267 node := directive.next
268 assertNodeType(node, ruleWS)
269
270 node = node.next
271 assertNodeType(node, ruleSymbolArgs)
272
273 var args []string
274 for node = skipWS(node.up); node != nil; node = skipWS(node.next) {
275 assertNodeType(node, ruleSymbolArg)
276 arg := node.up
277 var mapped string
278
279 for term := arg; term != nil; term = term.next {
280 if term.pegRule != ruleLocalSymbol {
281 mapped += d.contents(term)
282 continue
283 }
284
285 oldSymbol := d.contents(term)
286 newSymbol := d.mapLocalSymbol(oldSymbol)
287 if newSymbol != oldSymbol {
288 changed = true
289 }
290
291 mapped += newSymbol
292 }
293
294 args = append(args, mapped)
295 }
296
297 if !changed {
298 d.writeNode(statement)
299 } else {
300 d.writeCommentedNode(statement)
301 d.output.WriteString("\t" + name + "\t" + strings.Join(args, ", ") + "\n")
302 }
303
304 if name == ".localentry" {
305 d.output.WriteString(localEntryName(args[0]) + ":\n")
306 }
307
308 return statement, nil
309}
310
311func (d *delocation) processLabel(statement, label *node32) (*node32, error) {
312 symbol := d.contents(label)
313
314 switch label.pegRule {
315 case ruleLocalLabel:
316 d.output.WriteString(symbol + ":\n")
317 case ruleLocalSymbol:
318 // symbols need to be mapped so that local symbols from two
319 // different .s inputs don't collide.
320 d.output.WriteString(d.mapLocalSymbol(symbol) + ":\n")
321 case ruleSymbolName:
322 d.output.WriteString(localTargetName(symbol) + ":\n")
323 d.writeNode(statement)
324 default:
325 return nil, fmt.Errorf("unknown label type %q", rul3s[label.pegRule])
326 }
327
328 return statement, nil
329}
330
331// instructionArgs collects all the arguments to an instruction.
332func instructionArgs(node *node32) (argNodes []*node32) {
333 for node = skipWS(node); node != nil; node = skipWS(node.next) {
334 assertNodeType(node, ruleInstructionArg)
335 argNodes = append(argNodes, node.up)
336 }
337
338 return argNodes
339}
340
341/* ppc64le
342
Robert Sloane56da3e2017-06-26 08:26:42 -0700343[PABI]: “64-Bit ELF V2 ABI Specification. Power Architecture.” March 21st,
Robert Sloan8ff03552017-06-14 12:40:58 -0700344 2017
345
346(Also useful is “Power ISA Version 2.07 B”. Note that version three of that
347document is /not/ good as that's POWER9 specific.)
348
349ppc64le doesn't have IP-relative addressing and does a lot to work around this.
350Rather than reference a PLT and GOT direction, it has a single structure called
351the TOC (Table Of Contents). Within the TOC is the contents of .rodata, .data,
352.got, .plt, .bss, etc sections [PABI;3.3].
353
354A pointer to the TOC is maintained in r2 and the following pattern is used to
355load the address of an element into a register:
356
357 addis <address register>, 2, foo@toc@ha
358 addi <address register>, <address register>, foo@toc@l
359
360The “addis” instruction shifts a signed constant left 16 bits and adds the
361result to its second argument, saving the result in the first argument. The
362“addi” instruction does the same, but without shifting. Thus the “@toc@ha"
363suffix on a symbol means “the top 16 bits of the TOC offset” and “@toc@l” means
364“the bottom 16 bits of the offset”. However, note that both values are signed,
365thus offsets in the top half of a 64KB chunk will have an @ha value that's one
366greater than expected and a negative @l value.
367
368The TOC is specific to a “module” (basically an executable or shared object).
369This means that there's not a single TOC in a process and that r2 needs to
370change as control moves between modules. Thus functions have two entry points:
371the “global” entry point and the “local” entry point. Jumps from within the
372same module can use the local entry while jumps from other modules must use the
373global entry. The global entry establishes the correct value of r2 before
374running the function and the local entry skips that code.
375
376The global entry point for a function is defined by its label. The local entry
377is a power-of-two number of bytes from the global entry, set by the
378“.localentry” directive. (ppc64le instructions are always 32 bits, so an offset
379of 1 or 2 bytes is treated as an offset of zero.)
380
381In order to help the global entry code set r2 to point to the local TOC, r12 is
382set to the address of the global entry point when called [PABI;2.2.1.1]. Thus
383the global entry will typically use an addis+addi pair to add a known offset to
384r12 and store it in r2. For example:
385
386foo:
387 addis 2, 12, .TOC. - foo@ha
388 addi 2, 2, .TOC. - foo@l
389
390(It's worth noting that the '@' operator binds very loosely, so the 3rd
391arguments parse as (.TOC. - foo)@ha and (.TOC. - foo)@l.)
392
393When calling a function, the compiler doesn't know whether that function is in
394the same module or not. Thus it doesn't know whether r12 needs to be set nor
395whether r2 will be clobbered on return. Rather than always assume the worst,
396the linker fixes stuff up once it knows that a call is going out of module:
397
398Firstly, calling, say, memcpy (which we assume to be in a different module)
399won't actually jump directly to memcpy, or even a PLT resolution function.
400It'll call a synthesised function that:
401 a) saves r2 in the caller's stack frame
402 b) loads the address of memcpy@PLT into r12
403 c) jumps to r12.
404
405As this synthesised function loads memcpy@PLT, a call to memcpy from the
406compiled code just references “memcpy” directly, not “memcpy@PLT”.
407
408Since it jumps directly to memcpy@PLT, it can't restore r2 on return. Thus
409calls must be followed by a nop. If the call ends up going out-of-module, the
410linker will rewrite that nop to load r2 from the stack.
411
412Speaking of the stack, the stack pointer is kept in r1 and there's a 288-byte
413red-zone. The format of the stack frame is defined [PABI;2.2.2] and must be
414followed as called functions will write into their parent's stack frame. For
415example, the synthesised out-of-module trampolines will save r2 24 bytes into
416the caller's frame and all non-leaf functions save the return address 16 bytes
417into the caller's frame.
418
419A final point worth noting: some RISC ISAs have r0 wired to zero: all reads
420result in zero and all writes are discarded. POWER does something a little like
421that, but r0 is only special in certain argument positions for certain
422instructions. You just have to read the manual to know which they are.
423
424
425Delocation is easier than Intel because there's just TOC references, but it's
426also harder because there's no IP-relative addressing.
427
428Jumps are IP-relative however, and have a 24-bit immediate value. So we can
429jump to functions that set a register to the needed value. (r3 is the
430return-value register and so that's what is generally used here.) */
431
432// isPPC64LEAPair recognises an addis+addi pair that's adding the offset of
433// source to relative and writing the result to target.
434func (d *delocation) isPPC64LEAPair(statement *node32) (target, source, relative string, ok bool) {
435 instruction := skipWS(statement.up).up
436 assertNodeType(instruction, ruleInstructionName)
437 name1 := d.contents(instruction)
438 args1 := instructionArgs(instruction.next)
439
440 statement = statement.next
441 instruction = skipWS(statement.up).up
442 assertNodeType(instruction, ruleInstructionName)
443 name2 := d.contents(instruction)
444 args2 := instructionArgs(instruction.next)
445
446 if name1 != "addis" ||
447 len(args1) != 3 ||
448 name2 != "addi" ||
449 len(args2) != 3 {
450 return "", "", "", false
451 }
452
453 target = d.contents(args1[0])
454 relative = d.contents(args1[1])
455 source1 := d.contents(args1[2])
456 source2 := d.contents(args2[2])
457
458 if !strings.HasSuffix(source1, "@ha") ||
459 !strings.HasSuffix(source2, "@l") ||
460 source1[:len(source1)-3] != source2[:len(source2)-2] ||
461 d.contents(args2[0]) != target ||
462 d.contents(args2[1]) != target {
463 return "", "", "", false
464 }
465
466 source = source1[:len(source1)-3]
467 ok = true
468 return
469}
470
471// establishTOC writes the global entry prelude for a function. The standard
472// prelude involves relocations so this version moves the relocation outside
473// the integrity-checked area.
474func establishTOC(w stringWriter) {
475 w.WriteString("999:\n")
476 w.WriteString("\taddis 2, 12, .LBORINGSSL_external_toc-999b@ha\n")
477 w.WriteString("\taddi 2, 2, .LBORINGSSL_external_toc-999b@l\n")
478 w.WriteString("\tld 12, 0(2)\n")
479 w.WriteString("\tadd 2, 2, 12\n")
480}
481
482// loadTOCFuncName returns the name of a synthesized function that sets r3 to
483// the value of “symbol+offset”.
484func loadTOCFuncName(symbol, offset string) string {
485 symbol = strings.Replace(symbol, ".", "_dot_", -1)
486 ret := ".Lbcm_loadtoc_" + symbol
487 if len(offset) != 0 {
488 offset = strings.Replace(offset, "+", "_plus_", -1)
489 offset = strings.Replace(offset, "-", "_minus_", -1)
490 ret += "_" + offset
491 }
492 return ret
493}
494
495func (d *delocation) loadFromTOC(w stringWriter, symbol, offset, dest string) wrapperFunc {
496 d.tocLoaders[symbol+"\x00"+offset] = struct{}{}
497
498 return func(k func()) {
499 w.WriteString("\taddi 1, 1, -288\n") // Clear the red zone.
500 w.WriteString("\tmflr " + dest + "\n") // Stash the link register.
501 w.WriteString("\tstd " + dest + ", -8(1)\n")
502 // The TOC loader will use r3, so stash it if necessary.
503 if dest != "3" {
504 w.WriteString("\tstd 3, -16(1)\n")
505 }
506
507 // Because loadTOCFuncName returns a “.L” name, we don't need a
508 // nop after this call.
509 w.WriteString("\tbl " + loadTOCFuncName(symbol, offset) + "\n")
510
511 // Cycle registers around. We need r3 -> destReg, -8(1) ->
512 // lr and, optionally, -16(1) -> r3.
513 w.WriteString("\tstd 3, -24(1)\n")
514 w.WriteString("\tld 3, -8(1)\n")
515 w.WriteString("\tmtlr 3\n")
516 w.WriteString("\tld " + dest + ", -24(1)\n")
517 if dest != "3" {
518 w.WriteString("\tld 3, -16(1)\n")
519 }
520 w.WriteString("\taddi 1, 1, 288\n")
521
522 k()
523 }
524}
525
526func (d *delocation) gatherOffsets(symRef *node32, offsets string) (*node32, string) {
527 for symRef != nil && symRef.pegRule == ruleOffset {
528 offset := d.contents(symRef)
529 if offset[0] != '+' && offset[0] != '-' {
530 offset = "+" + offset
531 }
532 offsets = offsets + offset
533 symRef = symRef.next
534 }
535 return symRef, offsets
536}
537
538func (d *delocation) parseMemRef(memRef *node32) (symbol, offset, section string, didChange, symbolIsLocal bool, nextRef *node32) {
539 if memRef.pegRule != ruleSymbolRef {
540 return "", "", "", false, false, memRef
541 }
542
543 symRef := memRef.up
544 nextRef = memRef.next
545
546 // (Offset* '+')?
547 symRef, offset = d.gatherOffsets(symRef, offset)
548
549 // (LocalSymbol / SymbolName)
550 symbol = d.contents(symRef)
551 if symRef.pegRule == ruleLocalSymbol {
552 symbolIsLocal = true
553 mapped := d.mapLocalSymbol(symbol)
554 if mapped != symbol {
555 symbol = mapped
556 didChange = true
557 }
558 }
559 symRef = symRef.next
560
561 // Offset*
562 symRef, offset = d.gatherOffsets(symRef, offset)
563
564 // ('@' Section / Offset*)?
565 if symRef != nil {
566 assertNodeType(symRef, ruleSection)
567 section = d.contents(symRef)
568 symRef = symRef.next
569
570 symRef, offset = d.gatherOffsets(symRef, offset)
571 }
572
573 if symRef != nil {
574 panic(fmt.Sprintf("unexpected token in SymbolRef: %q", rul3s[symRef.pegRule]))
575 }
576
577 return
578}
579
580func (d *delocation) processPPCInstruction(statement, instruction *node32) (*node32, error) {
581 assertNodeType(instruction, ruleInstructionName)
582 instructionName := d.contents(instruction)
583 isBranch := instructionName[0] == 'b'
584
585 argNodes := instructionArgs(instruction.next)
586
587 var wrappers wrapperStack
588 var args []string
589 changed := false
590
591Args:
592 for i, arg := range argNodes {
593 fullArg := arg
594 isIndirect := false
595
596 if arg.pegRule == ruleIndirectionIndicator {
597 arg = arg.next
598 isIndirect = true
599 }
600
601 switch arg.pegRule {
602 case ruleRegisterOrConstant, ruleLocalLabelRef:
603 args = append(args, d.contents(fullArg))
604
605 case ruleTOCRefLow:
606 return nil, errors.New("Found low TOC reference outside preamble pattern")
607
608 case ruleTOCRefHigh:
609 target, _, relative, ok := d.isPPC64LEAPair(statement)
610 if !ok {
611 return nil, errors.New("Found high TOC reference outside preamble pattern")
612 }
613
614 if relative != "12" {
615 return nil, fmt.Errorf("preamble is relative to %q, not r12", relative)
616 }
617
618 if target != "2" {
619 return nil, fmt.Errorf("preamble is setting %q, not r2", target)
620 }
621
622 statement = statement.next
623 establishTOC(d.output)
624 instructionName = ""
625 changed = true
626 break Args
627
628 case ruleMemoryRef:
629 symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
630 changed = didChange
631
632 if len(symbol) > 0 {
633 if _, localEntrySymbol := d.localEntrySymbols[symbol]; localEntrySymbol && isBranch {
634 symbol = localEntryName(symbol)
635 changed = true
636 } else if _, knownSymbol := d.symbols[symbol]; knownSymbol {
637 symbol = localTargetName(symbol)
638 changed = true
639 } else if !symbolIsLocal && !isSynthesized(symbol) && len(section) == 0 {
640 changed = true
641 d.redirectors[symbol] = redirectorName(symbol)
642 symbol = redirectorName(symbol)
Robert Sloane56da3e2017-06-26 08:26:42 -0700643 // TODO(davidben): This should sanity-check the next
644 // instruction is a nop and ideally remove it.
645 wrappers = append(wrappers, func(k func()) {
646 k()
647 // Like the linker's PLT stubs, redirector functions
648 // expect callers to restore r2.
649 d.output.WriteString("\tld 2, 24(1)\n")
650 })
Robert Sloan8ff03552017-06-14 12:40:58 -0700651 }
652 }
653
654 switch section {
655 case "":
656
657 case "tls":
658 // This section identifier just tells the
659 // assembler to use r13, the pointer to the
660 // thread-local data [PABI;3.7.3.3].
661
662 case "toc@ha":
663 // Delete toc@ha instructions. Per
664 // [PABI;3.6.3], the linker is allowed to erase
665 // toc@ha instructions. We take advantage of
666 // this by unconditionally erasing the toc@ha
667 // instructions and doing the full lookup when
668 // processing toc@l.
669 //
670 // Note that any offset here applies before @ha
671 // and @l. That is, 42+foo@toc@ha is
672 // #ha(42+foo-.TOC.), not 42+#ha(foo-.TOC.). Any
673 // corresponding toc@l references are required
674 // by the ABI to have the same offset. The
675 // offset will be incorporated in full when
676 // those are processed.
677 if instructionName != "addis" || len(argNodes) != 3 || i != 2 || args[1] != "2" {
678 return nil, errors.New("can't process toc@ha reference")
679 }
680 changed = true
681 instructionName = ""
682 break Args
683
684 case "toc@l":
685 // Per [PAB;3.6.3], this instruction must take
686 // as input a register which was the output of
687 // a toc@ha computation and compute the actual
688 // address of some symbol. The toc@ha
689 // computation was elided, so we ignore that
690 // input register and compute the address
691 // directly.
692 changed = true
693
694 // For all supported toc@l instructions, the
695 // destination register is the first argument.
696 destReg := args[0]
697
698 wrappers = append(wrappers, d.loadFromTOC(d.output, symbol, offset, destReg))
699 switch instructionName {
700 case "addi":
701 // The original instruction was:
702 // addi destReg, tocHaReg, offset+symbol@toc@l
703 instructionName = ""
704
705 case "ld", "lhz", "lwz":
706 // The original instruction was:
707 // l?? destReg, offset+symbol@toc@l(tocHaReg)
708 //
709 // We transform that into the
710 // equivalent dereference of destReg:
711 // l?? destReg, 0(destReg)
712 origInstructionName := instructionName
713 instructionName = ""
714
715 assertNodeType(memRef, ruleBaseIndexScale)
716 assertNodeType(memRef.up, ruleRegisterOrConstant)
717 if memRef.next != nil || memRef.up.next != nil {
718 return nil, errors.New("expected single register in BaseIndexScale for ld argument")
719 }
720
721 baseReg := destReg
722 if baseReg == "0" {
723 // Register zero is special as the base register for a load.
724 // Avoid it by spilling and using r3 instead.
725 baseReg = "3"
726 wrappers = append(wrappers, func(k func()) {
Robert Sloane56da3e2017-06-26 08:26:42 -0700727 d.output.WriteString("\taddi 1, 1, -288\n") // Clear the red zone.
Robert Sloan8ff03552017-06-14 12:40:58 -0700728 d.output.WriteString("\tstd " + baseReg + ", -8(1)\n")
729 d.output.WriteString("\tmr " + baseReg + ", " + destReg + "\n")
730 k()
731 d.output.WriteString("\tld " + baseReg + ", -8(1)\n")
Robert Sloane56da3e2017-06-26 08:26:42 -0700732 d.output.WriteString("\taddi 1, 1, 288\n") // Clear the red zone.
Robert Sloan8ff03552017-06-14 12:40:58 -0700733 })
734 }
735
736 wrappers = append(wrappers, func(k func()) {
737 d.output.WriteString("\t" + origInstructionName + " " + destReg + ", 0(" + baseReg + ")\n")
738 })
739 default:
740 return nil, fmt.Errorf("can't process TOC argument to %q", instructionName)
741 }
742
743 default:
744 return nil, fmt.Errorf("Unknown section type %q", section)
745 }
746
747 argStr := ""
748 if isIndirect {
749 argStr += "*"
750 }
751 argStr += symbol
752 if len(offset) > 0 {
753 argStr += offset
754 }
755 if len(section) > 0 {
756 argStr += "@"
757 argStr += section
758 }
759
760 for ; memRef != nil; memRef = memRef.next {
761 argStr += d.contents(memRef)
762 }
763
764 args = append(args, argStr)
765
766 default:
767 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
768 }
769 }
770
771 if changed {
772 d.writeCommentedNode(statement)
773
774 var replacement string
775 if len(instructionName) > 0 {
776 replacement = "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
777 }
778
779 wrappers.do(func() {
780 d.output.WriteString(replacement)
781 })
782 } else {
783 d.writeNode(statement)
784 }
785
786 return statement, nil
787}
788
789/* Intel */
790
791type instructionType int
792
793const (
794 instrPush instructionType = iota
795 instrMove
Robert Sloan0da43952018-01-03 15:13:14 -0800796 // instrTransformingMove is essentially a move, but it performs some
797 // transformation of the data during the process.
798 instrTransformingMove
Robert Sloan8ff03552017-06-14 12:40:58 -0700799 instrJump
800 instrConditionalMove
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100801 // instrCombine merges the source and destination in some fashion, for example
802 // a 2-operand bitwise operation.
803 instrCombine
Robert Sloan89678152019-03-12 14:24:00 -0700804 // instrThreeArg merges two sources into a destination in some fashion.
805 instrThreeArg
Robert Sloan8ff03552017-06-14 12:40:58 -0700806 instrOther
807)
808
809func classifyInstruction(instr string, args []*node32) instructionType {
810 switch instr {
811 case "push", "pushq":
812 if len(args) == 1 {
813 return instrPush
814 }
815
Robert Sloan2e9e66a2017-09-25 09:08:29 -0700816 case "mov", "movq", "vmovq", "movsd", "vmovsd":
Robert Sloan8ff03552017-06-14 12:40:58 -0700817 if len(args) == 2 {
818 return instrMove
819 }
820
821 case "cmovneq", "cmoveq":
822 if len(args) == 2 {
823 return instrConditionalMove
824 }
825
826 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":
827 if len(args) == 1 {
828 return instrJump
829 }
Robert Sloan0da43952018-01-03 15:13:14 -0800830
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100831 case "orq", "andq", "xorq":
832 if len(args) == 2 {
833 return instrCombine
834 }
835
Robert Sloan89678152019-03-12 14:24:00 -0700836 case "sarxq", "shlxq", "shrxq":
837 if len(args) == 3 {
838 return instrThreeArg
839 }
840
Robert Sloan0da43952018-01-03 15:13:14 -0800841 case "vpbroadcastq":
842 if len(args) == 2 {
843 return instrTransformingMove
844 }
Robert Sloan8ff03552017-06-14 12:40:58 -0700845 }
846
847 return instrOther
848}
849
850func push(w stringWriter) wrapperFunc {
851 return func(k func()) {
852 w.WriteString("\tpushq %rax\n")
853 k()
854 w.WriteString("\txchg %rax, (%rsp)\n")
855 }
856}
857
858func (d *delocation) loadFromGOT(w stringWriter, destination, symbol, section string, redzoneCleared bool) wrapperFunc {
859 d.gotExternalsNeeded[symbol+"@"+section] = struct{}{}
860
861 return func(k func()) {
862 if !redzoneCleared {
863 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
864 }
865 w.WriteString("\tpushf\n")
866 w.WriteString(fmt.Sprintf("\tleaq %s_%s_external(%%rip), %s\n", symbol, section, destination))
867 w.WriteString(fmt.Sprintf("\taddq (%s), %s\n", destination, destination))
868 w.WriteString(fmt.Sprintf("\tmovq (%s), %s\n", destination, destination))
869 w.WriteString("\tpopf\n")
870 if !redzoneCleared {
871 w.WriteString("\tleaq\t128(%rsp), %rsp\n")
872 }
873 }
874}
875
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100876func saveFlags(w stringWriter, redzoneCleared bool) wrapperFunc {
Robert Sloan8ff03552017-06-14 12:40:58 -0700877 return func(k func()) {
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100878 if !redzoneCleared {
879 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
880 defer w.WriteString("\tleaq 128(%rsp), %rsp\n")
881 }
882 w.WriteString("\tpushfq\n")
Robert Sloan8ff03552017-06-14 12:40:58 -0700883 k()
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100884 w.WriteString("\tpopfq\n")
Robert Sloan8ff03552017-06-14 12:40:58 -0700885 }
886}
887
Robert Sloan89678152019-03-12 14:24:00 -0700888func saveRegister(w stringWriter, avoidRegs []string) (wrapperFunc, string) {
889 candidates := []string{"%rax", "%rbx", "%rcx", "%rdx"}
890
891 var reg string
892NextCandidate:
893 for _, candidate := range candidates {
894 for _, avoid := range avoidRegs {
895 if candidate == avoid {
896 continue NextCandidate
897 }
898 }
899
900 reg = candidate
901 break
902 }
903
904 if len(reg) == 0 {
905 panic("too many excluded registers")
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100906 }
907
908 return func(k func()) {
909 w.WriteString("\tleaq -128(%rsp), %rsp\n") // Clear the red zone.
910 w.WriteString("\tpushq " + reg + "\n")
911 k()
912 w.WriteString("\tpopq " + reg + "\n")
913 w.WriteString("\tleaq 128(%rsp), %rsp\n")
914 }, reg
915}
916
917func moveTo(w stringWriter, target string, isAVX bool, source string) wrapperFunc {
Robert Sloan8ff03552017-06-14 12:40:58 -0700918 return func(k func()) {
919 k()
Robert Sloan2e9e66a2017-09-25 09:08:29 -0700920 prefix := ""
921 if isAVX {
922 prefix = "v"
923 }
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100924 w.WriteString("\t" + prefix + "movq " + source + ", " + target + "\n")
Robert Sloan8ff03552017-06-14 12:40:58 -0700925 }
926}
927
Robert Sloan0da43952018-01-03 15:13:14 -0800928func finalTransform(w stringWriter, transformInstruction, reg string) wrapperFunc {
929 return func(k func()) {
930 k()
931 w.WriteString("\t" + transformInstruction + " " + reg + ", " + reg + "\n")
932 }
933}
934
Adam Vartanianbfcf3a72018-08-10 14:55:24 +0100935func combineOp(w stringWriter, instructionName, source, dest string) wrapperFunc {
936 return func(k func()) {
937 k()
938 w.WriteString("\t" + instructionName + " " + source + ", " + dest + "\n")
939 }
940}
941
Robert Sloan89678152019-03-12 14:24:00 -0700942func threeArgCombineOp(w stringWriter, instructionName, source1, source2, dest string) wrapperFunc {
943 return func(k func()) {
944 k()
945 w.WriteString("\t" + instructionName + " " + source1 + ", " + source2 + ", " + dest + "\n")
946 }
947}
948
Robert Sloan8ff03552017-06-14 12:40:58 -0700949func isValidLEATarget(reg string) bool {
950 return !strings.HasPrefix(reg, "%xmm") && !strings.HasPrefix(reg, "%ymm") && !strings.HasPrefix(reg, "%zmm")
951}
952
953func undoConditionalMove(w stringWriter, instr string) wrapperFunc {
954 var invertedCondition string
955
956 switch instr {
957 case "cmoveq":
958 invertedCondition = "ne"
959 case "cmovneq":
960 invertedCondition = "e"
961 default:
962 panic(fmt.Sprintf("don't know how to handle conditional move instruction %q", instr))
963 }
964
965 return func(k func()) {
966 w.WriteString("\tj" + invertedCondition + " 999f\n")
967 k()
968 w.WriteString("999:\n")
969 }
970}
971
972func (d *delocation) isRIPRelative(node *node32) bool {
973 return node != nil && node.pegRule == ruleBaseIndexScale && d.contents(node) == "(%rip)"
974}
975
976func (d *delocation) processIntelInstruction(statement, instruction *node32) (*node32, error) {
977 assertNodeType(instruction, ruleInstructionName)
978 instructionName := d.contents(instruction)
979
980 argNodes := instructionArgs(instruction.next)
981
982 var wrappers wrapperStack
983 var args []string
984 changed := false
985
986Args:
987 for i, arg := range argNodes {
988 fullArg := arg
989 isIndirect := false
990
991 if arg.pegRule == ruleIndirectionIndicator {
992 arg = arg.next
993 isIndirect = true
994 }
995
996 switch arg.pegRule {
997 case ruleRegisterOrConstant, ruleLocalLabelRef:
998 args = append(args, d.contents(fullArg))
999
1000 case ruleMemoryRef:
1001 symbol, offset, section, didChange, symbolIsLocal, memRef := d.parseMemRef(arg.up)
1002 changed = didChange
1003
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001004 if symbol == "OPENSSL_ia32cap_P" && section == "" {
1005 if instructionName != "leaq" {
1006 return nil, fmt.Errorf("non-leaq instruction %q referenced OPENSSL_ia32cap_P directly", instructionName)
Robert Sloan8ff03552017-06-14 12:40:58 -07001007 }
1008
1009 if i != 0 || len(argNodes) != 2 || !d.isRIPRelative(memRef) || len(offset) > 0 {
1010 return nil, fmt.Errorf("invalid OPENSSL_ia32cap_P reference in instruction %q", instructionName)
1011 }
1012
1013 target := argNodes[1]
1014 assertNodeType(target, ruleRegisterOrConstant)
1015 reg := d.contents(target)
1016
1017 if !strings.HasPrefix(reg, "%r") {
1018 return nil, fmt.Errorf("tried to load OPENSSL_ia32cap_P into %q, which is not a standard register.", reg)
1019 }
1020
1021 changed = true
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001022
1023 // Flag-altering instructions (i.e. addq) are going to be used so the
1024 // flags need to be preserved.
1025 wrappers = append(wrappers, saveFlags(d.output, false /* Red Zone not yet cleared */))
1026
Robert Sloan8ff03552017-06-14 12:40:58 -07001027 wrappers = append(wrappers, func(k func()) {
Robert Sloan8ff03552017-06-14 12:40:58 -07001028 d.output.WriteString("\tleaq\tOPENSSL_ia32cap_addr_delta(%rip), " + reg + "\n")
1029 d.output.WriteString("\taddq\t(" + reg + "), " + reg + "\n")
Robert Sloan8ff03552017-06-14 12:40:58 -07001030 })
1031
1032 break Args
1033 }
1034
1035 switch section {
1036 case "":
1037 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
1038 symbol = localTargetName(symbol)
1039 changed = true
1040 }
1041
1042 case "PLT":
1043 if classifyInstruction(instructionName, argNodes) != instrJump {
1044 return nil, fmt.Errorf("Cannot rewrite PLT reference for non-jump instruction %q", instructionName)
1045 }
1046
1047 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
1048 symbol = localTargetName(symbol)
1049 changed = true
1050 } else if !symbolIsLocal && !isSynthesized(symbol) {
1051 // Unknown symbol via PLT is an
1052 // out-call from the module, e.g.
1053 // memcpy.
1054 d.redirectors[symbol+"@"+section] = redirectorName(symbol)
1055 symbol = redirectorName(symbol)
1056 }
1057
1058 changed = true
1059
1060 case "GOTPCREL":
1061 if len(offset) > 0 {
1062 return nil, errors.New("loading from GOT with offset is unsupported")
1063 }
Robert Sloan8ff03552017-06-14 12:40:58 -07001064 if !d.isRIPRelative(memRef) {
1065 return nil, errors.New("GOT access must be IP-relative")
1066 }
1067
1068 useGOT := false
1069 if _, knownSymbol := d.symbols[symbol]; knownSymbol {
1070 symbol = localTargetName(symbol)
1071 changed = true
1072 } else if !isSynthesized(symbol) {
1073 useGOT = true
1074 }
1075
Robert Sloan89678152019-03-12 14:24:00 -07001076 classification := classifyInstruction(instructionName, argNodes)
1077 if classification != instrThreeArg && i != 0 {
1078 return nil, errors.New("GOT access must be source operand")
1079 }
1080
Robert Sloan8ff03552017-06-14 12:40:58 -07001081 // Reduce the instruction to movq symbol@GOTPCREL, targetReg.
1082 var targetReg string
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001083 var redzoneCleared bool
Robert Sloan89678152019-03-12 14:24:00 -07001084 switch classification {
Robert Sloan8ff03552017-06-14 12:40:58 -07001085 case instrPush:
1086 wrappers = append(wrappers, push(d.output))
1087 targetReg = "%rax"
1088 case instrConditionalMove:
1089 wrappers = append(wrappers, undoConditionalMove(d.output, instructionName))
1090 fallthrough
1091 case instrMove:
1092 assertNodeType(argNodes[1], ruleRegisterOrConstant)
1093 targetReg = d.contents(argNodes[1])
Robert Sloan0da43952018-01-03 15:13:14 -08001094 case instrTransformingMove:
1095 assertNodeType(argNodes[1], ruleRegisterOrConstant)
1096 targetReg = d.contents(argNodes[1])
1097 wrappers = append(wrappers, finalTransform(d.output, instructionName, targetReg))
1098 if isValidLEATarget(targetReg) {
Robert Sloan978112c2018-01-22 12:53:01 -08001099 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 -08001100 }
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001101 case instrCombine:
1102 targetReg = d.contents(argNodes[1])
1103 if !isValidLEATarget(targetReg) {
1104 return nil, fmt.Errorf("cannot handle combining instructions targeting non-general registers")
1105 }
Robert Sloan89678152019-03-12 14:24:00 -07001106 saveRegWrapper, tempReg := saveRegister(d.output, []string{targetReg})
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001107 redzoneCleared = true
1108 wrappers = append(wrappers, saveRegWrapper)
1109
1110 wrappers = append(wrappers, combineOp(d.output, instructionName, tempReg, targetReg))
1111 targetReg = tempReg
Robert Sloan89678152019-03-12 14:24:00 -07001112 case instrThreeArg:
1113 if n := len(argNodes); n != 3 {
1114 return nil, fmt.Errorf("three-argument instruction has %d arguments", n)
1115 }
1116 if i != 0 && i != 1 {
1117 return nil, errors.New("GOT access must be from soure operand")
1118 }
1119 targetReg = d.contents(argNodes[2])
1120
1121 otherSource := d.contents(argNodes[1])
1122 if i == 1 {
1123 otherSource = d.contents(argNodes[0])
1124 }
1125
1126 saveRegWrapper, tempReg := saveRegister(d.output, []string{targetReg, otherSource})
1127 redzoneCleared = true
1128 wrappers = append(wrappers, saveRegWrapper)
1129
1130 if i == 0 {
1131 wrappers = append(wrappers, threeArgCombineOp(d.output, instructionName, tempReg, otherSource, targetReg))
1132 } else {
1133 wrappers = append(wrappers, threeArgCombineOp(d.output, instructionName, otherSource, tempReg, targetReg))
1134 }
1135 targetReg = tempReg
Robert Sloan8ff03552017-06-14 12:40:58 -07001136 default:
1137 return nil, fmt.Errorf("Cannot rewrite GOTPCREL reference for instruction %q", instructionName)
1138 }
1139
Robert Sloan8ff03552017-06-14 12:40:58 -07001140 if !isValidLEATarget(targetReg) {
1141 // Sometimes the compiler will load from the GOT to an
1142 // XMM register, which is not a valid target of an LEA
1143 // instruction.
Robert Sloan89678152019-03-12 14:24:00 -07001144 saveRegWrapper, tempReg := saveRegister(d.output, nil)
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001145 wrappers = append(wrappers, saveRegWrapper)
Robert Sloan2e9e66a2017-09-25 09:08:29 -07001146 isAVX := strings.HasPrefix(instructionName, "v")
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001147 wrappers = append(wrappers, moveTo(d.output, targetReg, isAVX, tempReg))
1148 targetReg = tempReg
1149 if redzoneCleared {
1150 return nil, fmt.Errorf("internal error: Red Zone was already cleared")
1151 }
Robert Sloan8ff03552017-06-14 12:40:58 -07001152 redzoneCleared = true
1153 }
1154
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001155 if symbol == "OPENSSL_ia32cap_P" {
1156 // Flag-altering instructions (i.e. addq) are going to be used so the
1157 // flags need to be preserved.
1158 wrappers = append(wrappers, saveFlags(d.output, redzoneCleared))
1159 wrappers = append(wrappers, func(k func()) {
1160 d.output.WriteString("\tleaq\tOPENSSL_ia32cap_addr_delta(%rip), " + targetReg + "\n")
1161 d.output.WriteString("\taddq\t(" + targetReg + "), " + targetReg + "\n")
1162 })
1163 } else if useGOT {
Robert Sloan8ff03552017-06-14 12:40:58 -07001164 wrappers = append(wrappers, d.loadFromGOT(d.output, targetReg, symbol, section, redzoneCleared))
1165 } else {
1166 wrappers = append(wrappers, func(k func()) {
1167 d.output.WriteString(fmt.Sprintf("\tleaq\t%s(%%rip), %s\n", symbol, targetReg))
1168 })
1169 }
1170 changed = true
1171 break Args
1172
1173 default:
1174 return nil, fmt.Errorf("Unknown section type %q", section)
1175 }
1176
1177 if !changed && len(section) > 0 {
1178 panic("section was not handled")
1179 }
1180 section = ""
1181
1182 argStr := ""
1183 if isIndirect {
1184 argStr += "*"
1185 }
1186 argStr += symbol
1187 argStr += offset
1188
1189 for ; memRef != nil; memRef = memRef.next {
1190 argStr += d.contents(memRef)
1191 }
1192
1193 args = append(args, argStr)
1194
1195 default:
1196 panic(fmt.Sprintf("unknown instruction argument type %q", rul3s[arg.pegRule]))
1197 }
1198 }
1199
1200 if changed {
1201 d.writeCommentedNode(statement)
1202 replacement := "\t" + instructionName + "\t" + strings.Join(args, ", ") + "\n"
1203 wrappers.do(func() {
1204 d.output.WriteString(replacement)
1205 })
1206 } else {
1207 d.writeNode(statement)
1208 }
1209
1210 return statement, nil
1211}
1212
1213func (d *delocation) handleBSS(statement *node32) (*node32, error) {
1214 lastStatement := statement
1215 for statement = statement.next; statement != nil; lastStatement, statement = statement, statement.next {
1216 node := skipWS(statement.up)
1217 if node == nil {
1218 d.writeNode(statement)
1219 continue
1220 }
1221
1222 switch node.pegRule {
1223 case ruleGlobalDirective, ruleComment, ruleInstruction, ruleLocationDirective:
1224 d.writeNode(statement)
1225
1226 case ruleDirective:
1227 directive := node.up
1228 assertNodeType(directive, ruleDirectiveName)
1229 directiveName := d.contents(directive)
1230 if directiveName == "text" || directiveName == "section" || directiveName == "data" {
1231 return lastStatement, nil
1232 }
1233 d.writeNode(statement)
1234
1235 case ruleLabel:
1236 label := node.up
1237 d.writeNode(statement)
1238
1239 if label.pegRule != ruleLocalSymbol {
1240 symbol := d.contents(label)
1241 localSymbol := localTargetName(symbol)
1242 d.output.WriteString(fmt.Sprintf("\n%s:\n", localSymbol))
1243
1244 d.bssAccessorsNeeded[symbol] = localSymbol
1245 }
1246
1247 case ruleLabelContainingDirective:
1248 var err error
1249 statement, err = d.processLabelContainingDirective(statement, node.up)
1250 if err != nil {
1251 return nil, err
1252 }
1253
1254 default:
1255 return nil, fmt.Errorf("unknown BSS statement type %q in %q", rul3s[node.pegRule], d.contents(statement))
1256 }
1257 }
1258
1259 return lastStatement, nil
1260}
1261
1262func transform(w stringWriter, inputs []inputFile) error {
1263 // symbols contains all defined symbols.
1264 symbols := make(map[string]struct{})
1265 // localEntrySymbols contains all symbols with a .localentry directive.
1266 localEntrySymbols := make(map[string]struct{})
Robert Sloan5cbb5c82018-04-24 11:35:46 -07001267 // fileNumbers is the set of IDs seen in .file directives.
1268 fileNumbers := make(map[int]struct{})
1269 // maxObservedFileNumber contains the largest seen file number in a
1270 // .file directive. Zero is not a valid number.
1271 maxObservedFileNumber := 0
Robert Sloan8ff03552017-06-14 12:40:58 -07001272
Robert Sloan4c22c5f2019-03-01 15:53:37 -08001273 // OPENSSL_ia32cap_get will be synthesized by this script.
1274 symbols["OPENSSL_ia32cap_get"] = struct{}{}
1275
Robert Sloan8ff03552017-06-14 12:40:58 -07001276 for _, input := range inputs {
1277 forEachPath(input.ast.up, func(node *node32) {
1278 symbol := input.contents[node.begin:node.end]
1279 if _, ok := symbols[symbol]; ok {
1280 panic(fmt.Sprintf("Duplicate symbol found: %q in %q", symbol, input.path))
1281 }
1282 symbols[symbol] = struct{}{}
1283 }, ruleStatement, ruleLabel, ruleSymbolName)
1284
1285 forEachPath(input.ast.up, func(node *node32) {
1286 node = node.up
1287 assertNodeType(node, ruleLabelContainingDirectiveName)
1288 directive := input.contents[node.begin:node.end]
1289 if directive != ".localentry" {
1290 return
1291 }
1292 // Extract the first argument.
1293 node = skipWS(node.next)
1294 assertNodeType(node, ruleSymbolArgs)
1295 node = node.up
1296 assertNodeType(node, ruleSymbolArg)
1297 symbol := input.contents[node.begin:node.end]
1298 if _, ok := localEntrySymbols[symbol]; ok {
1299 panic(fmt.Sprintf("Duplicate .localentry directive found: %q in %q", symbol, input.path))
1300 }
1301 localEntrySymbols[symbol] = struct{}{}
1302 }, ruleStatement, ruleLabelContainingDirective)
Robert Sloan5cbb5c82018-04-24 11:35:46 -07001303
1304 forEachPath(input.ast.up, func(node *node32) {
1305 assertNodeType(node, ruleLocationDirective)
1306 directive := input.contents[node.begin:node.end]
1307 if !strings.HasPrefix(directive, ".file") {
1308 return
1309 }
1310 parts := strings.Fields(directive)
1311 if len(parts) == 2 {
1312 // This is a .file directive with just a
1313 // filename. Clang appears to generate just one
1314 // of these at the beginning of the output for
1315 // the compilation unit. Ignore it.
1316 return
1317 }
1318 fileNo, err := strconv.Atoi(parts[1])
1319 if err != nil {
1320 panic(fmt.Sprintf("Failed to parse file number from .file: %q", directive))
1321 }
1322
1323 if _, ok := fileNumbers[fileNo]; ok {
1324 panic(fmt.Sprintf("Duplicate file number %d observed", fileNo))
1325 }
1326 fileNumbers[fileNo] = struct{}{}
1327
1328 if fileNo > maxObservedFileNumber {
1329 maxObservedFileNumber = fileNo
1330 }
1331 }, ruleStatement, ruleLocationDirective)
Robert Sloan8ff03552017-06-14 12:40:58 -07001332 }
1333
1334 processor := x86_64
1335 if len(inputs) > 0 {
1336 processor = detectProcessor(inputs[0])
1337 }
1338
1339 d := &delocation{
1340 symbols: symbols,
1341 localEntrySymbols: localEntrySymbols,
1342 processor: processor,
1343 output: w,
1344 redirectors: make(map[string]string),
1345 bssAccessorsNeeded: make(map[string]string),
1346 tocLoaders: make(map[string]struct{}),
1347 gotExternalsNeeded: make(map[string]struct{}),
1348 }
1349
Robert Sloan5cbb5c82018-04-24 11:35:46 -07001350 w.WriteString(".text\n")
Srinivas Paladugudd42a612019-08-09 19:30:39 +00001351 w.WriteString(fmt.Sprintf(".file %d \"inserted_by_delocate.c\"\n", maxObservedFileNumber+1))
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001352 w.WriteString(fmt.Sprintf(".loc %d 1 0\n", maxObservedFileNumber+1))
Robert Sloan5cbb5c82018-04-24 11:35:46 -07001353 w.WriteString("BORINGSSL_bcm_text_start:\n")
Robert Sloan8ff03552017-06-14 12:40:58 -07001354
1355 for _, input := range inputs {
1356 if err := d.processInput(input); err != nil {
1357 return err
1358 }
1359 }
1360
Robert Sloan5cbb5c82018-04-24 11:35:46 -07001361 w.WriteString(".text\n")
Adam Vartanianbfcf3a72018-08-10 14:55:24 +01001362 w.WriteString(fmt.Sprintf(".loc %d 2 0\n", maxObservedFileNumber+1))
Robert Sloan5cbb5c82018-04-24 11:35:46 -07001363 w.WriteString("BORINGSSL_bcm_text_end:\n")
Robert Sloan8ff03552017-06-14 12:40:58 -07001364
1365 // Emit redirector functions. Each is a single jump instruction.
1366 var redirectorNames []string
1367 for name := range d.redirectors {
1368 redirectorNames = append(redirectorNames, name)
1369 }
1370 sort.Strings(redirectorNames)
1371
1372 for _, name := range redirectorNames {
1373 redirector := d.redirectors[name]
Robert Sloan8ff03552017-06-14 12:40:58 -07001374 if d.processor == ppc64le {
Robert Sloane56da3e2017-06-26 08:26:42 -07001375 w.WriteString(".section \".toc\", \"aw\"\n")
1376 w.WriteString(".Lredirector_toc_" + name + ":\n")
1377 w.WriteString(".quad " + name + "\n")
1378 w.WriteString(".text\n")
1379 w.WriteString(".type " + redirector + ", @function\n")
1380 w.WriteString(redirector + ":\n")
1381 // |name| will clobber r2, so save it. This is matched by a restore in
1382 // redirector calls.
1383 w.WriteString("\tstd 2, 24(1)\n")
1384 // Load and call |name|'s global entry point.
1385 w.WriteString("\taddis 12, 2, .Lredirector_toc_" + name + "@toc@ha\n")
1386 w.WriteString("\tld 12, .Lredirector_toc_" + name + "@toc@l(12)\n")
1387 w.WriteString("\tmtctr 12\n")
1388 w.WriteString("\tbctr\n")
Robert Sloan8ff03552017-06-14 12:40:58 -07001389 } else {
Robert Sloane56da3e2017-06-26 08:26:42 -07001390 w.WriteString(".type " + redirector + ", @function\n")
1391 w.WriteString(redirector + ":\n")
Robert Sloan8ff03552017-06-14 12:40:58 -07001392 w.WriteString("\tjmp\t" + name + "\n")
1393 }
1394 }
1395
1396 var accessorNames []string
1397 for accessor := range d.bssAccessorsNeeded {
1398 accessorNames = append(accessorNames, accessor)
1399 }
1400 sort.Strings(accessorNames)
1401
1402 // Emit BSS accessor functions. Each is a single LEA followed by RET.
1403 for _, name := range accessorNames {
1404 funcName := accessorName(name)
1405 w.WriteString(".type " + funcName + ", @function\n")
1406 w.WriteString(funcName + ":\n")
1407 target := d.bssAccessorsNeeded[name]
1408
1409 if d.processor == ppc64le {
1410 w.WriteString("\taddis 3, 2, " + target + "@toc@ha\n")
1411 w.WriteString("\taddi 3, 3, " + target + "@toc@l\n")
1412 w.WriteString("\tblr\n")
1413 } else {
1414 w.WriteString("\tleaq\t" + target + "(%rip), %rax\n\tret\n")
1415 }
1416 }
1417
1418 if d.processor == ppc64le {
1419 loadTOCNames := sortedSet(d.tocLoaders)
1420 for _, symbolAndOffset := range loadTOCNames {
1421 parts := strings.SplitN(symbolAndOffset, "\x00", 2)
1422 symbol, offset := parts[0], parts[1]
1423
1424 funcName := loadTOCFuncName(symbol, offset)
1425 ref := symbol + offset
1426
1427 w.WriteString(".type " + funcName[2:] + ", @function\n")
1428 w.WriteString(funcName[2:] + ":\n")
1429 w.WriteString(funcName + ":\n")
1430 w.WriteString("\taddis 3, 2, " + ref + "@toc@ha\n")
1431 w.WriteString("\taddi 3, 3, " + ref + "@toc@l\n")
1432 w.WriteString("\tblr\n")
1433 }
1434
1435 w.WriteString(".LBORINGSSL_external_toc:\n")
1436 w.WriteString(".quad .TOC.-.LBORINGSSL_external_toc\n")
1437 } else {
1438 externalNames := sortedSet(d.gotExternalsNeeded)
1439 for _, name := range externalNames {
1440 parts := strings.SplitN(name, "@", 2)
1441 symbol, section := parts[0], parts[1]
1442 w.WriteString(".type " + symbol + "_" + section + "_external, @object\n")
1443 w.WriteString(".size " + symbol + "_" + section + "_external, 8\n")
1444 w.WriteString(symbol + "_" + section + "_external:\n")
1445 // Ideally this would be .quad foo@GOTPCREL, but clang's
1446 // assembler cannot emit a 64-bit GOTPCREL relocation. Instead,
1447 // we manually sign-extend the value, knowing that the GOT is
1448 // always at the end, thus foo@GOTPCREL has a positive value.
1449 w.WriteString("\t.long " + symbol + "@" + section + "\n")
1450 w.WriteString("\t.long 0\n")
1451 }
1452
1453 w.WriteString(".type OPENSSL_ia32cap_get, @function\n")
Robert Sloan4c22c5f2019-03-01 15:53:37 -08001454 w.WriteString(".globl OPENSSL_ia32cap_get\n")
1455 w.WriteString(localTargetName("OPENSSL_ia32cap_get") + ":\n")
Robert Sloan8ff03552017-06-14 12:40:58 -07001456 w.WriteString("OPENSSL_ia32cap_get:\n")
1457 w.WriteString("\tleaq OPENSSL_ia32cap_P(%rip), %rax\n")
1458 w.WriteString("\tret\n")
1459
1460 w.WriteString(".extern OPENSSL_ia32cap_P\n")
1461 w.WriteString(".type OPENSSL_ia32cap_addr_delta, @object\n")
1462 w.WriteString(".size OPENSSL_ia32cap_addr_delta, 8\n")
1463 w.WriteString("OPENSSL_ia32cap_addr_delta:\n")
1464 w.WriteString(".quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta\n")
1465 }
1466
1467 w.WriteString(".type BORINGSSL_bcm_text_hash, @object\n")
1468 w.WriteString(".size BORINGSSL_bcm_text_hash, 64\n")
1469 w.WriteString("BORINGSSL_bcm_text_hash:\n")
Robert Sloanf573be72018-09-17 15:29:11 -07001470 for _, b := range fipscommon.UninitHashValue {
Robert Sloan8ff03552017-06-14 12:40:58 -07001471 w.WriteString(".byte 0x" + strconv.FormatUint(uint64(b), 16) + "\n")
1472 }
1473
1474 return nil
1475}
1476
1477func parseInputs(inputs []inputFile) error {
1478 for i, input := range inputs {
1479 var contents string
1480
1481 if input.isArchive {
1482 arFile, err := os.Open(input.path)
1483 if err != nil {
1484 return err
1485 }
1486 defer arFile.Close()
1487
Robert Sloanf068def2018-10-10 18:45:40 -07001488 ar, err := ar.ParseAR(arFile)
Robert Sloan8ff03552017-06-14 12:40:58 -07001489 if err != nil {
1490 return err
1491 }
1492
1493 if len(ar) != 1 {
1494 return fmt.Errorf("expected one file in archive, but found %d", len(ar))
1495 }
1496
1497 for _, c := range ar {
1498 contents = string(c)
1499 }
1500 } else {
1501 inBytes, err := ioutil.ReadFile(input.path)
1502 if err != nil {
1503 return err
1504 }
1505
1506 contents = string(inBytes)
1507 }
1508
1509 asm := Asm{Buffer: contents, Pretty: true}
1510 asm.Init()
1511 if err := asm.Parse(); err != nil {
1512 return fmt.Errorf("error while parsing %q: %s", input.path, err)
1513 }
1514 ast := asm.AST()
1515
1516 inputs[i].contents = contents
1517 inputs[i].ast = ast
1518 }
1519
1520 return nil
1521}
1522
1523func main() {
1524 // The .a file, if given, is expected to be an archive of textual
1525 // assembly sources. That's odd, but CMake really wants to create
1526 // archive files so it's the only way that we can make it work.
1527 arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
1528 outFile := flag.String("o", "", "Path to output assembly")
1529
1530 flag.Parse()
1531
1532 if len(*outFile) == 0 {
1533 fmt.Fprintf(os.Stderr, "Must give argument to -o.\n")
1534 os.Exit(1)
1535 }
1536
1537 var inputs []inputFile
1538 if len(*arInput) > 0 {
1539 inputs = append(inputs, inputFile{
1540 path: *arInput,
1541 index: 0,
1542 isArchive: true,
1543 })
1544 }
1545
1546 for i, path := range flag.Args() {
1547 if len(path) == 0 {
1548 continue
1549 }
1550
1551 inputs = append(inputs, inputFile{
1552 path: path,
1553 index: i + 1,
1554 })
1555 }
1556
1557 if err := parseInputs(inputs); err != nil {
1558 fmt.Fprintf(os.Stderr, "%s\n", err)
1559 os.Exit(1)
1560 }
1561
1562 out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
1563 if err != nil {
1564 panic(err)
1565 }
1566 defer out.Close()
1567
1568 if err := transform(out, inputs); err != nil {
1569 fmt.Fprintf(os.Stderr, "%s\n", err)
1570 os.Exit(1)
1571 }
1572}
1573
1574func forEachPath(node *node32, cb func(*node32), rules ...pegRule) {
1575 if node == nil {
1576 return
1577 }
1578
1579 if len(rules) == 0 {
1580 cb(node)
1581 return
1582 }
1583
1584 rule := rules[0]
1585 childRules := rules[1:]
1586
1587 for ; node != nil; node = node.next {
1588 if node.pegRule != rule {
1589 continue
1590 }
1591
1592 if len(childRules) == 0 {
1593 cb(node)
1594 } else {
1595 forEachPath(node.up, cb, childRules...)
1596 }
1597 }
1598}
1599
1600func skipNodes(node *node32, ruleToSkip pegRule) *node32 {
1601 for ; node != nil && node.pegRule == ruleToSkip; node = node.next {
1602 }
1603 return node
1604}
1605
1606func skipWS(node *node32) *node32 {
1607 return skipNodes(node, ruleWS)
1608}
1609
1610func assertNodeType(node *node32, expected pegRule) {
1611 if rule := node.pegRule; rule != expected {
1612 panic(fmt.Sprintf("node was %q, but wanted %q", rul3s[rule], rul3s[expected]))
1613 }
1614}
1615
1616type wrapperFunc func(func())
1617
1618type wrapperStack []wrapperFunc
1619
1620func (w *wrapperStack) do(baseCase func()) {
1621 if len(*w) == 0 {
1622 baseCase()
1623 return
1624 }
1625
1626 wrapper := (*w)[0]
1627 *w = (*w)[1:]
1628 wrapper(func() { w.do(baseCase) })
1629}
1630
1631// localTargetName returns the name of the local target label for a global
1632// symbol named name.
1633func localTargetName(name string) string {
1634 return ".L" + name + "_local_target"
1635}
1636
1637func localEntryName(name string) string {
1638 return ".L" + name + "_local_entry"
1639}
1640
1641func isSynthesized(symbol string) bool {
1642 return strings.HasSuffix(symbol, "_bss_get") ||
1643 symbol == "OPENSSL_ia32cap_get" ||
1644 strings.HasPrefix(symbol, "BORINGSSL_bcm_text_")
1645}
1646
1647func redirectorName(symbol string) string {
1648 return "bcm_redirector_" + symbol
1649}
1650
1651// sectionType returns the type of a section. I.e. a section called “.text.foo”
1652// is a “.text” section.
1653func sectionType(section string) (string, bool) {
1654 if len(section) == 0 || section[0] != '.' {
1655 return "", false
1656 }
1657
1658 i := strings.Index(section[1:], ".")
1659 if i != -1 {
1660 section = section[:i+1]
1661 }
1662
1663 if strings.HasPrefix(section, ".debug_") {
1664 return ".debug", true
1665 }
1666
1667 return section, true
1668}
1669
1670// accessorName returns the name of the accessor function for a BSS symbol
1671// named name.
1672func accessorName(name string) string {
1673 return name + "_bss_get"
1674}
1675
1676func (d *delocation) mapLocalSymbol(symbol string) string {
1677 if d.currentInput.index == 0 {
1678 return symbol
1679 }
1680 return symbol + "_BCM_" + strconv.Itoa(d.currentInput.index)
1681}
1682
1683func detectProcessor(input inputFile) processorType {
1684 for statement := input.ast.up; statement != nil; statement = statement.next {
1685 node := skipNodes(statement.up, ruleWS)
1686 if node == nil || node.pegRule != ruleInstruction {
1687 continue
1688 }
1689
1690 instruction := node.up
1691 instructionName := input.contents[instruction.begin:instruction.end]
1692
1693 switch instructionName {
1694 case "movq", "call", "leaq":
1695 return x86_64
1696 case "addis", "addi", "mflr":
1697 return ppc64le
1698 }
1699 }
1700
1701 panic("processed entire input and didn't recognise any instructions.")
1702}
1703
1704func sortedSet(m map[string]struct{}) []string {
1705 ret := make([]string, 0, len(m))
1706 for key := range m {
1707 ret = append(ret, key)
1708 }
1709 sort.Strings(ret)
1710 return ret
1711}