1// Copyright 2016 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cc 16 17import ( 18 "fmt" 19 "path/filepath" 20 "runtime" 21 "strings" 22 "sync" 23 24 "github.com/google/blueprint" 25 "github.com/google/blueprint/proptools" 26 27 "android/soong/android" 28 "android/soong/cc/config" 29) 30 31func init() { 32 pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen") 33 pctx.HostBinToolVariable("abidiff", "abidiff") 34 pctx.HostBinToolVariable("abitidy", "abitidy") 35 pctx.HostBinToolVariable("abidw", "abidw") 36} 37 38var ( 39 genStubSrc = pctx.AndroidStaticRule("genStubSrc", 40 blueprint.RuleParams{ 41 Command: "$ndkStubGenerator --arch $arch --api $apiLevel " + 42 "--api-map $apiMap $flags $in $out", 43 CommandDeps: []string{"$ndkStubGenerator"}, 44 }, "arch", "apiLevel", "apiMap", "flags") 45 46 abidw = pctx.AndroidStaticRule("abidw", 47 blueprint.RuleParams{ 48 Command: "$abidw --type-id-style hash --no-corpus-path " + 49 "--no-show-locs --no-comp-dir-path -w $symbolList " + 50 "$in --out-file $out", 51 CommandDeps: []string{"$abidw"}, 52 }, "symbolList") 53 54 abitidy = pctx.AndroidStaticRule("abitidy", 55 blueprint.RuleParams{ 56 Command: "$abitidy --all $flags -i $in -o $out", 57 CommandDeps: []string{"$abitidy"}, 58 }, "flags") 59 60 abidiff = pctx.AndroidStaticRule("abidiff", 61 blueprint.RuleParams{ 62 // Need to create *some* output for ninja. We don't want to use tee 63 // because we don't want to spam the build output with "nothing 64 // changed" messages, so redirect output message to $out, and if 65 // changes were detected print the output and fail. 66 Command: "$abidiff $args $in > $out || (cat $out && false)", 67 CommandDeps: []string{"$abidiff"}, 68 }, "args") 69 70 ndkLibrarySuffix = ".ndk" 71 72 ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey") 73 // protects ndkKnownLibs writes during parallel BeginMutator. 74 ndkKnownLibsLock sync.Mutex 75 76 stubImplementation = dependencyTag{name: "stubImplementation"} 77) 78 79// The First_version and Unversioned_until properties of this struct should not 80// be used directly, but rather through the ApiLevel returning methods 81// firstVersion() and unversionedUntil(). 82 83// Creates a stub shared library based on the provided version file. 84// 85// Example: 86// 87// ndk_library { 88// name: "libfoo", 89// symbol_file: "libfoo.map.txt", 90// first_version: "9", 91// } 92// 93type libraryProperties struct { 94 // Relative path to the symbol map. 95 // An example file can be seen here: TODO(danalbert): Make an example. 96 Symbol_file *string 97 98 // The first API level a library was available. A library will be generated 99 // for every API level beginning with this one. 100 First_version *string 101 102 // The first API level that library should have the version script applied. 103 // This defaults to the value of first_version, and should almost never be 104 // used. This is only needed to work around platform bugs like 105 // https://github.com/android-ndk/ndk/issues/265. 106 Unversioned_until *string 107 108 // If true, does not emit errors when APIs lacking type information are 109 // found. This is false by default and should not be enabled outside bionic, 110 // where it is enabled pending a fix for http://b/190554910 (no debug info 111 // for asm implemented symbols). 112 Allow_untyped_symbols *bool 113} 114 115type stubDecorator struct { 116 *libraryDecorator 117 118 properties libraryProperties 119 120 versionScriptPath android.ModuleGenPath 121 parsedCoverageXmlPath android.ModuleOutPath 122 installPath android.Path 123 abiDumpPath android.OutputPath 124 abiDiffPaths android.Paths 125 126 apiLevel android.ApiLevel 127 firstVersion android.ApiLevel 128 unversionedUntil android.ApiLevel 129} 130 131var _ versionedInterface = (*stubDecorator)(nil) 132 133func shouldUseVersionScript(ctx BaseModuleContext, stub *stubDecorator) bool { 134 return stub.apiLevel.GreaterThanOrEqualTo(stub.unversionedUntil) 135} 136 137func (stub *stubDecorator) implementationModuleName(name string) string { 138 return strings.TrimSuffix(name, ndkLibrarySuffix) 139} 140 141func ndkLibraryVersions(ctx android.BaseMutatorContext, from android.ApiLevel) []string { 142 var versions []android.ApiLevel 143 versionStrs := []string{} 144 for _, version := range ctx.Config().AllSupportedApiLevels() { 145 if version.GreaterThanOrEqualTo(from) { 146 versions = append(versions, version) 147 versionStrs = append(versionStrs, version.String()) 148 } 149 } 150 versionStrs = append(versionStrs, android.FutureApiLevel.String()) 151 152 return versionStrs 153} 154 155func (this *stubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string { 156 if !ctx.Module().Enabled() { 157 return nil 158 } 159 if ctx.Target().NativeBridge == android.NativeBridgeEnabled { 160 ctx.Module().Disable() 161 return nil 162 } 163 firstVersion, err := nativeApiLevelFromUser(ctx, 164 String(this.properties.First_version)) 165 if err != nil { 166 ctx.PropertyErrorf("first_version", err.Error()) 167 return nil 168 } 169 return ndkLibraryVersions(ctx, firstVersion) 170} 171 172func (this *stubDecorator) initializeProperties(ctx BaseModuleContext) bool { 173 this.apiLevel = nativeApiLevelOrPanic(ctx, this.stubsVersion()) 174 175 var err error 176 this.firstVersion, err = nativeApiLevelFromUser(ctx, 177 String(this.properties.First_version)) 178 if err != nil { 179 ctx.PropertyErrorf("first_version", err.Error()) 180 return false 181 } 182 183 str := proptools.StringDefault(this.properties.Unversioned_until, "minimum") 184 this.unversionedUntil, err = nativeApiLevelFromUser(ctx, str) 185 if err != nil { 186 ctx.PropertyErrorf("unversioned_until", err.Error()) 187 return false 188 } 189 190 return true 191} 192 193func getNDKKnownLibs(config android.Config) *[]string { 194 return config.Once(ndkKnownLibsKey, func() interface{} { 195 return &[]string{} 196 }).(*[]string) 197} 198 199func (c *stubDecorator) compilerInit(ctx BaseModuleContext) { 200 c.baseCompiler.compilerInit(ctx) 201 202 name := ctx.baseModuleName() 203 if strings.HasSuffix(name, ndkLibrarySuffix) { 204 ctx.PropertyErrorf("name", "Do not append %q manually, just use the base name", ndkLibrarySuffix) 205 } 206 207 ndkKnownLibsLock.Lock() 208 defer ndkKnownLibsLock.Unlock() 209 ndkKnownLibs := getNDKKnownLibs(ctx.Config()) 210 for _, lib := range *ndkKnownLibs { 211 if lib == name { 212 return 213 } 214 } 215 *ndkKnownLibs = append(*ndkKnownLibs, name) 216} 217 218var stubLibraryCompilerFlags = []string{ 219 // We're knowingly doing some otherwise unsightly things with builtin 220 // functions here. We're just generating stub libraries, so ignore it. 221 "-Wno-incompatible-library-redeclaration", 222 "-Wno-incomplete-setjmp-declaration", 223 "-Wno-builtin-requires-header", 224 "-Wno-invalid-noreturn", 225 "-Wall", 226 "-Werror", 227 // These libraries aren't actually used. Don't worry about unwinding 228 // (avoids the need to link an unwinder into a fake library). 229 "-fno-unwind-tables", 230} 231 232func init() { 233 config.ExportStringList("StubLibraryCompilerFlags", stubLibraryCompilerFlags) 234} 235 236func addStubLibraryCompilerFlags(flags Flags) Flags { 237 flags.Global.CFlags = append(flags.Global.CFlags, stubLibraryCompilerFlags...) 238 // All symbols in the stubs library should be visible. 239 if inList("-fvisibility=hidden", flags.Local.CFlags) { 240 flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default") 241 } 242 return flags 243} 244 245func (stub *stubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags { 246 flags = stub.baseCompiler.compilerFlags(ctx, flags, deps) 247 return addStubLibraryCompilerFlags(flags) 248} 249 250type ndkApiOutputs struct { 251 stubSrc android.ModuleGenPath 252 versionScript android.ModuleGenPath 253 symbolList android.ModuleGenPath 254} 255 256func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string, 257 apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs { 258 259 stubSrcPath := android.PathForModuleGen(ctx, "stub.c") 260 versionScriptPath := android.PathForModuleGen(ctx, "stub.map") 261 symbolFilePath := android.PathForModuleSrc(ctx, symbolFile) 262 symbolListPath := android.PathForModuleGen(ctx, "abi_symbol_list.txt") 263 apiLevelsJson := android.GetApiLevelsJson(ctx) 264 ctx.Build(pctx, android.BuildParams{ 265 Rule: genStubSrc, 266 Description: "generate stubs " + symbolFilePath.Rel(), 267 Outputs: []android.WritablePath{stubSrcPath, versionScriptPath, 268 symbolListPath}, 269 Input: symbolFilePath, 270 Implicits: []android.Path{apiLevelsJson}, 271 Args: map[string]string{ 272 "arch": ctx.Arch().ArchType.String(), 273 "apiLevel": apiLevel.String(), 274 "apiMap": apiLevelsJson.String(), 275 "flags": genstubFlags, 276 }, 277 }) 278 279 return ndkApiOutputs{ 280 stubSrc: stubSrcPath, 281 versionScript: versionScriptPath, 282 symbolList: symbolListPath, 283 } 284} 285 286func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects { 287 return compileObjs(ctx, flagsToBuilderFlags(flags), "", 288 android.Paths{src}, nil, nil, nil, nil) 289} 290 291func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path { 292 dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix), 293 stubImplementation) 294 if dep == nil { 295 ctx.ModuleErrorf("Could not find implementation for stub") 296 return nil 297 } 298 impl, ok := dep.(*Module) 299 if !ok { 300 ctx.ModuleErrorf("Implementation for stub is not correct module type") 301 } 302 output := impl.UnstrippedOutputFile() 303 if output == nil { 304 ctx.ModuleErrorf("implementation module (%s) has no output", impl) 305 return nil 306 } 307 308 return output 309} 310 311func (this *stubDecorator) libraryName(ctx ModuleContext) string { 312 return strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix) 313} 314 315func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext, 316 apiLevel android.ApiLevel) android.OptionalPath { 317 318 subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(), 319 ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.xml") 320 return android.ExistentPathForSource(ctx, subpath) 321} 322 323// Feature flag. 324func canDumpAbi(config android.Config) bool { 325 if runtime.GOOS == "darwin" { 326 return false 327 } 328 // abidw doesn't currently handle top-byte-ignore correctly. Disable ABI 329 // dumping for those configs while we wait for a fix. We'll still have ABI 330 // checking coverage from non-hwasan builds. 331 // http://b/190554910 332 if android.InList("hwaddress", config.SanitizeDevice()) { 333 return false 334 } 335 return true 336} 337 338// Feature flag to disable diffing against prebuilts. 339func canDiffAbi() bool { 340 return false 341} 342 343func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) { 344 implementationLibrary := this.findImplementationLibrary(ctx) 345 abiRawPath := getNdkAbiDumpInstallBase(ctx).Join(ctx, 346 this.apiLevel.String(), ctx.Arch().ArchType.String(), 347 this.libraryName(ctx), "abi.raw.xml") 348 ctx.Build(pctx, android.BuildParams{ 349 Rule: abidw, 350 Description: fmt.Sprintf("abidw %s", implementationLibrary), 351 Input: implementationLibrary, 352 Output: abiRawPath, 353 Implicit: symbolList, 354 Args: map[string]string{ 355 "symbolList": symbolList.String(), 356 }, 357 }) 358 359 this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx, 360 this.apiLevel.String(), ctx.Arch().ArchType.String(), 361 this.libraryName(ctx), "abi.xml") 362 untypedFlag := "--abort-on-untyped-symbols" 363 if proptools.BoolDefault(this.properties.Allow_untyped_symbols, false) { 364 untypedFlag = "" 365 } 366 ctx.Build(pctx, android.BuildParams{ 367 Rule: abitidy, 368 Description: fmt.Sprintf("abitidy %s", implementationLibrary), 369 Input: abiRawPath, 370 Output: this.abiDumpPath, 371 Args: map[string]string{ 372 "flags": untypedFlag, 373 }, 374 }) 375} 376 377func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel { 378 apiLevels := append(ctx.Config().AllSupportedApiLevels(), 379 android.FutureApiLevel) 380 for _, api := range apiLevels { 381 if api.GreaterThan(apiLevel) { 382 return &api 383 } 384 } 385 return nil 386} 387 388func (this *stubDecorator) diffAbi(ctx ModuleContext) { 389 missingPrebuiltError := fmt.Sprintf( 390 "Did not find prebuilt ABI dump for %q. Generate with "+ 391 "//development/tools/ndk/update_ndk_abi.sh.", this.libraryName(ctx)) 392 393 // Catch any ABI changes compared to the checked-in definition of this API 394 // level. 395 abiDiffPath := android.PathForModuleOut(ctx, "abidiff.timestamp") 396 prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel) 397 if !prebuiltAbiDump.Valid() { 398 ctx.Build(pctx, android.BuildParams{ 399 Rule: android.ErrorRule, 400 Output: abiDiffPath, 401 Args: map[string]string{ 402 "error": missingPrebuiltError, 403 }, 404 }) 405 } else { 406 ctx.Build(pctx, android.BuildParams{ 407 Rule: abidiff, 408 Description: fmt.Sprintf("abidiff %s %s", prebuiltAbiDump, 409 this.abiDumpPath), 410 Output: abiDiffPath, 411 Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath}, 412 }) 413 } 414 this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath) 415 416 // Also ensure that the ABI of the next API level (if there is one) matches 417 // this API level. *New* ABI is allowed, but any changes to APIs that exist 418 // in this API level are disallowed. 419 if !this.apiLevel.IsCurrent() { 420 nextApiLevel := findNextApiLevel(ctx, this.apiLevel) 421 if nextApiLevel == nil { 422 panic(fmt.Errorf("could not determine which API level follows "+ 423 "non-current API level %s", this.apiLevel)) 424 } 425 nextAbiDiffPath := android.PathForModuleOut(ctx, 426 "abidiff_next.timestamp") 427 nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel) 428 if !nextAbiDump.Valid() { 429 ctx.Build(pctx, android.BuildParams{ 430 Rule: android.ErrorRule, 431 Output: nextAbiDiffPath, 432 Args: map[string]string{ 433 "error": missingPrebuiltError, 434 }, 435 }) 436 } else { 437 ctx.Build(pctx, android.BuildParams{ 438 Rule: abidiff, 439 Description: fmt.Sprintf("abidiff %s %s", this.abiDumpPath, 440 nextAbiDump), 441 Output: nextAbiDiffPath, 442 Inputs: android.Paths{this.abiDumpPath, nextAbiDump.Path()}, 443 Args: map[string]string{ 444 "args": "--no-added-syms", 445 }, 446 }) 447 } 448 this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath) 449 } 450} 451 452func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects { 453 if !strings.HasSuffix(String(c.properties.Symbol_file), ".map.txt") { 454 ctx.PropertyErrorf("symbol_file", "must end with .map.txt") 455 } 456 457 if !c.buildStubs() { 458 // NDK libraries have no implementation variant, nothing to do 459 return Objects{} 460 } 461 462 if !c.initializeProperties(ctx) { 463 // Emits its own errors, so we don't need to. 464 return Objects{} 465 } 466 467 symbolFile := String(c.properties.Symbol_file) 468 nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "") 469 objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc) 470 c.versionScriptPath = nativeAbiResult.versionScript 471 if canDumpAbi(ctx.Config()) { 472 c.dumpAbi(ctx, nativeAbiResult.symbolList) 473 if canDiffAbi() { 474 c.diffAbi(ctx) 475 } 476 } 477 if c.apiLevel.IsCurrent() && ctx.PrimaryArch() { 478 c.parsedCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile) 479 } 480 return objs 481} 482 483func (linker *stubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps { 484 return Deps{} 485} 486 487func (linker *stubDecorator) Name(name string) string { 488 return name + ndkLibrarySuffix 489} 490 491func (stub *stubDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags { 492 stub.libraryDecorator.libName = ctx.baseModuleName() 493 return stub.libraryDecorator.linkerFlags(ctx, flags) 494} 495 496func (stub *stubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, 497 objs Objects) android.Path { 498 499 if !stub.buildStubs() { 500 // NDK libraries have no implementation variant, nothing to do 501 return nil 502 } 503 504 if shouldUseVersionScript(ctx, stub) { 505 linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String() 506 flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag) 507 flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath) 508 } 509 510 stub.libraryDecorator.skipAPIDefine = true 511 return stub.libraryDecorator.link(ctx, flags, deps, objs) 512} 513 514func (stub *stubDecorator) nativeCoverage() bool { 515 return false 516} 517 518func (stub *stubDecorator) install(ctx ModuleContext, path android.Path) { 519 arch := ctx.Target().Arch.ArchType.Name 520 // arm64 isn't actually a multilib toolchain, so unlike the other LP64 521 // architectures it's just installed to lib. 522 libDir := "lib" 523 if ctx.toolchain().Is64Bit() && arch != "arm64" { 524 libDir = "lib64" 525 } 526 527 installDir := getNdkInstallBase(ctx).Join(ctx, fmt.Sprintf( 528 "platforms/android-%s/arch-%s/usr/%s", stub.apiLevel, arch, libDir)) 529 stub.installPath = ctx.InstallFile(installDir, path.Base(), path) 530} 531 532func newStubLibrary() *Module { 533 module, library := NewLibrary(android.DeviceSupported) 534 library.BuildOnlyShared() 535 module.stl = nil 536 module.sanitize = nil 537 library.disableStripping() 538 539 stub := &stubDecorator{ 540 libraryDecorator: library, 541 } 542 module.compiler = stub 543 module.linker = stub 544 module.installer = stub 545 module.library = stub 546 547 module.Properties.AlwaysSdk = true 548 module.Properties.Sdk_version = StringPtr("current") 549 550 module.AddProperties(&stub.properties, &library.MutatedProperties) 551 552 return module 553} 554 555// ndk_library creates a library that exposes a stub implementation of functions 556// and variables for use at build time only. 557func NdkLibraryFactory() android.Module { 558 module := newStubLibrary() 559 android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibBoth) 560 return module 561} 562