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 17// The platform needs to provide the following artifacts for the NDK: 18// 1. Bionic headers. 19// 2. Platform API headers. 20// 3. NDK stub shared libraries. 21// 4. Bionic static libraries. 22// 23// TODO(danalbert): All of the above need to include NOTICE files. 24// 25// Components 1 and 2: Headers 26// The bionic and platform API headers are generalized into a single 27// `ndk_headers` rule. This rule has a `from` property that indicates a base 28// directory from which headers are to be taken, and a `to` property that 29// indicates where in the sysroot they should reside relative to usr/include. 30// There is also a `srcs` property that is glob compatible for specifying which 31// headers to include. 32// 33// Component 3: Stub Libraries 34// The shared libraries in the NDK are not the actual shared libraries they 35// refer to (to prevent people from accidentally loading them), but stub 36// libraries with placeholder implementations of everything for use at build time 37// only. 38// 39// Since we don't actually need to know anything about the stub libraries aside 40// from a list of functions and globals to be exposed, we can create these for 41// every platform level in the current tree. This is handled by the 42// ndk_library rule. 43// 44// Component 4: Static Libraries 45// The NDK only provides static libraries for bionic, not the platform APIs. 46// Since these need to be the actual implementation, we can't build old versions 47// in the current platform tree. As such, legacy versions are checked in 48// prebuilt to development/ndk, and a current version is built and archived as 49// part of the platform build. The platfrom already builds these libraries, our 50// NDK build rules only need to archive them for retrieval so they can be added 51// to the prebuilts. 52// 53// TODO(danalbert): Write `ndk_static_library` rule. 54 55import ( 56 "fmt" 57 "path/filepath" 58 "strings" 59 60 "android/soong/android" 61 62 "github.com/google/blueprint" 63) 64 65var ( 66 verifyCCompat = pctx.AndroidStaticRule("verifyCCompat", 67 blueprint.RuleParams{ 68 Command: "$ccCmd -x c -fsyntax-only $flags $in && touch $out", 69 CommandDeps: []string{"$ccCmd"}, 70 }, 71 "ccCmd", 72 "flags", 73 ) 74) 75 76func init() { 77 RegisterNdkModuleTypes(android.InitRegistrationContext) 78} 79 80func RegisterNdkModuleTypes(ctx android.RegistrationContext) { 81 ctx.RegisterModuleType("ndk_headers", NdkHeadersFactory) 82 ctx.RegisterModuleType("ndk_library", NdkLibraryFactory) 83 ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory) 84 ctx.RegisterParallelSingletonType("ndk", NdkSingleton) 85} 86 87func getNdkInstallBase(ctx android.PathContext) android.OutputPath { 88 return android.PathForNdkInstall(ctx) 89} 90 91// Returns the main install directory for the NDK sysroot. Usable with --sysroot. 92func getNdkSysrootBase(ctx android.PathContext) android.OutputPath { 93 return getNdkInstallBase(ctx).Join(ctx, "sysroot") 94} 95 96// The base timestamp file depends on the NDK headers and stub shared libraries, 97// but not the static libraries. This distinction is needed because the static 98// libraries themselves might need to depend on the base sysroot. 99func getNdkBaseTimestampFile(ctx android.PathContext) android.WritablePath { 100 return android.PathForOutput(ctx, "ndk_base.timestamp") 101} 102 103// The headers timestamp file depends only on the NDK headers. 104// This is used mainly for .tidy files that do not need any stub libraries. 105func getNdkHeadersTimestampFile(ctx android.PathContext) android.WritablePath { 106 return android.PathForOutput(ctx, "ndk_headers.timestamp") 107} 108 109// The full timestamp file depends on the base timestamp *and* the static 110// libraries. 111func getNdkFullTimestampFile(ctx android.PathContext) android.WritablePath { 112 return android.PathForOutput(ctx, "ndk.timestamp") 113} 114 115// The list of all NDK headers as they are located in the repo. 116// Used for ABI monitoring to track only structures defined in NDK headers. 117func getNdkABIHeadersFile(ctx android.PathContext) android.WritablePath { 118 return android.PathForOutput(ctx, "ndk_abi_headers.txt") 119} 120 121func verifyNdkHeaderIsCCompatible(ctx android.SingletonContext, 122 src android.Path, dest android.Path) android.Path { 123 sysrootInclude := getCurrentIncludePath(ctx) 124 baseOutputDir := android.PathForOutput(ctx, "c-compat-verification") 125 installRelPath, err := filepath.Rel(sysrootInclude.String(), dest.String()) 126 if err != nil { 127 ctx.Errorf("filepath.Rel(%q, %q) failed: %s", dest, sysrootInclude, err) 128 } 129 output := baseOutputDir.Join(ctx, installRelPath) 130 ctx.Build(pctx, android.BuildParams{ 131 Rule: verifyCCompat, 132 Description: fmt.Sprintf("Verifying C compatibility of %s", src), 133 Output: output, 134 Input: dest, 135 // Ensures that all the headers in the sysroot are already installed 136 // before testing any of the headers for C compatibility, and also that 137 // the check will be re-run whenever the sysroot changes. This is 138 // necessary because many of the NDK headers depend on other NDK 139 // headers, but we don't have explicit dependency tracking for that. 140 Implicits: []android.Path{getNdkHeadersTimestampFile(ctx)}, 141 Args: map[string]string{ 142 "ccCmd": "${config.ClangBin}/clang", 143 "flags": fmt.Sprintf( 144 // Ideally we'd check each ABI, multiple API levels, 145 // fortify/non-fortify, and a handful of other variations. It's 146 // a lot more difficult to do that though, and would eat up more 147 // build time. All the problems we've seen so far that this 148 // check would catch have been in arch-generic and 149 // minSdkVersion-generic code in frameworks though, so this is a 150 // good place to start. 151 "-target aarch64-linux-android%d --sysroot %s", 152 android.FutureApiLevel.FinalOrFutureInt(), 153 getNdkSysrootBase(ctx).String(), 154 ), 155 }, 156 }) 157 return output 158} 159 160func NdkSingleton() android.Singleton { 161 return &ndkSingleton{} 162} 163 164// Collect all NDK exported headers paths into a file that is used to 165// detect public types that should be ABI monitored. 166// 167// Assume that we have the following code in exported header: 168// 169// typedef struct Context Context; 170// typedef struct Output { 171// ... 172// } Output; 173// void DoSomething(Context* ctx, Output* output); 174// 175// If none of public headers exported to end-users contain definition of 176// "struct Context", then "struct Context" layout and members shouldn't be 177// monitored. However we use DWARF information from a real library, which 178// may have access to the definition of "string Context" from 179// implementation headers, and it will leak to ABI. 180// 181// STG tool doesn't access source and header files, only DWARF information 182// from compiled library. And the DWARF contains file name where a type is 183// defined. So we need a rule to build a list of paths to public headers, 184// so STG can distinguish private types from public and do not monitor 185// private types that are not accessible to library users. 186func writeNdkAbiSrcFilter(ctx android.BuilderContext, 187 headerSrcPaths android.Paths, outputFile android.WritablePath) { 188 var filterBuilder strings.Builder 189 filterBuilder.WriteString("[decl_file_allowlist]\n") 190 for _, headerSrcPath := range headerSrcPaths { 191 filterBuilder.WriteString(headerSrcPath.String()) 192 filterBuilder.WriteString("\n") 193 } 194 195 android.WriteFileRule(ctx, outputFile, filterBuilder.String()) 196} 197 198type ndkSingleton struct{} 199 200type srcDestPair struct { 201 src android.Path 202 dest android.Path 203} 204 205func (n *ndkSingleton) GenerateBuildActions(ctx android.SingletonContext) { 206 var staticLibInstallPaths android.Paths 207 var headerSrcPaths android.Paths 208 var headerInstallPaths android.Paths 209 var headersToVerify []srcDestPair 210 var headerCCompatVerificationTimestampPaths android.Paths 211 var installPaths android.Paths 212 var licensePaths android.Paths 213 ctx.VisitAllModuleProxies(func(module android.ModuleProxy) { 214 215 if !android.OtherModulePointerProviderOrDefault(ctx, module, android.CommonModuleInfoProvider).Enabled { 216 return 217 } 218 219 if m, ok := android.OtherModuleProvider(ctx, module, NdkHeaderInfoProvider); ok { 220 headerSrcPaths = append(headerSrcPaths, m.SrcPaths...) 221 headerInstallPaths = append(headerInstallPaths, m.InstallPaths...) 222 if !m.SkipVerification { 223 for i, installPath := range m.InstallPaths { 224 headersToVerify = append(headersToVerify, srcDestPair{ 225 src: m.SrcPaths[i], 226 dest: installPath, 227 }) 228 } 229 } 230 installPaths = append(installPaths, m.InstallPaths...) 231 licensePaths = append(licensePaths, m.LicensePath) 232 } 233 234 if m, ok := android.OtherModuleProvider(ctx, module, NdkPreprocessedHeaderInfoProvider); ok { 235 headerSrcPaths = append(headerSrcPaths, m.SrcPaths...) 236 headerInstallPaths = append(headerInstallPaths, m.InstallPaths...) 237 if !m.SkipVerification { 238 for i, installPath := range m.InstallPaths { 239 headersToVerify = append(headersToVerify, srcDestPair{ 240 src: m.SrcPaths[i], 241 dest: installPath, 242 }) 243 } 244 } 245 installPaths = append(installPaths, m.InstallPaths...) 246 licensePaths = append(licensePaths, m.LicensePath) 247 } 248 249 if ccInfo, ok := android.OtherModuleProvider(ctx, module, CcInfoProvider); ok { 250 if installer := ccInfo.InstallerInfo; installer != nil && installer.StubDecoratorInfo != nil && 251 ccInfo.LibraryInfo != nil && ccInfo.LibraryInfo.BuildStubs { 252 installPaths = append(installPaths, installer.StubDecoratorInfo.InstallPath) 253 } 254 255 if ccInfo.LinkerInfo != nil { 256 if library := ccInfo.LinkerInfo.LibraryDecoratorInfo; library != nil { 257 if library.NdkSysrootPath != nil { 258 staticLibInstallPaths = append( 259 staticLibInstallPaths, library.NdkSysrootPath) 260 } 261 } 262 263 if object := ccInfo.LinkerInfo.ObjectLinkerInfo; object != nil { 264 if object.NdkSysrootPath != nil { 265 staticLibInstallPaths = append( 266 staticLibInstallPaths, object.NdkSysrootPath) 267 } 268 } 269 } 270 } 271 }) 272 273 // Include only a single copy of each license file. The Bionic NOTICE is 274 // long and is referenced by multiple Bionic modules. 275 licensePaths = android.FirstUniquePaths(licensePaths) 276 277 combinedLicense := getNdkInstallBase(ctx).Join(ctx, "NOTICE") 278 ctx.Build(pctx, android.BuildParams{ 279 Rule: android.Cat, 280 Description: "combine licenses", 281 Output: combinedLicense, 282 Inputs: licensePaths, 283 }) 284 285 baseDepPaths := append(installPaths, combinedLicense) 286 287 ctx.Build(pctx, android.BuildParams{ 288 Rule: android.Touch, 289 Output: getNdkBaseTimestampFile(ctx), 290 Implicits: baseDepPaths, 291 Validation: getNdkAbiDiffTimestampFile(ctx), 292 }) 293 294 ctx.Build(pctx, android.BuildParams{ 295 Rule: android.Touch, 296 Output: getNdkHeadersTimestampFile(ctx), 297 Implicits: headerInstallPaths, 298 }) 299 300 for _, srcDestPair := range headersToVerify { 301 headerCCompatVerificationTimestampPaths = append( 302 headerCCompatVerificationTimestampPaths, 303 verifyNdkHeaderIsCCompatible(ctx, srcDestPair.src, srcDestPair.dest)) 304 } 305 306 writeNdkAbiSrcFilter(ctx, headerSrcPaths, getNdkABIHeadersFile(ctx)) 307 308 fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx)) 309 310 // There's a phony "ndk" rule defined in core/main.mk that depends on this. 311 // `m ndk` will build the sysroots for the architectures in the current 312 // lunch target. `build/soong/scripts/build-ndk-prebuilts.sh` will build the 313 // sysroots for all the NDK architectures and package them so they can be 314 // imported into the NDK's build. 315 ctx.Build(pctx, android.BuildParams{ 316 Rule: android.Touch, 317 Output: getNdkFullTimestampFile(ctx), 318 Implicits: append(fullDepPaths, headerCCompatVerificationTimestampPaths...), 319 }) 320} 321