Bug 1309172 - Updated breakpad to version 69c2c51dd89965d234eec16e3a9353634831916b; r=ted.mielczarek

This includes both the vanilla sources we haven't forked and the client
sources that we have. Client patches were applied manually up to version
69c2c51dd89965d234eec16e3a9353634831916b. The following changes were not
included as they break merging segments corresponding to libxul.so in the
module list:

8915f7be39448d9257b6da3ad0233944d1d9a92a
17ad0c18b179c135fc5a3d2bba199c3fa4276035
94b6309aecaddfcf11672f6cfad9575d68ad3b40

With these changes applied two entries for libxul.so are generated, the second
one is bogus and prevents symbolication from working correctly.

The build system and some of the tools relying on breakpad were also updated
to work with the new version.

--HG--
extra : source : fe4d49307f8890a0c430c257c96f74a9552eeb31
extra : histedit_source : bc84861445bd93856cd0d0c864fd15ad7d9ccc12%2C1efd65797da46e33481afa61a302098780b0f107
This commit is contained in:
Gabriele Svelto 2018-06-19 13:47:13 +02:00
parent aeb2ada3aa
commit b0e9d95a41
205 changed files with 8218 additions and 24127 deletions

View File

@ -255,7 +255,7 @@ CrashGenerationServer::ClientEvent(short revents)
}
if (crashing_pid == -1 || signal_fd == -1) {
if (signal_fd)
if (signal_fd != -1)
close(signal_fd);
return true;
}

View File

@ -41,8 +41,21 @@ namespace google_breakpad {
// One of these is produced for each mapping in the process (i.e. line in
// /proc/$x/maps).
struct MappingInfo {
// On Android, relocation packing can mean that the reported start
// address of the mapping must be adjusted by a bias in order to
// compensate for the compression of the relocation section. The
// following two members hold (after LateInit) the adjusted mapping
// range. See crbug.com/606972 for more information.
uintptr_t start_addr;
size_t size;
// When Android relocation packing causes |start_addr| and |size| to
// be modified with a load bias, we need to remember the unbiased
// address range. The following structure holds the original mapping
// address range as reported by the operating system.
struct {
uintptr_t start_addr;
uintptr_t end_addr;
} system_mapping_info;
size_t offset; // offset into the backed file.
bool exec; // true if the mapping has the execute bit set.
char name[NAME_MAX];

View File

@ -36,7 +36,7 @@ namespace google_breakpad {
// Minidump defines register structures which are different from the raw
// structures which we get from the kernel. These are platform specific
// functions to juggle the ucontext and user structures into minidump format.
// functions to juggle the ucontext_t and user structures into minidump format.
#if defined(__i386__)

View File

@ -39,7 +39,7 @@
namespace google_breakpad {
// Wraps platform-dependent implementations of accessors to ucontext structs.
// Wraps platform-dependent implementations of accessors to ucontext_t structs.
struct UContextReader {
static uintptr_t GetStackPointer(const ucontext_t* uc);

View File

@ -105,12 +105,6 @@
#define PR_SET_PTRACER 0x59616d61
#endif
// A wrapper for the tgkill syscall: send a signal to a specific thread.
static int _tgkill(pid_t tgid, pid_t tid, int sig) {
return syscall(__NR_tgkill, tgid, tid, sig);
return 0;
}
namespace google_breakpad {
namespace {
@ -218,6 +212,7 @@ pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
// time can use |g_crash_context_|.
ExceptionHandler::CrashContext g_crash_context_;
FirstChanceHandler g_first_chance_handler_ = nullptr;
} // namespace
// Runs before crashing: normal context.
@ -331,6 +326,18 @@ void ExceptionHandler::RestoreHandlersLocked() {
// Runs on the crashing thread.
// static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// Give the first chance handler a chance to recover from this signal
//
// This is primarily used by V8. V8 uses guard regions to guarantee memory
// safety in WebAssembly. This means some signals might be expected if they
// originate from Wasm code while accessing the guard region. We give V8 the
// chance to handle and recover from these signals first.
if (g_first_chance_handler_ != nullptr &&
g_first_chance_handler_(sig, info, uc)) {
return;
}
// All the exception signals are blocked at this point.
pthread_mutex_lock(&g_handler_stack_mutex_);
@ -346,6 +353,7 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// will call the function with the right arguments.
struct sigaction cur_handler;
if (sigaction(sig, NULL, &cur_handler) == 0 &&
cur_handler.sa_sigaction == SignalHandler &&
(cur_handler.sa_flags & SA_SIGINFO) == 0) {
// Reset signal handler with the right flags.
sigemptyset(&cur_handler.sa_mask);
@ -387,7 +395,7 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// In order to retrigger it, we have to queue a new signal by calling
// kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
// due to the kernel sending a SIGABRT from a user request via SysRQ.
if (_tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
if (sys_tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
// If we failed to kill ourselves (e.g. because a sandbox disallows us
// to do so), we instead resort to terminating our process. This will
// result in an incorrect exit code.
@ -414,9 +422,14 @@ struct ThreadArgument {
int ExceptionHandler::ThreadEntry(void *arg) {
const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
// Close the write end of the pipe. This allows us to fail if the parent dies
// while waiting for the continue signal.
sys_close(thread_arg->handler->fdes[1]);
// Block here until the crashing process unblocks us when
// we're allowed to use ptrace
thread_arg->handler->WaitForContinueSignal();
sys_close(thread_arg->handler->fdes[0]);
return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
thread_arg->context_size) == false;
@ -424,7 +437,7 @@ int ExceptionHandler::ThreadEntry(void *arg) {
// This function runs in a compromised context: see the top of the file.
// Runs on the crashing thread.
bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
bool ExceptionHandler::HandleSignal(int /*sig*/, siginfo_t* info, void* uc) {
if (filter_ && !filter_(callback_context_))
return false;
@ -523,8 +536,8 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
}
const pid_t child = sys_clone(
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
&thread_arg, NULL, NULL, NULL);
ThreadEntry, stack, CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL,
NULL);
if (child == -1) {
sys_close(fdes[0]);
sys_close(fdes[1]);
@ -548,13 +561,14 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
logger::write(childMsg, my_strlen(childMsg));
}
// Close the read end of the pipe.
sys_close(fdes[0]);
// Allow the child to ptrace us
sys_prctl(PR_SET_PTRACER, child, 0, 0, 0);
SendContinueSignalToChild();
int status;
int status = 0;
const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL));
sys_close(fdes[0]);
sys_close(fdes[1]);
if (r == -1) {
@ -610,12 +624,20 @@ void ExceptionHandler::WaitForContinueSignal() {
// Runs on the cloned process.
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
size_t context_size) {
const bool may_skip_dump =
minidump_descriptor_.skip_dump_if_principal_mapping_not_referenced();
const uintptr_t principal_mapping_address =
minidump_descriptor_.address_within_principal_mapping();
const bool sanitize_stacks = minidump_descriptor_.sanitize_stacks();
if (minidump_descriptor_.IsMicrodumpOnConsole()) {
return google_breakpad::WriteMicrodump(
crashing_process,
context,
context_size,
mapping_list_,
may_skip_dump,
principal_mapping_address,
sanitize_stacks,
*minidump_descriptor_.microdump_extra_info());
}
if (minidump_descriptor_.IsFD()) {
@ -625,7 +647,10 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
context,
context_size,
mapping_list_,
app_memory_list_);
app_memory_list_,
may_skip_dump,
principal_mapping_address,
sanitize_stacks);
}
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
minidump_descriptor_.size_limit(),
@ -633,7 +658,10 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
context,
context_size,
mapping_list_,
app_memory_list_);
app_memory_list_,
may_skip_dump,
principal_mapping_address,
sanitize_stacks);
}
// static
@ -786,4 +814,8 @@ bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
return callback ? callback(descriptor, callback_context, true) : true;
}
void SetFirstChanceExceptionHandler(FirstChanceHandler callback) {
g_first_chance_handler_ = callback;
}
} // namespace google_breakpad

View File

@ -194,8 +194,8 @@ class ExceptionHandler {
ucontext_t context;
#if !defined(__ARM_EABI__) && !defined(__mips__)
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
// In case of MIPS Linux FP state is already part of struct
// ucontext so 'float_state' is not required.
// In case of MIPS Linux FP state is already part of ucontext_t so
// 'float_state' is not required.
fpstate_t float_state;
#endif
};
@ -262,7 +262,7 @@ class ExceptionHandler {
// can do this. We create a pipe which we can use to block the
// cloned process after creating it, until we have explicitly enabled
// ptrace. This is used to store the file descriptors for the pipe
int fdes[2];
int fdes[2] = {-1, -1};
// Callers can add extra info about mappings for cases where the
// dumper code cannot extract enough information from /proc/<pid>/maps.
@ -273,6 +273,10 @@ class ExceptionHandler {
AppMemoryList app_memory_list_;
};
typedef bool (*FirstChanceHandler)(int, void*, void*);
void SetFirstChanceExceptionHandler(FirstChanceHandler callback);
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_

View File

@ -27,6 +27,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <pthread.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
@ -257,6 +258,66 @@ TEST(ExceptionHandlerTest, ChildCrashWithFD) {
ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
}
#if !defined(__ANDROID_API__) || __ANDROID_API__ >= __ANDROID_API_N__
static void* SleepFunction(void* unused) {
while (true) usleep(1000000);
return NULL;
}
static void* CrashFunction(void* b_ptr) {
pthread_barrier_t* b = reinterpret_cast<pthread_barrier_t*>(b_ptr);
pthread_barrier_wait(b);
DoNullPointerDereference();
return NULL;
}
// Tests that concurrent crashes do not enter a loop by alternately triggering
// the signal handler.
TEST(ExceptionHandlerTest, ParallelChildCrashesDontHang) {
AutoTempDir temp_dir;
const pid_t child = fork();
if (child == 0) {
google_breakpad::scoped_ptr<ExceptionHandler> handler(
new ExceptionHandler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
NULL, true, -1));
// We start a number of threads to make sure handling the signal takes
// enough time for the second thread to enter the signal handler.
int num_sleep_threads = 100;
google_breakpad::scoped_array<pthread_t> sleep_threads(
new pthread_t[num_sleep_threads]);
for (int i = 0; i < num_sleep_threads; ++i) {
ASSERT_EQ(0, pthread_create(&sleep_threads[i], NULL, SleepFunction,
NULL));
}
int num_crash_threads = 2;
google_breakpad::scoped_array<pthread_t> crash_threads(
new pthread_t[num_crash_threads]);
// Barrier to synchronize crashing both threads at the same time.
pthread_barrier_t b;
ASSERT_EQ(0, pthread_barrier_init(&b, NULL, num_crash_threads + 1));
for (int i = 0; i < num_crash_threads; ++i) {
ASSERT_EQ(0, pthread_create(&crash_threads[i], NULL, CrashFunction, &b));
}
pthread_barrier_wait(&b);
for (int i = 0; i < num_crash_threads; ++i) {
ASSERT_EQ(0, pthread_join(crash_threads[i], NULL));
}
}
// Wait a while until the child should have crashed.
usleep(1000000);
// Kill the child if it is still running.
kill(child, SIGKILL);
// If the child process terminated by itself, it will have returned SIGSEGV.
// If however it got stuck in a loop, it will have been killed by the
// SIGKILL.
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
}
#endif // !defined(__ANDROID_API__) || __ANDROID_API__ >= __ANDROID_API_N__
static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor,
void* context,
bool succeeded) {
@ -465,6 +526,29 @@ TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) {
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
}
namespace {
const int kSimpleFirstChanceReturnStatus = 42;
bool SimpleFirstChanceHandler(int, void*, void*) {
_exit(kSimpleFirstChanceReturnStatus);
}
}
TEST(ExceptionHandlerTest, FirstChanceHandlerRuns) {
AutoTempDir temp_dir;
const pid_t child = fork();
if (child == 0) {
ExceptionHandler handler(
MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
google_breakpad::SetFirstChanceExceptionHandler(SimpleFirstChanceHandler);
DoNullPointerDereference();
}
int status;
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
ASSERT_TRUE(WIFEXITED(status));
ASSERT_EQ(kSimpleFirstChanceReturnStatus, WEXITSTATUS(status));
}
#endif // !ADDRESS_SANITIZER
const unsigned char kIllegalInstruction[] = {

View File

@ -44,6 +44,11 @@ MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
directory_(descriptor.directory_),
c_path_(NULL),
size_limit_(descriptor.size_limit_),
address_within_principal_mapping_(
descriptor.address_within_principal_mapping_),
skip_dump_if_principal_mapping_not_referenced_(
descriptor.skip_dump_if_principal_mapping_not_referenced_),
sanitize_stacks_(descriptor.sanitize_stacks_),
microdump_extra_info_(descriptor.microdump_extra_info_) {
// The copy constructor is not allowed to be called on a MinidumpDescriptor
// with a valid path_, as getting its c_path_ would require the heap which
@ -65,6 +70,11 @@ MinidumpDescriptor& MinidumpDescriptor::operator=(
UpdatePath();
}
size_limit_ = descriptor.size_limit_;
address_within_principal_mapping_ =
descriptor.address_within_principal_mapping_;
skip_dump_if_principal_mapping_not_referenced_ =
descriptor.skip_dump_if_principal_mapping_not_referenced_;
sanitize_stacks_ = descriptor.sanitize_stacks_;
microdump_extra_info_ = descriptor.microdump_extra_info_;
return *this;
}

View File

@ -53,14 +53,19 @@ class MinidumpDescriptor {
MinidumpDescriptor()
: mode_(kUninitialized),
fd_(-1),
size_limit_(-1) {}
size_limit_(-1),
address_within_principal_mapping_(0),
skip_dump_if_principal_mapping_not_referenced_(false) {}
explicit MinidumpDescriptor(const string& directory)
: mode_(kWriteMinidumpToFile),
fd_(-1),
directory_(directory),
c_path_(NULL),
size_limit_(-1) {
size_limit_(-1),
address_within_principal_mapping_(0),
skip_dump_if_principal_mapping_not_referenced_(false),
sanitize_stacks_(false) {
assert(!directory.empty());
}
@ -68,14 +73,20 @@ class MinidumpDescriptor {
: mode_(kWriteMinidumpToFd),
fd_(fd),
c_path_(NULL),
size_limit_(-1) {
size_limit_(-1),
address_within_principal_mapping_(0),
skip_dump_if_principal_mapping_not_referenced_(false),
sanitize_stacks_(false) {
assert(fd != -1);
}
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
: mode_(kWriteMicrodumpToConsole),
fd_(-1),
size_limit_(-1) {}
size_limit_(-1),
address_within_principal_mapping_(0),
skip_dump_if_principal_mapping_not_referenced_(false),
sanitize_stacks_(false) {}
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
@ -101,6 +112,28 @@ class MinidumpDescriptor {
off_t size_limit() const { return size_limit_; }
void set_size_limit(off_t limit) { size_limit_ = limit; }
uintptr_t address_within_principal_mapping() const {
return address_within_principal_mapping_;
}
void set_address_within_principal_mapping(
uintptr_t address_within_principal_mapping) {
address_within_principal_mapping_ = address_within_principal_mapping;
}
bool skip_dump_if_principal_mapping_not_referenced() {
return skip_dump_if_principal_mapping_not_referenced_;
}
void set_skip_dump_if_principal_mapping_not_referenced(
bool skip_dump_if_principal_mapping_not_referenced) {
skip_dump_if_principal_mapping_not_referenced_ =
skip_dump_if_principal_mapping_not_referenced;
}
bool sanitize_stacks() const { return sanitize_stacks_; }
void set_sanitize_stacks(bool sanitize_stacks) {
sanitize_stacks_ = sanitize_stacks;
}
MicrodumpExtraInfo* microdump_extra_info() {
assert(IsMicrodumpOnConsole());
return &microdump_extra_info_;
@ -132,6 +165,23 @@ class MinidumpDescriptor {
off_t size_limit_;
// This member points somewhere into the main module for this
// process (the module that is considerered interesting for the
// purposes of debugging crashes).
uintptr_t address_within_principal_mapping_;
// If set, threads that do not reference the address range
// associated with |address_within_principal_mapping_| will not have their
// stacks logged.
bool skip_dump_if_principal_mapping_not_referenced_;
// If set, stacks are sanitized to remove PII. This involves
// overwriting any pointer-aligned words that are not either
// pointers into a process mapping or small integers (+/-4096). This
// leaves enough information to unwind stacks, and preserve some
// register values, but elides strings and other program data.
bool sanitize_stacks_;
// The extra microdump data (e.g. product name/version, build
// fingerprint, gpu fingerprint) that should be appended to the dump
// (microdump only). Microdumps don't have the ability of appending

View File

@ -132,6 +132,9 @@ class MicrodumpWriter {
public:
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
const MappingList& mappings,
bool skip_dump_if_principal_mapping_not_referenced,
uintptr_t address_within_principal_mapping,
bool sanitize_stack,
const MicrodumpExtraInfo& microdump_extra_info,
LinuxDumper* dumper)
: ucontext_(context ? &context->context : NULL),
@ -140,8 +143,16 @@ class MicrodumpWriter {
#endif
dumper_(dumper),
mapping_list_(mappings),
skip_dump_if_principal_mapping_not_referenced_(
skip_dump_if_principal_mapping_not_referenced),
address_within_principal_mapping_(address_within_principal_mapping),
sanitize_stack_(sanitize_stack),
microdump_extra_info_(microdump_extra_info),
log_line_(NULL) {
log_line_(NULL),
stack_copy_(NULL),
stack_len_(0),
stack_lower_bound_(0),
stack_pointer_(0) {
log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
if (log_line_)
log_line_[0] = '\0'; // Clear out the log line buffer.
@ -159,25 +170,32 @@ class MicrodumpWriter {
return dumper_->ThreadsSuspend() && dumper_->LateInit();
}
bool Dump() {
bool success;
void Dump() {
CaptureResult stack_capture_result = CaptureCrashingThreadStack(-1);
if (stack_capture_result == CAPTURE_UNINTERESTING) {
LogLine("Microdump skipped (uninteresting)");
return;
}
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
DumpProductInformation();
DumpOSInformation();
DumpProcessType();
DumpCrashReason();
DumpGPUInformation();
#if !defined(__LP64__)
DumpFreeSpace();
#endif
success = DumpCrashingThread();
if (success)
success = DumpMappings();
if (stack_capture_result == CAPTURE_OK)
DumpThreadStack();
DumpCPUState();
DumpMappings();
LogLine("-----END BREAKPAD MICRODUMP-----");
dumper_->ThreadsResume();
return success;
}
private:
enum CaptureResult { CAPTURE_OK, CAPTURE_FAILED, CAPTURE_UNINTERESTING };
// Writes one line to the system log.
void LogLine(const char* msg) {
#if defined(__ANDROID__)
@ -221,7 +239,44 @@ class MicrodumpWriter {
// Writes out the current line buffer on the system log.
void LogCommitLine() {
LogLine(log_line_);
my_strlcpy(log_line_, "", kLineBufferSize);
log_line_[0] = 0;
}
CaptureResult CaptureCrashingThreadStack(int max_stack_len) {
stack_pointer_ = UContextReader::GetStackPointer(ucontext_);
if (!dumper_->GetStackInfo(reinterpret_cast<const void**>(&stack_lower_bound_),
&stack_len_, stack_pointer_)) {
return CAPTURE_FAILED;
}
if (max_stack_len >= 0 &&
stack_len_ > static_cast<size_t>(max_stack_len)) {
stack_len_ = max_stack_len;
}
stack_copy_ = reinterpret_cast<uint8_t*>(Alloc(stack_len_));
dumper_->CopyFromProcess(stack_copy_, dumper_->crash_thread(),
reinterpret_cast<const void*>(stack_lower_bound_),
stack_len_);
if (!skip_dump_if_principal_mapping_not_referenced_) return CAPTURE_OK;
const MappingInfo* principal_mapping =
dumper_->FindMappingNoBias(address_within_principal_mapping_);
if (!principal_mapping) return CAPTURE_UNINTERESTING;
uintptr_t low_addr = principal_mapping->system_mapping_info.start_addr;
uintptr_t high_addr = principal_mapping->system_mapping_info.end_addr;
uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_);
if (low_addr <= pc && pc <= high_addr) return CAPTURE_OK;
if (dumper_->StackHasPointerToMapping(stack_copy_, stack_len_,
stack_pointer_ - stack_lower_bound_,
*principal_mapping)) {
return CAPTURE_OK;
}
return CAPTURE_UNINTERESTING;
}
void DumpProductInformation() {
@ -244,6 +299,16 @@ class MicrodumpWriter {
LogCommitLine();
}
void DumpCrashReason() {
LogAppend("R ");
LogAppend(dumper_->crash_signal());
LogAppend(" ");
LogAppend(dumper_->GetCrashSignalString());
LogAppend(" ");
LogAppend(dumper_->crash_address());
LogCommitLine();
}
void DumpOSInformation() {
const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
@ -315,87 +380,42 @@ class MicrodumpWriter {
LogCommitLine();
}
bool DumpThreadStack(uint32_t thread_id,
uintptr_t stack_pointer,
int max_stack_len,
uint8_t** stack_copy) {
*stack_copy = NULL;
const void* stack;
size_t stack_len;
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
// The stack pointer might not be available. In this case we don't hard
// fail, just produce a (almost useless) microdump w/o a stack section.
return true;
void DumpThreadStack() {
if (sanitize_stack_) {
dumper_->SanitizeStackCopy(stack_copy_, stack_len_, stack_pointer_,
stack_pointer_ - stack_lower_bound_);
}
LogAppend("S 0 ");
LogAppend(stack_pointer);
LogAppend(stack_pointer_);
LogAppend(" ");
LogAppend(reinterpret_cast<uintptr_t>(stack));
LogAppend(stack_lower_bound_);
LogAppend(" ");
LogAppend(stack_len);
LogAppend(stack_len_);
LogCommitLine();
if (max_stack_len >= 0 &&
stack_len > static_cast<unsigned int>(max_stack_len)) {
stack_len = max_stack_len;
}
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len);
// Dump the content of the stack, splicing it into chunks which size is
// compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD).
const size_t STACK_DUMP_CHUNK_SIZE = 384;
for (size_t stack_off = 0; stack_off < stack_len;
for (size_t stack_off = 0; stack_off < stack_len_;
stack_off += STACK_DUMP_CHUNK_SIZE) {
LogAppend("S ");
LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
LogAppend(stack_lower_bound_ + stack_off);
LogAppend(" ");
LogAppend(*stack_copy + stack_off,
std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off));
LogAppend(stack_copy_ + stack_off,
std::min(STACK_DUMP_CHUNK_SIZE, stack_len_ - stack_off));
LogCommitLine();
}
return true;
}
// Write information about the crashing thread.
bool DumpCrashingThread() {
const unsigned num_threads = dumper_->threads().size();
for (unsigned i = 0; i < num_threads; ++i) {
MDRawThread thread;
my_memset(&thread, 0, sizeof(thread));
thread.thread_id = dumper_->threads()[i];
// Dump only the crashing thread.
if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread())
continue;
assert(ucontext_);
assert(!dumper_->IsPostMortem());
uint8_t* stack_copy;
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy))
return false;
RawContextCPU cpu;
my_memset(&cpu, 0, sizeof(RawContextCPU));
void DumpCPUState() {
RawContextCPU cpu;
my_memset(&cpu, 0, sizeof(RawContextCPU));
#if !defined(__ARM_EABI__) && !defined(__mips__)
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
#else
UContextReader::FillCPUContext(&cpu, ucontext_);
UContextReader::FillCPUContext(&cpu, ucontext_);
#endif
DumpCPUState(&cpu);
}
return true;
}
void DumpCPUState(RawContextCPU* cpu) {
LogAppend("C ");
LogAppend(cpu, sizeof(*cpu));
LogAppend(&cpu, sizeof(cpu));
LogCommitLine();
}
@ -474,6 +494,12 @@ class MicrodumpWriter {
#if !defined(__LP64__)
void DumpFreeSpace() {
const MappingInfo* stack_mapping = nullptr;
ThreadInfo info;
if (dumper_->GetThreadInfoByIndex(dumper_->GetMainThreadIndex(), &info)) {
stack_mapping = dumper_->FindMappingNoBias(info.stack_pointer);
}
const google_breakpad::wasteful_vector<MappingInfo*>& mappings =
dumper_->mappings();
if (mappings.size() == 0) return;
@ -506,6 +532,14 @@ class MicrodumpWriter {
++curr;
}
if (mappings[curr] == stack_mapping) {
// Because we can't determine the top of userspace mappable
// memory we treat the start of the process stack as the top
// of the allocatable address space. Once we reach
// |stack_mapping| we are done scanning for free space regions.
break;
}
size_t next = NextOrderedMapping(mappings, curr);
if (next == std::numeric_limits<size_t>::max())
break;
@ -547,7 +581,7 @@ class MicrodumpWriter {
#endif
// Write information about the mappings in effect.
bool DumpMappings() {
void DumpMappings() {
// First write all the mappings from the dumper
for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
const MappingInfo& mapping = *dumper_->mappings()[i];
@ -566,7 +600,6 @@ class MicrodumpWriter {
++iter) {
DumpModule(iter->first, false, 0, iter->second);
}
return true;
}
void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
@ -577,8 +610,25 @@ class MicrodumpWriter {
#endif
LinuxDumper* dumper_;
const MappingList& mapping_list_;
bool skip_dump_if_principal_mapping_not_referenced_;
uintptr_t address_within_principal_mapping_;
bool sanitize_stack_;
const MicrodumpExtraInfo microdump_extra_info_;
char* log_line_;
// The local copy of crashed process stack memory, beginning at
// |stack_lower_bound_|.
uint8_t* stack_copy_;
// The length of crashed process stack copy.
size_t stack_len_;
// The address of the page containing the stack pointer in the
// crashed process. |stack_lower_bound_| <= |stack_pointer_|
uintptr_t stack_lower_bound_;
// The stack pointer of the crashed thread.
uintptr_t stack_pointer_;
};
} // namespace
@ -588,6 +638,9 @@ bool WriteMicrodump(pid_t crashing_process,
const void* blob,
size_t blob_size,
const MappingList& mappings,
bool skip_dump_if_principal_mapping_not_referenced,
uintptr_t address_within_principal_mapping,
bool sanitize_stack,
const MicrodumpExtraInfo& microdump_extra_info) {
LinuxPtraceDumper dumper(crashing_process);
const ExceptionHandler::CrashContext* context = NULL;
@ -595,15 +648,17 @@ bool WriteMicrodump(pid_t crashing_process,
if (blob_size != sizeof(ExceptionHandler::CrashContext))
return false;
context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
dumper.set_crash_address(
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
dumper.set_crash_signal(context->siginfo.si_signo);
dumper.SetCrashInfoFromSigInfo(context->siginfo);
dumper.set_crash_thread(context->tid);
}
MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper);
MicrodumpWriter writer(context, mappings,
skip_dump_if_principal_mapping_not_referenced,
address_within_principal_mapping, sanitize_stack,
microdump_extra_info, &dumper);
if (!writer.Init())
return false;
return writer.Dump();
writer.Dump();
return true;
}
} // namespace google_breakpad

View File

@ -58,6 +58,9 @@ bool WriteMicrodump(pid_t crashing_process,
const void* blob,
size_t blob_size,
const MappingList& mappings,
bool skip_dump_if_main_module_not_referenced,
uintptr_t address_within_main_module,
bool sanitize_stack,
const MicrodumpExtraInfo& microdump_extra_info);
} // namespace google_breakpad

View File

@ -31,6 +31,7 @@
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <ucontext.h>
#include <sstream>
#include <string>
@ -47,6 +48,11 @@
using namespace google_breakpad;
extern "C" {
extern char __executable_start;
extern char __etext;
}
namespace {
typedef testing::Test MicrodumpWriterTest;
@ -59,13 +65,24 @@ MicrodumpExtraInfo MakeMicrodumpExtraInfo(
info.build_fingerprint = build_fingerprint;
info.product_info = product_info;
info.gpu_fingerprint = gpu_fingerprint;
info.process_type = "Browser";
return info;
}
void CrashAndGetMicrodump(
const MappingList& mappings,
const MicrodumpExtraInfo& microdump_extra_info,
scoped_array<char>* buf) {
bool ContainsMicrodump(const std::string& buf) {
return std::string::npos != buf.find("-----BEGIN BREAKPAD MICRODUMP-----") &&
std::string::npos != buf.find("-----END BREAKPAD MICRODUMP-----");
}
const char kIdentifiableString[] = "_IDENTIFIABLE_";
const uintptr_t kCrashAddress = 0xdeaddeadu;
void CrashAndGetMicrodump(const MappingList& mappings,
const MicrodumpExtraInfo& microdump_extra_info,
std::string* microdump,
bool skip_dump_if_principal_mapping_not_referenced = false,
uintptr_t address_within_principal_mapping = 0,
bool sanitize_stack = false) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
@ -74,6 +91,14 @@ void CrashAndGetMicrodump(
int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ASSERT_NE(-1, err_fd);
char identifiable_string[sizeof(kIdentifiableString)];
// This string should not appear in the resulting microdump if it
// has been sanitized.
strcpy(identifiable_string, kIdentifiableString);
// Force the strcpy to not be optimized away.
IGNORE_RET(write(STDOUT_FILENO, identifiable_string, 0));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
@ -86,9 +111,14 @@ void CrashAndGetMicrodump(
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
// Pretend the current context is the child context (which is
// approximately right) so that we have a valid stack pointer, and
// can fetch child stack data via ptrace.
getcontext(&context.context);
// Set a non-zero tid to avoid tripping asserts.
context.tid = child;
context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
context.siginfo.si_addr = reinterpret_cast<void*>(kCrashAddress);
// Redirect temporarily stderr to the stderr.log file.
int save_err = dup(STDERR_FILENO);
@ -96,6 +126,8 @@ void CrashAndGetMicrodump(
ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
skip_dump_if_principal_mapping_not_referenced,
address_within_principal_mapping, sanitize_stack,
microdump_extra_info));
// Revert stderr back to the console.
@ -105,17 +137,38 @@ void CrashAndGetMicrodump(
// Read back the stderr file and check for the microdump marker.
fsync(err_fd);
lseek(err_fd, 0, SEEK_SET);
const size_t kBufSize = 64 * 1024;
buf->reset(new char[kBufSize]);
ASSERT_GT(read(err_fd, buf->get(), kBufSize), 0);
microdump->clear();
char buf[1024];
while (true) {
int bytes_read = IGNORE_EINTR(read(err_fd, buf, 1024));
if (bytes_read <= 0) break;
microdump->append(buf, buf + bytes_read);
}
close(err_fd);
close(fds[1]);
}
ASSERT_NE(static_cast<char*>(0), strstr(
buf->get(), "-----BEGIN BREAKPAD MICRODUMP-----"));
ASSERT_NE(static_cast<char*>(0), strstr(
buf->get(), "-----END BREAKPAD MICRODUMP-----"));
void ExtractMicrodumpStackContents(const string& microdump_content,
string* result) {
std::istringstream iss(microdump_content);
result->clear();
for (string line; std::getline(iss, line);) {
if (line.find("S ") == 0) {
std::istringstream stack_data(line);
std::string key;
std::string addr;
std::string data;
stack_data >> key >> addr >> data;
EXPECT_TRUE((data.size() & 1u) == 0u);
result->reserve(result->size() + data.size() / 2);
for (size_t i = 0; i < data.size(); i += 2) {
std::string byte = data.substr(i, 2);
result->push_back(static_cast<char>(strtoul(byte.c_str(), NULL, 16)));
}
}
}
}
void CheckMicrodumpContents(const string& microdump_content,
@ -123,6 +176,8 @@ void CheckMicrodumpContents(const string& microdump_content,
std::istringstream iss(microdump_content);
bool did_find_os_info = false;
bool did_find_product_info = false;
bool did_find_process_type = false;
bool did_find_crash_reason = false;
bool did_find_gpu_info = false;
for (string line; std::getline(iss, line);) {
if (line.find("O ") == 0) {
@ -141,9 +196,28 @@ void CheckMicrodumpContents(const string& microdump_content,
// Check that the build fingerprint is in the right place.
os_info_tokens >> token;
ASSERT_FALSE(os_info_tokens.fail());
if (expected_info.build_fingerprint)
ASSERT_EQ(expected_info.build_fingerprint, token);
did_find_os_info = true;
} else if (line.find("P ") == 0) {
if (expected_info.process_type)
ASSERT_EQ(string("P ") + expected_info.process_type, line);
did_find_process_type = true;
} else if (line.find("R ") == 0) {
std::istringstream crash_reason_tokens(line);
string token;
unsigned crash_reason;
string crash_reason_str;
intptr_t crash_address;
crash_reason_tokens.ignore(2); // Ignore the "R " preamble.
crash_reason_tokens >> std::hex >> crash_reason >> crash_reason_str >>
crash_address;
ASSERT_FALSE(crash_reason_tokens.fail());
ASSERT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, crash_reason);
ASSERT_EQ("DUMP_REQUESTED", crash_reason_str);
ASSERT_EQ(0xDEADDEADu, kCrashAddress);
did_find_crash_reason = true;
} else if (line.find("V ") == 0) {
if (expected_info.product_info)
ASSERT_EQ(string("V ") + expected_info.product_info, line);
@ -156,9 +230,18 @@ void CheckMicrodumpContents(const string& microdump_content,
}
ASSERT_TRUE(did_find_os_info);
ASSERT_TRUE(did_find_product_info);
ASSERT_TRUE(did_find_process_type);
ASSERT_TRUE(did_find_crash_reason);
ASSERT_TRUE(did_find_gpu_info);
}
bool MicrodumpStackContains(const string& microdump_content,
const string& expected_content) {
string result;
ExtractMicrodumpStackContents(microdump_content, &result);
return result.find(kIdentifiableString) != string::npos;
}
void CheckMicrodumpContents(const string& microdump_content,
const string& expected_fingerprint,
const string& expected_product_info,
@ -191,23 +274,101 @@ TEST(MicrodumpWriterTest, BasicWithMappings) {
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
scoped_array<char> buf;
std::string buf;
CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf);
ASSERT_TRUE(ContainsMicrodump(buf));
#ifdef __LP64__
ASSERT_NE(static_cast<char*>(0), strstr(
buf.get(), "M 0000000000001000 000000000000002A 0000000000001000 "
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
ASSERT_NE(std::string::npos,
buf.find("M 0000000000001000 000000000000002A 0000000000001000 "
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
#else
ASSERT_NE(static_cast<char*>(0), strstr(
buf.get(), "M 00001000 0000002A 00001000 "
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
ASSERT_NE(std::string::npos,
buf.find("M 00001000 0000002A 00001000 "
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
#endif
// In absence of a product info in the minidump, the writer should just write
// an unknown marker.
ASSERT_NE(static_cast<char*>(0), strstr(
buf.get(), "V UNKNOWN:0.0.0.0"));
ASSERT_NE(std::string::npos, buf.find("V UNKNOWN:0.0.0.0"));
}
// Ensure that no output occurs if the interest region is set, but
// doesn't overlap anything on the stack.
TEST(MicrodumpWriterTest, NoOutputIfUninteresting) {
const char kProductInfo[] = "MockProduct:42.0.2311.99";
const char kBuildFingerprint[] =
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
const char kGPUFingerprint[] =
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
const MicrodumpExtraInfo kMicrodumpExtraInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
std::string buf;
MappingList no_mappings;
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true, 0);
ASSERT_FALSE(ContainsMicrodump(buf));
}
// Ensure that stack content does not contain an identifiable string if the
// stack is sanitized.
TEST(MicrodumpWriterTest, StringRemovedBySanitization) {
const char kProductInfo[] = "MockProduct:42.0.2311.99";
const char kBuildFingerprint[] =
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
const char kGPUFingerprint[] =
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
const MicrodumpExtraInfo kMicrodumpExtraInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
std::string buf;
MappingList no_mappings;
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, true);
ASSERT_TRUE(ContainsMicrodump(buf));
ASSERT_FALSE(MicrodumpStackContains(buf, kIdentifiableString));
}
// Ensure that stack content does contain an identifiable string if the
// stack is not sanitized.
TEST(MicrodumpWriterTest, StringPresentIfNotSanitized) {
const char kProductInfo[] = "MockProduct:42.0.2311.99";
const char kBuildFingerprint[] =
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
const char kGPUFingerprint[] =
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
const MicrodumpExtraInfo kMicrodumpExtraInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
std::string buf;
MappingList no_mappings;
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, false);
ASSERT_TRUE(ContainsMicrodump(buf));
ASSERT_TRUE(MicrodumpStackContains(buf, kIdentifiableString));
}
// Ensure that output occurs if the interest region is set, and
// does overlap something on the stack.
TEST(MicrodumpWriterTest, OutputIfInteresting) {
const char kProductInfo[] = "MockProduct:42.0.2311.99";
const char kBuildFingerprint[] =
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
const char kGPUFingerprint[] =
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
const MicrodumpExtraInfo kMicrodumpExtraInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
std::string buf;
MappingList no_mappings;
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true,
reinterpret_cast<uintptr_t>(CrashAndGetMicrodump));
ASSERT_TRUE(ContainsMicrodump(buf));
}
// Ensure that the product info and build fingerprint metadata show up in the
@ -220,38 +381,40 @@ TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
const MicrodumpExtraInfo kMicrodumpExtraInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
scoped_array<char> buf;
std::string buf;
MappingList no_mappings;
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf);
CheckMicrodumpContents(string(buf.get()), kMicrodumpExtraInfo);
ASSERT_TRUE(ContainsMicrodump(buf));
CheckMicrodumpContents(buf, kMicrodumpExtraInfo);
}
TEST(MicrodumpWriterTest, NoProductInfo) {
const char kBuildFingerprint[] = "foobar";
const char kGPUFingerprint[] = "bazqux";
scoped_array<char> buf;
std::string buf;
MappingList no_mappings;
const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint));
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf);
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
"UNKNOWN:0.0.0.0", kGPUFingerprint);
ASSERT_TRUE(ContainsMicrodump(buf));
CheckMicrodumpContents(buf, kBuildFingerprint, "UNKNOWN:0.0.0.0",
kGPUFingerprint);
}
TEST(MicrodumpWriterTest, NoGPUInfo) {
const char kProductInfo[] = "bazqux";
const char kBuildFingerprint[] = "foobar";
scoped_array<char> buf;
std::string buf;
MappingList no_mappings;
const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo(
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL));
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf);
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
kProductInfo, "UNKNOWN");
ASSERT_TRUE(ContainsMicrodump(buf));
CheckMicrodumpContents(buf, kBuildFingerprint, kProductInfo, "UNKNOWN");
}
} // namespace

View File

@ -212,6 +212,7 @@ bool LinuxCoreDumper::EnumerateThreads() {
if (first_thread) {
crash_thread_ = pid;
crash_signal_ = status->pr_info.si_signo;
crash_signal_code_ = status->pr_info.si_code;
}
first_thread = false;
threads_.push_back(pid);

View File

@ -41,7 +41,7 @@ using namespace google_breakpad;
TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) {
const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root");
const MappingInfo mapping = { 0, 0, 0, false, "/usr/lib/libc.so" };
const MappingInfo mapping = {0, 0, {0, 0}, 0, false, "/usr/lib/libc.so"};
char path[PATH_MAX];
dumper.GetMappingAbsolutePath(mapping, path);

View File

@ -50,6 +50,7 @@
#include "common/linux/linux_libc_support.h"
#include "common/linux/memory_mapped_file.h"
#include "common/linux/safe_readlink.h"
#include "google_breakpad/common/minidump_exception_linux.h"
#include "third_party/lss/linux_syscall_support.h"
#if defined(__ANDROID__)
@ -85,10 +86,15 @@ inline static bool IsMappedFileOpenUnsafe(
namespace google_breakpad {
#if defined(__CHROMEOS__)
namespace {
bool MappingContainsAddress(const MappingInfo& mapping, uintptr_t address) {
return mapping.system_mapping_info.start_addr <= address &&
address < mapping.system_mapping_info.end_addr;
}
#if defined(__CHROMEOS__)
// Recover memory mappings before writing dump on ChromeOS
//
// On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from
@ -225,20 +231,21 @@ void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
l = m + 1;
}
// Try to merge segments into the first.
if (next < mappings.size()) {
TryRecoverMappings(mappings[0], mappings[next]);
if (next - 1 > 0)
TryRecoverMappings(mappings[next - 1], mappings[0], mappings[next]);
}
// Shows the range that contains the entry point is
// [first_start_addr, first_end_addr)
size_t first_start_addr = mappings[0]->start_addr;
size_t first_end_addr = mappings[0]->start_addr + mappings[0]->size;
// Put the out-of-order segment in order.
std::rotate(mappings.begin(), mappings.begin() + 1, mappings.begin() + next);
// Iterate through normal, sorted cases.
// Normal case 1.
for (size_t i = 1; i < mappings.size() - 1; i++)
for (size_t i = 0; i < mappings.size() - 1; i++)
TryRecoverMappings(mappings[i], mappings[i + 1]);
// Normal case 2.
for (size_t i = 1; i < mappings.size() - 2; i++)
for (size_t i = 0; i < mappings.size() - 2; i++)
TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]);
// Collect merged (size == 0) segments.
@ -247,11 +254,28 @@ void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
if (mappings[e]->size > 0)
mappings[f++] = mappings[e];
mappings.resize(f);
// The entry point is in the first mapping. We want to find the location
// of the entry point after merging segment. To do this, we want to find
// the mapping that covers the first mapping from the original mapping list.
// If the mapping is not in the beginning, we move it to the begining via
// a right rotate by using reverse iterators.
for (l = 0; l < mappings.size(); l++) {
if (mappings[l]->start_addr <= first_start_addr
&& (mappings[l]->start_addr + mappings[l]->size >= first_end_addr))
break;
}
if (l > 0) {
r = mappings.size();
std::rotate(mappings.rbegin() + r - l - 1, mappings.rbegin() + r - l,
mappings.rend());
}
}
} // namespace
#endif // __CHROMEOS__
} // namespace
// All interesting auvx entry types are below AT_SYSINFO_EHDR
#define AT_MAX AT_SYSINFO_EHDR
@ -260,6 +284,7 @@ LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix)
root_prefix_(root_prefix),
crash_address_(0),
crash_signal_(0),
crash_signal_code_(0),
crash_thread_(pid),
threads_(&allocator_, 8),
mappings_(&allocator_),
@ -331,6 +356,83 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
return success;
}
void LinuxDumper::SetCrashInfoFromSigInfo(const siginfo_t& siginfo) {
set_crash_address(reinterpret_cast<uintptr_t>(siginfo.si_addr));
set_crash_signal(siginfo.si_signo);
set_crash_signal_code(siginfo.si_code);
}
const char* LinuxDumper::GetCrashSignalString() const {
switch (static_cast<unsigned int>(crash_signal_)) {
case MD_EXCEPTION_CODE_LIN_SIGHUP:
return "SIGHUP";
case MD_EXCEPTION_CODE_LIN_SIGINT:
return "SIGINT";
case MD_EXCEPTION_CODE_LIN_SIGQUIT:
return "SIGQUIT";
case MD_EXCEPTION_CODE_LIN_SIGILL:
return "SIGILL";
case MD_EXCEPTION_CODE_LIN_SIGTRAP:
return "SIGTRAP";
case MD_EXCEPTION_CODE_LIN_SIGABRT:
return "SIGABRT";
case MD_EXCEPTION_CODE_LIN_SIGBUS:
return "SIGBUS";
case MD_EXCEPTION_CODE_LIN_SIGFPE:
return "SIGFPE";
case MD_EXCEPTION_CODE_LIN_SIGKILL:
return "SIGKILL";
case MD_EXCEPTION_CODE_LIN_SIGUSR1:
return "SIGUSR1";
case MD_EXCEPTION_CODE_LIN_SIGSEGV:
return "SIGSEGV";
case MD_EXCEPTION_CODE_LIN_SIGUSR2:
return "SIGUSR2";
case MD_EXCEPTION_CODE_LIN_SIGPIPE:
return "SIGPIPE";
case MD_EXCEPTION_CODE_LIN_SIGALRM:
return "SIGALRM";
case MD_EXCEPTION_CODE_LIN_SIGTERM:
return "SIGTERM";
case MD_EXCEPTION_CODE_LIN_SIGSTKFLT:
return "SIGSTKFLT";
case MD_EXCEPTION_CODE_LIN_SIGCHLD:
return "SIGCHLD";
case MD_EXCEPTION_CODE_LIN_SIGCONT:
return "SIGCONT";
case MD_EXCEPTION_CODE_LIN_SIGSTOP:
return "SIGSTOP";
case MD_EXCEPTION_CODE_LIN_SIGTSTP:
return "SIGTSTP";
case MD_EXCEPTION_CODE_LIN_SIGTTIN:
return "SIGTTIN";
case MD_EXCEPTION_CODE_LIN_SIGTTOU:
return "SIGTTOU";
case MD_EXCEPTION_CODE_LIN_SIGURG:
return "SIGURG";
case MD_EXCEPTION_CODE_LIN_SIGXCPU:
return "SIGXCPU";
case MD_EXCEPTION_CODE_LIN_SIGXFSZ:
return "SIGXFSZ";
case MD_EXCEPTION_CODE_LIN_SIGVTALRM:
return "SIGVTALRM";
case MD_EXCEPTION_CODE_LIN_SIGPROF:
return "SIGPROF";
case MD_EXCEPTION_CODE_LIN_SIGWINCH:
return "SIGWINCH";
case MD_EXCEPTION_CODE_LIN_SIGIO:
return "SIGIO";
case MD_EXCEPTION_CODE_LIN_SIGPWR:
return "SIGPWR";
case MD_EXCEPTION_CODE_LIN_SIGSYS:
return "SIGSYS";
case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED:
return "DUMP_REQUESTED";
default:
return "UNKNOWN";
}
}
bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping,
char path[PATH_MAX]) const {
return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX &&
@ -347,17 +449,16 @@ bool ElfFileSoNameFromMappedFile(
const void* segment_start;
size_t segment_size;
int elf_class;
if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC,
&segment_start, &segment_size, &elf_class)) {
if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC, &segment_start,
&segment_size)) {
// No dynamic section
return false;
}
const void* dynstr_start;
size_t dynstr_size;
if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB,
&dynstr_start, &dynstr_size, &elf_class)) {
if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB, &dynstr_start,
&dynstr_size)) {
// No dynstr section
return false;
}
@ -546,7 +647,10 @@ bool LinuxDumper::EnumerateMappings() {
}
}
MappingInfo* const module = new(allocator_) MappingInfo;
mappings_.push_back(module);
my_memset(module, 0, sizeof(MappingInfo));
module->system_mapping_info.start_addr = start_addr;
module->system_mapping_info.end_addr = end_addr;
module->start_addr = start_addr;
module->size = end_addr - start_addr;
module->offset = offset;
@ -556,31 +660,32 @@ bool LinuxDumper::EnumerateMappings() {
if (l < sizeof(module->name))
my_memcpy(module->name, name, l);
}
// If this is the entry-point mapping, and it's not already the
// first one, then we need to make it be first. This is because
// the minidump format assumes the first module is the one that
// corresponds to the main executable (as codified in
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
if (entry_point_loc &&
(entry_point_loc >=
reinterpret_cast<void*>(module->start_addr)) &&
(entry_point_loc <
reinterpret_cast<void*>(module->start_addr+module->size)) &&
!mappings_.empty()) {
// push the module onto the front of the list.
mappings_.resize(mappings_.size() + 1);
for (size_t idx = mappings_.size() - 1; idx > 0; idx--)
mappings_[idx] = mappings_[idx - 1];
mappings_[0] = module;
} else {
mappings_.push_back(module);
}
}
}
}
line_reader->PopLine(line_len);
}
if (entry_point_loc) {
for (size_t i = 0; i < mappings_.size(); ++i) {
MappingInfo* module = mappings_[i];
// If this module contains the entry-point, and it's not already the first
// one, then we need to make it be first. This is because the minidump
// format assumes the first module is the one that corresponds to the main
// executable (as codified in
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
if ((entry_point_loc >= reinterpret_cast<void*>(module->start_addr)) &&
(entry_point_loc <
reinterpret_cast<void*>(module->start_addr + module->size))) {
for (size_t j = i; j > 0; j--)
mappings_[j] = mappings_[j - 1];
mappings_[0] = module;
break;
}
}
}
sys_close(fd);
return !mappings_.empty();
@ -720,6 +825,126 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
return true;
}
void LinuxDumper::SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
uintptr_t stack_pointer,
uintptr_t sp_offset) {
// We optimize the search for containing mappings in three ways:
// 1) We expect that pointers into the stack mapping will be common, so
// we cache that address range.
// 2) The last referenced mapping is a reasonable predictor for the next
// referenced mapping, so we test that first.
// 3) We precompute a bitfield based upon bits 32:32-n of the start and
// stop addresses, and use that to short circuit any values that can
// not be pointers. (n=11)
const uintptr_t defaced =
#if defined(__LP64__)
0x0defaced0defaced;
#else
0x0defaced;
#endif
// the bitfield length is 2^test_bits long.
const unsigned int test_bits = 11;
// byte length of the corresponding array.
const unsigned int array_size = 1 << (test_bits - 3);
const unsigned int array_mask = array_size - 1;
// The amount to right shift pointers by. This captures the top bits
// on 32 bit architectures. On 64 bit architectures this would be
// uninformative so we take the same range of bits.
const unsigned int shift = 32 - 11;
const MappingInfo* last_hit_mapping = nullptr;
const MappingInfo* hit_mapping = nullptr;
const MappingInfo* stack_mapping = FindMappingNoBias(stack_pointer);
// The magnitude below which integers are considered to be to be
// 'small', and not constitute a PII risk. These are included to
// avoid eliding useful register values.
const ssize_t small_int_magnitude = 4096;
char could_hit_mapping[array_size];
my_memset(could_hit_mapping, 0, array_size);
// Initialize the bitfield such that if the (pointer >> shift)'th
// bit, modulo the bitfield size, is not set then there does not
// exist a mapping in mappings_ that would contain that pointer.
for (size_t i = 0; i < mappings_.size(); ++i) {
if (!mappings_[i]->exec) continue;
// For each mapping, work out the (unmodulo'ed) range of bits to
// set.
uintptr_t start = mappings_[i]->start_addr;
uintptr_t end = start + mappings_[i]->size;
start >>= shift;
end >>= shift;
for (size_t bit = start; bit <= end; ++bit) {
// Set each bit in the range, applying the modulus.
could_hit_mapping[(bit >> 3) & array_mask] |= 1 << (bit & 7);
}
}
// Zero memory that is below the current stack pointer.
const uintptr_t offset =
(sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
if (offset) {
my_memset(stack_copy, 0, offset);
}
// Apply sanitization to each complete pointer-aligned word in the
// stack.
uint8_t* sp;
for (sp = stack_copy + offset;
sp <= stack_copy + stack_len - sizeof(uintptr_t);
sp += sizeof(uintptr_t)) {
uintptr_t addr;
my_memcpy(&addr, sp, sizeof(uintptr_t));
if (static_cast<intptr_t>(addr) <= small_int_magnitude &&
static_cast<intptr_t>(addr) >= -small_int_magnitude) {
continue;
}
if (stack_mapping && MappingContainsAddress(*stack_mapping, addr)) {
continue;
}
if (last_hit_mapping && MappingContainsAddress(*last_hit_mapping, addr)) {
continue;
}
uintptr_t test = addr >> shift;
if (could_hit_mapping[(test >> 3) & array_mask] & (1 << (test & 7)) &&
(hit_mapping = FindMappingNoBias(addr)) != nullptr &&
hit_mapping->exec) {
last_hit_mapping = hit_mapping;
continue;
}
my_memcpy(sp, &defaced, sizeof(uintptr_t));
}
// Zero any partial word at the top of the stack, if alignment is
// such that that is required.
if (sp < stack_copy + stack_len) {
my_memset(sp, 0, stack_copy + stack_len - sp);
}
}
bool LinuxDumper::StackHasPointerToMapping(const uint8_t* stack_copy,
size_t stack_len,
uintptr_t sp_offset,
const MappingInfo& mapping) {
// Loop over all stack words that would have been on the stack in
// the target process (i.e. are word aligned, and at addresses >=
// the stack pointer). Regardless of the alignment of |stack_copy|,
// the memory starting at |stack_copy| + |offset| represents an
// aligned word in the target process.
const uintptr_t low_addr = mapping.system_mapping_info.start_addr;
const uintptr_t high_addr = mapping.system_mapping_info.end_addr;
const uintptr_t offset =
(sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
for (const uint8_t* sp = stack_copy + offset;
sp <= stack_copy + stack_len - sizeof(uintptr_t);
sp += sizeof(uintptr_t)) {
uintptr_t addr;
my_memcpy(&addr, sp, sizeof(uintptr_t));
if (low_addr <= addr && addr <= high_addr)
return true;
}
return false;
}
// Find the mapping which the given memory address falls in.
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
const uintptr_t addr = (uintptr_t) address;
@ -733,6 +958,19 @@ const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
return NULL;
}
// Find the mapping which the given memory address falls in. Uses the
// unadjusted mapping address range from the kernel, rather than the
// biased range.
const MappingInfo* LinuxDumper::FindMappingNoBias(uintptr_t address) const {
for (size_t i = 0; i < mappings_.size(); ++i) {
if (address >= mappings_[i]->system_mapping_info.start_addr &&
address < mappings_[i]->system_mapping_info.end_addr) {
return mappings_[i];
}
}
return NULL;
}
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;

View File

@ -99,10 +99,22 @@ class LinuxDumper {
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
size_t GetMainThreadIndex() const {
for (size_t i = 0; i < threads_.size(); ++i) {
if (threads_[i] == pid_) return i;
}
return -1u;
}
// These are only valid after a call to |Init|.
const wasteful_vector<pid_t> &threads() { return threads_; }
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
const MappingInfo* FindMapping(const void* address) const;
// Find the mapping which the given memory address falls in. Unlike
// FindMapping, this method uses the unadjusted mapping address
// ranges from the kernel, rather than the ranges that have had the
// load bias applied.
const MappingInfo* FindMappingNoBias(uintptr_t address) const;
const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }
// Find a block of memory to take as the stack given the top of stack pointer.
@ -111,6 +123,32 @@ class LinuxDumper {
// stack_top: the current top of the stack
bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
// Sanitize a copy of the stack by overwriting words that are not
// pointers with a sentinel (0x0defaced).
// stack_copy: a copy of the stack to sanitize. |stack_copy| might
// not be word aligned, but it represents word aligned
// data copied from another location.
// stack_len: the length of the allocation pointed to by |stack_copy|.
// stack_pointer: the address of the stack pointer (used to locate
// the stack mapping, as an optimization).
// sp_offset: the offset relative to stack_copy that reflects the
// current value of the stack pointer.
void SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
uintptr_t stack_pointer, uintptr_t sp_offset);
// Test whether |stack_copy| contains a pointer-aligned word that
// could be an address within a given mapping.
// stack_copy: a copy of the stack to check. |stack_copy| might
// not be word aligned, but it represents word aligned
// data copied from another location.
// stack_len: the length of the allocation pointed to by |stack_copy|.
// sp_offset: the offset relative to stack_copy that reflects the
// current value of the stack pointer.
// mapping: the mapping against which to test stack words.
bool StackHasPointerToMapping(const uint8_t* stack_copy, size_t stack_len,
uintptr_t sp_offset,
const MappingInfo& mapping);
PageAllocator* allocator() { return &allocator_; }
// Copy content of |length| bytes from a given process |child|,
@ -132,6 +170,8 @@ class LinuxDumper {
unsigned int mapping_id,
wasteful_vector<uint8_t>& identifier);
void SetCrashInfoFromSigInfo(const siginfo_t& siginfo);
uintptr_t crash_address() const { return crash_address_; }
void set_crash_address(uintptr_t crash_address) {
crash_address_ = crash_address;
@ -139,6 +179,10 @@ class LinuxDumper {
int crash_signal() const { return crash_signal_; }
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
const char* GetCrashSignalString() const;
void set_crash_signal_code(int code) { crash_signal_code_ = code; }
int crash_signal_code() const { return crash_signal_code_; }
pid_t crash_thread() const { return crash_thread_; }
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
@ -189,6 +233,9 @@ class LinuxDumper {
// Signal that terminated the crashed process.
int crash_signal_;
// The code associated with |crash_signal_|.
int crash_signal_code_;
// ID of the crashed thread.
pid_t crash_thread_;

View File

@ -57,14 +57,15 @@
void *thread_function(void *data) {
int pipefd = *static_cast<int *>(data);
volatile pid_t thread_id = syscall(__NR_gettid);
volatile pid_t* thread_id = new pid_t;
*thread_id = syscall(__NR_gettid);
// Signal parent that a thread has started.
uint8_t byte = 1;
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
perror("ERROR: parent notification failed");
return NULL;
}
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = thread_id;
while (true)
asm volatile ("" : : "r" (thread_id_ptr));
return NULL;

View File

@ -149,6 +149,50 @@ bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
return true;
}
bool LinuxPtraceDumper::ReadRegisterSet(ThreadInfo* info, pid_t tid)
{
#ifdef PTRACE_GETREGSET
struct iovec io;
info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len);
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
return false;
}
info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len);
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
return false;
}
return true;
#else
return false;
#endif
}
bool LinuxPtraceDumper::ReadRegisters(ThreadInfo* info, pid_t tid) {
#ifdef PTRACE_GETREGS
void* gp_addr;
info->GetGeneralPurposeRegisters(&gp_addr, NULL);
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) {
return false;
}
#if !(defined(__ANDROID__) && defined(__ARM_EABI__))
// When running an arm build on an arm64 device, attempting to get the
// floating point registers fails. On Android, the floating point registers
// aren't written to the cpu context anyway, so just don't get them here.
// See http://crbug.com/508324
void* fp_addr;
info->GetFloatingPointRegisters(&fp_addr, NULL);
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
return false;
}
#endif // !(defined(__ANDROID__) && defined(__ARM_EABI__))
return true;
#else // PTRACE_GETREGS
return false;
#endif
}
// Read thread info from /proc/$pid/status.
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
// these members are set to -1. Returns true iff all three members are
@ -188,37 +232,12 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (info->ppid == -1 || info->tgid == -1)
return false;
#ifdef PTRACE_GETREGSET
struct iovec io;
info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len);
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
return false;
if (!ReadRegisterSet(info, tid)) {
if (!ReadRegisters(info, tid)) {
return false;
}
}
info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len);
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
return false;
}
#else // PTRACE_GETREGSET
void* gp_addr;
info->GetGeneralPurposeRegisters(&gp_addr, NULL);
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) {
return false;
}
#if !(defined(__ANDROID__) && defined(__ARM_EABI__))
// When running an arm build on an arm64 device, attempting to get the
// floating point registers fails. On Android, the floating point registers
// aren't written to the cpu context anyway, so just don't get them here.
// See http://crbug.com/508324
void* fp_addr;
info->GetFloatingPointRegisters(&fp_addr, NULL);
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
return false;
}
#endif
#endif // PTRACE_GETREGSET
#if defined(__i386)
#if !defined(bit_FXSAVE) // e.g. Clang
#define bit_FXSAVE bit_FXSR
@ -249,6 +268,8 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
#endif
#if defined(__mips__)
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(PC), &info->mcontext.pc);
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1);
sys_ptrace(PTRACE_PEEKUSER, tid,

View File

@ -85,6 +85,15 @@ class LinuxPtraceDumper : public LinuxDumper {
private:
// Set to true if all threads of the crashed process are suspended.
bool threads_suspended_;
// Read the tracee's registers on kernel with PTRACE_GETREGSET support.
// Returns false if PTRACE_GETREGSET is not defined.
// Returns true on success.
bool ReadRegisterSet(ThreadInfo* info, pid_t tid);
// Read the tracee's registers on kernel with PTRACE_GETREGS support.
// Returns true on success.
bool ReadRegisters(ThreadInfo* info, pid_t tid);
};
} // namespace google_breakpad

View File

@ -66,6 +66,63 @@ using namespace google_breakpad;
namespace {
pid_t SetupChildProcess(int number_of_threads) {
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", number_of_threads);
int fds[2];
EXPECT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
fprintf(stderr, "Couldn't find helper binary\n");
_exit(1);
}
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(helper_path.c_str(),
"linux_dumper_unittest_helper",
pipe_fd_string,
kNumberOfThreadsArgument,
NULL);
// Kill if we get here.
printf("Errno from exec: %d", errno);
std::string err_str = "Exec of " + helper_path + " failed";
perror(err_str.c_str());
_exit(1);
}
close(fds[1]);
// Wait for all child threads to indicate that they have started
for (int threads = 0; threads < number_of_threads; threads++) {
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
EXPECT_EQ(1, r);
EXPECT_TRUE(pfd.revents & POLLIN);
uint8_t junk;
EXPECT_EQ(read(fds[0], &junk, sizeof(junk)),
static_cast<ssize_t>(sizeof(junk)));
}
close(fds[0]);
// There is a race here because we may stop a child thread before
// it is actually running the busy loop. Empirically this sleep
// is sufficient to avoid the race.
usleep(100000);
return child_pid;
}
typedef wasteful_vector<uint8_t> id_vector;
typedef testing::Test LinuxPtraceDumperTest;
@ -370,58 +427,9 @@ TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
static const int kNumberOfThreadsInHelperProgram = 5;
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(helper_path.c_str(),
"linux_dumper_unittest_helper",
pipe_fd_string,
kNumberOfThreadsArgument,
NULL);
// Kill if we get here.
printf("Errno from exec: %d", errno);
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
exit(0);
}
close(fds[1]);
// Wait for all child threads to indicate that they have started
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
ASSERT_EQ(1, r);
ASSERT_TRUE(pfd.revents & POLLIN);
uint8_t junk;
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
static_cast<ssize_t>(sizeof(junk)));
}
close(fds[0]);
// There is a race here because we may stop a child thread before
// it is actually running the busy loop. Empirically this sleep
// is sufficient to avoid the race.
usleep(100000);
pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram);
ASSERT_NE(child_pid, -1);
// Children are ready now.
LinuxPtraceDumper dumper(child_pid);
@ -468,3 +476,99 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(SIGKILL, WTERMSIG(status));
}
TEST_F(LinuxPtraceDumperTest, SanitizeStackCopy) {
static const int kNumberOfThreadsInHelperProgram = 1;
pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram);
ASSERT_NE(child_pid, -1);
LinuxPtraceDumper dumper(child_pid);
ASSERT_TRUE(dumper.Init());
EXPECT_TRUE(dumper.ThreadsSuspend());
ThreadInfo thread_info;
EXPECT_TRUE(dumper.GetThreadInfoByIndex(0, &thread_info));
const uintptr_t defaced =
#if defined(__LP64__)
0x0defaced0defaced;
#else
0x0defaced;
#endif
uintptr_t simulated_stack[2];
// Pointers into the stack shouldn't be sanitized.
memset(simulated_stack, 0xff, sizeof(simulated_stack));
simulated_stack[1] = thread_info.stack_pointer;
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
sizeof(simulated_stack), thread_info.stack_pointer,
sizeof(uintptr_t));
ASSERT_NE(simulated_stack[1], defaced);
// Memory prior to the stack pointer should be cleared.
ASSERT_EQ(simulated_stack[0], 0u);
// Small integers should not be sanitized.
for (int i = -4096; i <= 4096; ++i) {
memset(simulated_stack, 0, sizeof(simulated_stack));
simulated_stack[0] = static_cast<uintptr_t>(i);
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
sizeof(simulated_stack), thread_info.stack_pointer,
0u);
ASSERT_NE(simulated_stack[0], defaced);
}
// The instruction pointer definitely should point into an executable mapping.
const MappingInfo* mapping_info = dumper.FindMappingNoBias(
reinterpret_cast<uintptr_t>(thread_info.GetInstructionPointer()));
ASSERT_NE(mapping_info, nullptr);
ASSERT_TRUE(mapping_info->exec);
// Pointers to code shouldn't be sanitized.
memset(simulated_stack, 0, sizeof(simulated_stack));
simulated_stack[1] = thread_info.GetInstructionPointer();
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
sizeof(simulated_stack), thread_info.stack_pointer,
0u);
ASSERT_NE(simulated_stack[0], defaced);
// String fragments should be sanitized.
memcpy(simulated_stack, "abcdefghijklmnop", sizeof(simulated_stack));
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
sizeof(simulated_stack), thread_info.stack_pointer,
0u);
ASSERT_EQ(simulated_stack[0], defaced);
ASSERT_EQ(simulated_stack[1], defaced);
// Heap pointers should be sanititzed.
#if defined(__ARM_EABI__)
uintptr_t heap_addr = thread_info.regs.uregs[3];
#elif defined(__aarch64__)
uintptr_t heap_addr = thread_info.regs.regs[3];
#elif defined(__i386)
uintptr_t heap_addr = thread_info.regs.ecx;
#elif defined(__x86_64)
uintptr_t heap_addr = thread_info.regs.rcx;
#elif defined(__mips__)
uintptr_t heap_addr = thread_info.mcontext.gregs[1];
#else
#error This test has not been ported to this platform.
#endif
memset(simulated_stack, 0, sizeof(simulated_stack));
simulated_stack[0] = heap_addr;
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
sizeof(simulated_stack), thread_info.stack_pointer,
0u);
ASSERT_EQ(simulated_stack[0], defaced);
EXPECT_TRUE(dumper.ThreadsResume());
kill(child_pid, SIGKILL);
// Reap child.
int status;
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(SIGKILL, WTERMSIG(status));
}

View File

@ -129,6 +129,9 @@ class MinidumpWriter {
const ExceptionHandler::CrashContext* context,
const MappingList& mappings,
const AppMemoryList& appmem,
bool skip_stacks_if_mapping_unreferenced,
uintptr_t principal_mapping_address,
bool sanitize_stacks,
LinuxDumper* dumper)
: fd_(minidump_fd),
path_(minidump_path),
@ -140,7 +143,12 @@ class MinidumpWriter {
minidump_size_limit_(-1),
memory_blocks_(dumper_->allocator()),
mapping_list_(mappings),
app_memory_list_(appmem) {
app_memory_list_(appmem),
skip_stacks_if_mapping_unreferenced_(
skip_stacks_if_mapping_unreferenced),
principal_mapping_address_(principal_mapping_address),
principal_mapping_(nullptr),
sanitize_stacks_(sanitize_stacks) {
// Assert there should be either a valid fd or a valid path, not both.
assert(fd_ != -1 || minidump_path);
assert(fd_ == -1 || !minidump_path);
@ -150,12 +158,22 @@ class MinidumpWriter {
if (!dumper_->Init())
return false;
if (!dumper_->ThreadsSuspend() || !dumper_->LateInit())
return false;
if (skip_stacks_if_mapping_unreferenced_) {
principal_mapping_ =
dumper_->FindMappingNoBias(principal_mapping_address_);
if (!CrashingThreadReferencesPrincipalMapping())
return false;
}
if (fd_ != -1)
minidump_writer_.SetFile(fd_);
else if (!minidump_writer_.Open(path_))
return false;
return dumper_->ThreadsSuspend() && dumper_->LateInit();
return true;
}
~MinidumpWriter() {
@ -166,6 +184,38 @@ class MinidumpWriter {
dumper_->ThreadsResume();
}
bool CrashingThreadReferencesPrincipalMapping() {
if (!ucontext_ || !principal_mapping_)
return false;
const uintptr_t low_addr =
principal_mapping_->system_mapping_info.start_addr;
const uintptr_t high_addr =
principal_mapping_->system_mapping_info.end_addr;
const uintptr_t stack_pointer = UContextReader::GetStackPointer(ucontext_);
const uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_);
if (pc >= low_addr && pc < high_addr)
return true;
uint8_t* stack_copy;
const void* stack;
size_t stack_len;
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer))
return false;
stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
dumper_->CopyFromProcess(stack_copy, GetCrashThread(), stack, stack_len);
uintptr_t stack_pointer_offset =
stack_pointer - reinterpret_cast<uintptr_t>(stack);
return dumper_->StackHasPointerToMapping(
stack_copy, stack_len, stack_pointer_offset, *principal_mapping_);
}
bool Dump() {
// A minidump file contains a number of tagged streams. This is the number
// of stream which we write.
@ -265,12 +315,16 @@ class MinidumpWriter {
}
bool FillThreadStack(MDRawThread* thread, uintptr_t stack_pointer,
int max_stack_len, uint8_t** stack_copy) {
uintptr_t pc, int max_stack_len, uint8_t** stack_copy) {
*stack_copy = NULL;
const void* stack;
size_t stack_len;
thread->stack.start_of_memory_range = stack_pointer;
thread->stack.memory.data_size = 0;
thread->stack.memory.rva = minidump_writer_.position();
if (dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
UntypedMDRVA memory(&minidump_writer_);
if (max_stack_len >= 0 &&
stack_len > static_cast<unsigned int>(max_stack_len)) {
stack_len = max_stack_len;
@ -283,20 +337,38 @@ class MinidumpWriter {
}
stack = reinterpret_cast<const void*>(int_stack);
}
if (!memory.Allocate(stack_len))
return false;
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
dumper_->CopyFromProcess(*stack_copy, thread->thread_id, stack,
stack_len);
uintptr_t stack_pointer_offset =
stack_pointer - reinterpret_cast<uintptr_t>(stack);
if (skip_stacks_if_mapping_unreferenced_) {
if (!principal_mapping_) {
return true;
}
uintptr_t low_addr = principal_mapping_->system_mapping_info.start_addr;
uintptr_t high_addr = principal_mapping_->system_mapping_info.end_addr;
if ((pc < low_addr || pc > high_addr) &&
!dumper_->StackHasPointerToMapping(*stack_copy, stack_len,
stack_pointer_offset,
*principal_mapping_)) {
return true;
}
}
if (sanitize_stacks_) {
dumper_->SanitizeStackCopy(*stack_copy, stack_len, stack_pointer,
stack_pointer_offset);
}
UntypedMDRVA memory(&minidump_writer_);
if (!memory.Allocate(stack_len))
return false;
memory.Copy(*stack_copy, stack_len);
thread->stack.start_of_memory_range =
reinterpret_cast<uintptr_t>(stack);
thread->stack.start_of_memory_range = reinterpret_cast<uintptr_t>(stack);
thread->stack.memory = memory.location();
memory_blocks_.push_back(thread->stack);
} else {
thread->stack.start_of_memory_range = stack_pointer;
thread->stack.memory.data_size = 0;
thread->stack.memory.rva = minidump_writer_.position();
}
return true;
}
@ -343,7 +415,9 @@ class MinidumpWriter {
!dumper_->IsPostMortem()) {
uint8_t* stack_copy;
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
if (!FillThreadStack(&thread, stack_ptr, -1, &stack_copy))
if (!FillThreadStack(&thread, stack_ptr,
UContextReader::GetInstructionPointer(ucontext_),
-1, &stack_copy))
return false;
// Copy 256 bytes around crashing instruction pointer to minidump.
@ -409,8 +483,9 @@ class MinidumpWriter {
int max_stack_len = -1; // default to no maximum for this thread
if (minidump_size_limit_ >= 0 && i >= kLimitBaseThreadCount)
max_stack_len = extra_thread_stack_len;
if (!FillThreadStack(&thread, info.stack_pointer, max_stack_len,
&stack_copy))
if (!FillThreadStack(&thread, info.stack_pointer,
info.GetInstructionPointer(), max_stack_len,
&stack_copy))
return false;
TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
@ -626,15 +701,18 @@ class MinidumpWriter {
TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
if (!exc.Allocate())
return false;
my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
MDRawExceptionStream* stream = exc.get();
my_memset(stream, 0, sizeof(MDRawExceptionStream));
dirent->stream_type = MD_EXCEPTION_STREAM;
dirent->location = exc.location();
exc.get()->thread_id = GetCrashThread();
exc.get()->exception_record.exception_code = dumper_->crash_signal();
exc.get()->exception_record.exception_address = dumper_->crash_address();
exc.get()->thread_context = crashing_thread_context_;
stream->thread_id = GetCrashThread();
stream->exception_record.exception_code = dumper_->crash_signal();
stream->exception_record.exception_flags = dumper_->crash_signal_code();
stream->exception_record.exception_address = dumper_->crash_address();
stream->thread_context = crashing_thread_context_;
return true;
}
@ -1264,6 +1342,13 @@ class MinidumpWriter {
// Additional memory regions to be included in the dump,
// provided by the caller.
const AppMemoryList& app_memory_list_;
// If set, skip recording any threads that do not reference the
// mapping containing principal_mapping_address_.
bool skip_stacks_if_mapping_unreferenced_;
uintptr_t principal_mapping_address_;
const MappingInfo* principal_mapping_;
// If true, apply stack sanitization to stored stack data.
bool sanitize_stacks_;
};
@ -1273,20 +1358,22 @@ bool WriteMinidumpImpl(const char* minidump_path,
pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appmem) {
const AppMemoryList& appmem,
bool skip_stacks_if_mapping_unreferenced,
uintptr_t principal_mapping_address,
bool sanitize_stacks) {
LinuxPtraceDumper dumper(crashing_process);
const ExceptionHandler::CrashContext* context = NULL;
if (blob) {
if (blob_size != sizeof(ExceptionHandler::CrashContext))
return false;
context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
dumper.set_crash_address(
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
dumper.set_crash_signal(context->siginfo.si_signo);
dumper.SetCrashInfoFromSigInfo(context->siginfo);
dumper.set_crash_thread(context->tid);
}
MinidumpWriter writer(minidump_path, minidump_fd, context, mappings,
appmem, &dumper);
appmem, skip_stacks_if_mapping_unreferenced,
principal_mapping_address, sanitize_stacks, &dumper);
// Set desired limit for file size of minidump (-1 means no limit).
writer.set_minidump_size_limit(minidump_size_limit);
if (!writer.Init())
@ -1299,17 +1386,29 @@ bool WriteMinidumpImpl(const char* minidump_path,
namespace google_breakpad {
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size) {
const void* blob, size_t blob_size,
bool skip_stacks_if_mapping_unreferenced,
uintptr_t principal_mapping_address,
bool sanitize_stacks) {
return WriteMinidumpImpl(minidump_path, -1, -1,
crashing_process, blob, blob_size,
MappingList(), AppMemoryList());
MappingList(), AppMemoryList(),
skip_stacks_if_mapping_unreferenced,
principal_mapping_address,
sanitize_stacks);
}
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size) {
const void* blob, size_t blob_size,
bool skip_stacks_if_mapping_unreferenced,
uintptr_t principal_mapping_address,
bool sanitize_stacks) {
return WriteMinidumpImpl(NULL, minidump_fd, -1,
crashing_process, blob, blob_size,
MappingList(), AppMemoryList());
MappingList(), AppMemoryList(),
skip_stacks_if_mapping_unreferenced,
principal_mapping_address,
sanitize_stacks);
}
bool WriteMinidump(const char* minidump_path, pid_t process,
@ -1319,7 +1418,7 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
dumper.set_crash_signal(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED);
dumper.set_crash_thread(process_blamed_thread);
MinidumpWriter writer(minidump_path, -1, NULL, MappingList(),
AppMemoryList(), &dumper);
AppMemoryList(), false, 0, false, &dumper);
if (!writer.Init())
return false;
return writer.Dump();
@ -1328,46 +1427,71 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appmem) {
const AppMemoryList& appmem,
bool skip_stacks_if_mapping_unreferenced,
uintptr_t principal_mapping_address,
bool sanitize_stacks) {
return WriteMinidumpImpl(minidump_path, -1, -1, crashing_process,
blob, blob_size,
mappings, appmem);
mappings, appmem,
skip_stacks_if_mapping_unreferenced,
principal_mapping_address,
sanitize_stacks);
}
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appmem) {
const AppMemoryList& appmem,
bool skip_stacks_if_mapping_unreferenced,
uintptr_t principal_mapping_address,
bool sanitize_stacks) {
return WriteMinidumpImpl(NULL, minidump_fd, -1, crashing_process,
blob, blob_size,
mappings, appmem);
mappings, appmem,
skip_stacks_if_mapping_unreferenced,
principal_mapping_address,
sanitize_stacks);
}
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appmem) {
const AppMemoryList& appmem,
bool skip_stacks_if_mapping_unreferenced,
uintptr_t principal_mapping_address,
bool sanitize_stacks) {
return WriteMinidumpImpl(minidump_path, -1, minidump_size_limit,
crashing_process, blob, blob_size,
mappings, appmem);
mappings, appmem,
skip_stacks_if_mapping_unreferenced,
principal_mapping_address,
sanitize_stacks);
}
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appmem) {
const AppMemoryList& appmem,
bool skip_stacks_if_mapping_unreferenced,
uintptr_t principal_mapping_address,
bool sanitize_stacks) {
return WriteMinidumpImpl(NULL, minidump_fd, minidump_size_limit,
crashing_process, blob, blob_size,
mappings, appmem);
mappings, appmem,
skip_stacks_if_mapping_unreferenced,
principal_mapping_address,
sanitize_stacks);
}
bool WriteMinidump(const char* filename,
const MappingList& mappings,
const AppMemoryList& appmem,
LinuxDumper* dumper) {
MinidumpWriter writer(filename, -1, NULL, mappings, appmem, dumper);
MinidumpWriter writer(filename, -1, NULL, mappings, appmem,
false, 0, false, dumper);
if (!writer.Init())
return false;
return writer.Dump();

View File

@ -78,10 +78,16 @@ typedef std::list<AppMemory> AppMemoryList;
//
// Returns true iff successful.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size);
const void* blob, size_t blob_size,
bool skip_stacks_if_mapping_unreferenced = false,
uintptr_t principal_mapping_address = 0,
bool sanitize_stacks = false);
// Same as above but takes an open file descriptor instead of a path.
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size);
const void* blob, size_t blob_size,
bool skip_stacks_if_mapping_unreferenced = false,
uintptr_t principal_mapping_address = 0,
bool sanitize_stacks = false);
// Alternate form of WriteMinidump() that works with processes that
// are not expected to have crashed. If |process_blamed_thread| is
@ -96,23 +102,35 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
const AppMemoryList& appdata,
bool skip_stacks_if_mapping_unreferenced = false,
uintptr_t principal_mapping_address = 0,
bool sanitize_stacks = false);
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
const AppMemoryList& appdata,
bool skip_stacks_if_mapping_unreferenced = false,
uintptr_t principal_mapping_address = 0,
bool sanitize_stacks = false);
// These overloads also allow passing a file size limit for the minidump.
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
const AppMemoryList& appdata,
bool skip_stacks_if_mapping_unreferenced = false,
uintptr_t principal_mapping_address = 0,
bool sanitize_stacks = false);
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
const AppMemoryList& appdata,
bool skip_stacks_if_mapping_unreferenced = false,
uintptr_t principal_mapping_address = 0,
bool sanitize_stacks = false);
bool WriteMinidump(const char* filename,
const MappingList& mappings,

View File

@ -70,7 +70,7 @@ TEST(MinidumpWriterTest, SetupWithPath) {
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
syscall(__NR_exit_group);
}
close(fds[0]);
@ -87,6 +87,7 @@ TEST(MinidumpWriterTest, SetupWithPath) {
ASSERT_GT(st.st_size, 0);
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
TEST(MinidumpWriterTest, SetupWithFD) {
@ -99,7 +100,7 @@ TEST(MinidumpWriterTest, SetupWithFD) {
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
syscall(__NR_exit_group);
}
close(fds[0]);
@ -117,6 +118,7 @@ TEST(MinidumpWriterTest, SetupWithFD) {
ASSERT_GT(st.st_size, 0);
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
// Test that mapping info can be specified when writing a minidump,
@ -152,7 +154,7 @@ TEST(MinidumpWriterTest, MappingInfo) {
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
syscall(__NR_exit_group);
}
close(fds[0]);
@ -179,7 +181,7 @@ TEST(MinidumpWriterTest, MappingInfo) {
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
mappings, memory_list));
mappings, memory_list, false, 0, false));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
@ -213,6 +215,150 @@ TEST(MinidumpWriterTest, MappingInfo) {
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
// Test that minidumping is skipped while writing minidumps if principal mapping
// is not referenced.
TEST(MinidumpWriterTest, MinidumpSkippedIfRequested) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit_group);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
ASSERT_EQ(0, getcontext(&context.context));
context.tid = child;
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// pass an invalid principal mapping address, which will force
// WriteMinidump to not write a minidump.
ASSERT_FALSE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
true, static_cast<uintptr_t>(0x0102030405060708ull),
false));
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
// Test that minidumping is skipped while writing minidumps if principal mapping
// is not referenced.
TEST(MinidumpWriterTest, MinidumpStacksSkippedIfRequested) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
// Create a thread that does not return, and only references libc (not the
// current executable). This thread should not be captured in the minidump.
pthread_t thread;
pthread_attr_t thread_attributes;
pthread_attr_init(&thread_attributes);
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
sigset_t sigset;
sigemptyset(&sigset);
pthread_create(&thread, &thread_attributes,
reinterpret_cast<void* (*)(void*)>(&sigsuspend), &sigset);
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit_group);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
ASSERT_EQ(0, getcontext(&context.context));
context.tid = child;
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// Pass an invalid principal mapping address, which will force
// WriteMinidump to not dump any thread stacks.
ASSERT_TRUE(WriteMinidump(
templ.c_str(), child, &context, sizeof(context), true,
reinterpret_cast<uintptr_t>(google_breakpad::WriteFile), false));
// Read the minidump. And ensure that thread memory was dumped only for the
// main thread.
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
MinidumpThreadList *threads = minidump.GetThreadList();
int threads_with_stacks = 0;
for (unsigned int i = 0; i < threads->thread_count(); ++i) {
MinidumpThread *thread = threads->GetThreadAtIndex(i);
if (thread->GetMemory()) {
++threads_with_stacks;
}
}
ASSERT_EQ(1, threads_with_stacks);
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
// Test that stacks can be sanitized while writing minidumps.
TEST(MinidumpWriterTest, StacksAreSanitizedIfRequested) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit_group);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
ASSERT_EQ(0, getcontext(&context.context));
context.tid = child;
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// pass an invalid principal mapping address, which will force
// WriteMinidump to not dump any thread stacks.
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
false, 0, true));
// Read the minidump. And ensure that thread memory contains a defaced value.
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
const uintptr_t defaced =
#if defined(__LP64__)
0x0defaced0defaced;
#else
0x0defaced;
#endif
MinidumpThreadList *threads = minidump.GetThreadList();
for (unsigned int i = 0; i < threads->thread_count(); ++i) {
MinidumpThread *thread = threads->GetThreadAtIndex(i);
MinidumpMemoryRegion *mem = thread->GetMemory();
ASSERT_TRUE(mem != nullptr);
uint32_t sz = mem->GetSize();
const uint8_t *data = mem->GetMemory();
ASSERT_TRUE(memmem(data, sz, &defaced, sizeof(defaced)) != nullptr);
}
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
// Test that a binary with a longer-than-usual build id note
@ -229,7 +375,7 @@ TEST(MinidumpWriterTest, BuildIDLong) {
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
syscall(__NR_exit_group);
}
close(fds[0]);
@ -260,6 +406,8 @@ TEST(MinidumpWriterTest, BuildIDLong) {
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
EXPECT_EQ(module_identifier, module->debug_identifier());
EXPECT_EQ(build_id, module->code_identifier());
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
// Test that mapping info can be specified, and that it overrides
@ -308,7 +456,7 @@ TEST(MinidumpWriterTest, MappingInfoContained) {
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
syscall(__NR_exit_group);
}
close(fds[0]);
@ -354,6 +502,7 @@ TEST(MinidumpWriterTest, MappingInfoContained) {
EXPECT_EQ(module_identifier, module->debug_identifier());
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
TEST(MinidumpWriterTest, DeletedBinary) {
@ -445,6 +594,8 @@ TEST(MinidumpWriterTest, DeletedBinary) {
// which is always zero on Linux.
module_identifier += "0";
EXPECT_EQ(module_identifier, module->debug_identifier());
IGNORE_EINTR(waitpid(child_pid, nullptr, 0));
}
// Test that an additional memory region can be added to the minidump.
@ -472,7 +623,7 @@ TEST(MinidumpWriterTest, AdditionalMemory) {
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
syscall(__NR_exit_group);
}
close(fds[0]);
@ -517,6 +668,7 @@ TEST(MinidumpWriterTest, AdditionalMemory) {
delete[] memory;
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
// Test that an invalid thread stack pointer still results in a minidump.
@ -530,7 +682,7 @@ TEST(MinidumpWriterTest, InvalidStackPointer) {
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
syscall(__NR_exit_group);
}
close(fds[0]);
@ -596,6 +748,7 @@ TEST(MinidumpWriterTest, InvalidStackPointer) {
#endif
close(fds[1]);
IGNORE_EINTR(waitpid(child, nullptr, 0));
}
// Test that limiting the size of the minidump works.
@ -770,6 +923,7 @@ TEST(MinidumpWriterTest, MinidumpSizeLimit) {
// Kill the helper program.
kill(child_pid, SIGKILL);
IGNORE_EINTR(waitpid(child_pid, nullptr, 0));
}
} // namespace

View File

@ -32,6 +32,7 @@
// nlist function implemented to work on 64-bit.
#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
#define CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
#include <mach/machine.h>

View File

@ -126,7 +126,7 @@ extern "C" {
mach_msg_header_t* reply);
// This symbol must be visible to dlsym() - see
// http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=345 for details.
kern_return_t catch_exception_raise(mach_port_t target_port,
mach_port_t failed_thread,
mach_port_t task,
@ -356,6 +356,11 @@ bool ExceptionHandler::WriteMinidumpWithException(
bool report_current_thread) {
bool result = false;
#if TARGET_OS_IPHONE
// _exit() should never be called on iOS.
exit_after_write = false;
#endif
if (directCallback_) {
if (directCallback_(callback_context_,
exception_type,
@ -459,7 +464,7 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
kern_return_t result;
// TODO: Handle the case where |target_behavior| has MACH_EXCEPTION_CODES
// set. https://code.google.com/p/google-breakpad/issues/detail?id=551
// set. https://bugs.chromium.org/p/google-breakpad/issues/detail?id=551
switch (target_behavior) {
case EXCEPTION_DEFAULT:
result = exception_raise(target_port, failed_thread, task, exception,

View File

@ -68,7 +68,7 @@
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F310E8B07E800D7E813 /* dwarftests.mm */; };
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.mm */; };
F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.cc */; };
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; };
@ -151,7 +151,7 @@
F9721F300E8B07E800D7E813 /* dwarftests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarftests.h; sourceTree = "<group>"; };
F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = "<group>"; };
F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; };
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; };
F9721F390E8B0D0D00D7E813 /* dump_syms.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.cc; path = ../../../common/mac/dump_syms.cc; sourceTree = SOURCE_ROOT; };
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
@ -240,7 +240,7 @@
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */,
F9721F390E8B0D0D00D7E813 /* dump_syms.cc */,
F9721F380E8B0CFC00D7E813 /* dump_syms.h */,
F917C4F70E03265A00F86017 /* breakpad_exc_server.c */,
F917C4F80E03265A00F86017 /* breakpad_exc_server.h */,
@ -597,7 +597,7 @@
buildActionMask = 2147483647;
files = (
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */,
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */,
F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */,
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,

View File

@ -36,6 +36,7 @@
#include "breakpad_googletest_includes.h"
#include "client/mac/handler/exception_handler.h"
#include "common/linux/ignore_ret.h"
#include "common/mac/MachIPC.h"
#include "common/tests/auto_tempdir.h"
#include "google_breakpad/processor/minidump.h"
@ -93,7 +94,7 @@ static bool MDCallback(const char *dump_dir, const char *file_name,
path.append(".dmp");
int fd = *reinterpret_cast<int*>(context);
(void)write(fd, path.c_str(), path.length() + 1);
IGNORE_RET(write(fd, path.c_str(), path.length() + 1));
close(fd);
exit(0);
// not reached
@ -293,7 +294,7 @@ TEST_F(ExceptionHandlerTest, DumpChildProcess) {
// Unblock child process
uint8_t data = 1;
(void)write(fds[1], &data, 1);
IGNORE_RET(write(fds[1], &data, 1));
// Child process should have exited with a zero status.
int ret;

View File

@ -42,6 +42,7 @@
#include "breakpad_googletest_includes.h"
#include "client/mac/handler/minidump_generator.h"
#include "client/mac/tests/spawn_child_process.h"
#include "common/linux/ignore_ret.h"
#include "common/mac/MachIPC.h"
#include "common/tests/auto_tempdir.h"
#include "google_breakpad/processor/minidump.h"
@ -190,7 +191,7 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
// Unblock child process
uint8_t data = 1;
(void)write(fds[1], &data, 1);
IGNORE_RET(write(fds[1], &data, 1));
// Child process should have exited with a zero status.
int ret;

View File

@ -968,9 +968,20 @@ bool CrashGenerationServer::GenerateDump(const ClientInfo& client,
dump_generator.SetCallback(&callback);
}
if (!dump_generator.GenerateDumpFile(dump_path)) {
if (!dump_generator.GenerateDumpFile(dump_path)) {
return false;
}
// If the client requests a full memory dump, we will write a normal mini
// dump and a full memory dump. Both dump files use the same uuid as file
// name prefix.
if (client.dump_type() & MiniDumpWithFullMemory) {
std::wstring full_dump_path;
if (!dump_generator.GenerateFullDumpFile(&full_dump_path)) {
return false;
}
}
return dump_generator.WriteMinidump();
}

View File

@ -176,7 +176,7 @@ bool HandleTraceData::CollectHandleData(
stream_data->Reserved = 0;
std::copy(operations_.begin(),
operations_.end(),
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(_LIBCPP_STD_VER)
stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>(
reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1),
operations_.size())
@ -271,12 +271,14 @@ MinidumpGenerator::MinidumpGenerator(
dump_type_(dump_type),
is_client_pointers_(is_client_pointers),
dump_path_(dump_path),
uuid_generated_(false),
dump_file_(INVALID_HANDLE_VALUE),
full_dump_file_(INVALID_HANDLE_VALUE),
dump_file_is_internal_(false),
full_dump_file_is_internal_(false),
additional_streams_(NULL),
callback_info_(NULL) {
uuid_ = {0};
InitializeCriticalSection(&module_load_sync_);
InitializeCriticalSection(&get_proc_address_sync_);
}
@ -562,15 +564,17 @@ MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() {
}
bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
UUID id = {0};
if (!uuid_generated_) {
UuidCreateType create_uuid = GetCreateUuid();
if (!create_uuid) {
return false;
}
UuidCreateType create_uuid = GetCreateUuid();
if (!create_uuid) {
return false;
create_uuid(&uuid_);
uuid_generated_ = true;
}
create_uuid(&id);
wstring id_str = GUIDString::GUIDToWString(&id);
wstring id_str = GUIDString::GUIDToWString(&uuid_);
*file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp");
return true;

View File

@ -168,6 +168,10 @@ class MinidumpGenerator {
// Folder path to store dump files.
std::wstring dump_path_;
// UUID used to make dump file names.
UUID uuid_;
bool uuid_generated_;
// The file where the dump will be written.
HANDLE dump_file_;

View File

@ -41,6 +41,11 @@
namespace google_breakpad {
// This define is new to Windows 10.
#ifndef DBG_PRINTEXCEPTION_WIDE_C
#define DBG_PRINTEXCEPTION_WIDE_C ((DWORD)0x4001000A)
#endif
vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
LONG ExceptionHandler::handler_stack_index_ = 0;
CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
@ -473,7 +478,9 @@ LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) {
DWORD code = exinfo->ExceptionRecord->ExceptionCode;
LONG action;
bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) ||
(code == EXCEPTION_SINGLE_STEP);
(code == EXCEPTION_SINGLE_STEP) ||
(code == DBG_PRINTEXCEPTION_C) ||
(code == DBG_PRINTEXCEPTION_WIDE_C);
if (code == EXCEPTION_INVALID_HANDLE &&
current_handler->consume_invalid_handle_exceptions_) {

View File

@ -28,9 +28,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/include/gmock/gmock.h"
#include "breakpad_googletest_includes.h"
#include "client/windows/crash_generation/crash_generation_server.h"
#include "client/windows/common/ipc_protocol.h"

View File

@ -31,8 +31,8 @@
#include <objbase.h>
#include <dbghelp.h>
#include "breakpad_googletest_includes.h"
#include "client/windows/unittests/dump_analysis.h" // NOLINT
#include "testing/gtest/include/gtest/gtest.h"
DumpAnalysis::~DumpAnalysis() {
if (dump_file_view_ != NULL) {

View File

@ -82,7 +82,7 @@ void ExceptionHandlerDeathTest::SetUp() {
// The test case name is exposed as a c-style string,
// convert it to a wchar_t string.
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
strlen(test_info->name()),
static_cast<int>(strlen(test_info->name())),
test_name_wide,
MAX_PATH);
if (!dwRet) {
@ -293,8 +293,8 @@ wstring find_minidump_in_directory(const wstring &directory) {
wstring filename;
do {
const wchar_t extension[] = L".dmp";
const int extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
const int filename_length = wcslen(find_data.cFileName);
const size_t extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
const size_t filename_length = wcslen(find_data.cFileName);
if (filename_length > extension_length &&
wcsncmp(extension,
find_data.cFileName + filename_length - extension_length,
@ -357,8 +357,8 @@ TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) {
// Read the minidump. Locate the exception record and the
// memory list, and then ensure that there is a memory region
// in the memory list that covers the instruction pointer from
// the exception record.
// in the memory list that covers at least 128 bytes on either
// side of the instruction pointer from the exception record.
{
Minidump minidump(minidump_filename);
ASSERT_TRUE(minidump.Read());
@ -379,18 +379,23 @@ TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) {
memory_list->GetMemoryRegionForAddress(instruction_pointer);
ASSERT_TRUE(region);
EXPECT_EQ(kMemorySize, region->GetSize());
EXPECT_LE(kMemorySize, region->GetSize());
const uint8_t* bytes = region->GetMemory();
ASSERT_TRUE(bytes);
uint64_t ip_offset = instruction_pointer - region->GetBase();
EXPECT_GE(region->GetSize() - kOffset, ip_offset);
EXPECT_LE(kOffset, ip_offset);
uint8_t prefix_bytes[kOffset];
uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
memset(prefix_bytes, 0, sizeof(prefix_bytes));
memset(suffix_bytes, 0, sizeof(suffix_bytes));
EXPECT_EQ(0, memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)));
EXPECT_EQ(0, memcmp(bytes + kOffset, instructions, sizeof(instructions)));
EXPECT_EQ(0, memcmp(bytes + kOffset + sizeof(instructions),
suffix_bytes, sizeof(suffix_bytes)));
EXPECT_EQ(0, memcmp(bytes + ip_offset - kOffset, prefix_bytes,
sizeof(prefix_bytes)));
EXPECT_EQ(0, memcmp(bytes + ip_offset, instructions, sizeof(instructions)));
EXPECT_EQ(0, memcmp(bytes + ip_offset + sizeof(instructions), suffix_bytes,
sizeof(suffix_bytes)));
}
DeleteFileW(minidump_filename_wide.c_str());

View File

@ -120,7 +120,7 @@ void ExceptionHandlerTest::SetUp() {
// THe test case name is exposed to use as a c-style string,
// But we might be working in UNICODE here on Windows.
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
strlen(test_info->name()),
static_cast<int>(strlen(test_info->name())),
test_name_wide,
MAX_PATH);
if (!dwRet) {
@ -247,6 +247,7 @@ TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) {
EXPECT_EXIT(DoCrashInvalidParameter(), ::testing::ExitedWithCode(0), "");
ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
ASSERT_TRUE(DoesPathExist(full_dump_file.c_str()));
// Verify the dump for infos.
DumpAnalysis mini(dump_file);
@ -318,6 +319,7 @@ TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) {
EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
ASSERT_TRUE(DoesPathExist(full_dump_file.c_str()));
// Verify the dump for infos.
DumpAnalysis mini(dump_file);

View File

@ -31,11 +31,10 @@
#include <objbase.h>
#include <dbghelp.h>
#include "breakpad_googletest_includes.h"
#include "client/windows/crash_generation/minidump_generator.h"
#include "client/windows/unittests/dump_analysis.h" // NOLINT
#include "gtest/gtest.h"
namespace {
// Minidump with stacks, PEB, TEB, and unloaded module list.
@ -94,7 +93,7 @@ class MinidumpTest: public testing::Test {
STATUS_ACCESS_VIOLATION, // ExceptionCode
0, // ExceptionFlags
NULL, // ExceptionRecord;
reinterpret_cast<void*>(0xCAFEBABE), // ExceptionAddress;
reinterpret_cast<void*>(static_cast<uintptr_t>(0xCAFEBABE)), // ExceptionAddress;
2, // NumberParameters;
{ EXCEPTION_WRITE_FAULT, reinterpret_cast<ULONG_PTR>(this) }
};

View File

@ -27,6 +27,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Ignore other VCSs.
.repo/
.svn/
# Ignore common compiled artifacts.
@ -47,6 +48,12 @@ lib*.a
/src/tools/linux/symupload/minidump_upload
/src/tools/linux/symupload/sym_upload
/src/tools/mac/dump_syms/dump_syms
/src/tools/mac/dump_syms/dump_syms_mac
# Ignore unit test artifacts.
*_unittest
*.log
*.trs
# Ignore autotools generated artifacts.
.deps
@ -77,7 +84,6 @@ src/Makefile
# Ignore directories gclient syncs.
src/testing
src/third_party/glog
src/third_party/lss
src/third_party/protobuf
src/tools/gyp

View File

@ -33,18 +33,10 @@
# 'gclient runhooks' rather than 'gclient sync'.
deps = {
# Logging code.
"src/src/third_party/glog":
"https://github.com/google/glog.git" +
"@v0.3.4",
# Testing libraries and utilities.
"src/src/testing":
"https://github.com/google/googlemock.git" +
"@release-1.7.0",
"src/src/testing/gtest":
"https://github.com/google/googletest.git" +
"@release-1.7.0",
"@release-1.8.0",
# Protobuf.
"src/src/third_party/protobuf/protobuf":
@ -54,22 +46,39 @@ deps = {
# GYP project generator.
"src/src/tools/gyp":
"https://chromium.googlesource.com/external/gyp/" +
"@e8ab0833a42691cd2184bd4c45d779e43821d3e0",
"@324dd166b7c0b39d513026fa52d6280ac6d56770",
# Linux syscall support.
"src/src/third_party/lss":
"https://chromium.googlesource.com/linux-syscall-support/" +
"@3f6478ac95edf86cd3da300c2c0d34a438f5dbeb",
"@e6527b0cd469e3ff5764785dadcb39bf7d787154",
}
hooks = [
{
# TODO(chrisha): Fix the GYP files so that they work without
# --no-circular-check.
"pattern": ".",
"action": ["python",
"src/src/tools/gyp/gyp_main.py",
"--no-circular-check",
"src/src/client/windows/breakpad_client.gyp"],
# Keep the manifest up to date.
"action": ["python", "src/src/tools/python/deps-to-manifest.py",
"src/DEPS", "src/default.xml"],
},
]
hooks_os = {
'win': [
{
# TODO(chrisha): Fix the GYP files so that they work without
# --no-circular-check.
"pattern": ".",
"action": ["python",
"src/src/tools/gyp/gyp_main.py",
"--no-circular-check",
"src/src/client/windows/breakpad_client.gyp"],
},
{
# XXX: this and above should all be wired into build/all.gyp ?
"action": ["python",
"src/src/tools/gyp/gyp_main.py",
"--no-circular-check",
"src/src/tools/windows/tools_windows.gyp"],
},
],
}

View File

@ -1 +1 @@
704f41ec901c419f8c321742114b415e6f5ceacc
69c2c51dd89965d234eec16e3a9353634831916b

View File

@ -43,17 +43,7 @@ AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/include
AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/testing/include
endif
if GCC
# These are good warnings to be treated as errors
AM_CXXFLAGS += \
-Werror=missing-braces \
-Werror=non-virtual-dtor \
-Werror=overloaded-virtual \
-Werror=reorder \
-Werror=sign-compare \
-Werror=unused-variable \
-Werror=vla
endif
AM_CXXFLAGS += $(WARN_CXXFLAGS)
if LINUX_HOST
# Build as PIC on Linux, for linux_client_unittest_shlib
@ -120,8 +110,10 @@ TEST_DEPS =
else
TEST_CFLAGS = \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing/googletest/include \
-I$(top_srcdir)/src/testing/googletest \
-I$(top_srcdir)/src/testing/googlemock/include \
-I$(top_srcdir)/src/testing/googlemock \
-I$(top_srcdir)/src/testing
TEST_LIBS = src/testing/libtesting.a
TEST_DEPS = $(TEST_LIBS)
@ -141,9 +133,9 @@ check_LIBRARIES += src/testing/libtesting.a
if !SYSTEM_TEST_LIBS
src_testing_libtesting_a_SOURCES = \
src/breakpad_googletest_includes.h \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src/testing/googletest/src/gtest-all.cc \
src/testing/googletest/src/gtest_main.cc \
src/testing/googlemock/src/gmock-all.cc
src_testing_libtesting_a_CPPFLAGS = \
$(AM_CPPFLAGS) $(TEST_CFLAGS)
endif
@ -414,7 +406,8 @@ CLEANFILES += \
src/client/linux/linux_client_unittest_shlib
check_PROGRAMS += \
src/client/linux/linux_client_unittest
src/client/linux/linux_client_unittest \
src/common/linux/google_crashdump_uploader_test
if !DISABLE_TOOLS
check_PROGRAMS += \
@ -453,8 +446,12 @@ if ANDROID_HOST
LOG_DRIVER = $(top_srcdir)/android/test-driver
else
# The default Autotools test driver script.
if TESTS_AS_ROOT
LOG_DRIVER = $(top_srcdir)/autotools/root-test-driver $(top_srcdir)/autotools/test-driver
else
LOG_DRIVER = $(top_srcdir)/autotools/test-driver
endif
endif !TESTS_AS_ROOT
endif !ANDROID_HOST
if LINUX_HOST
src_client_linux_linux_dumper_unittest_helper_SOURCES = \
@ -576,6 +573,7 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \
src/common/dwarf_line_to_module.cc \
src/common/language.cc \
src/common/module.cc \
src/common/path_helper.cc \
src/common/stabs_reader.cc \
src/common/stabs_to_module.cc \
src/common/dwarf/bytereader.cc \
@ -593,9 +591,14 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \
src/common/linux/memory_mapped_file.cc \
src/common/linux/safe_readlink.cc \
src/tools/linux/dump_syms/dump_syms.cc
src_tools_linux_dump_syms_dump_syms_CXXFLAGS = \
$(RUST_DEMANGLE_CFLAGS)
src_tools_linux_dump_syms_dump_syms_LDADD = \
$(RUST_DEMANGLE_LIBS)
src_tools_linux_md2core_minidump_2_core_SOURCES = \
src/common/linux/memory_mapped_file.cc \
src/common/path_helper.cc \
src/tools/linux/md2core/minidump-2-core.cc \
src/tools/linux/md2core/minidump_memory_range.h
@ -619,6 +622,7 @@ src_tools_mac_dump_syms_dump_syms_mac_SOURCES = \
src/common/language.cc \
src/common/md5.cc \
src/common/module.cc \
src/common/path_helper.cc \
src/common/stabs_reader.cc \
src/common/stabs_to_module.cc \
src/common/dwarf/bytereader.cc \
@ -641,7 +645,10 @@ src_tools_mac_dump_syms_dump_syms_mac_SOURCES = \
src/tools/mac/dump_syms/dump_syms_tool.cc
src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS= \
-I$(top_srcdir)/src/third_party/mac_headers \
$(RUST_DEMANGLE_CFLAGS) \
-DHAVE_MACH_O_NLIST_H
src_tools_mac_dump_syms_dump_syms_mac_LDADD= \
$(RUST_DEMANGLE_LIBS)
src_common_dumper_unittest_SOURCES = \
src/common/byte_cursor_unittest.cc \
@ -655,6 +662,7 @@ src_common_dumper_unittest_SOURCES = \
src/common/memory_range_unittest.cc \
src/common/module.cc \
src/common/module_unittest.cc \
src/common/path_helper.cc \
src/common/stabs_reader.cc \
src/common/stabs_reader_unittest.cc \
src/common/stabs_to_module.cc \
@ -698,9 +706,11 @@ src_common_dumper_unittest_SOURCES = \
src/common/tests/file_utils.cc
src_common_dumper_unittest_CPPFLAGS = \
$(AM_CPPFLAGS) $(TEST_CFLAGS) \
$(RUST_DEMANGLE_CFLAGS) \
$(PTHREAD_CFLAGS)
src_common_dumper_unittest_LDADD = \
$(TEST_LIBS) \
$(RUST_DEMANGLE_LIBS) \
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
src_common_mac_macho_reader_unittest_SOURCES = \
@ -710,6 +720,7 @@ src_common_mac_macho_reader_unittest_SOURCES = \
src/common/language.cc \
src/common/md5.cc \
src/common/module.cc \
src/common/path_helper.cc \
src/common/stabs_reader.cc \
src/common/stabs_to_module.cc \
src/common/test_assembler.cc \
@ -736,6 +747,17 @@ src_common_mac_macho_reader_unittest_LDADD = \
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
endif
src_common_linux_google_crashdump_uploader_test_SOURCES = \
src/common/linux/google_crashdump_uploader.cc \
src/common/linux/google_crashdump_uploader_test.cc \
src/common/linux/libcurl_wrapper.cc
src_common_linux_google_crashdump_uploader_test_CPPFLAGS = \
$(AM_CPPFLAGS) $(TEST_CFLAGS)
src_common_linux_google_crashdump_uploader_test_LDADD = \
$(TEST_LIBS) \
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \
-ldl
src_tools_linux_md2core_minidump_2_core_unittest_SOURCES = \
src/tools/linux/md2core/minidump_memory_range_unittest.cc
src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS = \
@ -1174,6 +1196,7 @@ src_processor_minidump_dump_LDADD = \
src_processor_microdump_stackwalk_SOURCES = \
src/processor/microdump_stackwalk.cc
src_processor_microdump_stackwalk_LDADD = \
src/common/path_helper.o \
src/processor/basic_code_modules.o \
src/processor/basic_source_line_resolver.o \
src/processor/call_stack.o \
@ -1207,6 +1230,7 @@ src_processor_microdump_stackwalk_LDADD = \
src_processor_minidump_stackwalk_SOURCES = \
src/processor/minidump_stackwalk.cc
src_processor_minidump_stackwalk_LDADD = \
src/common/path_helper.o \
src/processor/basic_code_modules.o \
src/processor/basic_source_line_resolver.o \
src/processor/call_stack.o \
@ -1321,6 +1345,7 @@ EXTRA_DIST = \
src/common/windows/pdb_source_line_writer.h \
src/common/windows/string_utils-inl.h \
src/common/windows/string_utils.cc \
src/processor/microdump_stackwalk_test_vars \
src/processor/stackwalk_common.cc \
src/processor/stackwalk_common.h \
src/processor/stackwalker_selftest_sol.s \
@ -1347,6 +1372,7 @@ EXTRA_DIST = \
src/processor/testdata/linux_raise_sigabrt.dmp \
src/processor/testdata/linux_stack_pointer_in_module.dmp \
src/processor/testdata/linux_stack_pointer_in_stack.dmp \
src/processor/testdata/linux_stack_pointer_in_stack_alt_name.dmp \
src/processor/testdata/linux_stacksmash.dmp \
src/processor/testdata/linux_write_to_nonwritable_module.dmp \
src/processor/testdata/linux_write_to_nonwritable_region_math.dmp \
@ -1362,6 +1388,7 @@ EXTRA_DIST = \
src/processor/testdata/microdump.stackwalk-arm.out \
src/processor/testdata/microdump.stackwalk.machine_readable-arm64.out \
src/processor/testdata/microdump.stackwalk.machine_readable-arm.out \
src/processor/testdata/microdump-withcrashreason.dmp \
src/processor/testdata/microdump-x86.dmp \
src/processor/testdata/minidump2.dmp \
src/processor/testdata/minidump2.dump.out \
@ -1391,54 +1418,61 @@ EXTRA_DIST = \
src/processor/testdata/symbols/overflow/B0E1FC01EF48E39CAF5C881D2DF0C3840/overflow.sym \
src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1/test_app.sym \
src/processor/testdata/test_app.cc \
src/testing/gtest/include/gtest/gtest.h \
src/testing/gtest/include/gtest/gtest-death-test.h \
src/testing/gtest/include/gtest/gtest-message.h \
src/testing/gtest/include/gtest/gtest-param-test.h \
src/testing/gtest/include/gtest/gtest-printers.h \
src/testing/gtest/include/gtest/gtest-spi.h \
src/testing/gtest/include/gtest/gtest-test-part.h \
src/testing/gtest/include/gtest/gtest-typed-test.h \
src/testing/gtest/include/gtest/gtest_pred_impl.h \
src/testing/gtest/include/gtest/gtest_prod.h \
src/testing/gtest/include/gtest/internal/gtest-death-test-internal.h \
src/testing/gtest/include/gtest/internal/gtest-filepath.h \
src/testing/gtest/include/gtest/internal/gtest-internal.h \
src/testing/gtest/include/gtest/internal/gtest-linked_ptr.h \
src/testing/gtest/include/gtest/internal/gtest-param-util-generated.h \
src/testing/gtest/include/gtest/internal/gtest-param-util.h \
src/testing/gtest/include/gtest/internal/gtest-port.h \
src/testing/gtest/include/gtest/internal/gtest-string.h \
src/testing/gtest/include/gtest/internal/gtest-tuple.h \
src/testing/gtest/include/gtest/internal/gtest-type-util.h \
src/testing/gtest/src/gtest.cc \
src/testing/gtest/src/gtest-death-test.cc \
src/testing/gtest/src/gtest-filepath.cc \
src/testing/gtest/src/gtest-internal-inl.h \
src/testing/gtest/src/gtest-port.cc \
src/testing/gtest/src/gtest-printers.cc \
src/testing/gtest/src/gtest-test-part.cc \
src/testing/gtest/src/gtest-typed-test.cc \
src/testing/include/gmock/gmock.h \
src/testing/include/gmock/gmock-actions.h \
src/testing/include/gmock/gmock-cardinalities.h \
src/testing/include/gmock/gmock-generated-actions.h \
src/testing/include/gmock/gmock-generated-function-mockers.h \
src/testing/include/gmock/gmock-generated-matchers.h \
src/testing/include/gmock/gmock-generated-nice-strict.h \
src/testing/include/gmock/gmock-matchers.h \
src/testing/include/gmock/gmock-more-actions.h \
src/testing/include/gmock/gmock-more-matchers.h \
src/testing/include/gmock/gmock-spec-builders.h \
src/testing/include/gmock/internal/gmock-generated-internal-utils.h \
src/testing/include/gmock/internal/gmock-internal-utils.h \
src/testing/include/gmock/internal/gmock-port.h \
src/testing/src/gmock.cc \
src/testing/src/gmock-cardinalities.cc \
src/testing/src/gmock-internal-utils.cc \
src/testing/src/gmock-matchers.cc \
src/testing/src/gmock-spec-builders.cc \
src/testing/src/gmock_main.cc \
src/testing/googletest/include/gtest/gtest.h \
src/testing/googletest/include/gtest/gtest-death-test.h \
src/testing/googletest/include/gtest/gtest-message.h \
src/testing/googletest/include/gtest/gtest-param-test.h \
src/testing/googletest/include/gtest/gtest-printers.h \
src/testing/googletest/include/gtest/gtest-spi.h \
src/testing/googletest/include/gtest/gtest-test-part.h \
src/testing/googletest/include/gtest/gtest-typed-test.h \
src/testing/googletest/include/gtest/gtest_pred_impl.h \
src/testing/googletest/include/gtest/gtest_prod.h \
src/testing/googletest/include/gtest/internal/custom/gtest-port.h \
src/testing/googletest/include/gtest/internal/custom/gtest-printers.h \
src/testing/googletest/include/gtest/internal/custom/gtest.h \
src/testing/googletest/include/gtest/internal/gtest-death-test-internal.h \
src/testing/googletest/include/gtest/internal/gtest-filepath.h \
src/testing/googletest/include/gtest/internal/gtest-internal.h \
src/testing/googletest/include/gtest/internal/gtest-linked_ptr.h \
src/testing/googletest/include/gtest/internal/gtest-param-util-generated.h \
src/testing/googletest/include/gtest/internal/gtest-param-util.h \
src/testing/googletest/include/gtest/internal/gtest-port-arch.h \
src/testing/googletest/include/gtest/internal/gtest-port.h \
src/testing/googletest/include/gtest/internal/gtest-string.h \
src/testing/googletest/include/gtest/internal/gtest-tuple.h \
src/testing/googletest/include/gtest/internal/gtest-type-util.h \
src/testing/googletest/src/gtest.cc \
src/testing/googletest/src/gtest-death-test.cc \
src/testing/googletest/src/gtest-filepath.cc \
src/testing/googletest/src/gtest-internal-inl.h \
src/testing/googletest/src/gtest-port.cc \
src/testing/googletest/src/gtest-printers.cc \
src/testing/googletest/src/gtest-test-part.cc \
src/testing/googletest/src/gtest-typed-test.cc \
src/testing/googlemock/include/gmock/gmock.h \
src/testing/googlemock/include/gmock/gmock-actions.h \
src/testing/googlemock/include/gmock/gmock-cardinalities.h \
src/testing/googlemock/include/gmock/gmock-generated-actions.h \
src/testing/googlemock/include/gmock/gmock-generated-function-mockers.h \
src/testing/googlemock/include/gmock/gmock-generated-matchers.h \
src/testing/googlemock/include/gmock/gmock-generated-nice-strict.h \
src/testing/googlemock/include/gmock/gmock-matchers.h \
src/testing/googlemock/include/gmock/gmock-more-actions.h \
src/testing/googlemock/include/gmock/gmock-more-matchers.h \
src/testing/googlemock/include/gmock/gmock-spec-builders.h \
src/testing/googlemock/include/gmock/internal/custom/gmock-generated-actions.h \
src/testing/googlemock/include/gmock/internal/custom/gmock-matchers.h \
src/testing/googlemock/include/gmock/internal/custom/gmock-port.h \
src/testing/googlemock/include/gmock/internal/gmock-generated-internal-utils.h \
src/testing/googlemock/include/gmock/internal/gmock-internal-utils.h \
src/testing/googlemock/include/gmock/internal/gmock-port.h \
src/testing/googlemock/src/gmock.cc \
src/testing/googlemock/src/gmock-cardinalities.cc \
src/testing/googlemock/src/gmock-internal-utils.cc \
src/testing/googlemock/src/gmock-matchers.cc \
src/testing/googlemock/src/gmock-spec-builders.cc \
src/testing/googlemock/src/gmock_main.cc \
src/third_party/curl/COPYING \
src/third_party/curl/curlbuild.h \
src/third_party/curl/curl.h \

View File

@ -8,7 +8,7 @@ crash-reporting system.
* [Bugs](https://bugs.chromium.org/p/google-breakpad/)
* Discussion/Questions: [google-breakpad-discuss@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-discuss)
* Developer/Reviews: [google-breakpad-dev@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-dev)
* Tests: [![Build Status](https://travis-ci.org/google/breakpad.svg?branch=master)](https://travis-ci.org/google/breakpad)
* Tests: [![Build Status](https://travis-ci.org/google/breakpad.svg?branch=master)](https://travis-ci.org/google/breakpad) [![Build status](https://ci.appveyor.com/api/projects/status/eguv4emv2rhq68u2?svg=true)](https://ci.appveyor.com/project/vapier/breakpad)
* Coverage [![Coverity Status](https://scan.coverity.com/projects/9215/badge.svg)](https://scan.coverity.com/projects/google-breakpad)
## Getting started (from master)

View File

@ -1295,3 +1295,10 @@ AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
m4_include([m4/ax_append_compile_flags.m4])
m4_include([m4/ax_append_flag.m4])
m4_include([m4/ax_check_compile_flag.m4])
m4_include([m4/ax_check_define.m4])
m4_include([m4/ax_cxx_compile_stdcxx.m4])
m4_include([m4/ax_pthread.m4])
m4_include([m4/ax_require_defined.m4])

View File

@ -1,347 +0,0 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2012-10-14.11; # UTC
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,791 +0,0 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
scriptversion=2013-05-30.07; # UTC
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
Run PROGRAMS ARGS to compile a file, generating dependencies
as side-effects.
Environment variables:
depmode Dependency tracking mode.
source Source file read by 'PROGRAMS ARGS'.
object Object file output by 'PROGRAMS ARGS'.
DEPDIR directory where to store dependencies.
depfile Dependency file to output.
tmpdepfile Temporary file to use when outputting dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "depcomp $scriptversion"
exit $?
;;
esac
# Get the directory component of the given path, and save it in the
# global variables '$dir'. Note that this directory component will
# be either empty or ending with a '/' character. This is deliberate.
set_dir_from ()
{
case $1 in
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
*) dir=;;
esac
}
# Get the suffix-stripped basename of the given path, and save it the
# global variable '$base'.
set_base_from ()
{
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
}
# If no dependency file was actually created by the compiler invocation,
# we still have to create a dummy depfile, to avoid errors with the
# Makefile "include basename.Plo" scheme.
make_dummy_depfile ()
{
echo "#dummy" > "$depfile"
}
# Factor out some common post-processing of the generated depfile.
# Requires the auxiliary global variable '$tmpdepfile' to be set.
aix_post_process_depfile ()
{
# If the compiler actually managed to produce a dependency file,
# post-process it.
if test -f "$tmpdepfile"; then
# Each line is of the form 'foo.o: dependency.h'.
# Do two passes, one to just change these to
# $object: dependency.h
# and one to simply output
# dependency.h:
# which is needed to avoid the deleted-header problem.
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
} > "$depfile"
rm -f "$tmpdepfile"
else
make_dummy_depfile
fi
}
# A tabulation character.
tab=' '
# A newline character.
nl='
'
# Character ranges might be problematic outside the C locale.
# These definitions help.
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lower=abcdefghijklmnopqrstuvwxyz
digits=0123456789
alpha=${upper}${lower}
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
depfile=${depfile-`echo "$object" |
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Avoid interferences from the environment.
gccflag= dashmflag=
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
cygpath_u="cygpath -u -f -"
if test "$depmode" = msvcmsys; then
# This is just like msvisualcpp but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvisualcpp
fi
if test "$depmode" = msvc7msys; then
# This is just like msvc7 but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvc7
fi
if test "$depmode" = xlc; then
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
gccflag=-qmakedep=gcc,-MF
depmode=gcc
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
## the command line argument order; so add the flags where they
## appear in depend2.am. Note that the slowdown incurred here
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
for arg
do
case $arg in
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
*) set fnord "$@" "$arg" ;;
esac
shift # fnord
shift # $arg
done
"$@"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
## (see the conditional assignment to $gccflag above).
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say). Also, it might not be
## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The second -e expression handles DOS-style file names with drive
# letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the "deleted header file" problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
## Some versions of gcc put a space before the ':'. On the theory
## that the space means something, we add a space to the output as
## well. hp depmode also adds that space, but also prefixes the VPATH
## to the object. Take care to not repeat it in the output.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like '#:fec' to the end of the
# dependency line.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
| tr "$nl" ' ' >> "$depfile"
echo >> "$depfile"
# The second pass generates a dummy entry for each header file.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile"
;;
xlc)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. In older versions, this file always lives in the
# current directory. Also, the AIX compiler puts '$object:' at the
# start of each line; $object doesn't have directory information.
# Version 6 uses the directory in both cases.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.u
tmpdepfile2=$base.u
tmpdepfile3=$dir.libs/$base.u
"$@" -Wc,-M
else
tmpdepfile1=$dir$base.u
tmpdepfile2=$dir$base.u
tmpdepfile3=$dir$base.u
"$@" -M
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
aix_post_process_depfile
;;
tcc)
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
# FIXME: That version still under development at the moment of writing.
# Make that this statement remains true also for stable, released
# versions.
# It will wrap lines (doesn't matter whether long or short) with a
# trailing '\', as in:
#
# foo.o : \
# foo.c \
# foo.h \
#
# It will put a trailing '\' even on the last line, and will use leading
# spaces rather than leading tabs (at least since its commit 0394caf7
# "Emit spaces for -MD").
"$@" -MD -MF "$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
# We have to change lines of the first kind to '$object: \'.
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
# And for each line of the second kind, we have to emit a 'dep.h:'
# dummy dependency, to avoid the deleted-header problem.
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
rm -f "$tmpdepfile"
;;
## The order of this option in the case statement is important, since the
## shell code in configure will try each of these formats in the order
## listed in this file. A plain '-MD' option would be understood by many
## compilers, so we must ensure this comes after the gcc and icc options.
pgcc)
# Portland's C compiler understands '-MD'.
# Will always output deps to 'file.d' where file is the root name of the
# source file under compilation, even if file resides in a subdirectory.
# The object file name does not affect the name of the '.d' file.
# pgcc 10.2 will output
# foo.o: sub/foo.c sub/foo.h
# and will wrap long lines using '\' :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
set_dir_from "$object"
# Use the source, not the object, to determine the base name, since
# that's sadly what pgcc will do too.
set_base_from "$source"
tmpdepfile=$base.d
# For projects that build the same source file twice into different object
# files, the pgcc approach of using the *source* file root name can cause
# problems in parallel builds. Use a locking strategy to avoid stomping on
# the same $tmpdepfile.
lockdir=$base.d-lock
trap "
echo '$0: caught signal, cleaning up...' >&2
rmdir '$lockdir'
exit 1
" 1 2 13 15
numtries=100
i=$numtries
while test $i -gt 0; do
# mkdir is a portable test-and-set.
if mkdir "$lockdir" 2>/dev/null; then
# This process acquired the lock.
"$@" -MD
stat=$?
# Release the lock.
rmdir "$lockdir"
break
else
# If the lock is being held by a different process, wait
# until the winning process is done or we timeout.
while test -d "$lockdir" && test $i -gt 0; do
sleep 1
i=`expr $i - 1`
done
fi
i=`expr $i - 1`
done
trap - 1 2 13 15
if test $i -le 0; then
echo "$0: failed to acquire lock after $numtries attempts" >&2
echo "$0: check lockdir '$lockdir'" >&2
exit 1
fi
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each line is of the form `foo.o: dependent.h',
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp2)
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
# compilers, which have integrated preprocessors. The correct option
# to use with these is +Maked; it writes dependencies to a file named
# 'foo.d', which lands next to the object file, wherever that
# happens to be.
# Much of this is similar to the tru64 case; see comments there.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
"$@" -Wc,+Maked
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
"$@" +Maked
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
# Add 'dependent.h:' lines.
sed -ne '2,${
s/^ *//
s/ \\*$//
s/$/:/
p
}' "$tmpdepfile" >> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile" "$tmpdepfile2"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in 'foo.d' instead, so we check for that too.
# Subdirectories are respected.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
# Libtool generates 2 separate objects for the 2 libraries. These
# two compilations output dependencies in $dir.libs/$base.o.d and
# in $dir$base.o.d. We have to check for both files, because
# one of the two compilations can be disabled. We should prefer
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
# automatically cleaned when .libs/ is deleted, while ignoring
# the former would cause a distcleancheck panic.
tmpdepfile1=$dir$base.o.d # libtool 1.5
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
"$@" -Wc,-MD
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
tmpdepfile3=$dir$base.d
"$@" -MD
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
# Same post-processing that is required for AIX mode.
aix_post_process_depfile
;;
msvc7)
if test "$libtool" = yes; then
showIncludes=-Wc,-showIncludes
else
showIncludes=-showIncludes
fi
"$@" $showIncludes > "$tmpdepfile"
stat=$?
grep -v '^Note: including file: ' "$tmpdepfile"
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The first sed program below extracts the file names and escapes
# backslashes for cygpath. The second sed program outputs the file
# name when reading, but also accumulates all include files in the
# hold buffer in order to output them again at the end. This only
# works with sed implementations that can handle large buffers.
sed < "$tmpdepfile" -n '
/^Note: including file: *\(.*\)/ {
s//\1/
s/\\/\\\\/g
p
}' | $cygpath_u | sort -u | sed -n '
s/ /\\ /g
s/\(.*\)/'"$tab"'\1 \\/p
s/.\(.*\) \\/\1:/
H
$ {
s/.*/'"$tab"'/
G
p
}' >> "$depfile"
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
rm -f "$tmpdepfile"
;;
msvc7msys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
# Require at least two characters before searching for ':'
# in the target name. This is to cope with DOS-style filenames:
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
"$@" $dashmflag |
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this sed invocation
# correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# Remove any Libtool call
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# X makedepend
shift
cleared=no eat=no
for arg
do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
if test $eat = yes; then
eat=no
continue
fi
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
# Strip any option that makedepend may not understand. Remove
# the object too, otherwise makedepend will parse it as a source file.
-arch)
eat=yes ;;
-*|$object)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix=`echo "$object" | sed 's/^.*\././'`
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
# makedepend may prepend the VPATH from the source file name to the object.
# No need to regex-escape $object, excess matching of '.' is harmless.
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process the last invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed '1,2d' "$tmpdepfile" \
| tr ' ' "$nl" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E \
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
| sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
IFS=" "
for arg
do
case "$arg" in
-o)
shift
;;
$object)
shift
;;
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E 2>/dev/null |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
echo "$tab" >> "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvcmsys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

View File

@ -1,501 +0,0 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2013-12-25.23; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve the last data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-s) stripcmd=$stripprog;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename; won't work
# if double slashes aren't ignored.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# Create intermediate dirs using mode 755 as modified by the umask.
# This is like FreeBSD 'install' as of 1997-10-28.
umask=`umask`
case $stripcmd.$umask in
# Optimize common cases.
*[2367][2367]) mkdir_umask=$umask;;
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
*[0-7])
mkdir_umask=`expr $umask + 22 \
- $umask % 100 % 40 + $umask % 20 \
- $umask % 10 % 4 + $umask % 2
`;;
*) mkdir_umask=$umask,go-w;;
esac
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
case $umask in
*[123567][0-7][0-7])
# POSIX mkdir -p sets u+wx bits regardless of umask, which
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
;;
*)
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
if (umask $mkdir_umask &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
ls_ld_tmpdir=`ls -ld "$tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/d" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
fi
trap '' 0;;
esac;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# The umask is ridiculous, or mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask=$mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=$dstdir/_inst.$$_
rmtmp=$dstdir/_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd -f "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

File diff suppressed because it is too large Load Diff

View File

@ -1,215 +0,0 @@
#! /bin/sh
# Common wrapper for a few potentially missing GNU programs.
scriptversion=2013-10-28.13; # UTC
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try '$0 --help' for more information"
exit 1
fi
case $1 in
--is-lightweight)
# Used by our autoconf macros to check whether the available missing
# script is modern enough.
exit 0
;;
--run)
# Back-compat with the calling convention used by older automake.
shift
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
to PROGRAM being missing or too old.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
Supported PROGRAM values:
aclocal autoconf autoheader autom4te automake makeinfo
bison yacc flex lex help2man
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
'g' are ignored when checking the name.
Send bug reports to <bug-automake@gnu.org>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing $scriptversion (GNU Automake)"
exit $?
;;
-*)
echo 1>&2 "$0: unknown '$1' option"
echo 1>&2 "Try '$0 --help' for more information"
exit 1
;;
esac
# Run the given program, remember its exit status.
"$@"; st=$?
# If it succeeded, we are done.
test $st -eq 0 && exit 0
# Also exit now if we it failed (or wasn't found), and '--version' was
# passed; such an option is passed most likely to detect whether the
# program is present and works.
case $2 in --version|--help) exit $st;; esac
# Exit code 63 means version mismatch. This often happens when the user
# tries to use an ancient version of a tool on a file that requires a
# minimum version.
if test $st -eq 63; then
msg="probably too old"
elif test $st -eq 127; then
# Program was missing.
msg="missing on your system"
else
# Program was found and executed, but failed. Give up.
exit $st
fi
perl_URL=http://www.perl.org/
flex_URL=http://flex.sourceforge.net/
gnu_software_URL=http://www.gnu.org/software
program_details ()
{
case $1 in
aclocal|automake)
echo "The '$1' program is part of the GNU Automake package:"
echo "<$gnu_software_URL/automake>"
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/autoconf>"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
autoconf|autom4te|autoheader)
echo "The '$1' program is part of the GNU Autoconf package:"
echo "<$gnu_software_URL/autoconf/>"
echo "It also requires GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
esac
}
give_advice ()
{
# Normalize program name to check for.
normalized_program=`echo "$1" | sed '
s/^gnu-//; t
s/^gnu//; t
s/^g//; t'`
printf '%s\n' "'$1' is $msg."
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
case $normalized_program in
autoconf*)
echo "You should only need it if you modified 'configure.ac',"
echo "or m4 files included by it."
program_details 'autoconf'
;;
autoheader*)
echo "You should only need it if you modified 'acconfig.h' or"
echo "$configure_deps."
program_details 'autoheader'
;;
automake*)
echo "You should only need it if you modified 'Makefile.am' or"
echo "$configure_deps."
program_details 'automake'
;;
aclocal*)
echo "You should only need it if you modified 'acinclude.m4' or"
echo "$configure_deps."
program_details 'aclocal'
;;
autom4te*)
echo "You might have modified some maintainer files that require"
echo "the 'autom4te' program to be rebuilt."
program_details 'autom4te'
;;
bison*|yacc*)
echo "You should only need it if you modified a '.y' file."
echo "You may want to install the GNU Bison package:"
echo "<$gnu_software_URL/bison/>"
;;
lex*|flex*)
echo "You should only need it if you modified a '.l' file."
echo "You may want to install the Fast Lexical Analyzer package:"
echo "<$flex_URL>"
;;
help2man*)
echo "You should only need it if you modified a dependency" \
"of a man page."
echo "You may want to install the GNU Help2man package:"
echo "<$gnu_software_URL/help2man/>"
;;
makeinfo*)
echo "You should only need it if you modified a '.texi' file, or"
echo "any other file indirectly affecting the aspect of the manual."
echo "You might want to install the Texinfo package:"
echo "<$gnu_software_URL/texinfo/>"
echo "The spurious makeinfo call might also be the consequence of"
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
echo "want to install GNU make:"
echo "<$gnu_software_URL/make/>"
;;
*)
echo "You might have modified some files without having the proper"
echo "tools for further handling them. Check the 'README' file, it"
echo "often tells you about the needed prerequisites for installing"
echo "this package. You may also peek at any GNU archive site, in"
echo "case some other package contains this missing '$1' program."
;;
esac
}
give_advice "$1" | sed -e '1s/^/WARNING: /' \
-e '2,$s/^/ /' >&2
# Propagate the correct exit status (expected to be 127 for a program
# not found, 63 for a program that failed due to version mismatch).
exit $st
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

View File

@ -1,148 +0,0 @@
#! /bin/sh
# test-driver - basic testsuite driver script.
scriptversion=2013-07-13.22; # UTC
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
# Make unconditional expansion of undefined variables an error. This
# helps a lot in preventing typo-related bugs.
set -u
usage_error ()
{
echo "$0: $*" >&2
print_usage >&2
exit 2
}
print_usage ()
{
cat <<END
Usage:
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
[--expect-failure={yes|no}] [--color-tests={yes|no}]
[--enable-hard-errors={yes|no}] [--]
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
END
}
test_name= # Used for reporting.
log_file= # Where to save the output of the test script.
trs_file= # Where to save the metadata of the test run.
expect_failure=no
color_tests=no
enable_hard_errors=yes
while test $# -gt 0; do
case $1 in
--help) print_usage; exit $?;;
--version) echo "test-driver $scriptversion"; exit $?;;
--test-name) test_name=$2; shift;;
--log-file) log_file=$2; shift;;
--trs-file) trs_file=$2; shift;;
--color-tests) color_tests=$2; shift;;
--expect-failure) expect_failure=$2; shift;;
--enable-hard-errors) enable_hard_errors=$2; shift;;
--) shift; break;;
-*) usage_error "invalid option: '$1'";;
*) break;;
esac
shift
done
missing_opts=
test x"$test_name" = x && missing_opts="$missing_opts --test-name"
test x"$log_file" = x && missing_opts="$missing_opts --log-file"
test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
if test x"$missing_opts" != x; then
usage_error "the following mandatory options are missing:$missing_opts"
fi
if test $# -eq 0; then
usage_error "missing argument"
fi
if test $color_tests = yes; then
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
red='' # Red.
grn='' # Green.
lgn='' # Light green.
blu='' # Blue.
mgn='' # Magenta.
std='' # No color.
else
red= grn= lgn= blu= mgn= std=
fi
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
trap "st=129; $do_exit" 1
trap "st=130; $do_exit" 2
trap "st=141; $do_exit" 13
trap "st=143; $do_exit" 15
# Test script is run here.
"$@" >$log_file 2>&1
estatus=$?
if test $enable_hard_errors = no && test $estatus -eq 99; then
tweaked_estatus=1
else
tweaked_estatus=$estatus
fi
case $tweaked_estatus:$expect_failure in
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
0:*) col=$grn res=PASS recheck=no gcopy=no;;
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
esac
# Report the test outcome and exit status in the logs, so that one can
# know whether the test passed or failed simply by looking at the '.log'
# file, without the need of also peaking into the corresponding '.trs'
# file (automake bug#11814).
echo "$res $test_name (exit status: $estatus)" >>$log_file
# Report outcome to console.
echo "${col}${res}${std}: $test_name"
# Register the test result, and other relevant metadata.
echo ":test-result: $res" > $trs_file
echo ":global-test-result: $res" >> $trs_file
echo ":recheck: $recheck" >> $trs_file
echo ":copy-in-global-log: $gcopy" >> $trs_file
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

View File

@ -626,6 +626,10 @@ ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
TESTS_AS_ROOT_FALSE
TESTS_AS_ROOT_TRUE
RUST_DEMANGLE_LIBS
RUST_DEMANGLE_CFLAGS
SELFTEST_FALSE
SELFTEST_TRUE
GTEST_LIBS
@ -646,6 +650,7 @@ ANDROID_HOST_FALSE
ANDROID_HOST_TRUE
LINUX_HOST_FALSE
LINUX_HOST_TRUE
WARN_CXXFLAGS
HAVE_CXX11
PTHREAD_CFLAGS
PTHREAD_LIBS
@ -653,8 +658,6 @@ PTHREAD_CC
ax_pthread_config
EGREP
GREP
GCC_FALSE
GCC_TRUE
RANLIB
am__fastdepCXX_FALSE
am__fastdepCXX_TRUE
@ -775,6 +778,8 @@ enable_processor
enable_tools
enable_system_test_libs
enable_selftest
with_rust_demangle
with_tests_as_root
'
ac_precious_vars='build_alias
host_alias
@ -795,7 +800,9 @@ GMOCK_CFLAGS
GMOCK_LIBS
GTEST_CONFIG
GTEST_CFLAGS
GTEST_LIBS'
GTEST_LIBS
RUST_DEMANGLE_CFLAGS
RUST_DEMANGLE_LIBS'
# Initialize some variables set by options.
@ -1433,6 +1440,17 @@ Optional Features:
--enable-selftest Run extra tests with "make check" (may conflict with
optimizations) (default is no)
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-rust-demangle=/path/to/rust-demangle-capi
Link against the rust-demangle library to demangle
Rust language symbols during symbol dumping (default
is no) Pass the path to the crate root.
--with-tests-as-root Run the tests as root. Use this on platforms like
travis-ci.org that require root privileges to use
ptrace (default is no)
Some influential environment variables:
CC C compiler command
CFLAGS C compiler flags
@ -1456,6 +1474,10 @@ Some influential environment variables:
GTEST_CFLAGS
Compiler flags for gtest
GTEST_LIBS Linker flags for gtest
RUST_DEMANGLE_CFLAGS
Compiler flags for rust-demangle
RUST_DEMANGLE_LIBS
Linker flags for rust-demangle
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@ -1859,6 +1881,73 @@ $as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char $2 (); below.
Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
<limits.h> exists even on freestanding compilers. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
#undef $2
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char $2 ();
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined __stub_$2 || defined __stub___$2
choke me
#endif
int
main ()
{
return $2 ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
eval "$3=yes"
else
eval "$3=no"
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
@ -5720,14 +5809,6 @@ else
RANLIB="$ac_cv_prog_RANLIB"
fi
if test "$GCC" = yes; then
GCC_TRUE=
GCC_FALSE='#'
else
GCC_TRUE='#'
GCC_FALSE=
fi
# let the Makefile know if we're gcc
# Check whether --enable-m32 was given.
if test "${enable_m32+set}" = set; then :
@ -6192,104 +6273,6 @@ rm -rf conftest*
fi
# ===========================================================================
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well. e.g. you should link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threads programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 6
# This is what autoupdate's m4 run will expand. It fires
# the warning (with _au_warn_XXX), outputs it into the
# updated configure.ac (with AC_DIAGNOSE), and then outputs
# the replacement expansion.
# This is an auxiliary macro that is also run when
# autoupdate runs m4. It simply calls m4_warning, but
# we need a wrapper so that each warning is emitted only
# once. We break the quoting in m4_warning's argument in
# order to expand this macro's arguments, not AU_DEFUN's.
# Finally, this is the expansion that is picked up by
# autoconf. It tells the user to run autoupdate, and
# then outputs the replacement expansion. We do not care
# about autoupdate's warning because that contains
# information on what to do *after* running autoupdate.
@ -6662,79 +6645,30 @@ fi
done
for ac_header in a.out.h
for ac_header in a.out.h sys/random.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default"
if test "x$ac_cv_header_a_out_h" = xyes; then :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_A_OUT_H 1
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
for ac_func in arc4random getrandom
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXXFLAGS to
# enable support. VERSION may be '11' (for the C++11 standard) or '14'
# (for the C++14 standard).
#
# The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
# -std=c++11). If neither is specified, you get whatever works, with
# preference for an extended mode.
#
# The third argument, if specified 'mandatory' or if left unspecified,
# indicates that baseline support for the specified C++ standard is
# required and that the macro should error out if no mode with that
# support is found. If specified 'optional', then configuration proceeds
# regardless, after defining HAVE_CXX${VERSION} if and only if a
# supporting mode is found.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 1
fi
done
@ -7397,6 +7331,176 @@ $as_echo "#define HAVE_CXX11 1" >>confdefs.h
fi
WARN_CXXFLAGS=
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Werror=unknown-warning-option" >&5
$as_echo_n "checking whether C++ compiler accepts -Werror=unknown-warning-option... " >&6; }
if ${ax_cv_check_cxxflags___Werror_unknown_warning_option+:} false; then :
$as_echo_n "(cached) " >&6
else
ax_check_save_flags=$CXXFLAGS
CXXFLAGS="$CXXFLAGS -Werror=unknown-warning-option"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
ax_cv_check_cxxflags___Werror_unknown_warning_option=yes
else
ax_cv_check_cxxflags___Werror_unknown_warning_option=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
CXXFLAGS=$ax_check_save_flags
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Werror_unknown_warning_option" >&5
$as_echo "$ax_cv_check_cxxflags___Werror_unknown_warning_option" >&6; }
if test "x$ax_cv_check_cxxflags___Werror_unknown_warning_option" = xyes; then :
ax_compiler_flags_test="-Werror=unknown-warning-option"
else
ax_compiler_flags_test=""
fi
for flag in -Wmissing-braces -Wnon-virtual-dtor -Woverloaded-virtual -Wreorder -Wsign-compare -Wunused-local-typedefs -Wunused-variable -Wvla ; do
as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_${ax_compiler_flags_test}_$flag" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts $flag" >&5
$as_echo_n "checking whether C++ compiler accepts $flag... " >&6; }
if eval \${$as_CACHEVAR+:} false; then :
$as_echo_n "(cached) " >&6
else
ax_check_save_flags=$CXXFLAGS
CXXFLAGS="$CXXFLAGS ${ax_compiler_flags_test} $flag"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
main ()
{
;
return 0;
}
_ACEOF
if ac_fn_cxx_try_compile "$LINENO"; then :
eval "$as_CACHEVAR=yes"
else
eval "$as_CACHEVAR=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
CXXFLAGS=$ax_check_save_flags
fi
eval ac_res=\$$as_CACHEVAR
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then :
if ${WARN_CXXFLAGS+:} false; then :
case " $WARN_CXXFLAGS " in #(
*" $flag "*) :
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS already contains \$flag"; } >&5
(: WARN_CXXFLAGS already contains $flag) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } ;; #(
*) :
as_fn_append WARN_CXXFLAGS " $flag"
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS=\"\$WARN_CXXFLAGS\""; } >&5
(: WARN_CXXFLAGS="$WARN_CXXFLAGS") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
;;
esac
else
WARN_CXXFLAGS=$flag
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS=\"\$WARN_CXXFLAGS\""; } >&5
(: WARN_CXXFLAGS="$WARN_CXXFLAGS") 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
fi
else
:
fi
done
as_fn_append WARN_CXXFLAGS " -Werror"
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for O_CLOEXEC defined in fcntl.h" >&5
$as_echo_n "checking for O_CLOEXEC defined in fcntl.h... " >&6; }
if ${ac_cv_defined_O_CLOEXEC_fcntl_h+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <fcntl.h>
int
main ()
{
#ifdef O_CLOEXEC
int ok;
#else
choke me
#endif
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
ac_cv_defined_O_CLOEXEC_fcntl_h=yes
else
ac_cv_defined_O_CLOEXEC_fcntl_h=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined_O_CLOEXEC_fcntl_h" >&5
$as_echo "$ac_cv_defined_O_CLOEXEC_fcntl_h" >&6; }
if test $ac_cv_defined_O_CLOEXEC_fcntl_h != "no"; then :
else
$as_echo "#define O_CLOEXEC 0" >>confdefs.h
fi
# Only build Linux client libs when compiling for Linux
case $host in
*-*-linux* | *-android* )
@ -7747,6 +7851,58 @@ else
fi
# Check whether --with-rust-demangle was given.
if test "${with_rust_demangle+set}" = set; then :
withval=$with_rust_demangle; case "${withval}" in
yes)
as_fn_error $? "You must pass the path to the rust-demangle-capi crate for --with-rust-demangle" "$LINENO" 5
;;
no)
rust_demangle=false
;;
*)
if ! test -f "${withval}/Cargo.toml"; then
as_fn_error $? "You must pass the path to the rust-demangle-capi crate for --with-rust-demangle" "$LINENO" 5
fi
RUST_DEMANGLE_CFLAGS="-DHAVE_RUST_DEMANGLE -I${withval}/target/include"
RUST_DEMANGLE_LIBS="-L${withval}/target/release -lrust_demangle -lpthread -ldl"
;;
esac
else
rust_demangle=false
fi
# Check whether --with-tests-as-root was given.
if test "${with_tests_as_root+set}" = set; then :
withval=$with_tests_as_root; case "${withval}" in
yes)
tests_as_root=true
;;
no)
tests_as_root=false
;;
*)
as_fn_error $? "--with-tests-as-root can only be \"yes\" or \"no\"" "$LINENO" 5
;;
esac
else
tests_as_root=false
fi
if test x$tests_as_root = xtrue; then
TESTS_AS_ROOT_TRUE=
TESTS_AS_ROOT_FALSE='#'
else
TESTS_AS_ROOT_TRUE='#'
TESTS_AS_ROOT_FALSE=
fi
ac_config_files="$ac_config_files breakpad.pc breakpad-client.pc Makefile"
@ -7898,10 +8054,6 @@ if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
as_fn_error $? "conditional \"am__fastdepCXX\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -z "${GCC_TRUE}" && test -z "${GCC_FALSE}"; then
as_fn_error $? "conditional \"GCC\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -z "${LINUX_HOST_TRUE}" && test -z "${LINUX_HOST_FALSE}"; then
as_fn_error $? "conditional \"LINUX_HOST\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
@ -7930,6 +8082,10 @@ if test -z "${SELFTEST_TRUE}" && test -z "${SELFTEST_FALSE}"; then
as_fn_error $? "conditional \"SELFTEST\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -z "${TESTS_AS_ROOT_TRUE}" && test -z "${TESTS_AS_ROOT_FALSE}"; then
as_fn_error $? "conditional \"TESTS_AS_ROOT\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0

View File

@ -28,7 +28,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
AC_PREREQ(2.57)
AC_PREREQ(2.64)
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
dnl Sanity check: the argument is just a file that should exist.
@ -48,7 +48,6 @@ AM_PROG_CC_C_O
AC_PROG_CPP
AC_PROG_CXX
AC_PROG_RANLIB
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
dnl This must come before all the feature tests below.
AC_ARG_ENABLE(m32,
@ -72,13 +71,40 @@ AC_ARG_ENABLE(m32,
AC_HEADER_STDC
AC_SYS_LARGEFILE
m4_include(m4/ax_pthread.m4)
AX_PTHREAD
AC_CHECK_HEADERS([a.out.h])
AC_CHECK_HEADERS([a.out.h sys/random.h])
AC_CHECK_FUNCS([arc4random getrandom])
m4_include(m4/ax_cxx_compile_stdcxx.m4)
AX_CXX_COMPILE_STDCXX(11, noext, mandatory)
dnl Test supported warning flags.
WARN_CXXFLAGS=
dnl This warning flag is used by clang. Its default behavior is to warn when
dnl given an unknown flag rather than error out.
AC_LANG_PUSH([C++])
AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
ax_compiler_flags_test="-Werror=unknown-warning-option"
],[
ax_compiler_flags_test=""
])
AX_APPEND_COMPILE_FLAGS(m4_flatten([
-Wmissing-braces
-Wnon-virtual-dtor
-Woverloaded-virtual
-Wreorder
-Wsign-compare
-Wunused-local-typedefs
-Wunused-variable
-Wvla
]), [WARN_CXXFLAGS], [${ax_compiler_flags_test}])
AS_VAR_APPEND([WARN_CXXFLAGS], " -Werror")
AC_LANG_POP([C++])
AC_SUBST([WARN_CXXFLAGS])
dnl Test support for O_CLOEXEC
AX_CHECK_DEFINE([fcntl.h], [O_CLOEXEC], [],
[AC_DEFINE([O_CLOEXEC], [0], [Fallback definition for old systems])])
# Only build Linux client libs when compiling for Linux
case $host in
*-*-linux* | *-android* )
@ -195,6 +221,50 @@ AC_ARG_ENABLE(selftest,
[selftest=false])
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
AC_ARG_WITH(rust-demangle,
AS_HELP_STRING([--with-rust-demangle=/path/to/rust-demangle-capi],
[Link against the rust-demangle library]
[to demangle Rust language symbols during]
[symbol dumping (default is no)]
[Pass the path to the crate root.]),
[case "${withval}" in
yes)
AC_MSG_ERROR(You must pass the path to the rust-demangle-capi crate for --with-rust-demangle)
;;
no)
rust_demangle=false
;;
*)
if ! test -f "${withval}/Cargo.toml"; then
AC_MSG_ERROR(You must pass the path to the rust-demangle-capi crate for --with-rust-demangle)
fi
RUST_DEMANGLE_CFLAGS="-DHAVE_RUST_DEMANGLE -I${withval}/target/include"
RUST_DEMANGLE_LIBS="-L${withval}/target/release -lrust_demangle -lpthread -ldl"
;;
esac],
[rust_demangle=false])
AC_ARG_VAR([RUST_DEMANGLE_CFLAGS], [Compiler flags for rust-demangle])
AC_ARG_VAR([RUST_DEMANGLE_LIBS], [Linker flags for rust-demangle])
AC_ARG_WITH(tests-as-root,
AS_HELP_STRING([--with-tests-as-root],
[Run the tests as root. Use this on platforms]
[like travis-ci.org that require root privileges]
[to use ptrace (default is no)]),
[case "${withval}" in
yes)
tests_as_root=true
;;
no)
tests_as_root=false
;;
*)
AC_MSG_ERROR(--with-tests-as-root can only be "yes" or "no")
;;
esac],
[tests_as_root=false])
AM_CONDITIONAL(TESTS_AS_ROOT, test x$tests_as_root = xtrue)
AC_CONFIG_FILES(m4_flatten([
breakpad.pc
breakpad-client.pc

View File

@ -0,0 +1,43 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- AUTOGENERATED BY deps-to-manifest.py; DO NOT EDIT -->
<manifest>
<default revision='refs/heads/master'
remote='chromium'
sync-c='true'
sync-j='8' />
<remote name='github'
fetch='https://github.com/'
review='' />
<remote name='chromium'
fetch='https://chromium.googlesource.com/'
review='https://chromium-review.googlesource.com' />
<project path='src'
name='breakpad/breakpad'
revision='refs/heads/master'
remote='chromium' />
<project path='src/src/tools/gyp'
name='external/gyp/'
revision='324dd166b7c0b39d513026fa52d6280ac6d56770'
remote='chromium' />
<project path='src/src/testing'
name='google/googletest.git'
revision='refs/tags/release-1.8.0'
remote='github' />
<project path='src/src/third_party/lss'
name='linux-syscall-support/'
revision='e6527b0cd469e3ff5764785dadcb39bf7d787154'
remote='chromium' />
<project path='src/src/third_party/protobuf/protobuf'
name='google/protobuf.git'
revision='cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac'
remote='github' />
</manifest>

View File

@ -1,283 +0,0 @@
# ===========================================================================
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well. e.g. you should link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threads programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 6
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_SAVE
AC_LANG_C
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
AC_MSG_RESULT($ax_pthread_ok)
if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case "${host_cpu}-${host_os}" in
*solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
;;
*-darwin*)
acx_pthread_flags="-pthread $acx_pthread_flags"
;;
esac
if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do
case $flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $flag])
PTHREAD_CFLAGS="$flag"
;;
pthread-config)
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$flag])
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_TRY_LINK([#include <pthread.h>
static void routine(void* a) {a=0;}
static void* start_routine(void* a) {return a;}],
[pthread_t th; pthread_attr_t attr;
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_create(&th,0,start_routine,0);
pthread_cleanup_pop(0); ],
[ax_pthread_ok=yes])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($ax_pthread_ok)
if test "x$ax_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_MSG_CHECKING([for joinable pthread attribute])
attr_name=unknown
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
[attr_name=$attr; break])
done
AC_MSG_RESULT($attr_name)
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_CHECKING([if more special flags are required for pthreads])
flag=no
case "${host_cpu}-${host_os}" in
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: must compile with xlc_r or cc_r
if test x"$GCC" != xyes; then
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
else
PTHREAD_CC=$CC
fi
else
PTHREAD_CC="$CC"
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_RESTORE
])dnl AX_PTHREAD

File diff suppressed because it is too large Load Diff

View File

@ -1,368 +0,0 @@
# Helper functions for option handling. -*- Autoconf -*-
#
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltoptions.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
# ------------------------------------------
m4_define([_LT_MANGLE_OPTION],
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
# ---------------------------------------
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
# saved as a flag.
m4_define([_LT_SET_OPTION],
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
_LT_MANGLE_DEFUN([$1], [$2]),
[m4_warning([Unknown $1 option `$2'])])[]dnl
])
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
# ------------------------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
m4_define([_LT_IF_OPTION],
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
# -------------------------------------------------------
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
# are set.
m4_define([_LT_UNLESS_OPTIONS],
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
[m4_define([$0_found])])])[]dnl
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
])[]dnl
])
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
# ----------------------------------------
# OPTION-LIST is a space-separated list of Libtool options associated
# with MACRO-NAME. If any OPTION has a matching handler declared with
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
# the unknown option and exit.
m4_defun([_LT_SET_OPTIONS],
[# Set options
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[_LT_SET_OPTION([$1], _LT_Option)])
m4_if([$1],[LT_INIT],[
dnl
dnl Simply set some default values (i.e off) if boolean options were not
dnl specified:
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
])
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
])
dnl
dnl If no reference was made to various pairs of opposing options, then
dnl we run the default mode handler for the pair. For example, if neither
dnl `shared' nor `disable-shared' was passed, we enable building of shared
dnl archives by default:
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
[_LT_ENABLE_FAST_INSTALL])
])
])# _LT_SET_OPTIONS
## --------------------------------- ##
## Macros to handle LT_INIT options. ##
## --------------------------------- ##
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
# -----------------------------------------
m4_define([_LT_MANGLE_DEFUN],
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
# -----------------------------------------------
m4_define([LT_OPTION_DEFINE],
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
])# LT_OPTION_DEFINE
# dlopen
# ------
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
])
AU_DEFUN([AC_LIBTOOL_DLOPEN],
[_LT_SET_OPTION([LT_INIT], [dlopen])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `dlopen' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
# win32-dll
# ---------
# Declare package support for building win32 dll's.
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
[enable_win32_dll=yes
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
AC_CHECK_TOOL(AS, as, false)
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
AC_CHECK_TOOL(OBJDUMP, objdump, false)
;;
esac
test -z "$AS" && AS=as
_LT_DECL([], [AS], [0], [Assembler program])dnl
test -z "$DLLTOOL" && DLLTOOL=dlltool
_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
test -z "$OBJDUMP" && OBJDUMP=objdump
_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
])# win32-dll
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
_LT_SET_OPTION([LT_INIT], [win32-dll])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `win32-dll' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
# _LT_ENABLE_SHARED([DEFAULT])
# ----------------------------
# implement the --enable-shared flag, and supports the `shared' and
# `disable-shared' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_SHARED],
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([shared],
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
_LT_DECL([build_libtool_libs], [enable_shared], [0],
[Whether or not to build shared libraries])
])# _LT_ENABLE_SHARED
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
# Old names:
AC_DEFUN([AC_ENABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
])
AC_DEFUN([AC_DISABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], [disable-shared])
])
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
# _LT_ENABLE_STATIC([DEFAULT])
# ----------------------------
# implement the --enable-static flag, and support the `static' and
# `disable-static' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_STATIC],
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([static],
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
_LT_DECL([build_old_libs], [enable_static], [0],
[Whether or not to build static libraries])
])# _LT_ENABLE_STATIC
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
# Old names:
AC_DEFUN([AC_ENABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
])
AC_DEFUN([AC_DISABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], [disable-static])
])
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
# ----------------------------------
# implement the --enable-fast-install flag, and support the `fast-install'
# and `disable-fast-install' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_FAST_INSTALL],
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([fast-install],
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
_LT_DECL([fast_install], [enable_fast_install], [0],
[Whether or not to optimize for fast installation])dnl
])# _LT_ENABLE_FAST_INSTALL
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
# Old names:
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the `fast-install' option into LT_INIT's first parameter.])
])
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the `disable-fast-install' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
# _LT_WITH_PIC([MODE])
# --------------------
# implement the --with-pic flag, and support the `pic-only' and `no-pic'
# LT_INIT options.
# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
m4_define([_LT_WITH_PIC],
[AC_ARG_WITH([pic],
[AS_HELP_STRING([--with-pic],
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
[pic_mode="$withval"],
[pic_mode=default])
test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
])# _LT_WITH_PIC
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
# Old name:
AU_DEFUN([AC_LIBTOOL_PICMODE],
[_LT_SET_OPTION([LT_INIT], [pic-only])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `pic-only' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
## ----------------- ##
## LTDL_INIT Options ##
## ----------------- ##
m4_define([_LTDL_MODE], [])
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
[m4_define([_LTDL_MODE], [nonrecursive])])
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
[m4_define([_LTDL_MODE], [recursive])])
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
[m4_define([_LTDL_MODE], [subproject])])
m4_define([_LTDL_TYPE], [])
LT_OPTION_DEFINE([LTDL_INIT], [installable],
[m4_define([_LTDL_TYPE], [installable])])
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
[m4_define([_LTDL_TYPE], [convenience])])

View File

@ -1,123 +0,0 @@
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 6 ltsugar.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
# lt_join(SEP, ARG1, [ARG2...])
# -----------------------------
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
# associated separator.
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
# versions in m4sugar had bugs.
m4_define([lt_join],
[m4_if([$#], [1], [],
[$#], [2], [[$2]],
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
m4_define([_lt_join],
[m4_if([$#$2], [2], [],
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
# lt_car(LIST)
# lt_cdr(LIST)
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
# Autoconf-2.59 which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
[$#], 1, [],
[m4_dquote(m4_shift($@))])])
m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
# than defined and empty).
#
# This macro is needed until we can rely on Autoconf 2.62, since earlier
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
m4_define([lt_append],
[m4_define([$1],
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
# ----------------------------------------------------------
# Produce a SEP delimited list of all paired combinations of elements of
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
# has the form PREFIXmINFIXSUFFIXn.
# Needed until we can rely on m4_combine added in Autoconf 2.62.
m4_define([lt_combine],
[m4_if(m4_eval([$# > 3]), [1],
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
[[m4_foreach([_Lt_prefix], [$2],
[m4_foreach([_Lt_suffix],
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
# -----------------------------------------------------------------------
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
m4_define([lt_if_append_uniq],
[m4_ifdef([$1],
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
[lt_append([$1], [$2], [$3])$4],
[$5])],
[lt_append([$1], [$2], [$3])$4])])
# lt_dict_add(DICT, KEY, VALUE)
# -----------------------------
m4_define([lt_dict_add],
[m4_define([$1($2)], [$3])])
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
# --------------------------------------------
m4_define([lt_dict_add_subkey],
[m4_define([$1($2:$3)], [$4])])
# lt_dict_fetch(DICT, KEY, [SUBKEY])
# ----------------------------------
m4_define([lt_dict_fetch],
[m4_ifval([$3],
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
# -----------------------------------------------------------------
m4_define([lt_if_dict_fetch],
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
[$5],
[$6])])
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
# --------------------------------------------------------------
m4_define([lt_dict_filter],
[m4_if([$5], [], [],
[lt_join(m4_quote(m4_default([$4], [[, ]])),
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
])

View File

@ -1,23 +0,0 @@
# ltversion.m4 -- version numbers -*- Autoconf -*-
#
# Copyright (C) 2004 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# Generated from ltversion.in.
# serial 3017 ltversion.m4
# This file is part of GNU Libtool
m4_define([LT_PACKAGE_VERSION], [2.2.6b])
m4_define([LT_PACKAGE_REVISION], [1.3017])
AC_DEFUN([LTVERSION_VERSION],
[macro_version='2.2.6b'
macro_revision='1.3017'
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
_LT_DECL(, macro_revision, 0)
])

View File

@ -1,92 +0,0 @@
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
#
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004.
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
# serial 4 lt~obsolete.m4
# These exist entirely to fool aclocal when bootstrapping libtool.
#
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
# which have later been changed to m4_define as they aren't part of the
# exported API, or moved to Autoconf or Automake where they belong.
#
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
# using a macro with the same name in our local m4/libtool.m4 it'll
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
# and doesn't know about Autoconf macros at all.)
#
# So we provide this file, which has a silly filename so it's always
# included after everything else. This provides aclocal with the
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
# because those macros already exist, or will be overwritten later.
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
#
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
# Yes, that means every name once taken will need to remain here until
# we give up compatibility with versions before 1.7, at which point
# we need to keep only those names which we still refer to.
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])

View File

@ -894,7 +894,7 @@
],
'msvs_cygwin_dirs': ['<(DEPTH)/third_party/cygwin'],
'msvs_disabled_warnings': [
4100, 4127, 4396, 4503, 4512, 4819, 4995, 4702
4091, 4100, 4127, 4366, 4396, 4503, 4512, 4819, 4995, 4702
],
'msvs_settings': {
'VCCLCompilerTool': {

View File

@ -32,15 +32,15 @@
'target_name': 'gtest',
'type': 'static_library',
'sources': [
'../testing/gtest/src/gtest-all.cc',
'../testing/googletest/src/gtest-all.cc',
],
'include_dirs': [
'../testing/gtest',
'../testing/gtest/include',
'../testing/googletest',
'../testing/googletest/include',
],
'direct_dependent_settings': {
'include_dirs': [
'../testing/gtest/include',
'../testing/googletest/include',
],
},
},
@ -51,7 +51,7 @@
'gtest',
],
'sources': [
'gtest/src/gtest_main.cc',
'../testing/googletest/src/gtest_main.cc',
],
},
{
@ -61,15 +61,15 @@
'gtest',
],
'sources': [
'../testing/src/gmock-all.cc',
'../testing/googlemock/src/gmock-all.cc',
],
'include_dirs': [
'../testing',
'../testing/include',
'../testing/googlemock',
'../testing/googlemock/include',
],
'direct_dependent_settings': {
'include_dirs': [
'../testing/include',
'../testing/googlemock/include',
],
},
'export_dependent_settings': [
@ -83,7 +83,7 @@
'gmock',
],
'sources': [
'../testing/src/gmock_main.cc',
'../testing/googlemock/src/gmock_main.cc',
],
},
],

View File

@ -230,16 +230,13 @@ breakpad_getcontext:
#elif defined(__mips__)
// This implementation is inspired by implementation of getcontext in glibc.
#include <asm-mips/asm.h>
#include <asm-mips/regdef.h>
#if _MIPS_SIM == _ABIO32
#include <asm/asm.h>
#include <asm/regdef.h>
#include <asm/fpregdef.h>
#else
#include <machine/asm.h>
#include <machine/regdef.h>
#include <asm-mips/fpregdef.h>
#endif
// from asm/asm.h
// from asm-mips/asm.h
#if _MIPS_SIM == _ABIO32
#define ALSZ 7
#define ALMASK ~7

View File

@ -0,0 +1,9 @@
# asm-mips
The files in this directory are almost direct copies from Android NDK r12, with
the exception of changing the include guards to Breakpad ones. They are copied
from the MIPS asm/ directory, but are meant to be used as replacements for both
asm/ and machine/ includes since the files in each are largely duplicates.
Some MIPS asm/ and all machine/ headers were removed in the move to unified NDK
headers, so Breakpad fails to compile on newer NDK versions without these files.

View File

@ -0,0 +1,270 @@
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H
#if defined(__has_include_next) && __has_include_next(<asm/asm.h>)
#include_next <asm/asm.h>
#else
/****************************************************************************
****************************************************************************
***
*** This header was automatically generated from a Linux kernel header
*** of the same name, to make information necessary for userspace to
*** call into the kernel available to libc. It contains only constants,
*** structures, and macros generated from the original header, and thus,
*** contains no copyrightable information.
***
*** To edit the content of this header, modify the corresponding
*** source file (e.g. under external/kernel-headers/original/) then
*** run bionic/libc/kernel/tools/update_all.py
***
*** Any manual change here will be lost the next time this script will
*** be run. You've been warned!
***
****************************************************************************
****************************************************************************/
#include <asm/sgidefs.h>
#ifndef CAT
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#ifdef __STDC__
#define __CAT(str1, str2) str1##str2
#else
#define __CAT(str1, str2) str1 str2
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#define CAT(str1, str2) __CAT(str1, str2)
#endif
#ifdef __PIC__
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define CPRESTORE(register) .cprestore register
#define CPADD(register) .cpadd register
#define CPLOAD(register) .cpload register
#else
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define CPRESTORE(register)
#define CPADD(register)
#define CPLOAD(register)
#endif
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LEAF(symbol) .globl symbol; .align 2; .type symbol, @function; .ent symbol, 0; symbol: .frame sp, 0, ra
#define NESTED(symbol, framesize, rpc) .globl symbol; .align 2; .type symbol, @function; .ent symbol, 0; symbol: .frame sp, framesize, rpc
#define END(function) .end function; .size function, .-function
#define EXPORT(symbol) .globl symbol; symbol:
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define FEXPORT(symbol) .globl symbol; .type symbol, @function; symbol:
#define ABS(symbol,value) .globl symbol; symbol = value
#define PANIC(msg) .set push; .set reorder; PTR_LA a0, 8f; jal panic; 9: b 9b; .set pop; TEXT(msg)
#define PRINT(string)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define TEXT(msg) .pushsection .data; 8: .asciiz msg; .popsection;
#define TTABLE(string) .pushsection .text; .word 1f; .popsection .pushsection .data; 1: .asciiz string; .popsection
#define PREF(hint, addr)
#define PREFX(hint, addr)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#if _MIPS_ISA == _MIPS_ISA_MIPS1
#define MOVN(rd, rs, rt) .set push; .set reorder; beqz rt, 9f; move rd, rs; .set pop; 9:
#define MOVZ(rd, rs, rt) .set push; .set reorder; bnez rt, 9f; move rd, rs; .set pop; 9:
#endif
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#if _MIPS_ISA == _MIPS_ISA_MIPS2 || _MIPS_ISA == _MIPS_ISA_MIPS3
#define MOVN(rd, rs, rt) .set push; .set noreorder; bnezl rt, 9f; move rd, rs; .set pop; 9:
#define MOVZ(rd, rs, rt) .set push; .set noreorder; beqzl rt, 9f; move rd, rs; .set pop; 9:
#endif
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#if _MIPS_ISA == _MIPS_ISA_MIPS4 || _MIPS_ISA == _MIPS_ISA_MIPS5 || _MIPS_ISA == _MIPS_ISA_MIPS32 || _MIPS_ISA == _MIPS_ISA_MIPS64
#define MOVN(rd, rs, rt) movn rd, rs, rt
#define MOVZ(rd, rs, rt) movz rd, rs, rt
#endif
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#if _MIPS_SIM == _MIPS_SIM_ABI32
#define ALSZ 7
#define ALMASK ~7
#endif
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64
#define ALSZ 15
#define ALMASK ~15
#endif
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#ifdef __mips64
#define SZREG 8
#else
#define SZREG 4
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI32
#define REG_S sw
#define REG_L lw
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define REG_SUBU subu
#define REG_ADDU addu
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define REG_S sd
#define REG_L ld
#define REG_SUBU dsubu
#define REG_ADDU daddu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#if _MIPS_SZINT == 32
#define INT_ADD add
#define INT_ADDU addu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define INT_ADDI addi
#define INT_ADDIU addiu
#define INT_SUB sub
#define INT_SUBU subu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define INT_L lw
#define INT_S sw
#define INT_SLL sll
#define INT_SLLV sllv
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define INT_SRL srl
#define INT_SRLV srlv
#define INT_SRA sra
#define INT_SRAV srav
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#if _MIPS_SZINT == 64
#define INT_ADD dadd
#define INT_ADDU daddu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define INT_ADDI daddi
#define INT_ADDIU daddiu
#define INT_SUB dsub
#define INT_SUBU dsubu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define INT_L ld
#define INT_S sd
#define INT_SLL dsll
#define INT_SLLV dsllv
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define INT_SRL dsrl
#define INT_SRLV dsrlv
#define INT_SRA dsra
#define INT_SRAV dsrav
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#if _MIPS_SZLONG == 32
#define LONG_ADD add
#define LONG_ADDU addu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LONG_ADDI addi
#define LONG_ADDIU addiu
#define LONG_SUB sub
#define LONG_SUBU subu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LONG_L lw
#define LONG_S sw
#define LONG_SLL sll
#define LONG_SLLV sllv
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LONG_SRL srl
#define LONG_SRLV srlv
#define LONG_SRA sra
#define LONG_SRAV srav
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LONG .word
#define LONGSIZE 4
#define LONGMASK 3
#define LONGLOG 2
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#if _MIPS_SZLONG == 64
#define LONG_ADD dadd
#define LONG_ADDU daddu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LONG_ADDI daddi
#define LONG_ADDIU daddiu
#define LONG_SUB dsub
#define LONG_SUBU dsubu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LONG_L ld
#define LONG_S sd
#define LONG_SLL dsll
#define LONG_SLLV dsllv
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LONG_SRL dsrl
#define LONG_SRLV dsrlv
#define LONG_SRA dsra
#define LONG_SRAV dsrav
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define LONG .dword
#define LONGSIZE 8
#define LONGMASK 7
#define LONGLOG 3
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#if _MIPS_SZPTR == 32
#define PTR_ADD add
#define PTR_ADDU addu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_ADDI addi
#define PTR_ADDIU addiu
#define PTR_SUB sub
#define PTR_SUBU subu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_L lw
#define PTR_S sw
#define PTR_LA la
#define PTR_LI li
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_SLL sll
#define PTR_SLLV sllv
#define PTR_SRL srl
#define PTR_SRLV srlv
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_SRA sra
#define PTR_SRAV srav
#define PTR_SCALESHIFT 2
#define PTR .word
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTRSIZE 4
#define PTRLOG 2
#endif
#if _MIPS_SZPTR == 64
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_ADD dadd
#define PTR_ADDU daddu
#define PTR_ADDI daddi
#define PTR_ADDIU daddiu
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_SUB dsub
#define PTR_SUBU dsubu
#define PTR_L ld
#define PTR_S sd
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_LA dla
#define PTR_LI dli
#define PTR_SLL dsll
#define PTR_SLLV dsllv
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_SRL dsrl
#define PTR_SRLV dsrlv
#define PTR_SRA dsra
#define PTR_SRAV dsrav
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define PTR_SCALESHIFT 3
#define PTR .dword
#define PTRSIZE 8
#define PTRLOG 3
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI32
#define MFC0 mfc0
#define MTC0 mtc0
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64
#define MFC0 dmfc0
#define MTC0 dmtc0
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#define SSNOP sll zero, zero, 1
#define R10KCBARRIER(addr)
#endif // defined(__has_include_next) && __has_include_next(<asm/asm.h>)
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */

View File

@ -0,0 +1,117 @@
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H
#if defined(__has_include_next) && __has_include_next(<asm/fpregdef.h>)
#include_next <asm/fpregdef.h>
#else
/****************************************************************************
****************************************************************************
***
*** This header was automatically generated from a Linux kernel header
*** of the same name, to make information necessary for userspace to
*** call into the kernel available to libc. It contains only constants,
*** structures, and macros generated from the original header, and thus,
*** contains no copyrightable information.
***
*** To edit the content of this header, modify the corresponding
*** source file (e.g. under external/kernel-headers/original/) then
*** run bionic/libc/kernel/tools/update_all.py
***
*** Any manual change here will be lost the next time this script will
*** be run. You've been warned!
***
****************************************************************************
****************************************************************************/
#include <asm/sgidefs.h>
#if _MIPS_SIM == _MIPS_SIM_ABI32
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fv0 $f0
#define fv0f $f1
#define fv1 $f2
#define fv1f $f3
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fa0 $f12
#define fa0f $f13
#define fa1 $f14
#define fa1f $f15
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define ft0 $f4
#define ft0f $f5
#define ft1 $f6
#define ft1f $f7
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define ft2 $f8
#define ft2f $f9
#define ft3 $f10
#define ft3f $f11
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define ft4 $f16
#define ft4f $f17
#define ft5 $f18
#define ft5f $f19
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fs0 $f20
#define fs0f $f21
#define fs1 $f22
#define fs1f $f23
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fs2 $f24
#define fs2f $f25
#define fs3 $f26
#define fs3f $f27
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fs4 $f28
#define fs4f $f29
#define fs5 $f30
#define fs5f $f31
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fcr31 $31
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
#define fv0 $f0
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fv1 $f2
#define fa0 $f12
#define fa1 $f13
#define fa2 $f14
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fa3 $f15
#define fa4 $f16
#define fa5 $f17
#define fa6 $f18
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fa7 $f19
#define ft0 $f4
#define ft1 $f5
#define ft2 $f6
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define ft3 $f7
#define ft4 $f8
#define ft5 $f9
#define ft6 $f10
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define ft7 $f11
#define ft8 $f20
#define ft9 $f21
#define ft10 $f22
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define ft11 $f23
#define ft12 $f1
#define ft13 $f3
#define fs0 $f24
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fs1 $f25
#define fs2 $f26
#define fs3 $f27
#define fs4 $f28
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define fs5 $f29
#define fs6 $f30
#define fs7 $f31
#define fcr31 $31
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#endif
#endif // defined(__has_include_next) && __has_include_next(<asm/fpregdef.h>)
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H

View File

@ -0,0 +1,125 @@
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H
#if defined(__has_include_next) && __has_include_next(<asm/regdef.h>)
#include_next <asm/regdef.h>
#else
/****************************************************************************
****************************************************************************
***
*** This header was automatically generated from a Linux kernel header
*** of the same name, to make information necessary for userspace to
*** call into the kernel available to libc. It contains only constants,
*** structures, and macros generated from the original header, and thus,
*** contains no copyrightable information.
***
*** To edit the content of this header, modify the corresponding
*** source file (e.g. under external/kernel-headers/original/) then
*** run bionic/libc/kernel/tools/update_all.py
***
*** Any manual change here will be lost the next time this script will
*** be run. You've been warned!
***
****************************************************************************
****************************************************************************/
#include <asm/sgidefs.h>
#if _MIPS_SIM == _MIPS_SIM_ABI32
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define zero $0
#define AT $1
#define v0 $2
#define v1 $3
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define a0 $4
#define a1 $5
#define a2 $6
#define a3 $7
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define t0 $8
#define t1 $9
#define t2 $10
#define t3 $11
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define t4 $12
#define t5 $13
#define t6 $14
#define t7 $15
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define s0 $16
#define s1 $17
#define s2 $18
#define s3 $19
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define s4 $20
#define s5 $21
#define s6 $22
#define s7 $23
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define t8 $24
#define t9 $25
#define jp $25
#define k0 $26
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define k1 $27
#define gp $28
#define sp $29
#define fp $30
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define s8 $30
#define ra $31
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define zero $0
#define AT $at
#define v0 $2
#define v1 $3
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define a0 $4
#define a1 $5
#define a2 $6
#define a3 $7
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define a4 $8
#define ta0 $8
#define a5 $9
#define ta1 $9
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define a6 $10
#define ta2 $10
#define a7 $11
#define ta3 $11
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define t0 $12
#define t1 $13
#define t2 $14
#define t3 $15
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define s0 $16
#define s1 $17
#define s2 $18
#define s3 $19
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define s4 $20
#define s5 $21
#define s6 $22
#define s7 $23
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define t8 $24
#define t9 $25
#define jp $25
#define k0 $26
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define k1 $27
#define gp $28
#define sp $29
#define fp $30
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define s8 $30
#define ra $31
#endif
#endif // defined(__has_include_next) && __has_include_next(<asm/regdef.h>)
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */

View File

@ -49,7 +49,6 @@
extern "C" {
#endif // __cplusplus
#if defined(ANDROID) && ANDROID_VERSION <= 20
struct r_debug {
int r_version;
struct link_map* r_map;
@ -68,7 +67,6 @@ struct link_map {
struct link_map* l_next;
struct link_map* l_prev;
};
#endif
#ifdef __cplusplus
} // extern "C"

View File

@ -38,6 +38,10 @@
// TODO(primiano): remove these changes after Chromium has stably rolled to
// an NDK with the appropriate fixes. https://crbug.com/358831
// With traditional headers, <sys/user.h> forgot to do this. Unified headers get
// it right.
#include <sys/types.h>
#include_next <sys/user.h>
#include <android/api-level.h>

View File

@ -50,7 +50,6 @@
'android/breakpad_getcontext.S',
'android/include/elf.h',
'android/include/link.h',
'android/include/sgidefs.h',
'android/include/stab.h',
'android/include/sys/procfs.h',
'android/include/sys/signal.h',
@ -121,6 +120,8 @@
'linux/safe_readlink.h',
'linux/synth_elf.cc',
'linux/synth_elf.h',
'long_string_dictionary.cc',
'long_string_dictionary.h',
'mac/arch_utilities.cc',
'mac/arch_utilities.h',
'mac/bootstrap_compat.cc',
@ -220,6 +221,7 @@
'linux/tests/auto_testfile.h',
'linux/tests/crash_generator.cc',
'linux/tests/crash_generator.h',
'long_string_dictionary_unittest.cc',
'mac/macho_reader_unittest.cc',
'memory_allocator_unittest.cc',
'memory_range_unittest.cc',
@ -238,9 +240,9 @@
],
'dependencies': [
'common',
'../build/testing.gypi:gmock_main',
'../build/testing.gypi:gmock',
'../build/testing.gypi:gtest',
'../build/testing.gyp:gmock_main',
'../build/testing.gyp:gmock',
'../build/testing.gyp:gtest',
],
'libraries': [
'-ldl',

View File

@ -46,7 +46,9 @@ CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
unsigned return_address_register,
uint8_t version,
const string &augmentation,
bool dwarf64) {
bool dwarf64,
uint8_t address_size,
uint8_t segment_size) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = false;
@ -63,6 +65,10 @@ CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
}
D8(version);
AppendCString(augmentation);
if (version >= 4) {
D8(address_size);
D8(segment_size);
}
ULEB128(code_alignment_factor);
LEB128(data_alignment_factor);
if (version == 1)

View File

@ -138,7 +138,9 @@ class CFISection: public Section {
unsigned return_address_register,
uint8_t version = 3,
const string &augmentation = "",
bool dwarf64 = false);
bool dwarf64 = false,
uint8_t address_size = 8,
uint8_t segment_size = 0);
// Append a Frame Description Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial

View File

@ -232,7 +232,7 @@ enum DwarfAttribute {
DW_AT_call_column = 0x57,
DW_AT_call_file = 0x58,
DW_AT_call_line = 0x59,
// DWARF 4 values.
// DWARF 4
DW_AT_linkage_name = 0x6e,
// SGI/MIPS extensions.
DW_AT_MIPS_fde = 0x2001,
@ -501,7 +501,7 @@ enum DwarfOpcode {
DW_OP_call_frame_cfa =0x9c,
DW_OP_bit_piece =0x9d,
DW_OP_lo_user =0xe0,
DW_OP_hi_user =0xff,
DW_OP_hi_user =0xff,
// GNU extensions
DW_OP_GNU_push_tls_address =0xe0,
// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
@ -544,6 +544,8 @@ enum DwarfLanguage
DW_LANG_ObjC_plus_plus =0x0011,
DW_LANG_UPC =0x0012,
DW_LANG_D =0x0013,
DW_LANG_Rust =0x001c,
DW_LANG_Swift =0x001e,
// Implementation-defined language code range.
DW_LANG_lo_user = 0x8000,
DW_LANG_hi_user = 0xffff,
@ -670,7 +672,7 @@ enum DwarfPointerEncoding
// encoding (except DW_EH_PE_aligned), and indicates that the
// encoded value represents the address at which the true address
// is stored, not the true address itself.
DW_EH_PE_indirect = 0x80
DW_EH_PE_indirect = 0x80
};
} // namespace dwarf2reader

View File

@ -942,6 +942,13 @@ void LineInfo::ReadHeader() {
header_.min_insn_length = reader_->ReadOneByte(lineptr);
lineptr += 1;
if (header_.version >= 4) {
__attribute__((unused)) uint8 max_ops_per_insn =
reader_->ReadOneByte(lineptr);
++lineptr;
assert(max_ops_per_insn == 1);
}
header_.default_is_stmt = reader_->ReadOneByte(lineptr);
lineptr += 1;
@ -1258,12 +1265,12 @@ class CallFrameInfo::Rule {
public:
virtual ~Rule() { }
// Tell HANDLER that, at ADDRESS in the program, REGISTER can be
// recovered using this rule. If REGISTER is kCFARegister, then this rule
// describes how to compute the canonical frame address. Return what the
// HANDLER member function returned.
// Tell HANDLER that, at ADDRESS in the program, REG can be recovered using
// this rule. If REG is kCFARegister, then this rule describes how to compute
// the canonical frame address. Return what the HANDLER member function
// returned.
virtual bool Handle(Handler *handler,
uint64 address, int register) const = 0;
uint64 address, int reg) const = 0;
// Equality on rules. We use these to decide which rules we need
// to report after a DW_CFA_restore_state instruction.
@ -2253,11 +2260,11 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
cursor++;
// If we don't recognize the version, we can't parse any more fields of the
// CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
// CIE. For DWARF CFI, we handle versions 1 through 4 (there was never a
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 4 as well;
// the difference between those versions seems to be the same as for
// .debug_frame.
if (cie->version < 1 || cie->version > 3) {
if (cie->version < 1 || cie->version > 4) {
reporter_->UnrecognizedVersion(cie->offset, cie->version);
return false;
}
@ -2287,16 +2294,36 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
}
}
if (cie->version >= 4) {
uint8_t address_size = *cursor++;
if (address_size != 8) {
// TODO(scottmg): Only supporting x64 for now.
reporter_->UnexpectedAddressSize(cie->offset, address_size);
return false;
}
uint8_t segment_size = *cursor++;
if (segment_size != 0) {
// TODO(scottmg): Only supporting x64 for now.
// I would have perhaps expected 4 here, but LLVM emits a 0, near
// http://llvm.org/docs/doxygen/html/MCDwarf_8cpp_source.html#l00606. As
// we are not using the value, only succeed for now if it's the expected
// 0.
reporter_->UnexpectedSegmentSize(cie->offset, segment_size);
return false;
}
}
// Parse the code alignment factor.
cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
cursor += len;
// Parse the data alignment factor.
cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
cursor += len;
// Parse the return address register. This is a ubyte in version 1, and
// a ULEB128 in version 3.
if (cie->version == 1) {
@ -2407,7 +2434,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
return true;
}
bool CallFrameInfo::ReadFDEFields(FDE *fde) {
const uint8_t *cursor = fde->fields;
size_t size;
@ -2648,6 +2675,22 @@ void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) {
filename_.c_str(), offset, section_.c_str(), cie_offset);
}
void CallFrameInfo::Reporter::UnexpectedAddressSize(uint64 offset,
uint8_t address_size) {
fprintf(stderr,
"%s: CFI frame description entry at offset 0x%llx in '%s':"
" CIE specifies unexpected address size: %d\n",
filename_.c_str(), offset, section_.c_str(), address_size);
}
void CallFrameInfo::Reporter::UnexpectedSegmentSize(uint64 offset,
uint8_t segment_size) {
fprintf(stderr,
"%s: CFI frame description entry at offset 0x%llx in '%s':"
" CIE specifies unexpected segment size: %d\n",
filename_.c_str(), offset, section_.c_str(), segment_size);
}
void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
fprintf(stderr,
"%s: CFI frame description entry at offset 0x%llx in '%s':"

View File

@ -1227,6 +1227,14 @@ class CallFrameInfo::Reporter {
// there is not a CIE.
virtual void BadCIEId(uint64 offset, uint64 cie_offset);
// The FDE at OFFSET refers to a CIE with an address size we don't know how
// to handle.
virtual void UnexpectedAddressSize(uint64 offset, uint8_t address_size);
// The FDE at OFFSET refers to a CIE with an segment descriptor size we
// don't know how to handle.
virtual void UnexpectedSegmentSize(uint64 offset, uint8_t segment_size);
// The FDE at OFFSET refers to a CIE with version number VERSION,
// which we don't recognize. We cannot parse DWARF CFI if it uses
// a version number we don't recognize.

View File

@ -126,6 +126,8 @@ class MockCallFrameErrorReporter: public CallFrameInfo::Reporter {
MOCK_METHOD1(EarlyEHTerminator, void(uint64));
MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64));
MOCK_METHOD2(BadCIEId, void(uint64, uint64));
MOCK_METHOD2(UnexpectedAddressSize, void(uint64, uint8_t));
MOCK_METHOD2(UnexpectedSegmentSize, void(uint64, uint8_t));
MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version));
MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &));
MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8));
@ -605,6 +607,91 @@ TEST_F(CFI, CIEVersion3ReturnColumn) {
EXPECT_TRUE(parser.Start());
}
TEST_F(CFI, CIEVersion4AdditionalFields) {
CFISection section(kBigEndian, 4);
Label cie;
section
.Mark(&cie)
// CIE version 4 with expected address and segment size.
.CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 4, "", true, 8, 0)
.FinishEntry()
// FDE, citing that CIE.
.FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
.FinishEntry();
PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section);
{
InSequence s;
EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 4, "", 0x89))
.WillOnce(Return(true));
EXPECT_CALL(handler, End()).WillOnce(Return(true));
}
string contents;
EXPECT_TRUE(section.GetContents(&contents));
ByteReader byte_reader(ENDIANNESS_BIG);
byte_reader.SetAddressSize(4);
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
contents.size(),
&byte_reader, &handler, &reporter);
EXPECT_TRUE(parser.Start());
}
TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedAddressSize) {
CFISection section(kBigEndian, 4);
Label cie;
section
.Mark(&cie)
// Unexpected address size.
.CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 4, "", true, 3, 0)
.FinishEntry()
// FDE, citing that CIE.
.FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
.FinishEntry();
PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedAddress", section);
EXPECT_CALL(reporter, UnexpectedAddressSize(_, 3))
.WillOnce(Return());
string contents;
EXPECT_TRUE(section.GetContents(&contents));
ByteReader byte_reader(ENDIANNESS_BIG);
byte_reader.SetAddressSize(8);
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
contents.size(),
&byte_reader, &handler, &reporter);
EXPECT_FALSE(parser.Start());
}
TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedSegmentSize) {
CFISection section(kBigEndian, 4);
Label cie;
section
.Mark(&cie)
.CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 4, "", true, 8, 7)
.FinishEntry()
.FDEHeader(cie, 0x7bf0fda0, 0xcbcd28d8)
.FinishEntry();
PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedSegment", section);
EXPECT_CALL(reporter, UnexpectedSegmentSize(_, 7))
.WillOnce(Return());
string contents;
EXPECT_TRUE(section.GetContents(&contents));
ByteReader byte_reader(ENDIANNESS_BIG);
byte_reader.SetAddressSize(8);
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
contents.size(),
&byte_reader, &handler, &reporter);
EXPECT_FALSE(parser.Start());
}
struct CFIInsnFixture: public CFIFixture {
CFIInsnFixture() : CFIFixture() {
data_factor = 0xb6f;

View File

@ -19,8 +19,8 @@
#include <vector>
#include "common/dwarf/types.h"
#include "common/using_std_string.h"
using std::string;
using std::vector;
using std::pair;

View File

@ -195,7 +195,7 @@ void DwarfCFIToModule::Record(Module::Address address, int reg,
// Place the name in our global set of strings, and then use the string
// from the set. Even though the assignment looks like a copy, all the
// major std::string implementations use reference counting internally,
// major string implementations use reference counting internally,
// so the effect is to have all our data structures share copies of rules
// whenever possible. Since register names are drawn from a
// vector<string>, register names are already shared.

View File

@ -181,7 +181,7 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
// The names of the return address and canonical frame address. Putting
// these here instead of using string literals allows us to share their
// texts in reference-counted std::string implementations (all the
// texts in reference-counted string implementations (all the
// popular ones). Many, many rules cite these strings.
string cfa_name_, ra_name_;
@ -189,7 +189,7 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
// our data structures, insert it into this set, and then use the string
// from the set.
//
// Because std::string uses reference counting internally, simply using
// Because string uses reference counting internally, simply using
// strings from this set, even if passed by value, assigned, or held
// directly in structures and containers (map<string, ...>, for example),
// causes those strings to share a single instance of each distinct piece

View File

@ -39,9 +39,6 @@
#include "common/dwarf_cu_to_module.h"
#include <assert.h>
#if !defined(__ANDROID__)
#include <cxxabi.h>
#endif
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
@ -264,7 +261,7 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
uint64 offset_;
// Place the name in the global set of strings. Even though this looks
// like a copy, all the major std::string implementations use reference
// like a copy, all the major string implementations use reference
// counting internally, so the effect is to have all the data structures
// share copies of strings whenever possible.
// FIXME: Should this return something like a string_ref to avoid the
@ -354,22 +351,23 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
case dwarf2reader::DW_AT_name:
name_attribute_ = AddStringToPool(data);
break;
case dwarf2reader::DW_AT_linkage_name:
case dwarf2reader::DW_AT_MIPS_linkage_name: {
char* demangled = NULL;
int status = -1;
#if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle.
demangled = abi::__cxa_demangle(data.c_str(), NULL, NULL, &status);
#endif
if (status != 0) {
cu_context_->reporter->DemangleError(data, status);
demangled_name_ = "";
raw_name_ = AddStringToPool(data);
break;
}
if (demangled) {
demangled_name_ = AddStringToPool(demangled);
free(reinterpret_cast<void*>(demangled));
case dwarf2reader::DW_AT_MIPS_linkage_name:
case dwarf2reader::DW_AT_linkage_name: {
string demangled;
Language::DemangleResult result =
cu_context_->language->DemangleName(data, &demangled);
switch (result) {
case Language::kDemangleSuccess:
demangled_name_ = AddStringToPool(demangled);
break;
case Language::kDemangleFailure:
cu_context_->reporter->DemangleError(data);
// fallthrough
case Language::kDontDemangle:
demangled_name_.clear();
raw_name_ = AddStringToPool(data);
break;
}
break;
}
@ -684,11 +682,10 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
filename_.c_str(), offset);
}
void DwarfCUToModule::WarningReporter::DemangleError(
const string &input, int error) {
void DwarfCUToModule::WarningReporter::DemangleError(const string &input) {
CUHeading();
fprintf(stderr, "%s: warning: failed to demangle %s with error %d\n",
filename_.c_str(), input.c_str(), error);
fprintf(stderr, "%s: warning: failed to demangle %s\n",
filename_.c_str(), input.c_str());
}
void DwarfCUToModule::WarningReporter::UnhandledInterCUReference(
@ -769,6 +766,7 @@ dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
case dwarf2reader::DW_TAG_class_type:
case dwarf2reader::DW_TAG_structure_type:
case dwarf2reader::DW_TAG_union_type:
case dwarf2reader::DW_TAG_module:
return new NamedScopeHandler(cu_context_.get(), child_context_.get(),
offset);
default:
@ -782,6 +780,14 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
cu_context_->language = Language::Java;
break;
case dwarf2reader::DW_LANG_Swift:
cu_context_->language = Language::Swift;
break;
case dwarf2reader::DW_LANG_Rust:
cu_context_->language = Language::Rust;
break;
// DWARF has no generic language code for assembly language; this is
// what the GNU toolchain uses.
case dwarf2reader::DW_LANG_Mips_Assembler:

View File

@ -202,7 +202,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
virtual void UnnamedFunction(uint64 offset);
// __cxa_demangle() failed to demangle INPUT.
virtual void DemangleError(const string &input, int error);
virtual void DemangleError(const string &input);
// The DW_FORM_ref_addr at OFFSET to TARGET was not handled because
// FilePrivate did not retain the inter-CU specification data.

View File

@ -83,7 +83,7 @@ class MockWarningReporter: public DwarfCUToModule::WarningReporter {
MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function));
MOCK_METHOD1(UncoveredLine, void(const Module::Line &line));
MOCK_METHOD1(UnnamedFunction, void(uint64 offset));
MOCK_METHOD2(DemangleError, void(const string &input, int error));
MOCK_METHOD1(DemangleError, void(const string &input));
MOCK_METHOD2(UnhandledInterCUReference, void(uint64 offset, uint64 target));
};
@ -1205,6 +1205,7 @@ TEST_F(Specifications, Function) {
}
TEST_F(Specifications, MangledName) {
// Language defaults to C++, so no need to set it here.
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
StartCU();
@ -1221,6 +1222,53 @@ TEST_F(Specifications, MangledName) {
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
}
TEST_F(Specifications, MangledNameSwift) {
// Swift mangled names should pass through untouched.
SetLanguage(dwarf2reader::DW_LANG_Swift);
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
StartCU();
const string kName = "_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si";
DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
dwarf2reader::DW_TAG_subprogram, "declaration-name",
kName);
DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
0xcd3c51b946fb1eeeLL, "",
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
root_handler_.Finish();
TestFunctionCount(1);
TestFunction(0, kName,
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
}
TEST_F(Specifications, MangledNameRust) {
SetLanguage(dwarf2reader::DW_LANG_Rust);
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
StartCU();
const string kName = "_ZN14rustc_demangle8demangle17h373defa94bffacdeE";
DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
dwarf2reader::DW_TAG_subprogram, "declaration-name",
kName);
DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
0xcd3c51b946fb1eeeLL, "",
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
root_handler_.Finish();
TestFunctionCount(1);
TestFunction(0,
#ifndef HAVE_RUST_DEMANGLE
// Rust mangled names should pass through untouched if not
// using rust-demangle.
kName,
#else
// If rust-demangle is available this should be properly
// demangled.
"rustc_demangle::demangle",
#endif
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
}
TEST_F(Specifications, MemberFunction) {
PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691);

View File

@ -34,18 +34,70 @@
#include "common/language.h"
#include <stdlib.h>
#if !defined(__ANDROID__)
#include <cxxabi.h>
#endif
#if defined(HAVE_RUST_DEMANGLE)
#include <rust_demangle.h>
#endif
#include <limits>
namespace {
string MakeQualifiedNameWithSeparator(const string& parent_name,
const char* separator,
const string& name) {
if (parent_name.empty()) {
return name;
}
return parent_name + separator + name;
}
} // namespace
namespace google_breakpad {
// C++ language-specific operations.
class CPPLanguage: public Language {
public:
CPPLanguage() {}
string MakeQualifiedName(const string &parent_name,
const string &name) const {
if (parent_name.empty())
return name;
else
return parent_name + "::" + name;
return MakeQualifiedNameWithSeparator(parent_name, "::", name);
}
virtual DemangleResult DemangleName(const string& mangled,
string* demangled) const {
#if defined(__ANDROID__)
// Android NDK doesn't provide abi::__cxa_demangle.
demangled->clear();
return kDontDemangle;
#else
int status;
char* demangled_c =
abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
DemangleResult result;
if (status == 0) {
result = kDemangleSuccess;
demangled->assign(demangled_c);
} else {
result = kDemangleFailure;
demangled->clear();
}
if (demangled_c) {
free(reinterpret_cast<void*>(demangled_c));
}
return result;
#endif
}
};
@ -54,19 +106,79 @@ CPPLanguage CPPLanguageSingleton;
// Java language-specific operations.
class JavaLanguage: public Language {
public:
JavaLanguage() {}
string MakeQualifiedName(const string &parent_name,
const string &name) const {
if (parent_name.empty())
return name;
else
return parent_name + "." + name;
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
}
};
JavaLanguage JavaLanguageSingleton;
// Swift language-specific operations.
class SwiftLanguage: public Language {
public:
SwiftLanguage() {}
string MakeQualifiedName(const string &parent_name,
const string &name) const {
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
}
virtual DemangleResult DemangleName(const string& mangled,
string* demangled) const {
// There is no programmatic interface to a Swift demangler. Pass through the
// mangled form because it encodes more information than the qualified name
// that would have been built by MakeQualifiedName(). The output can be
// post-processed by xcrun swift-demangle to transform mangled Swift names
// into something more readable.
demangled->assign(mangled);
return kDemangleSuccess;
}
};
SwiftLanguage SwiftLanguageSingleton;
// Rust language-specific operations.
class RustLanguage: public Language {
public:
RustLanguage() {}
string MakeQualifiedName(const string &parent_name,
const string &name) const {
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
}
virtual DemangleResult DemangleName(const string& mangled,
string* demangled) const {
// Rust names use GCC C++ name mangling, but demangling them with
// abi_demangle doesn't produce stellar results due to them having
// another layer of encoding.
// If callers provide rustc-demangle, use that.
#if defined(HAVE_RUST_DEMANGLE)
char* rust_demangled = rust_demangle(mangled.c_str());
if (rust_demangled == nullptr) {
return kDemangleFailure;
}
demangled->assign(rust_demangled);
free_rust_demangled_name(rust_demangled);
#else
// Otherwise, pass through the mangled name so callers can demangle
// after the fact.
demangled->assign(mangled);
#endif
return kDemangleSuccess;
}
};
RustLanguage RustLanguageSingleton;
// Assembler language-specific operations.
class AssemblerLanguage: public Language {
public:
AssemblerLanguage() {}
bool HasFunctions() const { return false; }
string MakeQualifiedName(const string &parent_name,
const string &name) const {
@ -78,6 +190,8 @@ AssemblerLanguage AssemblerLanguageSingleton;
const Language * const Language::CPlusPlus = &CPPLanguageSingleton;
const Language * const Language::Java = &JavaLanguageSingleton;
const Language * const Language::Swift = &SwiftLanguageSingleton;
const Language * const Language::Rust = &RustLanguageSingleton;
const Language * const Language::Assembler = &AssemblerLanguageSingleton;
} // namespace google_breakpad

View File

@ -77,9 +77,26 @@ class Language {
virtual string MakeQualifiedName (const string &parent_name,
const string &name) const = 0;
enum DemangleResult {
// Demangling was not performed because its not appropriate to attempt.
kDontDemangle = -1,
kDemangleSuccess,
kDemangleFailure,
};
// Wraps abi::__cxa_demangle() or similar for languages where appropriate.
virtual DemangleResult DemangleName(const string& mangled,
string* demangled) const {
demangled->clear();
return kDontDemangle;
}
// Instances for specific languages.
static const Language * const CPlusPlus,
* const Java,
* const Swift,
* const Rust,
* const Assembler;
};

View File

@ -67,6 +67,7 @@
#include "common/linux/file_id.h"
#include "common/memory_allocator.h"
#include "common/module.h"
#include "common/path_helper.h"
#include "common/scoped_ptr.h"
#ifndef NO_STABS_SUPPORT
#include "common/stabs_reader.h"
@ -107,15 +108,6 @@ using google_breakpad::wasteful_vector;
#define EM_AARCH64 183
#endif
// Define SHT_ANDROID_REL and SHT_ANDROID_RELA if not defined by the host.
// Sections with this type contain Android packed relocations.
#ifndef SHT_ANDROID_REL
#define SHT_ANDROID_REL (SHT_LOOS + 1)
#endif
#ifndef SHT_ANDROID_RELA
#define SHT_ANDROID_RELA (SHT_LOOS + 2)
#endif
//
// FDWrapper
//
@ -662,7 +654,6 @@ bool LoadSymbols(const string& obj_file,
typedef typename ElfClass::Addr Addr;
typedef typename ElfClass::Phdr Phdr;
typedef typename ElfClass::Shdr Shdr;
typedef typename ElfClass::Word Word;
Addr loading_addr = GetLoadingAddress<ElfClass>(
GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
@ -670,8 +661,6 @@ bool LoadSymbols(const string& obj_file,
module->SetLoadAddress(loading_addr);
info->set_loading_addr(loading_addr, obj_file);
Word debug_section_type =
elf_header->e_machine == EM_MIPS ? SHT_MIPS_DWARF : SHT_PROGBITS;
const Shdr* sections =
GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
const Shdr* section_names = sections + elf_header->e_shstrndx;
@ -681,28 +670,6 @@ bool LoadSymbols(const string& obj_file,
bool found_debug_info_section = false;
bool found_usable_info = false;
// Reject files that contain Android packed relocations. The pre-packed
// version of the file should be symbolized; the packed version is only
// intended for use on the target system.
if (FindElfSectionByName<ElfClass>(".rel.dyn", SHT_ANDROID_REL,
sections, names,
names_end, elf_header->e_shnum)) {
fprintf(stderr, "%s: file contains a \".rel.dyn\" section "
"with type SHT_ANDROID_REL\n", obj_file.c_str());
fprintf(stderr, "Files containing Android packed relocations "
"may not be symbolized.\n");
return false;
}
if (FindElfSectionByName<ElfClass>(".rela.dyn", SHT_ANDROID_RELA,
sections, names,
names_end, elf_header->e_shnum)) {
fprintf(stderr, "%s: file contains a \".rela.dyn\" section "
"with type SHT_ANDROID_RELA\n", obj_file.c_str());
fprintf(stderr, "Files containing Android packed relocations "
"may not be symbolized.\n");
return false;
}
if (options.symbol_data != ONLY_CFI) {
#ifndef NO_STABS_SUPPORT
// Look for STABS debugging information, and load it if present.
@ -727,9 +694,19 @@ bool LoadSymbols(const string& obj_file,
// Look for DWARF debugging information, and load it if present.
const Shdr* dwarf_section =
FindElfSectionByName<ElfClass>(".debug_info", debug_section_type,
FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS,
sections, names, names_end,
elf_header->e_shnum);
// .debug_info section type is SHT_PROGBITS for mips on pnacl toolchains,
// but MIPS_DWARF for regular gnu toolchains, so both need to be checked
if (elf_header->e_machine == EM_MIPS && !dwarf_section) {
dwarf_section =
FindElfSectionByName<ElfClass>(".debug_info", SHT_MIPS_DWARF,
sections, names, names_end,
elf_header->e_shnum);
}
if (dwarf_section) {
found_debug_info_section = true;
found_usable_info = true;
@ -804,9 +781,19 @@ bool LoadSymbols(const string& obj_file,
// Dwarf Call Frame Information (CFI) is actually independent from
// the other DWARF debugging information, and can be used alone.
const Shdr* dwarf_cfi_section =
FindElfSectionByName<ElfClass>(".debug_frame", debug_section_type,
FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS,
sections, names, names_end,
elf_header->e_shnum);
// .debug_frame section type is SHT_PROGBITS for mips on pnacl toolchains,
// but MIPS_DWARF for regular gnu toolchains, so both need to be checked
if (elf_header->e_machine == EM_MIPS && !dwarf_cfi_section) {
dwarf_cfi_section =
FindElfSectionByName<ElfClass>(".debug_frame", SHT_MIPS_DWARF,
sections, names, names_end,
elf_header->e_shnum);
}
if (dwarf_cfi_section) {
// Ignore the return value of this function; even without call frame
// information, the other debugging information could be perfectly
@ -935,16 +922,6 @@ const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
}
}
// Return the non-directory portion of FILENAME: the portion after the
// last slash, or the whole filename if there are no slashes.
string BaseFileName(const string &filename) {
// Lots of copies! basename's behavior is less than ideal.
char* c_filename = strdup(filename.c_str());
string base = basename(c_filename);
free(c_filename);
return base;
}
template<typename ElfClass>
bool SanitizeDebugFile(const typename ElfClass::Ehdr* debug_elf_header,
const string& debuglink_file,
@ -995,7 +972,7 @@ bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
return false;
}
string name = BaseFileName(obj_filename);
string name = google_breakpad::BaseName(obj_filename);
string os = "Linux";
// Add an extra "0" at the end. PDB files on Windows have an 'age'
// number appended to the end of the file identifier; this isn't

View File

@ -78,14 +78,12 @@ void FindElfClassSection(const char *elf_base,
template<typename ElfClass>
void FindElfClassSegment(const char *elf_base,
typename ElfClass::Word segment_type,
const void **segment_start,
size_t *segment_size) {
wasteful_vector<ElfSegment> *segments) {
typedef typename ElfClass::Ehdr Ehdr;
typedef typename ElfClass::Phdr Phdr;
assert(elf_base);
assert(segment_start);
assert(segment_size);
assert(segments);
assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
@ -97,9 +95,10 @@ void FindElfClassSegment(const char *elf_base,
for (int i = 0; i < elf_header->e_phnum; ++i) {
if (phdrs[i].p_type == segment_type) {
*segment_start = elf_base + phdrs[i].p_offset;
*segment_size = phdrs[i].p_filesz;
return;
ElfSegment seg = {};
seg.start = elf_base + phdrs[i].p_offset;
seg.size = phdrs[i].p_filesz;
segments->push_back(seg);
}
}
}
@ -122,8 +121,7 @@ bool FindElfSection(const void *elf_mapped_base,
const char *section_name,
uint32_t section_type,
const void **section_start,
size_t *section_size,
int *elfclass) {
size_t *section_size) {
assert(elf_mapped_base);
assert(section_start);
assert(section_size);
@ -135,10 +133,6 @@ bool FindElfSection(const void *elf_mapped_base,
return false;
int cls = ElfClass(elf_mapped_base);
if (elfclass) {
*elfclass = cls;
}
const char* elf_base =
static_cast<const char*>(elf_mapped_base);
@ -155,37 +149,25 @@ bool FindElfSection(const void *elf_mapped_base,
return false;
}
bool FindElfSegment(const void *elf_mapped_base,
uint32_t segment_type,
const void **segment_start,
size_t *segment_size,
int *elfclass) {
bool FindElfSegments(const void* elf_mapped_base,
uint32_t segment_type,
wasteful_vector<ElfSegment>* segments) {
assert(elf_mapped_base);
assert(segment_start);
assert(segment_size);
*segment_start = NULL;
*segment_size = 0;
assert(segments);
if (!IsValidElf(elf_mapped_base))
return false;
int cls = ElfClass(elf_mapped_base);
if (elfclass) {
*elfclass = cls;
}
const char* elf_base =
static_cast<const char*>(elf_mapped_base);
if (cls == ELFCLASS32) {
FindElfClassSegment<ElfClass32>(elf_base, segment_type,
segment_start, segment_size);
return *segment_start != NULL;
FindElfClassSegment<ElfClass32>(elf_base, segment_type, segments);
return true;
} else if (cls == ELFCLASS64) {
FindElfClassSegment<ElfClass64>(elf_base, segment_type,
segment_start, segment_size);
return *segment_start != NULL;
FindElfClassSegment<ElfClass64>(elf_base, segment_type, segments);
return true;
}
return false;

View File

@ -37,6 +37,8 @@
#include <link.h>
#include <stdint.h>
#include "common/memory_allocator.h"
namespace google_breakpad {
// Traits classes so consumers can write templatized code to deal
@ -81,14 +83,12 @@ int ElfClass(const void* elf_base);
// Attempt to find a section named |section_name| of type |section_type|
// in the ELF binary data at |elf_mapped_base|. On success, returns true
// and sets |*section_start| to point to the start of the section data,
// and |*section_size| to the size of the section's data. If |elfclass|
// is not NULL, set |*elfclass| to the ELF file class.
// and |*section_size| to the size of the section's data.
bool FindElfSection(const void *elf_mapped_base,
const char *section_name,
uint32_t section_type,
const void **section_start,
size_t *section_size,
int *elfclass);
size_t *section_size);
// Internal helper method, exposed for convenience for callers
// that already have more info.
@ -101,16 +101,17 @@ FindElfSectionByName(const char* name,
const char* names_end,
int nsection);
// Attempt to find the first segment of type |segment_type| in the ELF
// binary data at |elf_mapped_base|. On success, returns true and sets
// |*segment_start| to point to the start of the segment data, and
// and |*segment_size| to the size of the segment's data. If |elfclass|
// is not NULL, set |*elfclass| to the ELF file class.
bool FindElfSegment(const void *elf_mapped_base,
uint32_t segment_type,
const void **segment_start,
size_t *segment_size,
int *elfclass);
struct ElfSegment {
const void* start;
size_t size;
};
// Attempt to find all segments of type |segment_type| in the ELF
// binary data at |elf_mapped_base|. On success, returns true and fills
// |*segments| with a list of segments of the given type.
bool FindElfSegments(const void* elf_mapped_base,
uint32_t segment_type,
wasteful_vector<ElfSegment>* segments);
// Convert an offset from an Elf header into a pointer to the mapped
// address in the current process. Takes an extra template parameter

View File

@ -61,10 +61,11 @@ FileID::FileID(const char* path) : path_(path) {}
// These functions are also used inside the crashed process, so be safe
// and use the syscall/libc wrappers instead of direct syscalls or libc.
template<typename ElfClass>
static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
wasteful_vector<uint8_t>& identifier) {
typedef typename ElfClass::Nhdr Nhdr;
static_assert(sizeof(ElfClass32::Nhdr) == sizeof(ElfClass64::Nhdr),
"Elf32_Nhdr and Elf64_Nhdr should be the same");
typedef typename ElfClass32::Nhdr Nhdr;
const void* section_end = reinterpret_cast<const char*>(section) + length;
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
@ -94,24 +95,22 @@ static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
// and copy it into |identifier|.
static bool FindElfBuildIDNote(const void* elf_mapped_base,
wasteful_vector<uint8_t>& identifier) {
void* note_section;
size_t note_size;
int elfclass;
if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
(const void**)&note_section, &note_size, &elfclass) ||
note_size == 0) &&
(!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
(const void**)&note_section, &note_size, &elfclass) ||
note_size == 0)) {
return false;
PageAllocator allocator;
// lld normally creates 2 PT_NOTEs, gold normally creates 1.
auto_wasteful_vector<ElfSegment, 2> segs(&allocator);
if (FindElfSegments(elf_mapped_base, PT_NOTE, &segs)) {
for (ElfSegment& seg : segs) {
if (ElfClassBuildIDNoteIdentifier(seg.start, seg.size, identifier)) {
return true;
}
}
}
if (elfclass == ELFCLASS32) {
return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size,
identifier);
} else if (elfclass == ELFCLASS64) {
return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size,
identifier);
void* note_section;
size_t note_size;
if (FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
(const void**)&note_section, &note_size)) {
return ElfClassBuildIDNoteIdentifier(note_section, note_size, identifier);
}
return false;
@ -126,7 +125,7 @@ static bool HashElfTextSection(const void* elf_mapped_base,
void* text_section;
size_t text_size;
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
(const void**)&text_section, &text_size, NULL) ||
(const void**)&text_section, &text_size) ||
text_size == 0) {
return false;
}

View File

@ -38,6 +38,7 @@
#include "common/linux/guid_creator.h"
#include "common/memory_allocator.h"
#include "common/using_std_string.h"
namespace google_breakpad {
@ -70,16 +71,16 @@ class FileID {
// Convert the |identifier| data to a string. The string will
// be formatted as a UUID in all uppercase without dashes.
// (e.g., 22F065BBFC9C49F780FE26A7CEBD7BCE).
static std::string ConvertIdentifierToUUIDString(
static string ConvertIdentifierToUUIDString(
const wasteful_vector<uint8_t>& identifier);
// Convert the entire |identifier| data to a hex string.
static std::string ConvertIdentifierToString(
static string ConvertIdentifierToString(
const wasteful_vector<uint8_t>& identifier);
private:
// Storage for the path specified
std::string path_;
string path_;
};
} // namespace google_breakpad

View File

@ -278,6 +278,40 @@ TYPED_TEST(FileIDTest, BuildIDPH) {
EXPECT_EQ(expected_identifier_string, identifier_string);
}
TYPED_TEST(FileIDTest, BuildIDMultiplePH) {
const uint8_t kExpectedIdentifierBytes[] =
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13};
const string expected_identifier_string =
this->get_file_id(kExpectedIdentifierBytes);
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
Section text(kLittleEndian);
text.Append(4096, 0);
elf.AddSection(".text", text, SHT_PROGBITS);
Notes notes1(kLittleEndian);
notes1.AddNote(0, "Linux",
reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4);
Notes notes2(kLittleEndian);
notes2.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
sizeof(kExpectedIdentifierBytes));
int note1_idx = elf.AddSection(".note1", notes1, SHT_NOTE);
int note2_idx = elf.AddSection(".note2", notes2, SHT_NOTE);
elf.AddSegment(note1_idx, note1_idx, PT_NOTE);
elf.AddSegment(note2_idx, note2_idx, PT_NOTE);
elf.Finish();
this->GetElfContents(elf);
id_vector identifier(this->make_vector());
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
identifier));
EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
EXPECT_EQ(expected_identifier_string, identifier_string);
}
// Test to make sure two files with different text sections produce
// different hashes when not using a build id.
TYPED_TEST(FileIDTest, UniqueHashes) {

View File

@ -27,15 +27,26 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "common/linux/eintr_wrapper.h"
#include "common/linux/guid_creator.h"
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#if defined(HAVE_SYS_RANDOM_H)
#include <sys/random.h>
#endif
//
// GUIDGenerator
//
@ -61,28 +72,101 @@ class GUIDGenerator {
}
static bool CreateGUID(GUID *guid) {
InitOnce();
guid->data1 = random();
guid->data2 = (uint16_t)(random());
guid->data3 = (uint16_t)(random());
UInt32ToBytes(&guid->data4[0], random());
UInt32ToBytes(&guid->data4[4], random());
#if defined(HAVE_ARC4RANDOM) // Android, BSD, ...
CreateGuidFromArc4Random(guid);
#else // Linux
bool success = false;
#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM)
success = CreateGUIDFromGetrandom(guid);
#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM
if (!success) {
success = CreateGUIDFromDevUrandom(guid);
}
if (!success) {
CreateGUIDFromRand(guid);
success = true;
}
#endif
// Put in the version according to RFC 4122.
guid->data3 &= 0x0fff;
guid->data3 |= 0x4000;
// Put in the variant according to RFC 4122.
guid->data4[0] &= 0x3f;
guid->data4[0] |= 0x80;
return true;
}
private:
#ifdef HAVE_ARC4RANDOM
static void CreateGuidFromArc4Random(GUID *guid) {
char *buf = reinterpret_cast<char *>(guid);
for (size_t i = 0; i < sizeof(GUID); i += sizeof(uint32_t)) {
uint32_t random_data = arc4random();
memcpy(buf + i, &random_data, sizeof(uint32_t));
}
}
#else
static void InitOnce() {
pthread_once(&once_control, &InitOnceImpl);
}
static void InitOnceImpl() {
srandom(time(NULL));
// time(NULL) is a very poor seed, so lacking anything better mix an
// address into it. We drop the four rightmost bits as they're likely to
// be 0 on almost all architectures.
srand(time(NULL) | ((uintptr_t)&once_control >> 4));
}
static pthread_once_t once_control;
#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM)
static bool CreateGUIDFromGetrandom(GUID *guid) {
char *buf = reinterpret_cast<char *>(guid);
int read_bytes = getrandom(buf, sizeof(GUID), GRND_NONBLOCK);
return (read_bytes == static_cast<int>(sizeof(GUID)));
}
#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM
// Populate the GUID using random bytes read from /dev/urandom, returns false
// if the GUID wasn't fully populated with random data.
static bool CreateGUIDFromDevUrandom(GUID *guid) {
char *buf = reinterpret_cast<char *>(guid);
int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
if (fd == -1) {
return false;
}
ssize_t read_bytes = HANDLE_EINTR(read(fd, buf, sizeof(GUID)));
close(fd);
return (read_bytes == static_cast<ssize_t>(sizeof(GUID)));
}
// Populate the GUID using a stream of random bytes obtained from rand().
static void CreateGUIDFromRand(GUID *guid) {
char *buf = reinterpret_cast<char *>(guid);
InitOnce();
for (size_t i = 0; i < sizeof(GUID); i++) {
buf[i] = rand();
}
}
#endif
};
#ifndef HAVE_ARC4RANDOM
pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
#endif
bool CreateGUID(GUID *guid) {
return GUIDGenerator::CreateGUID(guid);

View File

@ -202,7 +202,7 @@ bool CrashGenerator::CreateChildCrash(
// On Android the signal sometimes doesn't seem to get sent even though
// tkill returns '0'. Retry a couple of times if the signal doesn't get
// through on the first go:
// https://code.google.com/p/google-breakpad/issues/detail?id=579
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=579
#if defined(__ANDROID__)
const int kRetries = 60;
const unsigned int kSleepTimeInSeconds = 1;

View File

@ -0,0 +1,178 @@
// Copyright (c) 2017, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/long_string_dictionary.h"
#include <assert.h>
#include <string.h>
#include <algorithm>
#include <string>
#include "common/simple_string_dictionary.h"
#define arraysize(f) (sizeof(f) / sizeof(*f))
namespace {
// Suffixes for segment keys.
const char* const kSuffixes[] = {"__1", "__2", "__3", "__4", "__5", "__6",
"__7", "__8", "__9", "__10"};
#if !defined(NDEBUG)
// The maximum suffix string length.
const size_t kMaxSuffixLength = 4;
#endif
} // namespace
namespace google_breakpad {
using std::string;
void LongStringDictionary::SetKeyValue(const char* key, const char* value) {
assert(key);
if (!key)
return;
RemoveKey(key);
if (!value) {
return;
}
// Key must not be an empty string.
assert(key[0] != '\0');
if (key[0] == '\0')
return;
// If the value is not valid for segmentation, forwards the key and the value
// to SetKeyValue of SimpleStringDictionary and returns.
size_t value_length = strlen(value);
if (value_length <= (value_size - 1)) {
SimpleStringDictionary::SetKeyValue(key, value);
return;
}
size_t key_length = strlen(key);
assert(key_length + kMaxSuffixLength <= (key_size - 1));
char segment_key[key_size];
char segment_value[value_size];
strcpy(segment_key, key);
const char* remain_value = value;
size_t remain_value_length = strlen(value);
for (unsigned long i = 0; i < arraysize(kSuffixes); i++) {
if (remain_value_length == 0) {
return;
}
strcpy(segment_key + key_length, kSuffixes[i]);
size_t segment_value_length =
std::min(remain_value_length, value_size - 1);
strncpy(segment_value, remain_value, segment_value_length);
segment_value[segment_value_length] = '\0';
remain_value += segment_value_length;
remain_value_length -= segment_value_length;
SimpleStringDictionary::SetKeyValue(segment_key, segment_value);
}
}
bool LongStringDictionary::RemoveKey(const char* key) {
assert(key);
if (!key)
return false;
if (SimpleStringDictionary::RemoveKey(key)) {
return true;
}
size_t key_length = strlen(key);
assert(key_length + kMaxSuffixLength <= (key_size - 1));
char segment_key[key_size];
strcpy(segment_key, key);
unsigned long i = 0;
for (; i < arraysize(kSuffixes); i++) {
strcpy(segment_key + key_length, kSuffixes[i]);
if (!SimpleStringDictionary::RemoveKey(segment_key)) {
break;
}
}
return i != 0;
}
const string LongStringDictionary::GetValueForKey(const char* key) const {
assert(key);
if (!key)
return "";
// Key must not be an empty string.
assert(key[0] != '\0');
if (key[0] == '\0')
return "";
const char* value = SimpleStringDictionary::GetValueForKey(key);
if (value)
return string(value);
size_t key_length = strlen(key);
assert(key_length + kMaxSuffixLength <= (key_size - 1));
bool found_segment = false;
char segment_key[key_size];
string return_value;
strcpy(segment_key, key);
for (unsigned long i = 0; i < arraysize(kSuffixes); i++) {
strcpy(segment_key + key_length, kSuffixes[i]);
const char* segment_value =
SimpleStringDictionary::GetValueForKey(segment_key);
if (segment_value != NULL) {
found_segment = true;
return_value.append(segment_value);
} else {
break;
}
}
if (found_segment) {
return return_value;
}
return "";
}
} // namespace google_breakpad

View File

@ -0,0 +1,87 @@
// Copyright (c) 2017, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_LONG_STRING_DICTIONARY_H_
#define COMMON_LONG_STRING_DICTIONARY_H_
#include <string>
#include "common/simple_string_dictionary.h"
namespace google_breakpad {
// key_size is the maxium size that |key| can take in
// SimpleStringDictionary which is defined in simple_string_dictionary.h.
//
// value_size is the maxium size that |value| can take in
// SimpleStringDictionary which is defined in simple_string_dictionary.h.
//
// LongStringDictionary is a subclass of SimpleStringDictionary which supports
// longer values to be stored in the dictionary. The maximum length supported is
// (value_size - 1) * 10.
//
// For example, LongStringDictionary will store long value with key 'abc' into
// segment values with segment keys 'abc__1', 'abc__2', 'abc__3', ...
//
// Clients must avoid using the same suffixes as their key's suffix when
// LongStringDictionary is used.
class LongStringDictionary : public SimpleStringDictionary {
public:
// Stores |value| into |key|, or segment values into segment keys. The maxium
// number of segments is 10. If |value| can not be stored in 10 segments, it
// will be truncated. Replacing the existing value if |key| is already present
// and replacing the existing segment values if segment keys are already
// present.
//
// |key| must not be NULL. If the |value| need to be divided into segments,
// the lengh of |key| must be smaller enough so that lengths of segment keys
// which are key with suffixes are all samller than (key_size - 1). Currently,
// the max length of suffixes are 4.
//
// If |value| is NULL, the key and its corresponding segment keys are removed
// from the map. If there is no more space in the map, then the operation
// silently fails.
void SetKeyValue(const char* key, const char* value);
// Given |key|, removes any associated value or associated segment values.
// |key| must not be NULL. If the key is not found, searchs its segment keys
// and removes corresponding segment values if found.
bool RemoveKey(const char* key);
// Given |key|, returns its corresponding |value|. |key| must not be NULL. If
// the key is found, its corresponding |value| is returned.
//
// If no corresponding |value| is found, segment keys of the given |key| will
// be used to search for corresponding segment values. If segment values
// exist, assembled value from them is returned. If no segment value exists,
// NULL is returned.
const std::string GetValueForKey(const char* key) const;
};
} // namespace google_breakpad
#endif // COMMON_LONG_STRING_DICTIONARY_H_

View File

@ -0,0 +1,301 @@
// Copyright (c) 2017, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/long_string_dictionary.h"
namespace google_breakpad {
using std::string;
TEST(LongStringDictionary, LongStringDictionary) {
// Make a new dictionary
LongStringDictionary dict;
// Set three distinct values on three keys
dict.SetKeyValue("key1", "value1");
dict.SetKeyValue("key2", "value2");
dict.SetKeyValue("key3", "value3");
EXPECT_EQ("value1", dict.GetValueForKey("key1"));
EXPECT_EQ("value2", dict.GetValueForKey("key2"));
EXPECT_EQ("value3", dict.GetValueForKey("key3"));
EXPECT_EQ(3u, dict.GetCount());
// try an unknown key
EXPECT_EQ("", dict.GetValueForKey("key4"));
// Remove a key
dict.RemoveKey("key3");
// Now make sure it's not there anymore
EXPECT_EQ("", dict.GetValueForKey("key3"));
// Remove by setting value to NULL
dict.SetKeyValue("key2", NULL);
// Now make sure it's not there anymore
EXPECT_EQ("", dict.GetValueForKey("key2"));
}
// Add a bunch of values to the dictionary, remove some entries in the middle,
// and then add more.
TEST(LongStringDictionary, Iterator) {
LongStringDictionary* dict = new LongStringDictionary();
ASSERT_TRUE(dict);
char key[LongStringDictionary::key_size];
char value[LongStringDictionary::value_size];
const int kDictionaryCapacity = LongStringDictionary::num_entries;
const int kPartitionIndex = kDictionaryCapacity - 5;
// We assume at least this size in the tests below
ASSERT_GE(kDictionaryCapacity, 64);
// We'll keep track of the number of key/value pairs we think should
// be in the dictionary
int expectedDictionarySize = 0;
// Set a bunch of key/value pairs like key0/value0, key1/value1, ...
for (int i = 0; i < kPartitionIndex; ++i) {
sprintf(key, "key%d", i);
sprintf(value, "value%d", i);
dict->SetKeyValue(key, value);
}
expectedDictionarySize = kPartitionIndex;
// set a couple of the keys twice (with the same value) - should be nop
dict->SetKeyValue("key2", "value2");
dict->SetKeyValue("key4", "value4");
dict->SetKeyValue("key15", "value15");
// Remove some random elements in the middle
dict->RemoveKey("key7");
dict->RemoveKey("key18");
dict->RemoveKey("key23");
dict->RemoveKey("key31");
expectedDictionarySize -= 4; // we just removed four key/value pairs
// Set some more key/value pairs like key59/value59, key60/value60, ...
for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) {
sprintf(key, "key%d", i);
sprintf(value, "value%d", i);
dict->SetKeyValue(key, value);
}
expectedDictionarySize += kDictionaryCapacity - kPartitionIndex;
// Now create an iterator on the dictionary
SimpleStringDictionary::Iterator iter(*dict);
// We then verify that it iterates through exactly the number of
// key/value pairs we expect, and that they match one-for-one with what we
// would expect. The ordering of the iteration does not matter...
// used to keep track of number of occurrences found for key/value pairs
int count[kDictionaryCapacity];
memset(count, 0, sizeof(count));
int totalCount = 0;
const SimpleStringDictionary::Entry* entry;
while ((entry = iter.Next())) {
totalCount++;
// Extract keyNumber from a string of the form key<keyNumber>
int keyNumber;
sscanf(entry->key, "key%d", &keyNumber);
// Extract valueNumber from a string of the form value<valueNumber>
int valueNumber;
sscanf(entry->value, "value%d", &valueNumber);
// The value number should equal the key number since that's how we set them
EXPECT_EQ(keyNumber, valueNumber);
// Key and value numbers should be in proper range:
// 0 <= keyNumber < kDictionaryCapacity
bool isKeyInGoodRange = (keyNumber >= 0 && keyNumber < kDictionaryCapacity);
bool isValueInGoodRange =
(valueNumber >= 0 && valueNumber < kDictionaryCapacity);
EXPECT_TRUE(isKeyInGoodRange);
EXPECT_TRUE(isValueInGoodRange);
if (isKeyInGoodRange && isValueInGoodRange) {
++count[keyNumber];
}
}
// Make sure each of the key/value pairs showed up exactly one time, except
// for the ones which we removed.
for (size_t i = 0; i < kDictionaryCapacity; ++i) {
// Skip over key7, key18, key23, and key31, since we removed them
if (!(i == 7 || i == 18 || i == 23 || i == 31)) {
EXPECT_EQ(count[i], 1);
}
}
// Make sure the number of iterations matches the expected dictionary size.
EXPECT_EQ(totalCount, expectedDictionarySize);
}
TEST(LongStringDictionary, AddRemove) {
LongStringDictionary dict;
dict.SetKeyValue("rob", "ert");
dict.SetKeyValue("mike", "pink");
dict.SetKeyValue("mark", "allays");
EXPECT_EQ(3u, dict.GetCount());
EXPECT_EQ("ert", dict.GetValueForKey("rob"));
EXPECT_EQ("pink", dict.GetValueForKey("mike"));
EXPECT_EQ("allays", dict.GetValueForKey("mark"));
dict.RemoveKey("mike");
EXPECT_EQ(2u, dict.GetCount());
EXPECT_EQ("", dict.GetValueForKey("mike"));
dict.SetKeyValue("mark", "mal");
EXPECT_EQ(2u, dict.GetCount());
EXPECT_EQ("mal", dict.GetValueForKey("mark"));
dict.RemoveKey("mark");
EXPECT_EQ(1u, dict.GetCount());
EXPECT_EQ("", dict.GetValueForKey("mark"));
}
TEST(LongStringDictionary, AddRemoveLongValue) {
LongStringDictionary dict;
string long_value = string(256, 'x');
dict.SetKeyValue("rob", long_value.c_str());
EXPECT_EQ(2u, dict.GetCount());
string long_value_part_1 = string(255, 'x');
EXPECT_EQ(long_value_part_1, dict.GetValueForKey("rob__1"));
EXPECT_EQ("x", dict.GetValueForKey("rob__2"));
EXPECT_EQ(long_value, dict.GetValueForKey("rob"));
dict.RemoveKey("rob");
EXPECT_EQ(0u, dict.GetCount());
}
TEST(LongStringDictionary, AddRemoveSuperLongValue) {
LongStringDictionary dict;
string long_value = string(255 * 10, 'x');
dict.SetKeyValue("rob", long_value.c_str());
EXPECT_EQ(10u, dict.GetCount());
string long_value_part = string(255, 'x');
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10"));
EXPECT_EQ(10u, dict.GetCount());
EXPECT_EQ(long_value, dict.GetValueForKey("rob"));
dict.RemoveKey("rob");
EXPECT_EQ(0u, dict.GetCount());
}
TEST(LongStringDictionary, TruncateSuperLongValue) {
LongStringDictionary dict;
string long_value = string(255 * 11, 'x');
dict.SetKeyValue("rob", long_value.c_str());
EXPECT_EQ(10u, dict.GetCount());
string long_value_part = string(255, 'x');
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9"));
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10"));
EXPECT_EQ(10u, dict.GetCount());
string expected_long_value = string(255 * 10, 'x');
EXPECT_EQ(expected_long_value, dict.GetValueForKey("rob"));
dict.RemoveKey("rob");
EXPECT_EQ(0u, dict.GetCount());
}
TEST(LongStringDictionary, OverrideLongValue) {
LongStringDictionary dict;
string long_value = string(255 * 10, 'x');
dict.SetKeyValue("rob", long_value.c_str());
EXPECT_EQ(10u, dict.GetCount());
EXPECT_EQ(long_value, dict.GetValueForKey("rob"));
dict.SetKeyValue("rob", "short_value");
EXPECT_EQ(1u, dict.GetCount());
EXPECT_EQ("short_value", dict.GetValueForKey("rob"));
}
TEST(LongStringDictionary, OverrideShortValue) {
LongStringDictionary dict;
dict.SetKeyValue("rob", "short_value");
EXPECT_EQ(1u, dict.GetCount());
EXPECT_EQ("short_value", dict.GetValueForKey("rob"));
string long_value = string(255 * 10, 'x');
dict.SetKeyValue("rob", long_value.c_str());
EXPECT_EQ(10u, dict.GetCount());
EXPECT_EQ(long_value, dict.GetValueForKey("rob"));
}
} // namespace google_breakpad

View File

@ -117,7 +117,7 @@
// developer level errors. This implementation simply macros to NSLog/NSAssert.
// It is not intended to be a general logging/reporting system.
//
// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert
// Please see https://github.com/google/google-toolbox-for-mac/wiki/DevLogNAssert
// for a little more background on the usage of these macros.
//
// _GTMDevLog log some error/problem in debug builds

View File

@ -49,13 +49,13 @@ static NSString *PercentEncodeNSString(NSString *key) {
// As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has
// been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements
// it using -[NSURLSession dataTaskWithRequest:completionHandler:] when using
// those SDKs.
// it using -[NSURLSession dataTaskWithRequest:completionHandler:] which is
// available on iOS 7+.
static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
NSURLResponse **out_response,
NSError **out_error) {
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_9_0) && \
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0) || \
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) || \
(defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
defined(MAC_OS_X_VERSION_10_11) && \
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
@ -223,9 +223,7 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
// Add any files to the message
NSArray *fileNames = [files_ allKeys];
count = [fileNames count];
for (NSInteger i = 0; i < count; ++i) {
NSString *name = [fileNames objectAtIndex:i];
for (NSString *name in fileNames) {
id fileOrData = [files_ objectForKey:name];
NSData *fileData;

View File

@ -111,6 +111,7 @@ namespace {
enum Architecture {
kArch_i386 = 0,
kArch_x86_64,
kArch_x86_64h,
kArch_arm,
kArch_arm64,
kArch_ppc,
@ -135,6 +136,13 @@ const NXArchInfo kKnownArchitectures[] = {
NX_LittleEndian,
"Intel x86-64"
},
{
"x86_64h",
CPU_TYPE_X86_64,
CPU_SUBTYPE_X86_64_H,
NX_LittleEndian,
"Intel x86-64h Haswell"
},
{
"arm",
CPU_TYPE_ARM,
@ -189,23 +197,35 @@ const NXArchInfo *NXGetArchInfoFromName(const char *name) {
const NXArchInfo *NXGetArchInfoFromCpuType(cpu_type_t cputype,
cpu_subtype_t cpusubtype) {
const NXArchInfo *candidate = NULL;
for (int arch = 0; arch < kNumArchitectures; ++arch) {
if (kKnownArchitectures[arch].cputype == cputype) {
return &kKnownArchitectures[arch];
if (kKnownArchitectures[arch].cpusubtype == cpusubtype) {
return &kKnownArchitectures[arch];
}
if (!candidate) {
candidate = &kKnownArchitectures[arch];
}
}
}
return NULL;
return candidate;
}
struct fat_arch *NXFindBestFatArch(cpu_type_t cputype,
cpu_subtype_t cpusubtype,
struct fat_arch *fat_archs,
uint32_t nfat_archs) {
struct fat_arch *candidate = NULL;
for (uint32_t f = 0; f < nfat_archs; ++f) {
if (fat_archs[f].cputype == cputype) {
return &fat_archs[f];
if (fat_archs[f].cpusubtype == cpusubtype) {
return &fat_archs[f];
}
if (!candidate) {
candidate = &fat_archs[f];
}
}
}
return NULL;
return candidate;
}
#endif // !__APPLE__

View File

@ -31,14 +31,13 @@
// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dump_syms.mm: Create a symbol file for use with minidumps
// dump_syms.cc: Create a symbol file for use with minidumps
#include "common/mac/dump_syms.h"
#include <assert.h>
#include <dirent.h>
#include <errno.h>
#include <libgen.h>
#include <mach-o/arch.h>
#include <mach-o/fat.h>
#include <stdint.h>
@ -60,6 +59,7 @@
#include "common/mac/arch_utilities.h"
#include "common/mac/macho_reader.h"
#include "common/module.h"
#include "common/path_helper.h"
#include "common/scoped_ptr.h"
#include "common/stabs_reader.h"
#include "common/stabs_to_module.h"
@ -370,8 +370,7 @@ bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) {
}
// Compute a module name, to appear in the MODULE record.
string module_name = object_filename_;
module_name = basename(&module_name[0]);
string module_name = google_breakpad::BaseName(object_filename_);
// Choose an identifier string, to appear in the MODULE record.
string identifier = Identifier();

View File

@ -1114,7 +1114,7 @@ TEST_F(LoadCommand, SegmentBE32) {
Return(true)));
EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
EXPECT_EQ(false, actual_segment.bits_64);
EXPECT_FALSE(actual_segment.bits_64);
EXPECT_EQ("froon", actual_segment.name);
EXPECT_EQ(0x1891139cU, actual_segment.vmaddr);
EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
@ -1151,7 +1151,7 @@ TEST_F(LoadCommand, SegmentLE32) {
Return(true)));
EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
EXPECT_EQ(false, actual_segment.bits_64);
EXPECT_FALSE(actual_segment.bits_64);
EXPECT_EQ("sixteenprecisely", actual_segment.name);
EXPECT_EQ(0x4b877866U, actual_segment.vmaddr);
EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);

Some files were not shown because too many files have changed in this diff Show More