1 //===- Driver.cpp ---------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "Driver.h" 10 #include "Config.h" 11 #include "DriverUtils.h" 12 #include "InputFiles.h" 13 #include "ObjC.h" 14 #include "OutputSection.h" 15 #include "OutputSegment.h" 16 #include "SymbolTable.h" 17 #include "Symbols.h" 18 #include "SyntheticSections.h" 19 #include "Target.h" 20 #include "Writer.h" 21 22 #include "lld/Common/Args.h" 23 #include "lld/Common/Driver.h" 24 #include "lld/Common/ErrorHandler.h" 25 #include "lld/Common/LLVM.h" 26 #include "lld/Common/Memory.h" 27 #include "lld/Common/Version.h" 28 #include "llvm/ADT/DenseSet.h" 29 #include "llvm/ADT/StringExtras.h" 30 #include "llvm/ADT/StringRef.h" 31 #include "llvm/BinaryFormat/MachO.h" 32 #include "llvm/BinaryFormat/Magic.h" 33 #include "llvm/Object/Archive.h" 34 #include "llvm/Option/ArgList.h" 35 #include "llvm/Option/Option.h" 36 #include "llvm/Support/FileSystem.h" 37 #include "llvm/Support/Host.h" 38 #include "llvm/Support/MemoryBuffer.h" 39 #include "llvm/Support/Path.h" 40 41 #include <algorithm> 42 43 using namespace llvm; 44 using namespace llvm::MachO; 45 using namespace llvm::object; 46 using namespace llvm::opt; 47 using namespace llvm::sys; 48 using namespace lld; 49 using namespace lld::macho; 50 51 Configuration *lld::macho::config; 52 53 // Create prefix string literals used in Options.td 54 #define PREFIX(NAME, VALUE) const char *NAME[] = VALUE; 55 #include "Options.inc" 56 #undef PREFIX 57 58 // Create table mapping all options defined in Options.td 59 static const opt::OptTable::Info optInfo[] = { 60 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ 61 {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ 62 X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, 63 #include "Options.inc" 64 #undef OPTION 65 }; 66 67 MachOOptTable::MachOOptTable() : OptTable(optInfo) {} 68 69 opt::InputArgList MachOOptTable::parse(ArrayRef<const char *> argv) { 70 // Make InputArgList from string vectors. 71 unsigned missingIndex; 72 unsigned missingCount; 73 SmallVector<const char *, 256> vec(argv.data(), argv.data() + argv.size()); 74 75 opt::InputArgList args = ParseArgs(vec, missingIndex, missingCount); 76 77 if (missingCount) 78 error(Twine(args.getArgString(missingIndex)) + ": missing argument"); 79 80 for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) 81 error("unknown argument: " + arg->getSpelling()); 82 return args; 83 } 84 85 void MachOOptTable::printHelp(const char *argv0, bool showHidden) const { 86 PrintHelp(lld::outs(), (std::string(argv0) + " [options] file...").c_str(), 87 "LLVM Linker", showHidden); 88 lld::outs() << "\n"; 89 } 90 91 static Optional<std::string> findWithExtension(StringRef base, 92 ArrayRef<StringRef> extensions) { 93 for (StringRef ext : extensions) { 94 Twine location = base + ext; 95 if (fs::exists(location)) 96 return location.str(); 97 } 98 return {}; 99 } 100 101 static Optional<std::string> findLibrary(StringRef name) { 102 llvm::SmallString<261> location; 103 for (StringRef dir : config->librarySearchPaths) { 104 location = dir; 105 path::append(location, Twine("lib") + name); 106 if (Optional<std::string> path = 107 findWithExtension(location, {".tbd", ".dylib", ".a"})) 108 return path; 109 } 110 return {}; 111 } 112 113 static Optional<std::string> findFramework(StringRef name) { 114 llvm::SmallString<260> symlink; 115 StringRef suffix; 116 std::tie(name, suffix) = name.split(","); 117 for (StringRef dir : config->frameworkSearchPaths) { 118 symlink = dir; 119 path::append(symlink, name + ".framework", name); 120 121 if (!suffix.empty()) { 122 // NOTE: we must resolve the symlink before trying the suffixes, because 123 // there are no symlinks for the suffixed paths. 124 llvm::SmallString<260> location; 125 if (!fs::real_path(symlink, location)) { 126 // only append suffix if realpath() succeeds 127 Twine suffixed = location + suffix; 128 if (fs::exists(suffixed)) 129 return suffixed.str(); 130 } 131 // Suffix lookup failed, fall through to the no-suffix case. 132 } 133 134 if (Optional<std::string> path = resolveDylibPath(symlink)) 135 return path; 136 } 137 return {}; 138 } 139 140 static TargetInfo *createTargetInfo(opt::InputArgList &args) { 141 StringRef arch = args.getLastArgValue(OPT_arch, "x86_64"); 142 config->arch = llvm::MachO::getArchitectureFromName( 143 args.getLastArgValue(OPT_arch, arch)); 144 switch (config->arch) { 145 case llvm::MachO::AK_x86_64: 146 case llvm::MachO::AK_x86_64h: 147 return createX86_64TargetInfo(); 148 default: 149 fatal("missing or unsupported -arch " + arch); 150 } 151 } 152 153 static bool warnIfNotDirectory(StringRef option, StringRef path) { 154 if (!fs::exists(path)) { 155 warn("directory not found for option -" + option + path); 156 return false; 157 } else if (!fs::is_directory(path)) { 158 warn("option -" + option + path + " references a non-directory path"); 159 return false; 160 } 161 return true; 162 } 163 164 static void getSearchPaths(std::vector<StringRef> &paths, unsigned optionCode, 165 opt::InputArgList &args, 166 const std::vector<StringRef> &roots, 167 const SmallVector<StringRef, 2> &systemPaths) { 168 StringRef optionLetter{optionCode == OPT_F ? "F" : "L"}; 169 for (StringRef path : args::getStrings(args, optionCode)) { 170 // NOTE: only absolute paths are re-rooted to syslibroot(s) 171 bool found = false; 172 if (path::is_absolute(path, path::Style::posix)) { 173 for (StringRef root : roots) { 174 SmallString<261> buffer(root); 175 path::append(buffer, path); 176 // Do not warn about paths that are computed via the syslib roots 177 if (fs::is_directory(buffer)) { 178 paths.push_back(saver.save(buffer.str())); 179 found = true; 180 } 181 } 182 } 183 if (!found && warnIfNotDirectory(optionLetter, path)) 184 paths.push_back(path); 185 } 186 187 // `-Z` suppresses the standard "system" search paths. 188 if (args.hasArg(OPT_Z)) 189 return; 190 191 for (auto const &path : systemPaths) { 192 for (auto root : roots) { 193 SmallString<261> buffer(root); 194 path::append(buffer, path); 195 if (warnIfNotDirectory(optionLetter, buffer)) 196 paths.push_back(saver.save(buffer.str())); 197 } 198 } 199 } 200 201 static void getLibrarySearchPaths(opt::InputArgList &args, 202 const std::vector<StringRef> &roots, 203 std::vector<StringRef> &paths) { 204 getSearchPaths(paths, OPT_L, args, roots, {"/usr/lib", "/usr/local/lib"}); 205 } 206 207 static void getFrameworkSearchPaths(opt::InputArgList &args, 208 const std::vector<StringRef> &roots, 209 std::vector<StringRef> &paths) { 210 getSearchPaths(paths, OPT_F, args, roots, 211 {"/Library/Frameworks", "/System/Library/Frameworks"}); 212 } 213 214 // Returns slices of MB by parsing MB as an archive file. 215 // Each slice consists of a member file in the archive. 216 static std::vector<MemoryBufferRef> getArchiveMembers(MemoryBufferRef mb) { 217 std::unique_ptr<Archive> file = 218 CHECK(Archive::create(mb), 219 mb.getBufferIdentifier() + ": failed to parse archive"); 220 221 std::vector<MemoryBufferRef> v; 222 Error err = Error::success(); 223 for (const Archive::Child &c : file->children(err)) { 224 MemoryBufferRef mbref = 225 CHECK(c.getMemoryBufferRef(), 226 mb.getBufferIdentifier() + 227 ": could not get the buffer for a child of the archive"); 228 v.push_back(mbref); 229 } 230 if (err) 231 fatal(mb.getBufferIdentifier() + 232 ": Archive::children failed: " + toString(std::move(err))); 233 234 return v; 235 } 236 237 static void addFile(StringRef path) { 238 Optional<MemoryBufferRef> buffer = readFile(path); 239 if (!buffer) 240 return; 241 MemoryBufferRef mbref = *buffer; 242 243 switch (identify_magic(mbref.getBuffer())) { 244 case file_magic::archive: { 245 std::unique_ptr<object::Archive> file = CHECK( 246 object::Archive::create(mbref), path + ": failed to parse archive"); 247 248 if (!file->isEmpty() && !file->hasSymbolTable()) 249 error(path + ": archive has no index; run ranlib to add one"); 250 251 if (config->allLoad) { 252 if (Optional<MemoryBufferRef> buffer = readFile(path)) 253 for (MemoryBufferRef member : getArchiveMembers(*buffer)) 254 inputFiles.push_back(make<ObjFile>(member)); 255 } else if (config->forceLoadObjC) { 256 for (const object::Archive::Symbol &sym : file->symbols()) 257 if (sym.getName().startswith(objc::klass)) 258 symtab->addUndefined(sym.getName()); 259 260 // TODO: no need to look for ObjC sections for a given archive member if 261 // we already found that it contains an ObjC symbol. We should also 262 // consider creating a LazyObjFile class in order to avoid double-loading 263 // these files here and below (as part of the ArchiveFile). 264 if (Optional<MemoryBufferRef> buffer = readFile(path)) 265 for (MemoryBufferRef member : getArchiveMembers(*buffer)) 266 if (hasObjCSection(member)) 267 inputFiles.push_back(make<ObjFile>(member)); 268 } 269 270 inputFiles.push_back(make<ArchiveFile>(std::move(file))); 271 break; 272 } 273 case file_magic::macho_object: 274 inputFiles.push_back(make<ObjFile>(mbref)); 275 break; 276 case file_magic::macho_dynamically_linked_shared_lib: 277 inputFiles.push_back(make<DylibFile>(mbref)); 278 break; 279 case file_magic::tapi_file: { 280 Optional<DylibFile *> dylibFile = makeDylibFromTAPI(mbref); 281 if (!dylibFile) 282 return; 283 inputFiles.push_back(*dylibFile); 284 break; 285 } 286 default: 287 error(path + ": unhandled file type"); 288 } 289 } 290 291 static void addFileList(StringRef path) { 292 Optional<MemoryBufferRef> buffer = readFile(path); 293 if (!buffer) 294 return; 295 MemoryBufferRef mbref = *buffer; 296 for (StringRef path : args::getLines(mbref)) 297 addFile(path); 298 } 299 300 static void forceLoadArchive(StringRef path) { 301 if (Optional<MemoryBufferRef> buffer = readFile(path)) 302 for (MemoryBufferRef member : getArchiveMembers(*buffer)) 303 inputFiles.push_back(make<ObjFile>(member)); 304 } 305 306 static std::array<StringRef, 6> archNames{"arm", "arm64", "i386", 307 "x86_64", "ppc", "ppc64"}; 308 static bool isArchString(StringRef s) { 309 static DenseSet<StringRef> archNamesSet(archNames.begin(), archNames.end()); 310 return archNamesSet.find(s) != archNamesSet.end(); 311 } 312 313 // An order file has one entry per line, in the following format: 314 // 315 // <arch>:<object file>:<symbol name> 316 // 317 // <arch> and <object file> are optional. If not specified, then that entry 318 // matches any symbol of that name. 319 // 320 // If a symbol is matched by multiple entries, then it takes the lowest-ordered 321 // entry (the one nearest to the front of the list.) 322 // 323 // The file can also have line comments that start with '#'. 324 static void parseOrderFile(StringRef path) { 325 Optional<MemoryBufferRef> buffer = readFile(path); 326 if (!buffer) { 327 error("Could not read order file at " + path); 328 return; 329 } 330 331 MemoryBufferRef mbref = *buffer; 332 size_t priority = std::numeric_limits<size_t>::max(); 333 for (StringRef rest : args::getLines(mbref)) { 334 StringRef arch, objectFile, symbol; 335 336 std::array<StringRef, 3> fields; 337 uint8_t fieldCount = 0; 338 while (rest != "" && fieldCount < 3) { 339 std::pair<StringRef, StringRef> p = getToken(rest, ": \t\n\v\f\r"); 340 StringRef tok = p.first; 341 rest = p.second; 342 343 // Check if we have a comment 344 if (tok == "" || tok[0] == '#') 345 break; 346 347 fields[fieldCount++] = tok; 348 } 349 350 switch (fieldCount) { 351 case 3: 352 arch = fields[0]; 353 objectFile = fields[1]; 354 symbol = fields[2]; 355 break; 356 case 2: 357 (isArchString(fields[0]) ? arch : objectFile) = fields[0]; 358 symbol = fields[1]; 359 break; 360 case 1: 361 symbol = fields[0]; 362 break; 363 case 0: 364 break; 365 default: 366 llvm_unreachable("too many fields in order file"); 367 } 368 369 if (!arch.empty()) { 370 if (!isArchString(arch)) { 371 error("invalid arch \"" + arch + "\" in order file: expected one of " + 372 llvm::join(archNames, ", ")); 373 continue; 374 } 375 376 // TODO: Update when we extend support for other archs 377 if (arch != "x86_64") 378 continue; 379 } 380 381 if (!objectFile.empty() && !objectFile.endswith(".o")) { 382 error("invalid object file name \"" + objectFile + 383 "\" in order file: should end with .o"); 384 continue; 385 } 386 387 if (!symbol.empty()) { 388 SymbolPriorityEntry &entry = config->priorities[symbol]; 389 if (!objectFile.empty()) 390 entry.objectFiles.insert(std::make_pair(objectFile, priority)); 391 else 392 entry.anyObjectFile = std::max(entry.anyObjectFile, priority); 393 } 394 395 --priority; 396 } 397 } 398 399 // We expect sub-library names of the form "libfoo", which will match a dylib 400 // with a path of .*/libfoo.{dylib, tbd}. 401 // XXX ld64 seems to ignore the extension entirely when matching sub-libraries; 402 // I'm not sure what the use case for that is. 403 static bool markSubLibrary(StringRef searchName) { 404 for (InputFile *file : inputFiles) { 405 if (auto *dylibFile = dyn_cast<DylibFile>(file)) { 406 StringRef filename = path::filename(dylibFile->getName()); 407 if (filename.consume_front(searchName) && 408 (filename == ".dylib" || filename == ".tbd")) { 409 dylibFile->reexport = true; 410 return true; 411 } 412 } 413 } 414 return false; 415 } 416 417 static inline char toLowerDash(char x) { 418 if (x >= 'A' && x <= 'Z') 419 return x - 'A' + 'a'; 420 else if (x == ' ') 421 return '-'; 422 return x; 423 } 424 425 static std::string lowerDash(StringRef s) { 426 return std::string(map_iterator(s.begin(), toLowerDash), 427 map_iterator(s.end(), toLowerDash)); 428 } 429 430 static void handlePlatformVersion(const opt::Arg *arg) { 431 StringRef platformStr = arg->getValue(0); 432 StringRef minVersionStr = arg->getValue(1); 433 StringRef sdkVersionStr = arg->getValue(2); 434 435 // TODO(compnerd) see if we can generate this case list via XMACROS 436 config->platform.kind = 437 llvm::StringSwitch<llvm::MachO::PlatformKind>(lowerDash(platformStr)) 438 .Cases("macos", "1", llvm::MachO::PlatformKind::macOS) 439 .Cases("ios", "2", llvm::MachO::PlatformKind::iOS) 440 .Cases("tvos", "3", llvm::MachO::PlatformKind::tvOS) 441 .Cases("watchos", "4", llvm::MachO::PlatformKind::watchOS) 442 .Cases("bridgeos", "5", llvm::MachO::PlatformKind::bridgeOS) 443 .Cases("mac-catalyst", "6", llvm::MachO::PlatformKind::macCatalyst) 444 .Cases("ios-simulator", "7", llvm::MachO::PlatformKind::iOSSimulator) 445 .Cases("tvos-simulator", "8", 446 llvm::MachO::PlatformKind::tvOSSimulator) 447 .Cases("watchos-simulator", "9", 448 llvm::MachO::PlatformKind::watchOSSimulator) 449 .Default(llvm::MachO::PlatformKind::unknown); 450 if (config->platform.kind == llvm::MachO::PlatformKind::unknown) 451 error(Twine("malformed platform: ") + platformStr); 452 // TODO: check validity of version strings, which varies by platform 453 // NOTE: ld64 accepts version strings with 5 components 454 // llvm::VersionTuple accepts no more than 4 components 455 // Has Apple ever published version strings with 5 components? 456 if (config->platform.minimum.tryParse(minVersionStr)) 457 error(Twine("malformed minimum version: ") + minVersionStr); 458 if (config->platform.sdk.tryParse(sdkVersionStr)) 459 error(Twine("malformed sdk version: ") + sdkVersionStr); 460 } 461 462 static void warnIfDeprecatedOption(const opt::Option &opt) { 463 if (!opt.getGroup().isValid()) 464 return; 465 if (opt.getGroup().getID() == OPT_grp_deprecated) { 466 warn("Option `" + opt.getPrefixedName() + "' is deprecated in ld64:"); 467 warn(opt.getHelpText()); 468 } 469 } 470 471 static void warnIfUnimplementedOption(const opt::Option &opt) { 472 if (!opt.getGroup().isValid()) 473 return; 474 switch (opt.getGroup().getID()) { 475 case OPT_grp_deprecated: 476 // warn about deprecated options elsewhere 477 break; 478 case OPT_grp_undocumented: 479 warn("Option `" + opt.getPrefixedName() + 480 "' is undocumented. Should lld implement it?"); 481 break; 482 case OPT_grp_obsolete: 483 warn("Option `" + opt.getPrefixedName() + 484 "' is obsolete. Please modernize your usage."); 485 break; 486 case OPT_grp_ignored: 487 warn("Option `" + opt.getPrefixedName() + "' is ignored."); 488 break; 489 default: 490 warn("Option `" + opt.getPrefixedName() + 491 "' is not yet implemented. Stay tuned..."); 492 break; 493 } 494 } 495 496 bool macho::link(llvm::ArrayRef<const char *> argsArr, bool canExitEarly, 497 raw_ostream &stdoutOS, raw_ostream &stderrOS) { 498 lld::stdoutOS = &stdoutOS; 499 lld::stderrOS = &stderrOS; 500 501 stderrOS.enable_colors(stderrOS.has_colors()); 502 // TODO: Set up error handler properly, e.g. the errorLimitExceededMsg 503 504 MachOOptTable parser; 505 opt::InputArgList args = parser.parse(argsArr.slice(1)); 506 507 if (args.hasArg(OPT_help_hidden)) { 508 parser.printHelp(argsArr[0], /*showHidden=*/true); 509 return true; 510 } else if (args.hasArg(OPT_help)) { 511 parser.printHelp(argsArr[0], /*showHidden=*/false); 512 return true; 513 } 514 515 config = make<Configuration>(); 516 symtab = make<SymbolTable>(); 517 target = createTargetInfo(args); 518 519 config->entry = symtab->addUndefined(args.getLastArgValue(OPT_e, "_main")); 520 config->outputFile = args.getLastArgValue(OPT_o, "a.out"); 521 config->installName = 522 args.getLastArgValue(OPT_install_name, config->outputFile); 523 config->headerPad = args::getHex(args, OPT_headerpad, /*Default=*/32); 524 config->outputType = args.hasArg(OPT_dylib) ? MH_DYLIB : MH_EXECUTE; 525 config->runtimePaths = args::getStrings(args, OPT_rpath); 526 config->allLoad = args.hasArg(OPT_all_load); 527 528 std::vector<StringRef> &roots = config->systemLibraryRoots; 529 for (const Arg *arg : args.filtered(OPT_syslibroot)) 530 roots.push_back(arg->getValue()); 531 // NOTE: the final `-syslibroot` being `/` will ignore all roots 532 if (roots.size() && roots.back() == "/") 533 roots.clear(); 534 // NOTE: roots can never be empty - add an empty root to simplify the library 535 // and framework search path computation. 536 if (roots.empty()) 537 roots.emplace_back(""); 538 539 getLibrarySearchPaths(args, roots, config->librarySearchPaths); 540 getFrameworkSearchPaths(args, roots, config->frameworkSearchPaths); 541 config->forceLoadObjC = args.hasArg(OPT_ObjC); 542 543 if (args.hasArg(OPT_v)) { 544 message(getLLDVersion()); 545 message(StringRef("Library search paths:") + 546 (config->librarySearchPaths.size() 547 ? "\n\t" + llvm::join(config->librarySearchPaths, "\n\t") 548 : "")); 549 message(StringRef("Framework search paths:") + 550 (config->frameworkSearchPaths.size() 551 ? "\n\t" + llvm::join(config->frameworkSearchPaths, "\n\t") 552 : "")); 553 freeArena(); 554 return !errorCount(); 555 } 556 557 for (const auto &arg : args) { 558 const auto &opt = arg->getOption(); 559 warnIfDeprecatedOption(opt); 560 switch (arg->getOption().getID()) { 561 case OPT_INPUT: 562 addFile(arg->getValue()); 563 break; 564 case OPT_filelist: 565 addFileList(arg->getValue()); 566 break; 567 case OPT_force_load: 568 forceLoadArchive(arg->getValue()); 569 break; 570 case OPT_l: { 571 StringRef name = arg->getValue(); 572 if (Optional<std::string> path = findLibrary(name)) { 573 addFile(*path); 574 break; 575 } 576 error("library not found for -l" + name); 577 break; 578 } 579 case OPT_framework: { 580 StringRef name = arg->getValue(); 581 if (Optional<std::string> path = findFramework(name)) { 582 addFile(*path); 583 break; 584 } 585 error("framework not found for -framework " + name); 586 break; 587 } 588 case OPT_platform_version: 589 handlePlatformVersion(arg); 590 break; 591 case OPT_all_load: 592 case OPT_o: 593 case OPT_dylib: 594 case OPT_e: 595 case OPT_F: 596 case OPT_L: 597 case OPT_ObjC: 598 case OPT_headerpad: 599 case OPT_install_name: 600 case OPT_rpath: 601 case OPT_sub_library: 602 case OPT_Z: 603 case OPT_arch: 604 case OPT_syslibroot: 605 case OPT_sectcreate: 606 // handled elsewhere 607 break; 608 default: 609 warnIfUnimplementedOption(opt); 610 break; 611 } 612 } 613 614 // Now that all dylibs have been loaded, search for those that should be 615 // re-exported. 616 for (opt::Arg *arg : args.filtered(OPT_sub_library)) { 617 config->hasReexports = true; 618 StringRef searchName = arg->getValue(); 619 if (!markSubLibrary(searchName)) 620 error("-sub_library " + searchName + " does not match a supplied dylib"); 621 } 622 623 StringRef orderFile = args.getLastArgValue(OPT_order_file); 624 if (!orderFile.empty()) 625 parseOrderFile(orderFile); 626 627 if (config->outputType == MH_EXECUTE && !isa<Defined>(config->entry)) { 628 error("undefined symbol: " + config->entry->getName()); 629 return false; 630 } 631 632 createSyntheticSections(); 633 symtab->addDSOHandle(in.header); 634 635 for (opt::Arg *arg : args.filtered(OPT_sectcreate)) { 636 StringRef segName = arg->getValue(0); 637 StringRef sectName = arg->getValue(1); 638 StringRef fileName = arg->getValue(2); 639 Optional<MemoryBufferRef> buffer = readFile(fileName); 640 if (buffer) 641 inputFiles.push_back(make<OpaqueFile>(*buffer, segName, sectName)); 642 } 643 644 // Initialize InputSections. 645 for (InputFile *file : inputFiles) { 646 for (SubsectionMap &map : file->subsections) { 647 for (auto &p : map) { 648 InputSection *isec = p.second; 649 inputSections.push_back(isec); 650 } 651 } 652 } 653 654 // Write to an output file. 655 writeResult(); 656 657 if (canExitEarly) 658 exitLld(errorCount() ? 1 : 0); 659 660 freeArena(); 661 return !errorCount(); 662 } 663