Commit 4e7b4431 authored by simonb@chromium.org's avatar simonb@chromium.org

Upgrade logging to resemble base/logging.h.

First phase of extending the relocation packer for arm64.

Upgrade logging to be streams-based.  Add new unit tests to cover logging
and checking features.  Mechanical changes to logging call sites.

Also correct a type promotion issue that causes incorrect DT_RELCOUNT
adjustments when the packer is compiled as a 32-bit Linux binary.

BUG=385553

Review URL: https://codereview.chromium.org/392653002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283552 0039d316-1c4b-4281-b951-d872f2087c98
parent 78539dfc
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
'../..', '../..',
], ],
'sources': [ 'sources': [
'src/debug_unittest.cc',
'src/elf_file_unittest.cc', 'src/elf_file_unittest.cc',
'src/leb128_unittest.cc', 'src/leb128_unittest.cc',
'src/packer_unittest.cc', 'src/packer_unittest.cc',
......
...@@ -2,43 +2,54 @@ ...@@ -2,43 +2,54 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <stdarg.h>
#include <stdio.h>
#include "debug.h" #include "debug.h"
namespace relocation_packer { #include <stdlib.h>
#include <iostream>
#include <string>
Logger* Logger::instance_ = NULL; namespace relocation_packer {
Logger* Logger::GetInstance() { // Construct a new message logger. Prints if level is less than or equal to
if (instance_ == NULL) // the level set with SetVerbose() and predicate is true.
instance_ = new Logger; Logger::Logger(Severity severity, int level, bool predicate) {
return instance_; severity_ = severity;
level_ = level;
predicate_ = predicate;
} }
void Logger::Log(const char* format, va_list args) { // On destruction, flush and print the strings accumulated. Abort if FATAL.
vfprintf(stdout, format, args); Logger::~Logger() {
if (predicate_) {
if (level_ <= max_level_) {
std::ostream* log = severity_ == INFO ? info_stream_ : error_stream_;
std::string tag;
switch (severity_) {
case INFO: tag = "INFO"; break;
case WARNING: tag = "WARNING"; break;
case ERROR: tag = "ERROR"; break;
case FATAL: tag = "FATAL"; break;
}
stream_.flush();
*log << tag << ": " << stream_.str() << std::endl;
}
if (severity_ == FATAL)
abort();
}
} }
void Logger::Log(const char* format, ...) { // Reset to initial state.
va_list args; void Logger::Reset() {
va_start(args, format); max_level_ = -1;
GetInstance()->Log(format, args); info_stream_ = &std::cout;
va_end(args); error_stream_ = &std::cerr;
} }
void Logger::VLog(const char* format, ...) { // Verbosity. Not thread-safe.
if (GetInstance()->is_verbose_) { int Logger::max_level_ = -1;
va_list args;
va_start(args, format);
GetInstance()->Log(format, args);
va_end(args);
}
}
void Logger::SetVerbose(bool flag) { // Logging streams. Not thread-safe.
GetInstance()->is_verbose_ = flag; std::ostream* Logger::info_stream_ = &std::cout;
} std::ostream* Logger::error_stream_ = &std::cerr;
} // namespace relocation_packer } // namespace relocation_packer
...@@ -2,87 +2,115 @@ ...@@ -2,87 +2,115 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Logging and checks. // Logging and checks. Avoids a dependency on base.
// //
// Log messages to stdout. LOG() prints normal user messages, VLOG() // LOG(tag) prints messages. Tags are INFO, WARNING, ERROR and FATAL.
// is verbose, for tracing and debugging. SetVerbose() enables/disables // INFO prints to stdout, the others to stderr. FATAL aborts after printing.
// VLOG() output.
// //
// LOG() and VLOG() are printf-like, and arguments are checked by gcc. // LOG_IF(tag, predicate) logs if predicate evaluates to true, else silent.
// LOG_IF() and VLOG_IF() call LOG/VLOG if their predicate is true. //
// CHECK() aborts if its predicate is false. NOTREACHED() always aborts. // VLOG(level) logs INFO messages where level is less than or equal to the
// Logging is not thread-safe. // verbosity level set with SetVerbose().
//
// VLOG_IF(level, predicate) logs INFO if predicate evaluates to true,
// else silent.
//
// CHECK(predicate) logs a FATAL error if predicate is false.
// NOTREACHED() always aborts.
// Log streams can be changed with SetStreams(). Logging is not thread-safe.
// //
#ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ #ifndef TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
#define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ #define TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
#ifdef NDEBUG #include <limits.h>
#undef NDEBUG #include <algorithm>
#include <assert.h> #include <ostream>
#define NDEBUG #include <sstream>
#else
#include <assert.h>
#endif
#include <stdarg.h>
#include <string.h>
namespace relocation_packer { namespace relocation_packer {
// If gcc, define PRINTF_ATTRIBUTE so that gcc checks Log() as printf-like.
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define PRINTF_ATTRIBUTE(string_index, first_to_check) \
__attribute__((__format__(__printf__, string_index, first_to_check)))
#else
#define PRINTF_ATTRIBUTE(string_index, first_to_check)
#endif
// Logging and checking macros.
#define LOG(...) ::relocation_packer::Logger::Log(__VA_ARGS__)
#define VLOG(...) ::relocation_packer::Logger::VLog(__VA_ARGS__)
#define LOG_IF(cond, ...) \
do { \
if ((cond)) \
LOG(__VA_ARGS__); \
} while (0)
#define VLOG_IF(cond, ...) \
do { \
if ((cond)) \
VLOG(__VA_ARGS__); \
} while (0)
#define CHECK(expr) assert((expr))
#define NOTREACHED(_) assert(false)
class Logger { class Logger {
public: public:
// Log and verbose log to stdout. enum Severity {INFO = 0, WARNING, ERROR, FATAL};
// |format| is a printf format string.
static void Log(const char* format, ...) PRINTF_ATTRIBUTE(1, 2); // Construct a new message logger. Prints if level is less than or
static void VLog(const char* format, ...) PRINTF_ATTRIBUTE(1, 2); // equal to the level set with SetVerbose() and predicate is true.
// |severity| is an enumerated severity.
// |level| is the verbosity level.
// |predicate| controls if the logger prints or is silent.
Logger(Severity severity, int level, bool predicate);
// Set verbose mode. // On destruction, flush and print the strings accumulated in stream_.
// |flag| is true to enable verbose logging, false to disable it. ~Logger();
static void SetVerbose(bool flag);
// Return the stream for this logger.
std::ostream& GetStream() { return stream_; }
// Set verbosity level. Messages with a level less than or equal to
// this level are printed, others are discarded. Static, not thread-safe.
static void SetVerbose(int level) { max_level_ = level; }
// Set info and error logging streams. Static, not thread-safe.
static void SetStreams(std::ostream* info_stream,
std::ostream* error_stream) {
info_stream_ = info_stream;
error_stream_ = error_stream;
}
// Reset to initial state.
static void Reset();
private: private:
Logger() : is_verbose_(false) { } // Message severity, verbosity level, and predicate.
~Logger() {} Severity severity_;
int level_;
bool predicate_;
// Implementation of log to stdout. // String stream, accumulates message text.
// |format| is a printf format string. std::ostringstream stream_;
// |args| is a varargs list of printf arguments.
void Log(const char* format, va_list args);
// If set, VLOG is enabled, otherwise it is a no-op. // Verbosity for INFO messages. Not thread-safe.
bool is_verbose_; static int max_level_;
// Singleton support. Not thread-safe. // Logging streams. Not thread-safe.
static Logger* GetInstance(); static std::ostream* info_stream_;
static Logger* instance_; static std::ostream* error_stream_;
}; };
} // namespace relocation_packer } // namespace relocation_packer
// Make logging severities visible globally.
typedef relocation_packer::Logger::Severity LogSeverity;
const LogSeverity INFO = relocation_packer::Logger::INFO;
const LogSeverity WARNING = relocation_packer::Logger::WARNING;
const LogSeverity ERROR = relocation_packer::Logger::ERROR;
const LogSeverity FATAL = relocation_packer::Logger::FATAL;
// LOG(severity) prints a message with the given severity, and aborts if
// severity is FATAL. LOG_IF(severity, predicate) does the same but only if
// predicate is true. INT_MIN is guaranteed to be less than or equal to
// any verbosity level.
#define LOG(severity) \
(relocation_packer::Logger(severity, INT_MIN, true).GetStream())
#define LOG_IF(severity, predicate) \
(relocation_packer::Logger(severity, INT_MIN, (predicate)).GetStream())
// VLOG(level) prints its message as INFO if level is less than or equal to
// the current verbosity level.
#define VLOG(level) \
(relocation_packer::Logger(INFO, (level), true).GetStream())
#define VLOG_IF(level, predicate) \
(relocation_packer::Logger(INFO, (level), (predicate)).GetStream())
// CHECK(predicate) fails with a FATAL log message if predicate is false.
#define CHECK(predicate) (LOG_IF(FATAL, !(predicate)) \
<< __FILE__ << ":" << __LINE__ << ": " \
<< __FUNCTION__ << ": CHECK '" #predicate "' failed")
// NOTREACHED() always fails with a FATAL log message.
#define NOTREACHED(_) (LOG(FATAL) \
<< __FILE__ << ":" << __LINE__ << ": " \
<< __FUNCTION__ << ": NOTREACHED() hit")
#endif // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_ #endif // TOOLS_RELOCATION_PACKER_SRC_DEBUG_H_
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "debug.h"
#include <sstream>
#include <string>
#include "testing/gtest/include/gtest/gtest.h"
namespace relocation_packer {
TEST(Debug, Log) {
Logger::Reset();
std::ostringstream info;
std::ostringstream error;
Logger::SetStreams(&info, &error);
LOG(INFO) << "INFO log message";
LOG(WARNING) << "WARNING log message";
LOG(ERROR) << "ERROR log message";
EXPECT_EQ("INFO: INFO log message\n", info.str());
EXPECT_EQ("WARNING: WARNING log message\n"
"ERROR: ERROR log message\n", error.str());
Logger::Reset();
}
TEST(Debug, LogIf) {
Logger::Reset();
std::ostringstream info;
std::ostringstream error;
Logger::SetStreams(&info, &error);
LOG_IF(INFO, true) << "INFO log message";
LOG_IF(INFO, false) << "INFO log message, SHOULD NOT PRINT";
LOG_IF(WARNING, true) << "WARNING log message";
LOG_IF(WARNING, false) << "WARNING log message, SHOULD NOT PRINT";
LOG_IF(ERROR, true) << "ERROR log message";
LOG_IF(ERROR, false) << "ERROR log message, SHOULD NOT PRINT";
LOG_IF(FATAL, false) << "FATAL log message, SHOULD NOT PRINT";
EXPECT_EQ("INFO: INFO log message\n", info.str());
EXPECT_EQ("WARNING: WARNING log message\n"
"ERROR: ERROR log message\n", error.str());
Logger::Reset();
}
TEST(Debug, Vlog) {
Logger::Reset();
std::ostringstream info;
std::ostringstream error;
Logger::SetStreams(&info, &error);
VLOG(0) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
VLOG(1) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
EXPECT_EQ("", info.str());
EXPECT_EQ("", error.str());
Logger::SetVerbose(1);
VLOG(0) << "VLOG 0 INFO log message";
VLOG(1) << "VLOG 1 INFO log message";
VLOG(2) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
EXPECT_EQ("INFO: VLOG 0 INFO log message\n"
"INFO: VLOG 1 INFO log message\n", info.str());
EXPECT_EQ("", error.str());
Logger::Reset();
}
TEST(Debug, VlogIf) {
Logger::Reset();
std::ostringstream info;
std::ostringstream error;
Logger::SetStreams(&info, &error);
VLOG_IF(0, true) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
VLOG_IF(1, true) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
EXPECT_EQ("", info.str());
EXPECT_EQ("", error.str());
Logger::SetVerbose(1);
VLOG_IF(0, true) << "VLOG 0 INFO log message";
VLOG_IF(0, false) << "VLOG 0 INFO log message, SHOULD NOT PRINT";
VLOG_IF(1, true) << "VLOG 1 INFO log message";
VLOG_IF(1, false) << "VLOG 1 INFO log message, SHOULD NOT PRINT";
VLOG_IF(2, true) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
VLOG_IF(2, false) << "VLOG 2 INFO log message, SHOULD NOT PRINT";
EXPECT_EQ("INFO: VLOG 0 INFO log message\n"
"INFO: VLOG 1 INFO log message\n", info.str());
EXPECT_EQ("", error.str());
Logger::Reset();
}
TEST(DebugDeathTest, Fatal) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
Logger::Reset();
EXPECT_DEATH(LOG(FATAL) << "FATAL log message", "FATAL: FATAL log message");
EXPECT_DEATH(
LOG_IF(FATAL, true) << "FATAL log message", "FATAL: FATAL log message");
}
TEST(DebugDeathTest, Check) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
Logger::Reset();
CHECK(0 == 0);
EXPECT_DEATH(CHECK(0 == 1), "FATAL: .*:.*: .*: CHECK '0 == 1' failed");
}
TEST(DebugDeathTest, NotReached) {
::testing::FLAGS_gtest_death_test_style = "threadsafe";
Logger::Reset();
EXPECT_DEATH(NOTREACHED(), "FATAL: .*:.*: .*: NOTREACHED\\(\\) hit");
}
} // namespace relocation_packer
...@@ -56,13 +56,13 @@ void RewriteSectionData(Elf_Data* data, ...@@ -56,13 +56,13 @@ void RewriteSectionData(Elf_Data* data,
// Verbose ELF header logging. // Verbose ELF header logging.
void VerboseLogElfHeader(const Elf32_Ehdr* elf_header) { void VerboseLogElfHeader(const Elf32_Ehdr* elf_header) {
VLOG("e_phoff = %u\n", elf_header->e_phoff); VLOG(1) << "e_phoff = " << elf_header->e_phoff;
VLOG("e_shoff = %u\n", elf_header->e_shoff); VLOG(1) << "e_shoff = " << elf_header->e_shoff;
VLOG("e_ehsize = %u\n", elf_header->e_ehsize); VLOG(1) << "e_ehsize = " << elf_header->e_ehsize;
VLOG("e_phentsize = %u\n", elf_header->e_phentsize); VLOG(1) << "e_phentsize = " << elf_header->e_phentsize;
VLOG("e_phnum = %u\n", elf_header->e_phnum); VLOG(1) << "e_phnum = " << elf_header->e_phnum;
VLOG("e_shnum = %u\n", elf_header->e_shnum); VLOG(1) << "e_shnum = " << elf_header->e_shnum;
VLOG("e_shstrndx = %u\n", elf_header->e_shstrndx); VLOG(1) << "e_shstrndx = " << elf_header->e_shstrndx;
} }
// Verbose ELF program header logging. // Verbose ELF program header logging.
...@@ -80,31 +80,31 @@ void VerboseLogProgramHeader(size_t program_header_index, ...@@ -80,31 +80,31 @@ void VerboseLogProgramHeader(size_t program_header_index,
case PT_TLS: type = "TLS"; break; case PT_TLS: type = "TLS"; break;
default: type = "(OTHER)"; break; default: type = "(OTHER)"; break;
} }
VLOG("phdr %lu : %s\n", program_header_index, type.c_str()); VLOG(1) << "phdr " << program_header_index << " : " << type;
VLOG(" p_offset = %u\n", program_header->p_offset); VLOG(1) << " p_offset = " << program_header->p_offset;
VLOG(" p_vaddr = %u\n", program_header->p_vaddr); VLOG(1) << " p_vaddr = " << program_header->p_vaddr;
VLOG(" p_paddr = %u\n", program_header->p_paddr); VLOG(1) << " p_paddr = " << program_header->p_paddr;
VLOG(" p_filesz = %u\n", program_header->p_filesz); VLOG(1) << " p_filesz = " << program_header->p_filesz;
VLOG(" p_memsz = %u\n", program_header->p_memsz); VLOG(1) << " p_memsz = " << program_header->p_memsz;
} }
// Verbose ELF section header logging. // Verbose ELF section header logging.
void VerboseLogSectionHeader(const std::string& section_name, void VerboseLogSectionHeader(const std::string& section_name,
const Elf32_Shdr* section_header) { const Elf32_Shdr* section_header) {
VLOG("section %s\n", section_name.c_str()); VLOG(1) << "section " << section_name;
VLOG(" sh_addr = %u\n", section_header->sh_addr); VLOG(1) << " sh_addr = " << section_header->sh_addr;
VLOG(" sh_offset = %u\n", section_header->sh_offset); VLOG(1) << " sh_offset = " << section_header->sh_offset;
VLOG(" sh_size = %u\n", section_header->sh_size); VLOG(1) << " sh_size = " << section_header->sh_size;
VLOG(" sh_addralign = %u\n", section_header->sh_addralign); VLOG(1) << " sh_addralign = " << section_header->sh_addralign;
} }
// Verbose ELF section data logging. // Verbose ELF section data logging.
void VerboseLogSectionData(const Elf_Data* data) { void VerboseLogSectionData(const Elf_Data* data) {
VLOG(" data\n"); VLOG(1) << " data";
VLOG(" d_buf = %p\n", data->d_buf); VLOG(1) << " d_buf = " << data->d_buf;
VLOG(" d_off = %lu\n", data->d_off); VLOG(1) << " d_off = " << data->d_off;
VLOG(" d_size = %lu\n", data->d_size); VLOG(1) << " d_size = " << data->d_size;
VLOG(" d_align = %lu\n", data->d_align); VLOG(1) << " d_align = " << data->d_align;
} }
} // namespace } // namespace
...@@ -120,17 +120,17 @@ bool ElfFile::Load() { ...@@ -120,17 +120,17 @@ bool ElfFile::Load() {
CHECK(elf_); CHECK(elf_);
if (elf_kind(elf_) != ELF_K_ELF) { if (elf_kind(elf_) != ELF_K_ELF) {
LOG("ERROR: File not in ELF format\n"); LOG(ERROR) << "File not in ELF format";
return false; return false;
} }
Elf32_Ehdr* elf_header = elf32_getehdr(elf_); Elf32_Ehdr* elf_header = elf32_getehdr(elf_);
if (!elf_header) { if (!elf_header) {
LOG("ERROR: Failed to load ELF header\n"); LOG(ERROR) << "Failed to load ELF header";
return false; return false;
} }
if (elf_header->e_machine != EM_ARM) { if (elf_header->e_machine != EM_ARM) {
LOG("ERROR: File is not an arm32 ELF file\n"); LOG(ERROR) << "File is not an arm32 ELF file";
return false; return false;
} }
...@@ -140,7 +140,7 @@ bool ElfFile::Load() { ...@@ -140,7 +140,7 @@ bool ElfFile::Load() {
CHECK(endian == ELFDATA2LSB); CHECK(endian == ELFDATA2LSB);
CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__); CHECK(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__);
VLOG("endian = %u\n", endian); VLOG(1) << "endian = " << endian;
VerboseLogElfHeader(elf_header); VerboseLogElfHeader(elf_header);
const Elf32_Phdr* elf_program_header = elf32_getphdr(elf_); const Elf32_Phdr* elf_program_header = elf32_getphdr(elf_);
...@@ -208,21 +208,22 @@ bool ElfFile::Load() { ...@@ -208,21 +208,22 @@ bool ElfFile::Load() {
// Loading failed if we did not find the required special sections. // Loading failed if we did not find the required special sections.
if (!found_rel_dyn_section) { if (!found_rel_dyn_section) {
LOG("ERROR: Missing .rel.dyn section\n"); LOG(ERROR) << "Missing .rel.dyn section";
return false; return false;
} }
if (!found_dynamic_section) { if (!found_dynamic_section) {
LOG("ERROR: Missing .dynamic section\n"); LOG(ERROR) << "Missing .dynamic section";
return false; return false;
} }
if (!found_android_rel_dyn_section) { if (!found_android_rel_dyn_section) {
LOG("ERROR: Missing .android.rel.dyn section " LOG(ERROR) << "Missing .android.rel.dyn section "
"(to fix, run with --help and follow the pre-packing instructions)\n"); << "(to fix, run with --help and follow the pre-packing "
<< "instructions)";
return false; return false;
} }
if (has_debug_section) { if (has_debug_section) {
LOG("WARNING: found .debug section(s), and ignored them\n"); LOG(WARNING) << "Found .debug section(s), and ignored them";
} }
rel_dyn_section_ = found_rel_dyn_section; rel_dyn_section_ = found_rel_dyn_section;
...@@ -239,11 +240,11 @@ void AdjustElfHeaderForHole(Elf32_Ehdr* elf_header, ...@@ -239,11 +240,11 @@ void AdjustElfHeaderForHole(Elf32_Ehdr* elf_header,
int32_t hole_size) { int32_t hole_size) {
if (elf_header->e_phoff > hole_start) { if (elf_header->e_phoff > hole_start) {
elf_header->e_phoff += hole_size; elf_header->e_phoff += hole_size;
VLOG("e_phoff adjusted to %u\n", elf_header->e_phoff); VLOG(1) << "e_phoff adjusted to " << elf_header->e_phoff;
} }
if (elf_header->e_shoff > hole_start) { if (elf_header->e_shoff > hole_start) {
elf_header->e_shoff += hole_size; elf_header->e_shoff += hole_size;
VLOG("e_shoff adjusted to %u\n", elf_header->e_shoff); VLOG(1) << "e_shoff adjusted to " << elf_header->e_shoff;
} }
} }
...@@ -258,25 +259,30 @@ void AdjustProgramHeadersForHole(Elf32_Phdr* elf_program_header, ...@@ -258,25 +259,30 @@ void AdjustProgramHeadersForHole(Elf32_Phdr* elf_program_header,
if (program_header->p_offset > hole_start) { if (program_header->p_offset > hole_start) {
// The hole start is past this segment, so adjust offsets and addrs. // The hole start is past this segment, so adjust offsets and addrs.
program_header->p_offset += hole_size; program_header->p_offset += hole_size;
VLOG("phdr %lu p_offset adjusted to %u\n", i, program_header->p_offset); VLOG(1) << "phdr " << i
<< " p_offset adjusted to "<< program_header->p_offset;
// Only adjust vaddr and paddr if this program header has them. // Only adjust vaddr and paddr if this program header has them.
if (program_header->p_vaddr != 0) { if (program_header->p_vaddr != 0) {
program_header->p_vaddr += hole_size; program_header->p_vaddr += hole_size;
VLOG("phdr %lu p_vaddr adjusted to %u\n", i, program_header->p_vaddr); VLOG(1) << "phdr " << i
<< " p_vaddr adjusted to " << program_header->p_vaddr;
} }
if (program_header->p_paddr != 0) { if (program_header->p_paddr != 0) {
program_header->p_paddr += hole_size; program_header->p_paddr += hole_size;
VLOG("phdr %lu p_paddr adjusted to %u\n", i, program_header->p_paddr); VLOG(1) << "phdr " << i
<< " p_paddr adjusted to " << program_header->p_paddr;
} }
} else if (program_header->p_offset + } else if (program_header->p_offset +
program_header->p_filesz > hole_start) { program_header->p_filesz > hole_start) {
// The hole start is within this segment, so adjust file and in-memory // The hole start is within this segment, so adjust file and in-memory
// sizes, but leave offsets and addrs unchanged. // sizes, but leave offsets and addrs unchanged.
program_header->p_filesz += hole_size; program_header->p_filesz += hole_size;
VLOG("phdr %lu p_filesz adjusted to %u\n", i, program_header->p_filesz); VLOG(1) << "phdr " << i
<< " p_filesz adjusted to " << program_header->p_filesz;
program_header->p_memsz += hole_size; program_header->p_memsz += hole_size;
VLOG("phdr %lu p_memsz adjusted to %u\n", i, program_header->p_memsz); VLOG(1) << "phdr " << i
<< " p_memsz adjusted to " << program_header->p_memsz;
} }
} }
} }
...@@ -295,13 +301,13 @@ void AdjustSectionHeadersForHole(Elf* elf, ...@@ -295,13 +301,13 @@ void AdjustSectionHeadersForHole(Elf* elf,
if (section_header->sh_offset > hole_start) { if (section_header->sh_offset > hole_start) {
section_header->sh_offset += hole_size; section_header->sh_offset += hole_size;
VLOG("section %s sh_offset" VLOG(1) << "section " << name
" adjusted to %u\n", name.c_str(), section_header->sh_offset); << " sh_offset adjusted to " << section_header->sh_offset;
// Only adjust section addr if this section has one. // Only adjust section addr if this section has one.
if (section_header->sh_addr != 0) { if (section_header->sh_addr != 0) {
section_header->sh_addr += hole_size; section_header->sh_addr += hole_size;
VLOG("section %s sh_addr" VLOG(1) << "section " << name
" adjusted to %u\n", name.c_str(), section_header->sh_addr); << " sh_addr adjusted to " << section_header->sh_addr;
} }
} }
} }
...@@ -337,8 +343,8 @@ void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, ...@@ -337,8 +343,8 @@ void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
tag == DT_ANDROID_ARM_REL_OFFSET); tag == DT_ANDROID_ARM_REL_OFFSET);
if (is_adjustable && dynamic->d_un.d_ptr > hole_start) { if (is_adjustable && dynamic->d_un.d_ptr > hole_start) {
dynamic->d_un.d_ptr += hole_size; dynamic->d_un.d_ptr += hole_size;
VLOG("dynamic[%lu] %u" VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
" d_ptr adjusted to %u\n", i, dynamic->d_tag, dynamic->d_un.d_ptr); << " d_ptr adjusted to " << dynamic->d_un.d_ptr;
} }
// If we are specifically resizing .rel.dyn, we need to make some added // If we are specifically resizing .rel.dyn, we need to make some added
...@@ -348,20 +354,20 @@ void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section, ...@@ -348,20 +354,20 @@ void AdjustDynamicSectionForHole(Elf_Scn* dynamic_section,
// DT_RELSZ is the overall size of relocations. Adjust by hole size. // DT_RELSZ is the overall size of relocations. Adjust by hole size.
if (tag == DT_RELSZ) { if (tag == DT_RELSZ) {
dynamic->d_un.d_val += hole_size; dynamic->d_un.d_val += hole_size;
VLOG("dynamic[%lu] %u" VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
" d_val adjusted to %u\n", i, dynamic->d_tag, dynamic->d_un.d_val); << " d_val adjusted to " << dynamic->d_un.d_val;
} }
// The crazy linker does not use DT_RELCOUNT, but we keep it updated // DT_RELCOUNT is the count of relative relocations. Packing reduces it
// anyway. In practice the section hole is always equal to the size // to the alignment padding, if any; unpacking restores it to its former
// of R_ARM_RELATIVE relocations, and DT_RELCOUNT is the count of // value. The crazy linker does not use it, but we update it anyway.
// relative relocations. So closing a hole on packing reduces
// DT_RELCOUNT to zero, and opening a hole on unpacking restores it to
// its pre-packed value.
if (tag == DT_RELCOUNT) { if (tag == DT_RELCOUNT) {
dynamic->d_un.d_val += hole_size / sizeof(Elf32_Rel); // Cast sizeof to a signed type to avoid the division result being
VLOG("dynamic[%lu] %u" // promoted into an unsigned size_t.
" d_val adjusted to %u\n", i, dynamic->d_tag, dynamic->d_un.d_val); const ssize_t sizeof_rel = static_cast<ssize_t>(sizeof(Elf32_Rel));
dynamic->d_un.d_val += hole_size / sizeof_rel;
VLOG(1) << "dynamic[" << i << "] " << dynamic->d_tag
<< " d_val adjusted to " << dynamic->d_un.d_val;
} }
// DT_RELENT doesn't change, but make sure it is what we expect. // DT_RELENT doesn't change, but make sure it is what we expect.
...@@ -399,8 +405,8 @@ void AdjustDynSymSectionForHole(Elf_Scn* dynsym_section, ...@@ -399,8 +405,8 @@ void AdjustDynSymSectionForHole(Elf_Scn* dynsym_section,
type == STT_TLS); type == STT_TLS);
if (is_adjustable && dynsym->st_value > hole_start) { if (is_adjustable && dynsym->st_value > hole_start) {
dynsym->st_value += hole_size; dynsym->st_value += hole_size;
VLOG("dynsym[%lu] type=%u" VLOG(1) << "dynsym[" << i << "] type=" << type
" st_value adjusted to %u\n", i, type, dynsym->st_value); << " st_value adjusted to " << dynsym->st_value;
} }
} }
...@@ -426,7 +432,8 @@ void AdjustRelPltSectionForHole(Elf_Scn* relplt_section, ...@@ -426,7 +432,8 @@ void AdjustRelPltSectionForHole(Elf_Scn* relplt_section,
Elf32_Rel* relplt = &relplts[i]; Elf32_Rel* relplt = &relplts[i];
if (relplt->r_offset > hole_start) { if (relplt->r_offset > hole_start) {
relplt->r_offset += hole_size; relplt->r_offset += hole_size;
VLOG("relplt[%lu] r_offset adjusted to %u\n", i, relplt->r_offset); VLOG(1) << "relplt[" << i
<< "] r_offset adjusted to " << relplt->r_offset;
} }
} }
...@@ -452,7 +459,7 @@ void AdjustSymTabSectionForHole(Elf_Scn* symtab_section, ...@@ -452,7 +459,7 @@ void AdjustSymTabSectionForHole(Elf_Scn* symtab_section,
Elf32_Sym* sym = &symtab[i]; Elf32_Sym* sym = &symtab[i];
if (sym->st_value > hole_start) { if (sym->st_value > hole_start) {
sym->st_value += hole_size; sym->st_value += hole_size;
VLOG("symtab[%lu] value adjusted to %u\n", i, sym->st_value); VLOG(1) << "symtab[" << i << "] value adjusted to " << sym->st_value;
} }
} }
...@@ -490,8 +497,8 @@ void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) { ...@@ -490,8 +497,8 @@ void ResizeSection(Elf* elf, Elf_Scn* section, size_t new_size) {
const Elf32_Off hole_start = section_header->sh_offset; const Elf32_Off hole_start = section_header->sh_offset;
const int32_t hole_size = new_size - data->d_size; const int32_t hole_size = new_size - data->d_size;
VLOG_IF(hole_size > 0, "expand section size = %lu\n", data->d_size); VLOG_IF(1, (hole_size > 0)) << "expand section size = " << data->d_size;
VLOG_IF(hole_size < 0, "shrink section size = %lu\n", data->d_size); VLOG_IF(1, (hole_size < 0)) << "shrink section size = " << data->d_size;
// Resize the data and the section header. // Resize the data and the section header.
data->d_size += hole_size; data->d_size += hole_size;
...@@ -595,15 +602,14 @@ void AddDynamicEntry(Elf32_Dyn dyn, ...@@ -595,15 +602,14 @@ void AddDynamicEntry(Elf32_Dyn dyn,
Elf32_Dyn &slot = dynamics->at(i); Elf32_Dyn &slot = dynamics->at(i);
if (slot.d_tag == DT_NULL) { if (slot.d_tag == DT_NULL) {
slot = dyn; slot = dyn;
VLOG("dynamic[%lu] overwritten with %u\n", i, dyn.d_tag); VLOG(1) << "dynamic[" << i << "] overwritten with " << dyn.d_tag;
return; return;
} }
} }
// No free dynamics vector slot was found. // No free dynamics vector slot was found.
LOG("FATAL: No spare dynamic vector slots found " LOG(FATAL) << "No spare dynamic vector slots found "
"(to fix, increase gold's --spare-dynamic-tags value)\n"); << "(to fix, increase gold's --spare-dynamic-tags value)";
NOTREACHED();
} }
// Remove the element in the dynamics vector that matches the given tag with // Remove the element in the dynamics vector that matches the given tag with
...@@ -617,7 +623,8 @@ void RemoveDynamicEntry(Elf32_Sword tag, ...@@ -617,7 +623,8 @@ void RemoveDynamicEntry(Elf32_Sword tag,
if (slot.d_tag == tag) { if (slot.d_tag == tag) {
for ( ; i < dynamics->size() - 1; ++i) { for ( ; i < dynamics->size() - 1; ++i) {
dynamics->at(i) = dynamics->at(i + 1); dynamics->at(i) = dynamics->at(i + 1);
VLOG("dynamic[%lu] overwritten with dynamic[%lu]\n", i, i + 1); VLOG(1) << "dynamic[" << i
<< "] overwritten with dynamic[" << i + 1 << "]";
} }
CHECK(dynamics->at(i).d_tag == DT_NULL); CHECK(dynamics->at(i).d_tag == DT_NULL);
return; return;
...@@ -673,7 +680,7 @@ void AdjustRelocationTargets(Elf* elf, ...@@ -673,7 +680,7 @@ void AdjustRelocationTargets(Elf* elf,
} }
*target += hole_size; *target += hole_size;
VLOG("relocation[%lu] target adjusted to %u\n", i, *target); VLOG(1) << "relocation[" << i << "] target adjusted to " << *target;
} }
} }
} }
...@@ -704,7 +711,8 @@ void AdjustRelocations(Elf32_Off hole_start, ...@@ -704,7 +711,8 @@ void AdjustRelocations(Elf32_Off hole_start,
Elf32_Rel* relocation = &relocations->at(i); Elf32_Rel* relocation = &relocations->at(i);
if (relocation->r_offset > hole_start) { if (relocation->r_offset > hole_start) {
relocation->r_offset += hole_size; relocation->r_offset += hole_size;
VLOG("relocation[%lu] offset adjusted to %u\n", i, relocation->r_offset); VLOG(1) << "relocation[" << i
<< "] offset adjusted to " << relocation->r_offset;
} }
} }
} }
...@@ -716,7 +724,7 @@ void AdjustRelocations(Elf32_Off hole_start, ...@@ -716,7 +724,7 @@ void AdjustRelocations(Elf32_Off hole_start,
bool ElfFile::PackRelocations() { bool ElfFile::PackRelocations() {
// Load the ELF file into libelf. // Load the ELF file into libelf.
if (!Load()) { if (!Load()) {
LOG("ERROR: Failed to load as ELF (elf_error=%d)\n", elf_errno()); LOG(ERROR) << "Failed to load as ELF (elf_error=" << elf_errno() << ")";
return false; return false;
} }
...@@ -742,14 +750,14 @@ bool ElfFile::PackRelocations() { ...@@ -742,14 +750,14 @@ bool ElfFile::PackRelocations() {
other_relocations.push_back(relocation); other_relocations.push_back(relocation);
} }
} }
LOG("R_ARM_RELATIVE: %lu entries\n", relative_relocations.size()); LOG(INFO) << "R_ARM_RELATIVE: " << relative_relocations.size() << " entries";
LOG("Other : %lu entries\n", other_relocations.size()); LOG(INFO) << "Other : " << other_relocations.size() << " entries";
LOG("Total : %lu entries\n", relocations.size()); LOG(INFO) << "Total : " << relocations.size() << " entries";
// If no relative relocations then we have nothing packable. Perhaps // If no relative relocations then we have nothing packable. Perhaps
// the shared object has already been packed? // the shared object has already been packed?
if (relative_relocations.empty()) { if (relative_relocations.empty()) {
LOG("ERROR: No R_ARM_RELATIVE relocations found (already packed?)\n"); LOG(ERROR) << "No R_ARM_RELATIVE relocations found (already packed?)";
return false; return false;
} }
...@@ -766,11 +774,11 @@ bool ElfFile::PackRelocations() { ...@@ -766,11 +774,11 @@ bool ElfFile::PackRelocations() {
// Adjust the actual hole size to preserve alignment. // Adjust the actual hole size to preserve alignment.
hole_size -= hole_size % kPreserveAlignment; hole_size -= hole_size % kPreserveAlignment;
LOG("Compaction : %lu bytes\n", hole_size); LOG(INFO) << "Compaction : " << hole_size << " bytes";
// Adjusting for alignment may have removed any packing benefit. // Adjusting for alignment may have removed any packing benefit.
if (hole_size == 0) { if (hole_size == 0) {
LOG("Too few R_ARM_RELATIVE relocations to pack after alignment\n"); LOG(INFO) << "Too few R_ARM_RELATIVE relocations to pack after alignment";
return false; return false;
} }
...@@ -779,7 +787,7 @@ bool ElfFile::PackRelocations() { ...@@ -779,7 +787,7 @@ bool ElfFile::PackRelocations() {
CHECK(padding_bytes % sizeof(other_relocations[0]) == 0); CHECK(padding_bytes % sizeof(other_relocations[0]) == 0);
const size_t required = padding_bytes / sizeof(other_relocations[0]); const size_t required = padding_bytes / sizeof(other_relocations[0]);
PadRelocations(required, &other_relocations); PadRelocations(required, &other_relocations);
LOG("Alignment pad : %lu relocations\n", required); LOG(INFO) << "Alignment pad : " << required << " relocations";
// Apply relocations to all R_ARM_RELATIVE data to relocate it into the // Apply relocations to all R_ARM_RELATIVE data to relocate it into the
// area it will occupy once the hole in .rel.dyn is removed. // area it will occupy once the hole in .rel.dyn is removed.
...@@ -799,18 +807,18 @@ bool ElfFile::PackRelocations() { ...@@ -799,18 +807,18 @@ bool ElfFile::PackRelocations() {
// Pack R_ARM_RELATIVE relocations. // Pack R_ARM_RELATIVE relocations.
const size_t initial_bytes = const size_t initial_bytes =
relative_relocations.size() * sizeof(relative_relocations[0]); relative_relocations.size() * sizeof(relative_relocations[0]);
LOG("Unpacked R_ARM_RELATIVE: %lu bytes\n", initial_bytes); LOG(INFO) << "Unpacked R_ARM_RELATIVE: " << initial_bytes << " bytes";
std::vector<uint8_t> packed; std::vector<uint8_t> packed;
RelocationPacker packer; RelocationPacker packer;
packer.PackRelativeRelocations(relative_relocations, &packed); packer.PackRelativeRelocations(relative_relocations, &packed);
const void* packed_data = &packed[0]; const void* packed_data = &packed[0];
const size_t packed_bytes = packed.size() * sizeof(packed[0]); const size_t packed_bytes = packed.size() * sizeof(packed[0]);
LOG("Packed R_ARM_RELATIVE: %lu bytes\n", packed_bytes); LOG(INFO) << "Packed R_ARM_RELATIVE: " << packed_bytes << " bytes";
// If we have insufficient R_ARM_RELATIVE relocations to form a run then // If we have insufficient R_ARM_RELATIVE relocations to form a run then
// packing fails. // packing fails.
if (packed.empty()) { if (packed.empty()) {
LOG("Too few R_ARM_RELATIVE relocations to pack\n"); LOG(INFO) << "Too few R_ARM_RELATIVE relocations to pack";
return false; return false;
} }
...@@ -825,7 +833,7 @@ bool ElfFile::PackRelocations() { ...@@ -825,7 +833,7 @@ bool ElfFile::PackRelocations() {
// Make sure packing saved some space. // Make sure packing saved some space.
if (packed_bytes >= initial_bytes) { if (packed_bytes >= initial_bytes) {
LOG("Packing R_ARM_RELATIVE relocations saves no space\n"); LOG(INFO) << "Packing R_ARM_RELATIVE relocations saves no space";
return false; return false;
} }
...@@ -869,7 +877,7 @@ bool ElfFile::PackRelocations() { ...@@ -869,7 +877,7 @@ bool ElfFile::PackRelocations() {
bool ElfFile::UnpackRelocations() { bool ElfFile::UnpackRelocations() {
// Load the ELF file into libelf. // Load the ELF file into libelf.
if (!Load()) { if (!Load()) {
LOG("ERROR: Failed to load as ELF (elf_error=%d)\n", elf_errno()); LOG(ERROR) << "Failed to load as ELF (elf_error=" << elf_errno() << ")";
return false; return false;
} }
...@@ -886,19 +894,19 @@ bool ElfFile::UnpackRelocations() { ...@@ -886,19 +894,19 @@ bool ElfFile::UnpackRelocations() {
if (packed.empty() || if (packed.empty() ||
packed[0] != 'A' || packed[1] != 'P' || packed[0] != 'A' || packed[1] != 'P' ||
packed[2] != 'R' || packed[3] != '1') { packed[2] != 'R' || packed[3] != '1') {
LOG("ERROR: Packed R_ARM_RELATIVE relocations not found (not packed?)\n"); LOG(ERROR) << "Packed R_ARM_RELATIVE relocations not found (not packed?)";
return false; return false;
} }
// Unpack the data to re-materialize the R_ARM_RELATIVE relocations. // Unpack the data to re-materialize the R_ARM_RELATIVE relocations.
const size_t packed_bytes = packed.size() * sizeof(packed[0]); const size_t packed_bytes = packed.size() * sizeof(packed[0]);
LOG("Packed R_ARM_RELATIVE: %lu bytes\n", packed_bytes); LOG(INFO) << "Packed R_ARM_RELATIVE: " << packed_bytes << " bytes";
std::vector<Elf32_Rel> relative_relocations; std::vector<Elf32_Rel> relative_relocations;
RelocationPacker packer; RelocationPacker packer;
packer.UnpackRelativeRelocations(packed, &relative_relocations); packer.UnpackRelativeRelocations(packed, &relative_relocations);
const size_t unpacked_bytes = const size_t unpacked_bytes =
relative_relocations.size() * sizeof(relative_relocations[0]); relative_relocations.size() * sizeof(relative_relocations[0]);
LOG("Unpacked R_ARM_RELATIVE: %lu bytes\n", unpacked_bytes); LOG(INFO) << "Unpacked R_ARM_RELATIVE: " << unpacked_bytes << " bytes";
// Retrieve the current .rel.dyn section data. // Retrieve the current .rel.dyn section data.
data = GetSectionData(rel_dyn_section_); data = GetSectionData(rel_dyn_section_);
...@@ -922,8 +930,8 @@ bool ElfFile::UnpackRelocations() { ...@@ -922,8 +930,8 @@ bool ElfFile::UnpackRelocations() {
++padding; ++padding;
} }
} }
LOG("R_ARM_RELATIVE: %lu entries\n", relative_relocations.size()); LOG(INFO) << "R_ARM_RELATIVE: " << relative_relocations.size() << " entries";
LOG("Other : %lu entries\n", other_relocations.size()); LOG(INFO) << "Other : " << other_relocations.size() << " entries";
// If we found the same number of R_ARM_NONE entries in .rel.dyn as we // If we found the same number of R_ARM_NONE entries in .rel.dyn as we
// hold as unpacked relative relocations, then this is a padded file. // hold as unpacked relative relocations, then this is a padded file.
...@@ -941,7 +949,7 @@ bool ElfFile::UnpackRelocations() { ...@@ -941,7 +949,7 @@ bool ElfFile::UnpackRelocations() {
// Adjust the hole size for the padding added to preserve alignment. // Adjust the hole size for the padding added to preserve alignment.
hole_size -= padding * sizeof(other_relocations[0]); hole_size -= padding * sizeof(other_relocations[0]);
LOG("Expansion : %lu bytes\n", hole_size); LOG(INFO) << "Expansion : " << hole_size << " bytes";
// Apply relocations to all R_ARM_RELATIVE data to relocate it into the // Apply relocations to all R_ARM_RELATIVE data to relocate it into the
// area it will occupy once the hole in .rel.dyn is opened. // area it will occupy once the hole in .rel.dyn is opened.
...@@ -960,7 +968,7 @@ bool ElfFile::UnpackRelocations() { ...@@ -960,7 +968,7 @@ bool ElfFile::UnpackRelocations() {
other_relocations.begin(), other_relocations.end()); other_relocations.begin(), other_relocations.end());
const void* section_data = &relocations[0]; const void* section_data = &relocations[0];
const size_t bytes = relocations.size() * sizeof(relocations[0]); const size_t bytes = relocations.size() * sizeof(relocations[0]);
LOG("Total : %lu entries\n", relocations.size()); LOG(INFO) << "Total : " << relocations.size() << " entries";
ResizeSection(elf_, rel_dyn_section_, bytes); ResizeSection(elf_, rel_dyn_section_, bytes);
RewriteSectionData(data, section_data, bytes); RewriteSectionData(data, section_data, bytes);
...@@ -999,7 +1007,7 @@ void ElfFile::Flush() { ...@@ -999,7 +1007,7 @@ void ElfFile::Flush() {
// Write ELF data back to disk. // Write ELF data back to disk.
const off_t file_bytes = elf_update(elf_, ELF_C_WRITE); const off_t file_bytes = elf_update(elf_, ELF_C_WRITE);
CHECK(file_bytes > 0); CHECK(file_bytes > 0);
VLOG("elf_update returned: %lu\n", file_bytes); VLOG(1) << "elf_update returned: " << file_bytes;
// Clean up libelf, and truncate the output file to the number of bytes // Clean up libelf, and truncate the output file to the number of bytes
// written by elf_update(). // written by elf_update().
......
...@@ -29,7 +29,8 @@ ...@@ -29,7 +29,8 @@
#include "debug.h" #include "debug.h"
#include "elf_file.h" #include "elf_file.h"
#include "libelf.h" #include "libelf.h"
#include "packer.h"
namespace {
void PrintUsage(const char* argv0) { void PrintUsage(const char* argv0) {
std::string temporary = argv0; std::string temporary = argv0;
...@@ -66,6 +67,8 @@ void PrintUsage(const char* argv0) { ...@@ -66,6 +67,8 @@ void PrintUsage(const char* argv0) {
basename, basename, basename); basename, basename, basename);
} }
} // namespace
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
bool is_unpacking = false; bool is_unpacking = false;
bool is_verbose = false; bool is_verbose = false;
...@@ -92,7 +95,7 @@ int main(int argc, char* argv[]) { ...@@ -92,7 +95,7 @@ int main(int argc, char* argv[]) {
PrintUsage(argv[0]); PrintUsage(argv[0]);
return 0; return 0;
case '?': case '?':
LOG("Try '%s --help' for more information.\n", argv[0]); LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
return 1; return 1;
case -1: case -1:
has_options = false; has_options = false;
...@@ -102,22 +105,23 @@ int main(int argc, char* argv[]) { ...@@ -102,22 +105,23 @@ int main(int argc, char* argv[]) {
} }
} }
if (optind != argc - 1) { if (optind != argc - 1) {
LOG("Try '%s --help' for more information.\n", argv[0]); LOG(INFO) << "Try '" << argv[0] << " --help' for more information.";
return 1; return 1;
} }
if (elf_version(EV_CURRENT) == EV_NONE) { if (elf_version(EV_CURRENT) == EV_NONE) {
LOG("WARNING: Elf Library is out of date!\n"); LOG(WARNING) << "Elf Library is out of date!";
} }
const char* file = argv[argc - 1]; const char* file = argv[argc - 1];
const int fd = open(file, O_RDWR); const int fd = open(file, O_RDWR);
if (fd == -1) { if (fd == -1) {
LOG("%s: %s\n", file, strerror(errno)); LOG(ERROR) << file << ": " << strerror(errno);
return 1; return 1;
} }
relocation_packer::Logger::SetVerbose(is_verbose); if (is_verbose)
relocation_packer::Logger::SetVerbose(1);
relocation_packer::ElfFile elf_file(fd); relocation_packer::ElfFile elf_file(fd);
elf_file.SetPadding(is_padding); elf_file.SetPadding(is_padding);
...@@ -131,7 +135,7 @@ int main(int argc, char* argv[]) { ...@@ -131,7 +135,7 @@ int main(int argc, char* argv[]) {
close(fd); close(fd);
if (!status) { if (!status) {
LOG("ERROR: %s: failed to pack/unpack file\n", file); LOG(ERROR) << file << ": failed to pack/unpack file";
return 1; return 1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment