1 //===-- llvm-size.cpp - Print the size of each object section -------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This program is a utility that works like traditional Unix "size", 11 // that is, it prints out the size of each section, and the total size of all 12 // sections. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "llvm/ADT/APInt.h" 17 #include "llvm/Object/Archive.h" 18 #include "llvm/Object/ObjectFile.h" 19 #include "llvm/Support/Casting.h" 20 #include "llvm/Support/CommandLine.h" 21 #include "llvm/Support/FileSystem.h" 22 #include "llvm/Support/Format.h" 23 #include "llvm/Support/ManagedStatic.h" 24 #include "llvm/Support/MemoryBuffer.h" 25 #include "llvm/Support/PrettyStackTrace.h" 26 #include "llvm/Support/raw_ostream.h" 27 #include "llvm/Support/Signals.h" 28 #include "llvm/Support/system_error.h" 29 #include <algorithm> 30 #include <string> 31 using namespace llvm; 32 using namespace object; 33 34 namespace { 35 enum OutputFormatTy {berkeley, sysv}; 36 cl::opt<OutputFormatTy> 37 OutputFormat("format", 38 cl::desc("Specify output format"), 39 cl::values(clEnumVal(sysv, "System V format"), 40 clEnumVal(berkeley, "Berkeley format"), 41 clEnumValEnd), 42 cl::init(berkeley)); 43 44 cl::opt<OutputFormatTy> 45 OutputFormatShort(cl::desc("Specify output format"), 46 cl::values(clEnumValN(sysv, "A", "System V format"), 47 clEnumValN(berkeley, "B", "Berkeley format"), 48 clEnumValEnd), 49 cl::init(berkeley)); 50 51 enum RadixTy {octal = 8, decimal = 10, hexadecimal = 16}; 52 cl::opt<int> 53 Radix("-radix", 54 cl::desc("Print size in radix. Only 8, 10, and 16 are valid"), 55 cl::init(decimal)); 56 57 cl::opt<RadixTy> 58 RadixShort(cl::desc("Print size in radix:"), 59 cl::values(clEnumValN(octal, "o", "Print size in octal"), 60 clEnumValN(decimal, "d", "Print size in decimal"), 61 clEnumValN(hexadecimal, "x", "Print size in hexadecimal"), 62 clEnumValEnd), 63 cl::init(decimal)); 64 65 cl::list<std::string> 66 InputFilenames(cl::Positional, cl::desc("<input files>"), 67 cl::ZeroOrMore); 68 69 std::string ToolName; 70 } 71 72 static bool error(error_code ec) { 73 if (!ec) return false; 74 75 outs() << ToolName << ": error reading file: " << ec.message() << ".\n"; 76 outs().flush(); 77 return true; 78 } 79 80 static int getNumLengthAsString(uint64_t num) { 81 APInt conv(64, num); 82 SmallString<32> result; 83 conv.toString(result, unsigned int(Radix), false, true); 84 return result.size(); 85 } 86 87 static void PrintObjectSectionSizes(ObjectFile *o) { 88 uint64_t total = 0; 89 std::string fmtbuf; 90 raw_string_ostream fmt(fmtbuf); 91 92 const char *radix_fmt = 0; 93 switch (Radix) { 94 case octal: 95 radix_fmt = "llo"; 96 break; 97 case decimal: 98 radix_fmt = "llu"; 99 break; 100 case hexadecimal: 101 radix_fmt = "llx"; 102 break; 103 } 104 if (OutputFormat == sysv) { 105 // Run two passes over all sections. The first gets the lengths needed for 106 // formatting the output. The second actually does the output. 107 std::size_t max_name_len = strlen("section"); 108 int max_size_len = strlen("size"); 109 int max_addr_len = strlen("addr"); 110 error_code ec; 111 for (ObjectFile::section_iterator i = o->begin_sections(), 112 e = o->end_sections(); i != e; 113 i.increment(ec)) { 114 if (error(ec)) 115 return; 116 uint64_t size = 0; 117 if (error(i->getSize(size))) 118 return; 119 total += size; 120 121 StringRef name; 122 uint64_t addr = 0; 123 if (error(i->getName(name))) return; 124 if (error(i->getAddress(addr))) return; 125 max_name_len = std::max(max_name_len, name.size()); 126 max_size_len = std::max(max_size_len, getNumLengthAsString(size)); 127 max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr)); 128 } 129 130 max_name_len += 2; 131 max_size_len += 2; 132 max_addr_len += 2; 133 134 fmt << "%-" << max_name_len << "s " 135 << "%" << max_size_len << "s " 136 << "%" << max_addr_len << "s\n"; 137 138 // Print header 139 outs() << format(fmt.str().c_str(), 140 static_cast<const char*>("section"), 141 static_cast<const char*>("size"), 142 static_cast<const char*>("addr")); 143 fmtbuf.clear(); 144 145 // Setup per section format. 146 fmt << "%-" << max_name_len << "s " 147 << "%#" << max_size_len << radix_fmt << " " 148 << "%#" << max_addr_len << radix_fmt << "\n"; 149 150 // Print each section. 151 for (ObjectFile::section_iterator i = o->begin_sections(), 152 e = o->end_sections(); i != e; 153 i.increment(ec)) { 154 if (error(ec)) 155 return; 156 157 StringRef name; 158 uint64_t size = 0; 159 uint64_t addr = 0; 160 if (error(i->getName(name))) return; 161 if (error(i->getSize(size))) return; 162 if (error(i->getAddress(addr))) return; 163 std::string namestr = name; 164 165 outs() << format(fmt.str().c_str(), 166 namestr.c_str(), 167 size, 168 addr); 169 } 170 171 // Print total. 172 fmtbuf.clear(); 173 fmt << "%-" << max_name_len << "s " 174 << "%#" << max_size_len << radix_fmt << "\n"; 175 outs() << format(fmt.str().c_str(), 176 static_cast<const char*>("Total"), 177 total); 178 } else { 179 uint64_t total_text = 0; 180 uint64_t total_data = 0; 181 uint64_t total_bss = 0; 182 183 error_code ec; 184 // Collect section data. 185 for (ObjectFile::section_iterator i = o->begin_sections(), 186 e = o->end_sections(); i != e; 187 i.increment(ec)) { 188 if (error(ec)) 189 return; 190 191 uint64_t size = 0; 192 bool isText = false; 193 bool isData = false; 194 bool isBSS = false; 195 if (error(i->getSize(size))) return; 196 if (error(i->isText(isText))) return; 197 if (error(i->isData(isData))) return; 198 if (error(i->isBSS(isBSS))) return; 199 if (isText) 200 total_text += size; 201 else if (isData) 202 total_data += size; 203 else if (isBSS) 204 total_bss += size; 205 } 206 207 total = total_text + total_data + total_bss; 208 209 // Print result. 210 fmt << "%#7" << radix_fmt << " " 211 << "%#7" << radix_fmt << " " 212 << "%#7" << radix_fmt << " "; 213 outs() << format(fmt.str().c_str(), 214 total_text, 215 total_data, 216 total_bss); 217 fmtbuf.clear(); 218 fmt << "%7" << (Radix == octal ? "llo" : "llu") << " " 219 << "%7llx "; 220 outs() << format(fmt.str().c_str(), 221 total, 222 total); 223 } 224 } 225 226 static void PrintFileSectionSizes(StringRef file) { 227 // If file is not stdin, check that it exists. 228 if (file != "-") { 229 bool exists; 230 if (sys::fs::exists(file, exists) || !exists) { 231 errs() << ToolName << ": '" << file << "': " << "No such file\n"; 232 return; 233 } 234 } 235 236 OwningPtr<Binary> binary; 237 if (error_code ec = createBinary(file, binary)) { 238 errs() << ToolName << ": " << file << ": " << ec.message() << ".\n"; 239 return; 240 } 241 242 if (Archive *a = dyn_cast<Archive>(binary.get())) { 243 for (object::Archive::child_iterator i = a->begin_children(), 244 e = a->end_children(); i != e; ++i) { 245 OwningPtr<Binary> child; 246 if (error_code ec = i->getAsBinary(child)) { 247 errs() << ToolName << ": " << file << ": " << ec.message() << ".\n"; 248 continue; 249 } 250 if (ObjectFile *o = dyn_cast<ObjectFile>(child.get())) { 251 if (OutputFormat == sysv) 252 outs() << o->getFileName() << " (ex " << a->getFileName() 253 << "):\n"; 254 PrintObjectSectionSizes(o); 255 if (OutputFormat == berkeley) 256 outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n"; 257 } 258 } 259 } else if (ObjectFile *o = dyn_cast<ObjectFile>(binary.get())) { 260 if (OutputFormat == sysv) 261 outs() << o->getFileName() << " :\n"; 262 PrintObjectSectionSizes(o); 263 if (OutputFormat == berkeley) 264 outs() << o->getFileName() << "\n"; 265 } else { 266 errs() << ToolName << ": " << file << ": " << "Unrecognized file type.\n"; 267 } 268 if (OutputFormat == sysv) 269 outs() << "\n"; 270 } 271 272 int main(int argc, char **argv) { 273 // Print a stack trace if we signal out. 274 sys::PrintStackTraceOnErrorSignal(); 275 PrettyStackTraceProgram X(argc, argv); 276 277 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 278 cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n"); 279 280 ToolName = argv[0]; 281 if (OutputFormatShort.getNumOccurrences()) 282 OutputFormat = OutputFormatShort; 283 if (RadixShort.getNumOccurrences()) 284 Radix = int(RadixShort); 285 286 if (InputFilenames.size() == 0) 287 InputFilenames.push_back("a.out"); 288 289 if (OutputFormat == berkeley) 290 outs() << " text data bss " 291 << (Radix == int(octal) ? "oct" : "dec") 292 << " hex filename\n"; 293 294 std::for_each(InputFilenames.begin(), InputFilenames.end(), 295 PrintFileSectionSizes); 296 297 return 0; 298 } 299