1// Copyright 2019 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 java 16 17import ( 18 "path/filepath" 19 "sort" 20 "strings" 21 22 "android/soong/android" 23 "android/soong/dexpreopt" 24 25 "github.com/google/blueprint/pathtools" 26 "github.com/google/blueprint/proptools" 27) 28 29func init() { 30 android.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory) 31} 32 33// The image "location" is a symbolic path that with multiarchitecture 34// support doesn't really exist on the device. Typically it is 35// /system/framework/boot.art and should be the same for all supported 36// architectures on the device. The concrete architecture specific 37// content actually ends up in a "filename" that contains an 38// architecture specific directory name such as arm, arm64, mips, 39// mips64, x86, x86_64. 40// 41// Here are some example values for an x86_64 / x86 configuration: 42// 43// bootImages["x86_64"] = "out/soong/generic_x86_64/dex_bootjars/system/framework/x86_64/boot.art" 44// dexpreopt.PathToLocation(bootImages["x86_64"], "x86_64") = "out/soong/generic_x86_64/dex_bootjars/system/framework/boot.art" 45// 46// bootImages["x86"] = "out/soong/generic_x86_64/dex_bootjars/system/framework/x86/boot.art" 47// dexpreopt.PathToLocation(bootImages["x86"])= "out/soong/generic_x86_64/dex_bootjars/system/framework/boot.art" 48// 49// The location is passed as an argument to the ART tools like dex2oat instead of the real path. The ART tools 50// will then reconstruct the real path, so the rules must have a dependency on the real path. 51 52type bootImageConfig struct { 53 name string 54 modules []string 55 dexLocations []string 56 dexPaths android.WritablePaths 57 dir android.OutputPath 58 symbolsDir android.OutputPath 59 images map[android.ArchType]android.OutputPath 60} 61 62type bootImage struct { 63 bootImageConfig 64 65 installs map[android.ArchType]android.RuleBuilderInstalls 66 vdexInstalls map[android.ArchType]android.RuleBuilderInstalls 67 unstrippedInstalls map[android.ArchType]android.RuleBuilderInstalls 68 69 profileInstalls android.RuleBuilderInstalls 70} 71 72func newBootImage(ctx android.PathContext, config bootImageConfig) *bootImage { 73 image := &bootImage{ 74 bootImageConfig: config, 75 76 installs: make(map[android.ArchType]android.RuleBuilderInstalls), 77 vdexInstalls: make(map[android.ArchType]android.RuleBuilderInstalls), 78 unstrippedInstalls: make(map[android.ArchType]android.RuleBuilderInstalls), 79 } 80 81 return image 82} 83 84func concat(lists ...[]string) []string { 85 var size int 86 for _, l := range lists { 87 size += len(l) 88 } 89 ret := make([]string, 0, size) 90 for _, l := range lists { 91 ret = append(ret, l...) 92 } 93 return ret 94} 95 96func dexpreoptBootJarsFactory() android.Singleton { 97 return &dexpreoptBootJars{} 98} 99 100func skipDexpreoptBootJars(ctx android.PathContext) bool { 101 if ctx.Config().UnbundledBuild() { 102 return true 103 } 104 105 if len(ctx.Config().Targets[android.Android]) == 0 { 106 // Host-only build 107 return true 108 } 109 110 return false 111} 112 113type dexpreoptBootJars struct { 114 defaultBootImage *bootImage 115 otherImages []*bootImage 116} 117 118// dexpreoptBoot singleton rules 119func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) { 120 if skipDexpreoptBootJars(ctx) { 121 return 122 } 123 124 global := dexpreoptGlobalConfig(ctx) 125 126 // Skip recompiling the boot image for the second sanitization phase. We'll get separate paths 127 // and invalidate first-stage artifacts which are crucial to SANITIZE_LITE builds. 128 // Note: this is technically incorrect. Compiled code contains stack checks which may depend 129 // on ASAN settings. 130 if len(ctx.Config().SanitizeDevice()) == 1 && 131 ctx.Config().SanitizeDevice()[0] == "address" && 132 global.SanitizeLite { 133 return 134 } 135 136 // Always create the default boot image first, to get a unique profile rule for all images. 137 d.defaultBootImage = buildBootImage(ctx, defaultBootImageConfig(ctx)) 138 if global.GenerateApexImage { 139 d.otherImages = append(d.otherImages, buildBootImage(ctx, apexBootImageConfig(ctx))) 140 } 141 142 dumpOatRules(ctx, d.defaultBootImage) 143} 144 145// buildBootImage takes a bootImageConfig, creates rules to build it, and returns a *bootImage. 146func buildBootImage(ctx android.SingletonContext, config bootImageConfig) *bootImage { 147 global := dexpreoptGlobalConfig(ctx) 148 149 image := newBootImage(ctx, config) 150 151 bootDexJars := make(android.Paths, len(image.modules)) 152 153 ctx.VisitAllModules(func(module android.Module) { 154 // Collect dex jar paths for the modules listed above. 155 if j, ok := module.(interface{ DexJar() android.Path }); ok { 156 name := ctx.ModuleName(module) 157 if i := android.IndexList(name, image.modules); i != -1 { 158 bootDexJars[i] = j.DexJar() 159 } 160 } 161 }) 162 163 var missingDeps []string 164 // Ensure all modules were converted to paths 165 for i := range bootDexJars { 166 if bootDexJars[i] == nil { 167 if ctx.Config().AllowMissingDependencies() { 168 missingDeps = append(missingDeps, image.modules[i]) 169 bootDexJars[i] = android.PathForOutput(ctx, "missing") 170 } else { 171 ctx.Errorf("failed to find dex jar path for module %q", 172 image.modules[i]) 173 } 174 } 175 } 176 177 // The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before 178 // the bootclasspath modules have been compiled. Copy the dex jars there so the module rules that have 179 // already been set up can find them. 180 for i := range bootDexJars { 181 ctx.Build(pctx, android.BuildParams{ 182 Rule: android.Cp, 183 Input: bootDexJars[i], 184 Output: image.dexPaths[i], 185 }) 186 } 187 188 profile := bootImageProfileRule(ctx, image, missingDeps) 189 190 if !global.DisablePreopt { 191 targets := ctx.Config().Targets[android.Android] 192 if ctx.Config().SecondArchIsTranslated() { 193 targets = targets[:1] 194 } 195 196 for _, target := range targets { 197 buildBootImageRuleForArch(ctx, image, target.Arch.ArchType, profile, missingDeps) 198 } 199 } 200 201 return image 202} 203 204func buildBootImageRuleForArch(ctx android.SingletonContext, image *bootImage, 205 arch android.ArchType, profile android.Path, missingDeps []string) { 206 207 global := dexpreoptGlobalConfig(ctx) 208 209 symbolsDir := image.symbolsDir.Join(ctx, "system/framework", arch.String()) 210 symbolsFile := symbolsDir.Join(ctx, image.name+".oat") 211 outputDir := image.dir.Join(ctx, "system/framework", arch.String()) 212 outputPath := image.images[arch] 213 oatLocation := pathtools.ReplaceExtension(dexpreopt.PathToLocation(outputPath, arch), "oat") 214 215 rule := android.NewRuleBuilder() 216 rule.MissingDeps(missingDeps) 217 218 rule.Command().Text("mkdir").Flag("-p").Flag(symbolsDir.String()) 219 rule.Command().Text("rm").Flag("-f"). 220 Flag(symbolsDir.Join(ctx, "*.art").String()). 221 Flag(symbolsDir.Join(ctx, "*.oat").String()). 222 Flag(symbolsDir.Join(ctx, "*.invocation").String()) 223 rule.Command().Text("rm").Flag("-f"). 224 Flag(outputDir.Join(ctx, "*.art").String()). 225 Flag(outputDir.Join(ctx, "*.oat").String()). 226 Flag(outputDir.Join(ctx, "*.invocation").String()) 227 228 cmd := rule.Command() 229 230 extraFlags := ctx.Config().Getenv("ART_BOOT_IMAGE_EXTRA_ARGS") 231 if extraFlags == "" { 232 // Use ANDROID_LOG_TAGS to suppress most logging by default... 233 cmd.Text(`ANDROID_LOG_TAGS="*:e"`) 234 } else { 235 // ...unless the boot image is generated specifically for testing, then allow all logging. 236 cmd.Text(`ANDROID_LOG_TAGS="*:v"`) 237 } 238 239 invocationPath := outputPath.ReplaceExtension(ctx, "invocation") 240 241 cmd.Tool(global.Tools.Dex2oat). 242 Flag("--avoid-storing-invocation"). 243 FlagWithOutput("--write-invocation-to=", invocationPath).ImplicitOutput(invocationPath). 244 Flag("--runtime-arg").FlagWithArg("-Xms", global.Dex2oatImageXms). 245 Flag("--runtime-arg").FlagWithArg("-Xmx", global.Dex2oatImageXmx) 246 247 if profile != nil { 248 cmd.FlagWithArg("--compiler-filter=", "speed-profile") 249 cmd.FlagWithInput("--profile-file=", profile) 250 } else if global.PreloadedClasses.Valid() { 251 cmd.FlagWithInput("--image-classes=", global.PreloadedClasses.Path()) 252 } 253 254 if global.DirtyImageObjects.Valid() { 255 cmd.FlagWithInput("--dirty-image-objects=", global.DirtyImageObjects.Path()) 256 } 257 258 cmd. 259 FlagForEachInput("--dex-file=", image.dexPaths.Paths()). 260 FlagForEachArg("--dex-location=", image.dexLocations). 261 Flag("--generate-debug-info"). 262 Flag("--generate-build-id"). 263 FlagWithOutput("--oat-symbols=", symbolsFile). 264 Flag("--strip"). 265 FlagWithOutput("--oat-file=", outputPath.ReplaceExtension(ctx, "oat")). 266 FlagWithArg("--oat-location=", oatLocation). 267 FlagWithOutput("--image=", outputPath). 268 FlagWithArg("--base=", ctx.Config().LibartImgDeviceBaseAddress()). 269 FlagWithArg("--instruction-set=", arch.String()). 270 FlagWithArg("--instruction-set-variant=", global.CpuVariant[arch]). 271 FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch]). 272 FlagWithArg("--android-root=", global.EmptyDirectory). 273 FlagWithArg("--no-inline-from=", "core-oj.jar"). 274 Flag("--abort-on-hard-verifier-error") 275 276 if global.BootFlags != "" { 277 cmd.Flag(global.BootFlags) 278 } 279 280 if extraFlags != "" { 281 cmd.Flag(extraFlags) 282 } 283 284 cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage)) 285 286 installDir := filepath.Join("/system/framework", arch.String()) 287 vdexInstallDir := filepath.Join("/system/framework") 288 289 var extraFiles android.WritablePaths 290 var vdexInstalls android.RuleBuilderInstalls 291 var unstrippedInstalls android.RuleBuilderInstalls 292 293 // dex preopt on the bootclasspath produces multiple files. The first dex file 294 // is converted into to 'name'.art (to match the legacy assumption that 'name'.art 295 // exists), and the rest are converted to 'name'-<jar>.art. 296 // In addition, each .art file has an associated .oat and .vdex file, and an 297 // unstripped .oat file 298 for i, m := range image.modules { 299 name := image.name 300 if i != 0 { 301 name += "-" + m 302 } 303 304 art := outputDir.Join(ctx, name+".art") 305 oat := outputDir.Join(ctx, name+".oat") 306 vdex := outputDir.Join(ctx, name+".vdex") 307 unstrippedOat := symbolsDir.Join(ctx, name+".oat") 308 309 extraFiles = append(extraFiles, art, oat, vdex, unstrippedOat) 310 311 // Install the .oat and .art files. 312 rule.Install(art, filepath.Join(installDir, art.Base())) 313 rule.Install(oat, filepath.Join(installDir, oat.Base())) 314 315 // The vdex files are identical between architectures, install them to a shared location. The Make rules will 316 // only use the install rules for one architecture, and will create symlinks into the architecture-specific 317 // directories. 318 vdexInstalls = append(vdexInstalls, 319 android.RuleBuilderInstall{vdex, filepath.Join(vdexInstallDir, vdex.Base())}) 320 321 // Install the unstripped oat files. The Make rules will put these in $(TARGET_OUT_UNSTRIPPED) 322 unstrippedInstalls = append(unstrippedInstalls, 323 android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())}) 324 } 325 326 cmd.ImplicitOutputs(extraFiles) 327 328 rule.Build(pctx, ctx, image.name+"JarsDexpreopt_"+arch.String(), "dexpreopt "+image.name+" jars "+arch.String()) 329 330 // save output and installed files for makevars 331 image.installs[arch] = rule.Installs() 332 image.vdexInstalls[arch] = vdexInstalls 333 image.unstrippedInstalls[arch] = unstrippedInstalls 334} 335 336const failureMessage = `ERROR: Dex2oat failed to compile a boot image. 337It is likely that the boot classpath is inconsistent. 338Rebuild with ART_BOOT_IMAGE_EXTRA_ARGS="--runtime-arg -verbose:verifier" to see verification errors.` 339 340func bootImageProfileRule(ctx android.SingletonContext, image *bootImage, missingDeps []string) android.WritablePath { 341 global := dexpreoptGlobalConfig(ctx) 342 343 if !global.UseProfileForBootImage || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() { 344 return nil 345 } 346 return ctx.Config().Once(bootImageProfileRuleKey, func() interface{} { 347 tools := global.Tools 348 349 rule := android.NewRuleBuilder() 350 rule.MissingDeps(missingDeps) 351 352 var bootImageProfile android.Path 353 if len(global.BootImageProfiles) > 1 { 354 combinedBootImageProfile := image.dir.Join(ctx, "boot-image-profile.txt") 355 rule.Command().Text("cat").Inputs(global.BootImageProfiles).Text(">").Output(combinedBootImageProfile) 356 bootImageProfile = combinedBootImageProfile 357 } else if len(global.BootImageProfiles) == 1 { 358 bootImageProfile = global.BootImageProfiles[0] 359 } else { 360 // If not set, use the default. Some branches like master-art-host don't have frameworks/base, so manually 361 // handle the case that the default is missing. Those branches won't attempt to build the profile rule, 362 // and if they do they'll get a missing deps error. 363 defaultProfile := "frameworks/base/config/boot-image-profile.txt" 364 path := android.ExistentPathForSource(ctx, defaultProfile) 365 if path.Valid() { 366 bootImageProfile = path.Path() 367 } else { 368 missingDeps = append(missingDeps, defaultProfile) 369 bootImageProfile = android.PathForOutput(ctx, "missing") 370 } 371 } 372 373 profile := image.dir.Join(ctx, "boot.prof") 374 375 rule.Command(). 376 Text(`ANDROID_LOG_TAGS="*:e"`). 377 Tool(tools.Profman). 378 FlagWithInput("--create-profile-from=", bootImageProfile). 379 FlagForEachInput("--apk=", image.dexPaths.Paths()). 380 FlagForEachArg("--dex-location=", image.dexLocations). 381 FlagWithOutput("--reference-profile-file=", profile) 382 383 rule.Install(profile, "/system/etc/boot-image.prof") 384 385 rule.Build(pctx, ctx, "bootJarsProfile", "profile boot jars") 386 387 image.profileInstalls = rule.Installs() 388 389 return profile 390 }).(android.WritablePath) 391} 392 393var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule") 394 395func dumpOatRules(ctx android.SingletonContext, image *bootImage) { 396 var archs []android.ArchType 397 for arch := range image.images { 398 archs = append(archs, arch) 399 } 400 sort.Slice(archs, func(i, j int) bool { return archs[i].String() < archs[j].String() }) 401 402 var allPhonies android.Paths 403 for _, arch := range archs { 404 // Create a rule to call oatdump. 405 output := android.PathForOutput(ctx, "boot."+arch.String()+".oatdump.txt") 406 rule := android.NewRuleBuilder() 407 rule.Command(). 408 // TODO: for now, use the debug version for better error reporting 409 Tool(ctx.Config().HostToolPath(ctx, "oatdumpd")). 410 FlagWithInputList("--runtime-arg -Xbootclasspath:", image.dexPaths.Paths(), ":"). 411 FlagWithList("--runtime-arg -Xbootclasspath-locations:", image.dexLocations, ":"). 412 FlagWithArg("--image=", dexpreopt.PathToLocation(image.images[arch], arch)).Implicit(image.images[arch]). 413 FlagWithOutput("--output=", output). 414 FlagWithArg("--instruction-set=", arch.String()) 415 rule.Build(pctx, ctx, "dump-oat-boot-"+arch.String(), "dump oat boot "+arch.String()) 416 417 // Create a phony rule that depends on the output file and prints the path. 418 phony := android.PathForPhony(ctx, "dump-oat-boot-"+arch.String()) 419 rule = android.NewRuleBuilder() 420 rule.Command(). 421 Implicit(output). 422 ImplicitOutput(phony). 423 Text("echo").FlagWithArg("Output in ", output.String()) 424 rule.Build(pctx, ctx, "phony-dump-oat-boot-"+arch.String(), "dump oat boot "+arch.String()) 425 426 allPhonies = append(allPhonies, phony) 427 } 428 429 phony := android.PathForPhony(ctx, "dump-oat-boot") 430 ctx.Build(pctx, android.BuildParams{ 431 Rule: android.Phony, 432 Output: phony, 433 Inputs: allPhonies, 434 Description: "dump-oat-boot", 435 }) 436 437} 438 439// Export paths for default boot image to Make 440func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) { 441 image := d.defaultBootImage 442 if image != nil { 443 ctx.Strict("DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED", image.profileInstalls.String()) 444 ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(image.dexPaths.Strings(), " ")) 445 ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS", strings.Join(image.dexLocations, " ")) 446 447 var imageNames []string 448 for _, current := range append(d.otherImages, image) { 449 imageNames = append(imageNames, current.name) 450 var arches []android.ArchType 451 for arch, _ := range current.images { 452 arches = append(arches, arch) 453 } 454 455 sort.Slice(arches, func(i, j int) bool { return arches[i].String() < arches[j].String() }) 456 457 for _, arch := range arches { 458 ctx.Strict("DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.vdexInstalls[arch].String()) 459 ctx.Strict("DEXPREOPT_IMAGE_"+current.name+"_"+arch.String(), current.images[arch].String()) 460 ctx.Strict("DEXPREOPT_IMAGE_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.installs[arch].String()) 461 ctx.Strict("DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_"+current.name+"_"+arch.String(), current.unstrippedInstalls[arch].String()) 462 } 463 } 464 ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(imageNames, " ")) 465 } 466} 467