in util/fipstools/delocate/delocate.go [1838:2159]
func transform(w stringWriter, includes []string, inputs []inputFile, startEndDebugDirectives bool) error {
// symbols contains all defined symbols.
symbols := make(map[string]struct{})
// localEntrySymbols contains all symbols with a .localentry directive.
localEntrySymbols := make(map[string]struct{})
// fileNumbers is the set of IDs seen in .file directives.
fileNumbers := make(map[int]struct{})
// maxObservedFileNumber contains the largest seen file number in a
// .file directive. Zero is not a valid number.
maxObservedFileNumber := 0
// fileDirectivesContainMD5 is true if the compiler is outputting MD5
// checksums in .file directives. If it does so, then this script needs
// to match that behaviour otherwise warnings result.
fileDirectivesContainMD5 := false
// OPENSSL_ia32cap_get will be synthesized by this script.
symbols["OPENSSL_ia32cap_get"] = struct{}{}
for _, include := range includes {
relative, err := relativeHeaderIncludePath(include)
if err != nil {
return err
}
w.WriteString(fmt.Sprintf("#include <%s>\n", relative))
}
for _, input := range inputs {
forEachPath(input.ast.up, func(node *node32) {
symbol := input.contents[node.begin:node.end]
if _, ok := symbols[symbol]; ok {
panic(fmt.Sprintf("Duplicate symbol found: %q in %q", symbol, input.path))
}
symbols[symbol] = struct{}{}
}, ruleStatement, ruleLabel, ruleSymbolName)
forEachPath(input.ast.up, func(node *node32) {
node = node.up
assertNodeType(node, ruleLabelContainingDirectiveName)
directive := input.contents[node.begin:node.end]
if directive != ".localentry" {
return
}
// Extract the first argument.
node = skipWS(node.next)
assertNodeType(node, ruleSymbolArgs)
node = node.up
assertNodeType(node, ruleSymbolArg)
symbol := input.contents[node.begin:node.end]
if _, ok := localEntrySymbols[symbol]; ok {
panic(fmt.Sprintf("Duplicate .localentry directive found: %q in %q", symbol, input.path))
}
localEntrySymbols[symbol] = struct{}{}
}, ruleStatement, ruleLabelContainingDirective)
forEachPath(input.ast.up, func(node *node32) {
assertNodeType(node, ruleLocationDirective)
directive := input.contents[node.begin:node.end]
if !strings.HasPrefix(directive, ".file") {
return
}
parts := strings.Fields(directive)
if len(parts) == 2 {
// This is a .file directive with just a
// filename. Clang appears to generate just one
// of these at the beginning of the output for
// the compilation unit. Ignore it.
return
}
fileNo, err := strconv.Atoi(parts[1])
if err != nil {
panic(fmt.Sprintf("Failed to parse file number from .file: %q", directive))
}
if _, ok := fileNumbers[fileNo]; ok {
panic(fmt.Sprintf("Duplicate file number %d observed", fileNo))
}
fileNumbers[fileNo] = struct{}{}
if fileNo > maxObservedFileNumber {
maxObservedFileNumber = fileNo
}
for _, token := range parts[2:] {
if token == "md5" {
fileDirectivesContainMD5 = true
}
}
}, ruleStatement, ruleLocationDirective)
}
processor := x86_64
if len(inputs) > 0 {
processor = detectProcessor(inputs[0])
}
commentIndicator := "#"
if processor == aarch64 {
commentIndicator = "//"
}
d := &delocation{
symbols: symbols,
localEntrySymbols: localEntrySymbols,
processor: processor,
commentIndicator: commentIndicator,
output: w,
cpuCapUniqueSymbols: []*cpuCapUniqueSymbol{},
redirectors: make(map[string]string),
bssAccessorsNeeded: make(map[string]string),
tocLoaders: make(map[string]struct{}),
gotExternalsNeeded: make(map[string]struct{}),
gotOffsetsNeeded: make(map[string]struct{}),
gotOffOffsetsNeeded: make(map[string]struct{}),
}
w.WriteString(".text\n")
if startEndDebugDirectives {
var fileTrailing string
if fileDirectivesContainMD5 {
fileTrailing = " md5 0x00000000000000000000000000000000"
}
w.WriteString(fmt.Sprintf(".file %d \"inserted_by_delocate.c\"%s\n", maxObservedFileNumber+1, fileTrailing))
w.WriteString(fmt.Sprintf(".loc %d 1 0\n", maxObservedFileNumber+1))
}
if d.processor == aarch64 {
// Grab the address of BORINGSSL_bcm_text_hash via a relocation
// from a redirector function. For this to work, need to add the markers
// to the symbol table.
w.WriteString(".global BORINGSSL_bcm_text_hash\n")
w.WriteString(".type BORINGSSL_bcm_text_hash, @function\n")
} else {
w.WriteString(".type BORINGSSL_bcm_text_hash, @object\n")
w.WriteString(".size BORINGSSL_bcm_text_hash, 32\n")
}
w.WriteString("BORINGSSL_bcm_text_hash:\n")
for _, b := range fipscommon.UninitHashValue {
w.WriteString(".byte 0x" + strconv.FormatUint(uint64(b), 16) + "\n")
}
if d.processor == aarch64 {
// Grab the address of BORINGSSL_bcm_text_[start,end] via a relocation
// from a redirector function. For this to work, need to add the markers
// to the symbol table.
w.WriteString(fmt.Sprintf(".global BORINGSSL_bcm_text_start\n"))
w.WriteString(fmt.Sprintf(".type BORINGSSL_bcm_text_start, @function\n"))
}
w.WriteString("BORINGSSL_bcm_text_start:\n")
for _, input := range inputs {
if err := d.processInput(input); err != nil {
return err
}
}
w.WriteString(".text\n")
if startEndDebugDirectives {
w.WriteString(fmt.Sprintf(".loc %d 2 0\n", maxObservedFileNumber+1))
if d.processor == aarch64 {
w.WriteString(fmt.Sprintf(".global BORINGSSL_bcm_text_end\n"))
w.WriteString(fmt.Sprintf(".type BORINGSSL_bcm_text_end, @function\n"))
}
}
w.WriteString("BORINGSSL_bcm_text_end:\n")
// Emit redirector functions. Each is a single jump instruction.
var redirectorNames []string
for name := range d.redirectors {
redirectorNames = append(redirectorNames, name)
}
sort.Strings(redirectorNames)
for _, name := range redirectorNames {
redirector := d.redirectors[name]
switch d.processor {
case ppc64le:
w.WriteString(".section \".toc\", \"aw\"\n")
w.WriteString(".Lredirector_toc_" + name + ":\n")
w.WriteString(".quad " + name + "\n")
w.WriteString(".text\n")
w.WriteString(".type " + redirector + ", @function\n")
w.WriteString(redirector + ":\n")
// |name| will clobber r2, so save it. This is matched by a restore in
// redirector calls.
w.WriteString("\tstd 2, 24(1)\n")
// Load and call |name|'s global entry point.
w.WriteString("\taddis 12, 2, .Lredirector_toc_" + name + "@toc@ha\n")
w.WriteString("\tld 12, .Lredirector_toc_" + name + "@toc@l(12)\n")
w.WriteString("\tmtctr 12\n")
w.WriteString("\tbctr\n")
case aarch64:
writeAarch64Function(w, redirector, func(w stringWriter) {
w.WriteString("\tb " + name + "\n")
})
case x86_64:
w.WriteString(".type " + redirector + ", @function\n")
w.WriteString(redirector + ":\n")
w.WriteString("\tjmp\t" + name + "\n")
}
}
var accessorNames []string
for accessor := range d.bssAccessorsNeeded {
accessorNames = append(accessorNames, accessor)
}
sort.Strings(accessorNames)
// Emit BSS accessor functions. Each is a single LEA followed by RET.
for _, name := range accessorNames {
funcName := accessorName(name)
target := d.bssAccessorsNeeded[name]
switch d.processor {
case ppc64le:
w.WriteString(".type " + funcName + ", @function\n")
w.WriteString(funcName + ":\n")
w.WriteString("\taddis 3, 2, " + target + "@toc@ha\n")
w.WriteString("\taddi 3, 3, " + target + "@toc@l\n")
w.WriteString("\tblr\n")
case x86_64:
w.WriteString(".type " + funcName + ", @function\n")
w.WriteString(funcName + ":\n")
w.WriteString("\tleaq\t" + target + "(%rip), %rax\n\tret\n")
case aarch64:
writeAarch64Function(w, funcName, func(w stringWriter) {
w.WriteString("\tadrp x0, " + target + "\n")
w.WriteString("\tadd x0, x0, :lo12:" + target + "\n")
w.WriteString("\tret\n")
})
}
}
switch d.processor {
case ppc64le:
loadTOCNames := sortedSet(d.tocLoaders)
for _, symbolAndOffset := range loadTOCNames {
parts := strings.SplitN(symbolAndOffset, "\x00", 2)
symbol, offset := parts[0], parts[1]
funcName := loadTOCFuncName(symbol, offset)
ref := symbol + offset
w.WriteString(".type " + funcName[2:] + ", @function\n")
w.WriteString(funcName[2:] + ":\n")
w.WriteString(funcName + ":\n")
w.WriteString("\taddis 3, 2, " + ref + "@toc@ha\n")
w.WriteString("\taddi 3, 3, " + ref + "@toc@l\n")
w.WriteString("\tblr\n")
}
w.WriteString(".LBORINGSSL_external_toc:\n")
w.WriteString(".quad .TOC.-.LBORINGSSL_external_toc\n")
case aarch64:
externalNames := sortedSet(d.gotExternalsNeeded)
for _, symbol := range externalNames {
writeAarch64Function(w, gotHelperName(symbol), func(w stringWriter) {
w.WriteString("\tadrp x0, :got:" + symbol + "\n")
w.WriteString("\tldr x0, [x0, :got_lo12:" + symbol + "]\n")
w.WriteString("\tret\n")
})
}
writeAarch64Function(w, ".LOPENSSL_armcap_P_addr", func(w stringWriter) {
w.WriteString("\tadrp x0, OPENSSL_armcap_P\n")
w.WriteString("\tadd x0, x0, :lo12:OPENSSL_armcap_P\n")
w.WriteString("\tret\n")
})
case x86_64:
externalNames := sortedSet(d.gotExternalsNeeded)
for _, name := range externalNames {
parts := strings.SplitN(name, "@", 2)
symbol, section := parts[0], parts[1]
w.WriteString(".type " + symbol + "_" + section + "_external, @object\n")
w.WriteString(".size " + symbol + "_" + section + "_external, 8\n")
w.WriteString(symbol + "_" + section + "_external:\n")
// Ideally this would be .quad foo@GOTPCREL, but clang's
// assembler cannot emit a 64-bit GOTPCREL relocation. Instead,
// we manually sign-extend the value, knowing that the GOT is
// always at the end, thus foo@GOTPCREL has a positive value.
w.WriteString("\t.long " + symbol + "@" + section + "\n")
w.WriteString("\t.long 0\n")
}
w.WriteString(".type OPENSSL_ia32cap_get, @function\n")
w.WriteString(".globl OPENSSL_ia32cap_get\n")
w.WriteString(localTargetName("OPENSSL_ia32cap_get") + ":\n")
w.WriteString("OPENSSL_ia32cap_get:\n")
w.WriteString("\tleaq OPENSSL_ia32cap_P(%rip), %rax\n")
w.WriteString("\tret\n")
// Luckily, this is a fixed order iteration. So, we can write
// deterministic tests for this in /testdata.
for _, uniqueSymbol := range d.cpuCapUniqueSymbols {
w.WriteString(".type " + uniqueSymbol.getx86Symbol() + ", @function\n")
w.WriteString(uniqueSymbol.getx86Symbol() + ":\n")
w.WriteString("\tleaq OPENSSL_ia32cap_P(%rip), %" + uniqueSymbol.registerName + "\n")
w.WriteString("\tjmp " + uniqueSymbol.getx86SymbolReturn() + "\n")
}
if d.gotDeltaNeeded {
w.WriteString(".Lboringssl_got_delta:\n")
w.WriteString("\t.quad _GLOBAL_OFFSET_TABLE_-.Lboringssl_got_delta\n")
}
for _, name := range sortedSet(d.gotOffsetsNeeded) {
w.WriteString(".Lboringssl_got_" + name + ":\n")
w.WriteString("\t.quad " + name + "@GOT\n")
}
for _, name := range sortedSet(d.gotOffOffsetsNeeded) {
w.WriteString(".Lboringssl_gotoff_" + name + ":\n")
w.WriteString("\t.quad " + name + "@GOTOFF\n")
}
}
return nil
}