Index: llvm/test/tools/llvm-objcopy/COFF/section-name-encoding.s =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/COFF/section-name-encoding.s @@ -0,0 +1,95 @@ +// Check that COFF section names of sections added by llvm-objcopy are properly +// encoded. +// +// Encodings for different name lengths and string table index: +// [0, 8]: raw name +// (8, 999999]: base 10 string table index (/9999999) +// (999999, 0xFFFFFFFF]: base 64 string table index (//AAAAAA) +// +// Note: the names in the string table will be sorted in reverse +// lexicographical order. Use a suffix letter (z, y, x, ...) to +// get the preferred ordering of names in the test. +// +// REQUIRES: x86-registered-target +// +// RUN: echo DEADBEEF > %t.sec +// RUN: llvm-mc -triple x86_64-pc-win32 -filetype=obj %s -o %t.obj +// RUN: llvm-objcopy --add-section=s1234567=%t.sec \ +// RUN: --add-section=s1234567z=%t.sec \ +// RUN: --add-section=sevendigitx=%t.sec \ +// RUN: --add-section=doubleslashv=%t.sec \ +// RUN: %t.obj %t +// RUN: llvm-readobj --sections %t | FileCheck %s + +// Raw encoding + +// CHECK: Section { +// CHECK: Number: 14 +// CHECK: Name: s1234567 (73 31 32 33 34 35 36 37) +// CHECK: } + +// Base 10 encoding with a small offset, section name at the beginning of the +// string table. + +// /4 +// +// CHECK: Section { +// CHECK: Number: 15 +// CHECK: Name: s1234567z (2F 34 00 00 00 00 00 00) +// CHECK: } + +// Base 10 encoding with a 7 digit offset, section name after the y padding in +// the string table. + +// /1000029 == 4 + 10 + (5 * (2 + (20 * 10 * 1000) + 1)) +// v | | v ~~~~~~~~~~~~~~ v +// table size v v "p0" y pad NULL separator +// "s1234567z\0" # of pad sections +// +// CHECK: Section { +// CHECK: Number: 16 +// CHECK: Name: sevendigitx (2F 31 30 30 30 30 32 39) +// CHECK: } + +// Base 64 encoding, section name after the w padding in the string table. + +// //AAmJa4 == 1000029 + 12 + (5 * (2 + (9 * 20 * 10 * 1000) + 1)) == 38*64^3 + 9*64^2 + 26*64 + 56 +// v | | v ~~~~~~~~~~~~~~~~~~ v +// sevendigitx offset v v "p0" w pad NULL separator +// "sevendigitx\0" # of pad sections +// +// "2F 2F 41 41 6D 4A 61 34" is "//AAmJa4", which decodes to "0 0 38 9 26 56". +// +// CHECK: Section { +// CHECK: Number: 17 +// CHECK: Name: doubleslashv (2F 2F 41 41 6D 4A 61 34) +// CHECK: } + +// Generate padding sections to increase the string table size to at least +// 1,000,000 bytes. +.macro pad_sections2 pad + // 10x \pad + .section p0\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad; .long 1 + .section p1\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad; .long 1 + .section p2\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad; .long 1 + .section p3\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad; .long 1 + .section p4\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad; .long 1 +.endm + +.macro pad_sections pad + // 20x \pad + pad_sections2 \pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad\pad +.endm + +// 1000x 'y' +pad_sections yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy + +// Generate padding sections to increase the string table size to at least +// 10,000,000 bytes. +.macro pad_sections_ex pad + // 9x \pad + pad_sections \pad\pad\pad\pad\pad\pad\pad\pad\pad +.endm + +// 1000x 'w' +pad_sections_ex wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww Index: llvm/tools/llvm-objcopy/COFF/Writer.h =================================================================== --- llvm/tools/llvm-objcopy/COFF/Writer.h +++ llvm/tools/llvm-objcopy/COFF/Writer.h @@ -35,7 +35,7 @@ Error finalizeRelocTargets(); Error finalizeSymbolContents(); void layoutSections(); - size_t finalizeStringTable(); + Expected finalizeStringTable(); Error finalize(bool IsBigObj); Index: llvm/tools/llvm-objcopy/COFF/Writer.cpp =================================================================== --- llvm/tools/llvm-objcopy/COFF/Writer.cpp +++ llvm/tools/llvm-objcopy/COFF/Writer.cpp @@ -116,7 +116,33 @@ } } -size_t COFFWriter::finalizeStringTable() { +// Maximum offsets for different string table entry encodings. +enum : unsigned { Max7DecimalOffset = 9999999U }; +enum : uint64_t { MaxBase64Offset = 0xFFFFFFFFFULL }; // 64^6, including 0 + +// Encode a string table entry offset in base 64, padded to 6 chars, and +// prefixed with a double slash: '//AAAAAA', '//AAAAAB', ... +// Buffer must be at least 8 bytes large. No terminating null appended. +static void encodeBase64StringEntry(char *Buffer, uint64_t Value) { + assert(Value > Max7DecimalOffset && Value <= MaxBase64Offset && + "Illegal section name encoding for value"); + + static const char Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + Buffer[0] = '/'; + Buffer[1] = '/'; + + char *Ptr = Buffer + 7; + for (unsigned i = 0; i < 6; ++i) { + unsigned Rem = Value % 64; + Value /= 64; + *(Ptr--) = Alphabet[Rem]; + } +} + +Expected COFFWriter::finalizeStringTable() { for (const auto &S : Obj.getSections()) if (S.Name.size() > COFF::NameSize) StrTabBuilder.add(S.Name); @@ -129,11 +155,34 @@ for (auto &S : Obj.getMutableSections()) { memset(S.Header.Name, 0, sizeof(S.Header.Name)); - if (S.Name.size() > COFF::NameSize) { - snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", - (int)StrTabBuilder.getOffset(S.Name)); - } else { + if (S.Name.size() <= COFF::NameSize) { + // Short names can go in the field directly. memcpy(S.Header.Name, S.Name.data(), S.Name.size()); + } else { + // Longer names need to go in the string table. + char Str[sizeof(S.Header.Name) + 1]{}; + + // Offset of the section name in the string table. + size_t Offset = StrTabBuilder.getOffset(S.Name); + + if (Offset <= Max7DecimalOffset) { + // Offsets with 7 digits or less are encoded with a / then the number in + // ASCII, the snprintf size needs to be one more than the field size as + // it adds a null termination character. + snprintf(Str, sizeof(S.Header.Name) + 1, "/%d", (int)Offset); + } else if (Offset <= MaxBase64Offset) { + // Offsets with more than 7 digits are encoded with two / then the + // number in base64. + encodeBase64StringEntry(Str, Offset); + } else { + // The section offset is too large to be correctly encoded. + return createStringError(object_error::invalid_section_index, + "COFF string table is greater than 64GB, " + "unable to encode section name offset"); + } + + // Copy the correctly encoded section name into the header. + memcpy(S.Header.Name, Str, sizeof(S.Header.Name)); } } for (auto &S : Obj.getMutableSymbols()) { @@ -219,7 +268,11 @@ Obj.PeHeader.CheckSum = 0; } - size_t StrTabSize = finalizeStringTable(); + Expected StrTabSizeOrErr = finalizeStringTable(); + if (!StrTabSizeOrErr) + return StrTabSizeOrErr.takeError(); + + size_t StrTabSize = *StrTabSizeOrErr; size_t PointerToSymbolTable = FileSize; // StrTabSize <= 4 is the size of an empty string table, only consisting