Generate SHA-1 build ID for host-generated *.oat files (1/2).
For host-generated *.oat files, generate a SHA-1 build ID based on the
file content and write it to .note.gnu.build-id ELF section. This
should allow various developer tools like profilers correlate the data
captured for files like boot.oat on the device with the corresponding
known version of the file during an offline analysis.
Test: Verified that boot.oat contains the build ID section now (with
this change and https://android-review.googlesource.com/#/c/275630
applied)
Test: Added ElfWriterTest::CheckBuildIdPresent test.
Test: make test-art-host
Bug: 31292208
Change-Id: Ie5e89da2ef87e34c27c0237ab34ddc7d2dc0aa3b
diff --git a/compiler/elf_builder.h b/compiler/elf_builder.h
index 73240be..31a7529 100644
--- a/compiler/elf_builder.h
+++ b/compiler/elf_builder.h
@@ -36,6 +36,7 @@
// The basic layout of the elf file:
// Elf_Ehdr - The ELF header.
// Elf_Phdr[] - Program headers for the linker.
+// .note.gnu.build-id - Optional build ID section (SHA-1 digest).
// .rodata - DEX files and oat metadata.
// .text - Compiled code.
// .bss - Zero-initialized writeable section.
@@ -75,6 +76,10 @@
class ElfBuilder FINAL {
public:
static constexpr size_t kMaxProgramHeaders = 16;
+ // SHA-1 digest. Not using SHA_DIGEST_LENGTH from openssl/sha.h to avoid
+ // spreading this header dependency for just this single constant.
+ static constexpr size_t kBuildIdLen = 20;
+
using Elf_Addr = typename ElfTypes::Addr;
using Elf_Off = typename ElfTypes::Off;
using Elf_Word = typename ElfTypes::Word;
@@ -458,6 +463,49 @@
} abiflags_;
};
+ class BuildIdSection FINAL : public Section {
+ public:
+ BuildIdSection(ElfBuilder<ElfTypes>* owner,
+ const std::string& name,
+ Elf_Word type,
+ Elf_Word flags,
+ const Section* link,
+ Elf_Word info,
+ Elf_Word align,
+ Elf_Word entsize)
+ : Section(owner, name, type, flags, link, info, align, entsize),
+ digest_start_(-1) {
+ }
+
+ void Write() {
+ // The size fields are 32-bit on both 32-bit and 64-bit systems, confirmed
+ // with the 64-bit linker and libbfd code. The size of name and desc must
+ // be a multiple of 4 and it currently is.
+ this->WriteUint32(4); // namesz.
+ this->WriteUint32(kBuildIdLen); // descsz.
+ this->WriteUint32(3); // type = NT_GNU_BUILD_ID.
+ this->WriteFully("GNU", 4); // name.
+ digest_start_ = this->Seek(0, kSeekCurrent);
+ static_assert(kBuildIdLen % 4 == 0, "expecting a mutliple of 4 for build ID length");
+ this->WriteFully(std::string(kBuildIdLen, '\0').c_str(), kBuildIdLen); // desc.
+ }
+
+ off_t GetDigestStart() {
+ CHECK_GT(digest_start_, 0);
+ return digest_start_;
+ }
+
+ private:
+ bool WriteUint32(uint32_t v) {
+ return this->WriteFully(&v, sizeof(v));
+ }
+
+ // File offset where the build ID digest starts.
+ // Populated with zeros first, then updated with the actual value as the
+ // very last thing in the output file creation.
+ off_t digest_start_;
+ };
+
ElfBuilder(InstructionSet isa, const InstructionSetFeatures* features, OutputStream* output)
: isa_(isa),
features_(features),
@@ -479,6 +527,7 @@
shstrtab_(this, ".shstrtab", 0, 1),
abiflags_(this, ".MIPS.abiflags", SHT_MIPS_ABIFLAGS, SHF_ALLOC, nullptr, 0, kPageSize, 0,
isa, features),
+ build_id_(this, ".note.gnu.build-id", SHT_NOTE, SHF_ALLOC, nullptr, 0, 4, 0),
started_(false),
write_program_headers_(false),
loaded_size_(0u),
@@ -489,6 +538,7 @@
dynamic_.phdr_type_ = PT_DYNAMIC;
eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME;
abiflags_.phdr_type_ = PT_MIPS_ABIFLAGS;
+ build_id_.phdr_type_ = PT_NOTE;
}
~ElfBuilder() {}
@@ -741,6 +791,17 @@
abiflags_.End();
}
+ void WriteBuildIdSection() {
+ build_id_.Start();
+ build_id_.Write();
+ build_id_.End();
+ }
+
+ void WriteBuildId(uint8_t build_id[kBuildIdLen]) {
+ stream_.Seek(build_id_.GetDigestStart(), kSeekSet);
+ stream_.WriteFully(build_id, kBuildIdLen);
+ }
+
// Returns true if all writes and seeks on the output stream succeeded.
bool Good() {
return stream_.Good();
@@ -932,6 +993,7 @@
Section debug_line_;
StringSection shstrtab_;
AbiflagsSection abiflags_;
+ BuildIdSection build_id_;
std::vector<std::unique_ptr<Section>> other_sections_;
// List of used section in the order in which they were written.