blob: fd11a21c1d646eb6aba14af7dc4deb313f3a4b4c [file] [log] [blame]
Robert Sloan572a4e22017-04-17 10:52:19 -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// delocate performs several transformations of textual assembly code. See
16// FIPS.md in this directory for an overview.
17package main
18
19import (
20 "bufio"
21 "bytes"
22 "flag"
23 "fmt"
24 "os"
25 "path/filepath"
26 "sort"
27 "strconv"
28 "strings"
Robert Sloan2424d842017-05-01 07:46:28 -070029 "unicode"
Robert Sloan572a4e22017-04-17 10:52:19 -070030 "unicode/utf8"
31)
32
33func main() {
34 // The .a file, if given, is expected to be an archive of textual
35 // assembly sources. That's odd, but CMake really wants to create
36 // archive files so it's the only way that we can make it work.
37 arInput := flag.String("a", "", "Path to a .a file containing assembly sources")
38
39 outFile := flag.String("o", "", "Path to output assembly")
Robert Sloan572a4e22017-04-17 10:52:19 -070040
41 flag.Parse()
42
43 var lines []string
44 var err error
45 if len(*arInput) > 0 {
46 if lines, err = arLines(lines, *arInput); err != nil {
47 panic(err)
48 }
49 }
50
Robert Sloan2424d842017-05-01 07:46:28 -070051 for i, path := range flag.Args() {
Robert Sloan572a4e22017-04-17 10:52:19 -070052 if len(path) == 0 {
53 continue
54 }
55
56 if lines, err = asLines(lines, path, i); err != nil {
57 panic(err)
58 }
59 }
60
61 symbols := definedSymbols(lines)
62 lines = transform(lines, symbols)
63
64 out, err := os.OpenFile(*outFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
65 if err != nil {
66 panic(err)
67 }
68 defer out.Close()
69
70 for _, line := range lines {
71 out.WriteString(line)
72 out.WriteString("\n")
73 }
74}
75
Robert Sloan9254e682017-04-24 09:42:06 -070076func removeComment(line string) string {
77 if i := strings.Index(line, "#"); i != -1 {
78 return line[:i]
79 }
80 return line
81}
82
Robert Sloan572a4e22017-04-17 10:52:19 -070083// isSymbolDef returns detects whether line contains a (non-local) symbol
84// definition. If so, it returns the symbol and true. Otherwise it returns ""
85// and false.
86func isSymbolDef(line string) (string, bool) {
Robert Sloan9254e682017-04-24 09:42:06 -070087 line = strings.TrimSpace(removeComment(line))
Robert Sloan572a4e22017-04-17 10:52:19 -070088
89 if len(line) > 0 && line[len(line)-1] == ':' && line[0] != '.' {
90 symbol := line[:len(line)-1]
91 if validSymbolName(symbol) {
92 return symbol, true
93 }
94 }
95
96 return "", false
97}
98
99// definedSymbols finds all (non-local) symbols from lines and returns a map
100// from symbol name to whether or not that symbol is global.
101func definedSymbols(lines []string) map[string]bool {
102 globalSymbols := make(map[string]struct{})
103 symbols := make(map[string]bool)
104
105 for _, line := range lines {
106 if len(line) == 0 {
107 continue
108 }
109
110 if symbol, ok := isSymbolDef(line); ok {
111 _, isGlobal := globalSymbols[symbol]
112 symbols[symbol] = isGlobal
113 }
114
115 parts := strings.Fields(strings.TrimSpace(line))
116 if parts[0] == ".globl" {
117 globalSymbols[parts[1]] = struct{}{}
118 }
119 }
120
121 return symbols
122}
123
Robert Sloan9254e682017-04-24 09:42:06 -0700124// threadLocalOffsetFunc describes a function that fetches the offset to symbol
125// in the thread-local space and writes it to the given target register.
126type threadLocalOffsetFunc struct {
127 target string
128 symbol string
129}
130
131type lineSource struct {
132 lines []string
133 lineNo int
134}
135
136func (ls *lineSource) Next() (string, bool) {
137 if ls.lineNo == len(ls.lines) {
138 return "", false
139 }
140
141 ret := ls.lines[ls.lineNo]
142 ls.lineNo++
143 return ret, true
144}
145
146func (ls *lineSource) Unread() {
147 ls.lineNo--
Robert Sloan572a4e22017-04-17 10:52:19 -0700148}
149
Robert Sloan2424d842017-05-01 07:46:28 -0700150func parseInstruction(line string) (instr string, args []string) {
151 line = strings.TrimSpace(line)
152 if len(line) == 0 || line[0] == '#' {
153 return "", nil
154 }
155
156 idx := strings.IndexFunc(line, unicode.IsSpace)
157 if idx < 0 {
158 return line, nil
159 }
160
161 instr = strings.TrimSpace(line[:idx])
162 line = strings.TrimSpace(line[idx:])
163 for len(line) > 0 {
164 var inQuote bool
165 var parens int
166 Loop:
167 for idx = 0; idx < len(line); idx++ {
168 if inQuote {
169 if line[idx] == '\\' {
170 if idx == len(line)-1 {
171 panic(fmt.Sprintf("could not parse %q", line))
172 }
173 idx++
174 } else {
175 inQuote = line[idx] != '"'
176 }
177 continue
178 }
179 switch line[idx] {
180 case '"':
181 inQuote = true
182 case '(':
183 parens++
184 case ')':
185 if parens == 0 {
186 panic(fmt.Sprintf("could not parse %q", line))
187 }
188 parens--
189 case ',':
190 if parens == 0 {
191 break Loop
192 }
193 case '#':
194 line = line[:idx]
195 break Loop
196 }
197 }
198
199 if inQuote || parens > 0 {
200 panic(fmt.Sprintf("could not parse %q", line))
201 }
202
203 args = append(args, strings.TrimSpace(line[:idx]))
204 if idx < len(line) {
205 // Skip the comma.
206 line = line[idx+1:]
207 } else {
208 line = line[idx:]
209 }
210 }
211
212 return
213}
214
Robert Sloan572a4e22017-04-17 10:52:19 -0700215// transform performs a number of transformations on the given assembly code.
216// See FIPS.md in the current directory for an overview.
217func transform(lines []string, symbols map[string]bool) (ret []string) {
218 ret = append(ret, ".text", "BORINGSSL_bcm_text_start:")
219
220 // redirectors maps from out-call symbol name to the name of a
221 // redirector function for that symbol.
222 redirectors := make(map[string]string)
223
Robert Sloan2424d842017-05-01 07:46:28 -0700224 // ia32capAddrDeltaNeeded is true iff OPENSSL_ia32cap_addr_delta has
225 // been referenced and thus needs to be emitted outside the module.
226 ia32capAddrDeltaNeeded := false
227
228 // ia32capGetNeeded is true iff OPENSSL_ia32cap_get has been referenced
229 // and thus needs to be emitted outside the module.
230 ia32capGetNeeded := false
Robert Sloan572a4e22017-04-17 10:52:19 -0700231
Robert Sloan9254e682017-04-24 09:42:06 -0700232 // bssAccessorsNeeded maps the names of BSS variables for which
233 // accessor functions need to be emitted outside of the module, to the
234 // BSS symbols they point to. For example, “EVP_sha256_once” could map
235 // to “.LEVP_sha256_once_local_target” or “EVP_sha256_once” (if .comm
236 // was used).
237 bssAccessorsNeeded := make(map[string]string)
Robert Sloan572a4e22017-04-17 10:52:19 -0700238
Robert Sloan9254e682017-04-24 09:42:06 -0700239 // threadLocalOffsets records the accessor functions needed for getting
240 // offsets in the thread-local storage.
241 threadLocalOffsets := make(map[string]threadLocalOffsetFunc)
Robert Sloan572a4e22017-04-17 10:52:19 -0700242
Robert Sloan9254e682017-04-24 09:42:06 -0700243 source := &lineSource{lines: lines}
244
245 for {
246 line, ok := source.Next()
247 if !ok {
248 break
249 }
250
Robert Sloan2424d842017-05-01 07:46:28 -0700251 if strings.Contains(line, "OPENSSL_ia32cap_get@PLT") {
252 ia32capGetNeeded = true
Robert Sloan572a4e22017-04-17 10:52:19 -0700253 }
254
Robert Sloan9254e682017-04-24 09:42:06 -0700255 line = strings.Replace(line, "@PLT", "", -1)
Robert Sloan572a4e22017-04-17 10:52:19 -0700256
Robert Sloan2424d842017-05-01 07:46:28 -0700257 instr, args := parseInstruction(line)
258 if len(instr) == 0 {
Robert Sloan572a4e22017-04-17 10:52:19 -0700259 ret = append(ret, line)
260 continue
261 }
262
Robert Sloan2424d842017-05-01 07:46:28 -0700263 switch instr {
Robert Sloan572a4e22017-04-17 10:52:19 -0700264 case "call", "callq", "jmp", "jne", "jb", "jz", "jnz", "ja":
Robert Sloan2424d842017-05-01 07:46:28 -0700265 target := args[0]
Robert Sloan572a4e22017-04-17 10:52:19 -0700266 // indirect via register or local label
267 if strings.HasPrefix(target, "*") || strings.HasPrefix(target, ".L") {
268 ret = append(ret, line)
269 continue
270 }
271
Robert Sloan9254e682017-04-24 09:42:06 -0700272 if strings.HasSuffix(target, "_bss_get") {
273 // reference to a synthesised function. Don't
274 // indirect it.
275 ret = append(ret, line)
276 continue
277 }
278
Robert Sloan572a4e22017-04-17 10:52:19 -0700279 if isGlobal, ok := symbols[target]; ok {
280 newTarget := target
281 if isGlobal {
282 newTarget = localTargetName(target)
283 }
Robert Sloan2424d842017-05-01 07:46:28 -0700284 ret = append(ret, fmt.Sprintf("\t%s %s", instr, newTarget))
Robert Sloan572a4e22017-04-17 10:52:19 -0700285 continue
286 }
287
288 redirectorName := "bcm_redirector_" + target
Robert Sloan2424d842017-05-01 07:46:28 -0700289 ret = append(ret, fmt.Sprintf("\t%s %s", instr, redirectorName))
Robert Sloan572a4e22017-04-17 10:52:19 -0700290 redirectors[redirectorName] = target
291 continue
292
Robert Sloan2424d842017-05-01 07:46:28 -0700293 case "pushq":
294 target := args[0]
295 if strings.HasSuffix(target, "@GOTPCREL(%rip)") {
296 target = target[:len(target)-15]
297 if !symbols[target] {
298 panic(fmt.Sprintf("Reference to unknown symbol on line %d: %s", source.lineNo, line))
299 }
300
301 ret = append(ret, "\tpushq %rax")
302 ret = append(ret, "\tleaq "+localTargetName(target)+"(%rip), %rax")
303 ret = append(ret, "\txchg %rax, (%rsp)")
304 continue
305 }
306
307 ret = append(ret, line)
308 continue
309
Robert Sloan9254e682017-04-24 09:42:06 -0700310 case "leaq", "movq", "cmpq":
Robert Sloan2424d842017-05-01 07:46:28 -0700311 if instr == "movq" && strings.Contains(line, "@GOTTPOFF(%rip)") {
Robert Sloan9254e682017-04-24 09:42:06 -0700312 // GOTTPOFF are offsets into the thread-local
313 // storage that are stored in the GOT. We have
314 // to move these relocations out of the module,
315 // but do not know whether rax is live at this
316 // point. Thus a normal function call might
317 // clobber a register and so we synthesize
318 // different functions for writing to each
319 // target register.
320 //
321 // (BoringSSL itself does not use __thread
322 // variables, but ASAN and MSAN may add these
323 // references for their bookkeeping.)
Robert Sloan2424d842017-05-01 07:46:28 -0700324 targetRegister := args[1][1:]
325 symbol := strings.SplitN(args[0], "@", 2)[0]
Robert Sloan9254e682017-04-24 09:42:06 -0700326 functionName := fmt.Sprintf("BORINGSSL_bcm_tpoff_to_%s_for_%s", targetRegister, symbol)
327 threadLocalOffsets[functionName] = threadLocalOffsetFunc{target: targetRegister, symbol: symbol}
Robert Sloan2424d842017-05-01 07:46:28 -0700328 ret = append(ret, "leaq -128(%rsp), %rsp") // Clear the red zone.
Robert Sloan9254e682017-04-24 09:42:06 -0700329 ret = append(ret, "\tcallq "+functionName+"\n")
Robert Sloan2424d842017-05-01 07:46:28 -0700330 ret = append(ret, "leaq 128(%rsp), %rsp")
Robert Sloan9254e682017-04-24 09:42:06 -0700331 continue
Robert Sloan572a4e22017-04-17 10:52:19 -0700332 }
333
Robert Sloan2424d842017-05-01 07:46:28 -0700334 target := args[0]
Robert Sloan572a4e22017-04-17 10:52:19 -0700335 if strings.HasSuffix(target, "(%rip)") {
336 target = target[:len(target)-6]
337 if isGlobal := symbols[target]; isGlobal {
338 line = strings.Replace(line, target, localTargetName(target), 1)
339 }
Robert Sloan9254e682017-04-24 09:42:06 -0700340
Robert Sloan2424d842017-05-01 07:46:28 -0700341 if strings.Contains(line, "@GOTPCREL") && instr == "movq" {
Robert Sloan9254e682017-04-24 09:42:06 -0700342 line = strings.Replace(line, "@GOTPCREL", "", -1)
343 target = strings.Replace(target, "@GOTPCREL", "", -1)
344
345 if isGlobal := symbols[target]; isGlobal {
346 line = strings.Replace(line, target, localTargetName(target), 1)
347 }
348
349 // Nobody actually wants to read the
350 // code of a function. This is a load
351 // from the GOT which, now that we're
352 // referencing the symbol directly,
353 // needs to be transformed into an LEA.
354 line = strings.Replace(line, "movq", "leaq", 1)
Robert Sloan2424d842017-05-01 07:46:28 -0700355 instr = "leaq"
356 }
357
358 if target == "OPENSSL_ia32cap_P" {
359 if instr != "leaq" {
360 panic("reference to OPENSSL_ia32cap_P needs to be changed to go through leaq or GOTPCREL")
361 }
362 if args[1][0] != '%' {
363 panic("reference to OPENSSL_ia32cap_P must target a register.")
364 }
365
366 // We assume pushfq is safe, after
367 // clearing the red zone, because any
368 // signals will be delivered using
369 // %rsp. Thus perlasm and
370 // compiler-generated code must not use
371 // %rsp as a general-purpose register.
372 //
373 // TODO(davidben): This messes up CFI
374 // for a small window if %rsp is the CFI
375 // register.
376 ia32capAddrDeltaNeeded = true
377 ret = append(ret, "leaq -128(%rsp), %rsp") // Clear the red zone.
378 ret = append(ret, "pushfq")
379 ret = append(ret, fmt.Sprintf("leaq OPENSSL_ia32cap_addr_delta(%%rip), %s", args[1]))
380 ret = append(ret, fmt.Sprintf("addq OPENSSL_ia32cap_addr_delta(%%rip), %s", args[1]))
381 ret = append(ret, "popfq")
382 ret = append(ret, "leaq 128(%rsp), %rsp")
383 continue
Robert Sloan9254e682017-04-24 09:42:06 -0700384 }
Robert Sloan572a4e22017-04-17 10:52:19 -0700385 }
386
387 ret = append(ret, line)
388 continue
389
Robert Sloan572a4e22017-04-17 10:52:19 -0700390 case ".comm":
Robert Sloan2424d842017-05-01 07:46:28 -0700391 name := args[0]
Robert Sloan9254e682017-04-24 09:42:06 -0700392 bssAccessorsNeeded[name] = name
Robert Sloan572a4e22017-04-17 10:52:19 -0700393 ret = append(ret, line)
394
395 case ".section":
Robert Sloan2424d842017-05-01 07:46:28 -0700396 section := strings.Trim(args[0], "\"")
Robert Sloan9254e682017-04-24 09:42:06 -0700397 if section == ".data.rel.ro" {
398 // In a normal build, this is an indication of
399 // a problem but any references from the module
400 // to this section will result in a relocation
401 // and thus will break the integrity check.
402 // However, ASAN can generate these sections
403 // and so we cannot forbid them.
404 ret = append(ret, line)
405 continue
406 }
407
408 sectionType, ok := sectionType(section)
409 if !ok {
410 panic(fmt.Sprintf("unknown section %q on line %d", section, source.lineNo))
411 }
412
413 switch sectionType {
414 case ".rodata", ".text":
Robert Sloan572a4e22017-04-17 10:52:19 -0700415 // Move .rodata to .text so it may be accessed
416 // without a relocation. GCC with
417 // -fmerge-constants will place strings into
418 // separate sections, so we move all sections
419 // named like .rodata. Also move .text.startup
420 // so the self-test function is also in the
421 // module.
422 ret = append(ret, ".text # "+section)
Robert Sloan572a4e22017-04-17 10:52:19 -0700423
Robert Sloan9254e682017-04-24 09:42:06 -0700424 case ".data":
Robert Sloan2424d842017-05-01 07:46:28 -0700425 panic(fmt.Sprintf("bad section %q on line %d", args[0], source.lineNo))
Robert Sloan572a4e22017-04-17 10:52:19 -0700426
Robert Sloan9254e682017-04-24 09:42:06 -0700427 case ".init_array", ".fini_array", ".ctors", ".dtors":
428 // init_array/ctors/dtors contains function
429 // pointers to constructor/destructor
430 // functions. These contain relocations, but
431 // they're in a different section anyway.
432 ret = append(ret, line)
433
434 case ".debug", ".note":
435 ret = append(ret, line)
436
437 case ".bss":
438 ret = append(ret, line)
439
440 var accessors map[string]string
441 accessors, ret = handleBSSSection(ret, source)
442 for accessor, name := range accessors {
443 bssAccessorsNeeded[accessor] = name
444 }
Robert Sloan572a4e22017-04-17 10:52:19 -0700445
446 default:
Robert Sloan9254e682017-04-24 09:42:06 -0700447 panic(fmt.Sprintf("unknown section %q on line %d", section, source.lineNo))
Robert Sloan572a4e22017-04-17 10:52:19 -0700448 }
449
Robert Sloan572a4e22017-04-17 10:52:19 -0700450 default:
Robert Sloan572a4e22017-04-17 10:52:19 -0700451 if symbol, ok := isSymbolDef(line); ok {
452 if isGlobal := symbols[symbol]; isGlobal {
453 ret = append(ret, localTargetName(symbol)+":")
454 }
455 }
456
457 ret = append(ret, line)
458 }
459 }
460
461 ret = append(ret, ".text")
462 ret = append(ret, "BORINGSSL_bcm_text_end:")
463
464 // Emit redirector functions. Each is a single JMP instruction.
465 var redirectorNames []string
466 for name := range redirectors {
467 redirectorNames = append(redirectorNames, name)
468 }
469 sort.Strings(redirectorNames)
470
471 for _, name := range redirectorNames {
472 ret = append(ret, ".type "+name+", @function")
473 ret = append(ret, name+":")
Robert Sloan9254e682017-04-24 09:42:06 -0700474 ret = append(ret, "\tjmp "+redirectors[name]+"@PLT")
Robert Sloan572a4e22017-04-17 10:52:19 -0700475 }
476
Robert Sloan9254e682017-04-24 09:42:06 -0700477 var accessorNames []string
478 for accessor := range bssAccessorsNeeded {
479 accessorNames = append(accessorNames, accessor)
480 }
481 sort.Strings(accessorNames)
482
Robert Sloan572a4e22017-04-17 10:52:19 -0700483 // Emit BSS accessor functions. Each is a single LEA followed by RET.
Robert Sloan9254e682017-04-24 09:42:06 -0700484 for _, name := range accessorNames {
Robert Sloan572a4e22017-04-17 10:52:19 -0700485 funcName := accessorName(name)
486 ret = append(ret, ".type "+funcName+", @function")
487 ret = append(ret, funcName+":")
Robert Sloan9254e682017-04-24 09:42:06 -0700488 ret = append(ret, "\tleaq "+bssAccessorsNeeded[name]+"(%rip), %rax")
Robert Sloan572a4e22017-04-17 10:52:19 -0700489 ret = append(ret, "\tret")
490 }
491
Robert Sloan2424d842017-05-01 07:46:28 -0700492 // Emit an OPENSSL_ia32cap_get accessor.
493 if ia32capGetNeeded {
494 ret = append(ret, ".type OPENSSL_ia32cap_get, @function")
495 ret = append(ret, "OPENSSL_ia32cap_get:")
496 ret = append(ret, "\tleaq OPENSSL_ia32cap_P(%rip), %rax")
497 ret = append(ret, "\tret")
498 }
499
Robert Sloan572a4e22017-04-17 10:52:19 -0700500 // Emit an indirect reference to OPENSSL_ia32cap_P.
Robert Sloan2424d842017-05-01 07:46:28 -0700501 if ia32capAddrDeltaNeeded {
Robert Sloan572a4e22017-04-17 10:52:19 -0700502 ret = append(ret, ".extern OPENSSL_ia32cap_P")
Robert Sloan2424d842017-05-01 07:46:28 -0700503 ret = append(ret, ".type OPENSSL_ia32cap_addr_delta,@object")
504 ret = append(ret, ".size OPENSSL_ia32cap_addr_delta,8")
505 ret = append(ret, "OPENSSL_ia32cap_addr_delta:")
506 ret = append(ret, "\t.quad OPENSSL_ia32cap_P-OPENSSL_ia32cap_addr_delta")
Robert Sloan572a4e22017-04-17 10:52:19 -0700507 }
508
Robert Sloan9254e682017-04-24 09:42:06 -0700509 // Emit accessors for thread-local offsets.
510 var threadAccessorNames []string
511 for name := range threadLocalOffsets {
512 threadAccessorNames = append(threadAccessorNames, name)
513 }
514 sort.Strings(threadAccessorNames)
515
516 for _, name := range threadAccessorNames {
517 f := threadLocalOffsets[name]
518
519 ret = append(ret, ".type "+name+",@function")
520 ret = append(ret, name+":")
521 ret = append(ret, "\tmovq "+f.symbol+"@GOTTPOFF(%rip), %"+f.target)
522 ret = append(ret, "\tret")
523 }
524
Robert Sloan572a4e22017-04-17 10:52:19 -0700525 // Emit an array for storing the module hash.
526 ret = append(ret, ".type BORINGSSL_bcm_text_hash,@object")
Robert Sloan9254e682017-04-24 09:42:06 -0700527 ret = append(ret, ".size BORINGSSL_bcm_text_hash,32")
Robert Sloan572a4e22017-04-17 10:52:19 -0700528 ret = append(ret, "BORINGSSL_bcm_text_hash:")
529 for _, b := range uninitHashValue {
530 ret = append(ret, ".byte 0x"+strconv.FormatUint(uint64(b), 16))
531 }
532
Robert Sloan572a4e22017-04-17 10:52:19 -0700533 return ret
534}
535
Robert Sloan9254e682017-04-24 09:42:06 -0700536// handleBSSSection reads lines from source until the next section and adds a
537// local symbol for each BSS symbol found.
538func handleBSSSection(lines []string, source *lineSource) (map[string]string, []string) {
539 accessors := make(map[string]string)
540
541 for {
542 line, ok := source.Next()
543 if !ok {
544 return accessors, lines
545 }
546
547 parts := strings.Fields(strings.TrimSpace(line))
548 if len(parts) == 0 {
549 lines = append(lines, line)
550 continue
551 }
552
553 if strings.HasSuffix(parts[0], ":") {
554 symbol := parts[0][:len(parts[0])-1]
555 localSymbol := ".L" + symbol + "_local_target"
556
557 lines = append(lines, line)
558 lines = append(lines, localSymbol+":")
559
560 accessors[symbol] = localSymbol
561 continue
562 }
563
564 switch parts[0] {
565 case ".text", ".section":
566 source.Unread()
567 return accessors, lines
568
569 default:
570 lines = append(lines, line)
571 }
572 }
573}
574
Robert Sloan572a4e22017-04-17 10:52:19 -0700575// accessorName returns the name of the accessor function for a BSS symbol
576// named name.
577func accessorName(name string) string {
578 return name + "_bss_get"
579}
580
581// localTargetName returns the name of the local target label for a global
582// symbol named name.
583func localTargetName(name string) string {
584 return ".L" + name + "_local_target"
585}
586
Robert Sloan9254e682017-04-24 09:42:06 -0700587// sectionType returns the type of a section. I.e. a section called “.text.foo”
588// is a “.text” section.
589func sectionType(section string) (string, bool) {
590 if len(section) == 0 || section[0] != '.' {
591 return "", false
592 }
593
594 i := strings.Index(section[1:], ".")
595 if i != -1 {
596 section = section[:i+1]
597 }
598
599 if strings.HasPrefix(section, ".debug_") {
600 return ".debug", true
601 }
602
603 return section, true
604}
605
Robert Sloan572a4e22017-04-17 10:52:19 -0700606// asLines appends the contents of path to lines. Local symbols are renamed
607// using uniqueId to avoid collisions.
608func asLines(lines []string, path string, uniqueId int) ([]string, error) {
609 basename := symbolRuneOrUnderscore(filepath.Base(path))
610
611 asFile, err := os.Open(path)
612 if err != nil {
613 return nil, err
614 }
615 defer asFile.Close()
616
Robert Sloan572a4e22017-04-17 10:52:19 -0700617 // localSymbols maps from the symbol name used in the input, to a
618 // unique symbol name.
619 localSymbols := make(map[string]string)
620
621 scanner := bufio.NewScanner(asFile)
Robert Sloan9254e682017-04-24 09:42:06 -0700622 var contents []string
623
624 if len(lines) == 0 {
625 // If this is the first assembly file, don't rewrite symbols.
626 // Only all-but-one file needs to be rewritten and so time can
627 // be saved by putting the (large) bcm.s first.
628 for scanner.Scan() {
629 lines = append(lines, scanner.Text())
630 }
631
632 if err := scanner.Err(); err != nil {
633 return nil, err
634 }
635
636 return lines, nil
637 }
638
Robert Sloan572a4e22017-04-17 10:52:19 -0700639 for scanner.Scan() {
640 line := scanner.Text()
641 trimmed := strings.TrimSpace(line)
642 if strings.HasPrefix(trimmed, ".L") && strings.HasSuffix(trimmed, ":") {
643 symbol := trimmed[:len(trimmed)-1]
644 mappedSymbol := fmt.Sprintf(".L%s_%d_%s", basename, uniqueId, symbol[2:])
645 localSymbols[symbol] = mappedSymbol
646 contents = append(contents, mappedSymbol+":")
647 continue
648 }
649
Robert Sloan9254e682017-04-24 09:42:06 -0700650 contents = append(contents, line)
Robert Sloan572a4e22017-04-17 10:52:19 -0700651 }
652 if err := scanner.Err(); err != nil {
653 return nil, err
654 }
655
656 for _, line := range contents {
657 for symbol, mappedSymbol := range localSymbols {
Robert Sloan9254e682017-04-24 09:42:06 -0700658 i := 0
659 for match := strings.Index(line, symbol); match >= 0; match = strings.Index(line[i:], symbol) {
660 i += match
661
Robert Sloan572a4e22017-04-17 10:52:19 -0700662 before := ' '
663 if i > 0 {
664 before, _ = utf8.DecodeLastRuneInString(line[:i])
665 }
666
667 after, _ := utf8.DecodeRuneInString(line[i+len(symbol):])
668
669 if !symbolRune(before) && !symbolRune(after) {
670 line = strings.Replace(line, symbol, mappedSymbol, 1)
671 i += len(mappedSymbol)
672 } else {
673 i += len(symbol)
674 }
675 }
676 }
677
678 lines = append(lines, line)
679 }
680
681 return lines, nil
682}
683
684func arLines(lines []string, arPath string) ([]string, error) {
685 arFile, err := os.Open(arPath)
686 if err != nil {
687 return nil, err
688 }
689 defer arFile.Close()
690
691 ar, err := ParseAR(arFile)
692 if err != nil {
693 return nil, err
694 }
695
696 if len(ar) != 1 {
697 return nil, fmt.Errorf("expected one file in archive, but found %d", len(ar))
698 }
699
700 for _, contents := range ar {
701 scanner := bufio.NewScanner(bytes.NewBuffer(contents))
702 for scanner.Scan() {
703 lines = append(lines, scanner.Text())
704 }
705 if err := scanner.Err(); err != nil {
706 return nil, err
707 }
708 }
709
710 return lines, nil
711}
712
713// validSymbolName returns true if s is a valid (non-local) name for a symbol.
714func validSymbolName(s string) bool {
715 if len(s) == 0 {
716 return false
717 }
718
719 r, n := utf8.DecodeRuneInString(s)
720 // symbols don't start with a digit.
721 if r == utf8.RuneError || !symbolRune(r) || ('0' <= s[0] && s[0] <= '9') {
722 return false
723 }
724
725 return strings.IndexFunc(s[n:], func(r rune) bool {
726 return !symbolRune(r)
727 }) == -1
728}
729
730// symbolRune returns true if r is valid in a symbol name.
731func symbolRune(r rune) bool {
732 return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '$' || r == '_'
733}
734
735// symbolRuneOrUnderscore maps s where runes valid in a symbol name map to
736// themselves and all other runs map to underscore.
737func symbolRuneOrUnderscore(s string) string {
738 runes := make([]rune, 0, len(s))
739
740 for _, r := range s {
741 if symbolRune(r) {
742 runes = append(runes, r)
743 } else {
744 runes = append(runes, '_')
745 }
746 }
747
748 return string(runes)
749}