wip: merge official 9.5.3

This commit is contained in:
luvletter2333 2023-03-28 05:12:27 +08:00
commit 4933c7107b
No known key found for this signature in database
GPG Key ID: 9EB7723F3A0ACF92
8272 changed files with 505752 additions and 117921 deletions

View File

@ -3,15 +3,15 @@ import cn.hutool.core.util.RuntimeUtil
apply plugin: "com.android.application"
apply plugin: "kotlin-android"
def verName = "9.3.3"
def verCode = 710
def verName = "9.5.3-preview01"
def verCode = 720
if (System.getenv("DEBUG_BUILD") == "true") {
verName += "-" + RuntimeUtil.execForStr("git log --pretty=format:'%h' -n 1").trim().replace('\'','')
}
def officialVer = "9.3.3"
def officialCode = 3026
def officialVer = "9.5.3"
def officialCode = 3213
def serviceAccountCredentialsFile = rootProject.file("service_account_credentials.json")
@ -72,8 +72,8 @@ def nativeTarget = System.getenv("NATIVE_TARGET")
if (nativeTarget == null) nativeTarget = ""
android {
compileSdkVersion 32
buildToolsVersion "31.0.0"
compileSdkVersion 33
buildToolsVersion "33.0.0"
ndkVersion rootProject.ext.ndkVersion
defaultConfig.applicationId = "nekox.messenger"
@ -331,26 +331,27 @@ def playCoreVersion = "1.10.3"
dependencies {
implementation "androidx.browser:browser:1.4.0"
implementation "androidx.core:core-ktx:1.8.0"
implementation "androidx.browser:browser:1.5.0"
implementation "androidx.core:core-ktx:1.9.0"
implementation "androidx.palette:palette-ktx:1.0.0"
implementation "androidx.viewpager:viewpager:1.0.0"
implementation "androidx.exifinterface:exifinterface:1.3.3"
implementation "androidx.exifinterface:exifinterface:1.3.6"
implementation "androidx.interpolator:interpolator:1.0.0"
implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"
implementation "androidx.multidex:multidex:2.0.1"
implementation "androidx.sharetarget:sharetarget:1.2.0"
compileOnly "org.checkerframework:checker-qual:3.16.0"
compileOnly "org.checkerframework:checker-compat-qual:2.5.5"
// just follow official
compileOnly 'org.checkerframework:checker-qual:2.5.2'
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
// don"t change this :)
//noinspection GradleDependency
implementation "com.googlecode.mp4parser:isoparser:1.0.6"
implementation "com.google.code.gson:gson:2.8.8"
implementation "com.google.code.gson:gson:2.8.9"
implementation "org.osmdroid:osmdroid-android:6.1.10"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.10"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1"
implementation "com.squareup.okhttp3:okhttp:5.0.0-alpha.10"
@ -362,6 +363,7 @@ dependencies {
implementation "cn.hutool:hutool-crypto:5.7.13"
implementation 'cn.hutool:hutool-http:5.7.5'
implementation "com.jakewharton:process-phoenix:2.1.2"
implementation 'com.google.guava:guava:31.1-android'
compileOnly 'org.yaml:snakeyaml:1.29'
fullImplementation 'org.yaml:snakeyaml:1.29'
@ -387,7 +389,7 @@ dependencies {
releaseImplementation "com.google.android.play:core:$playCoreVersion"
testImplementation "junit:junit:4.13.2"
testImplementation "androidx.test:core:1.4.0"
testImplementation "androidx.test:core:1.5.0"
testImplementation "org.robolectric:robolectric:4.5.1"
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.2.0"

View File

@ -459,10 +459,41 @@ set_target_properties(sqlite PROPERTIES
target_compile_definitions(sqlite PUBLIC
NULL=0 SOCKLEN_T=socklen_t LOCALE_NOT_USED ANDROID_NDK DISABLE_IMPORTGL AVOID_TABLES ANDROID_TILE_BASED_DECODE HAVE_STRCHRNUL=0 ANDROID_ARMV6_IDCT)
#breakpad
add_library(breakpad STATIC
third_party/breakpad/src/client/linux/crash_generation/crash_generation_client.cc
third_party/breakpad/src/client/linux/handler/exception_handler.cc
third_party/breakpad/src/client/linux/handler/minidump_descriptor.cc
third_party/breakpad/src/client/linux/log/log.cc
third_party/breakpad/src/client/linux/dump_writer_common/thread_info.cc
third_party/breakpad/src/client/linux/dump_writer_common/seccomp_unwinder.cc
third_party/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc
third_party/breakpad/src/client/linux/microdump_writer/microdump_writer.cc
third_party/breakpad/src/client/linux/minidump_writer/linux_dumper.cc
third_party/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
third_party/breakpad/src/client/linux/minidump_writer/minidump_writer.cc
third_party/breakpad/src/client/minidump_file_writer.cc
third_party/breakpad/src/common/android/breakpad_getcontext.S
third_party/breakpad/src/common/convert_UTF.c
third_party/breakpad/src/common/md5.cc
third_party/breakpad/src/common/string_conversion.cc
third_party/breakpad/src/common/linux/elfutils.cc
third_party/breakpad/src/common/linux/file_id.cc
third_party/breakpad/src/common/linux/guid_creator.cc
third_party/breakpad/src/common/linux/linux_libc_support.cc
third_party/breakpad/src/common/linux/memory_mapped_file.cc
third_party/breakpad/src/common/linux/safe_readlink.cc)
set_target_properties(breakpad PROPERTIES ANDROID_ARM_MODE arm)
set_property(SOURCE third_party/breakpad/src/common/android/breakpad_getcontext.S PROPERTY LANGUAGE C)
target_include_directories(breakpad PUBLIC
third_party/breakpad/src/common/android/include
third_party/breakpad/src)
#voip
include(${CMAKE_HOME_DIRECTORY}/voip/CMakeLists.txt)
set(NATIVE_LIB "tmessages.42")
set(NATIVE_LIB "tmessages.43")
#tmessages
add_library(${NATIVE_LIB} SHARED
@ -710,7 +741,12 @@ target_sources(${NATIVE_LIB} PRIVATE
third_party/libyuv/source/scale_win.cc
third_party/libyuv/source/scale.cc
third_party/libyuv/source/video_common.cc
third_party/libyuv/source/scale_uv.cc)
third_party/libyuv/source/scale_uv.cc
third_party/libyuv/source/rotate_lsx.cc
third_party/libyuv/source/row_lasx.cc
third_party/libyuv/source/row_lsx.cc
third_party/libyuv/source/scale_lsx.cc
third_party/libyuv/source/scale_rgb.cc)
target_include_directories(${NATIVE_LIB} PUBLIC
opus/include
@ -760,7 +796,8 @@ target_link_libraries(${NATIVE_LIB}
EGL
android
OpenSLES
cpufeatures)
cpufeatures
breakpad)
include(AndroidNdkModules)

View File

@ -65,10 +65,10 @@ jobject getJavaByteBuffer(JNIEnv *env, jclass c, jlong address) {
static const char *NativeByteBufferClassPathName = "org/telegram/tgnet/NativeByteBuffer";
static JNINativeMethod NativeByteBufferMethods[] = {
{"native_getFreeBuffer", "(I)J", (void *) getFreeBuffer},
{"native_limit", "(J)I", (void *) limit},
{"native_position", "(J)I", (void *) position},
{"native_reuse", "(J)V", (void *) reuse},
{"native_getFreeBuffer", "(I)J", (void *) getFreeBuffer},
{"native_limit", "(J)I", (void *) limit},
{"native_position", "(J)I", (void *) position},
{"native_reuse", "(J)V", (void *) reuse},
{"native_getJavaByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void *) getJavaByteBuffer}
};
@ -434,7 +434,7 @@ void init(JNIEnv *env, jclass c, jint instanceNum, jint version, jint layer, jin
jstring deviceModel, jstring systemVersion, jstring appVersion, jstring langCode,
jstring systemLangCode, jstring configPath, jstring logPath, jstring regId,
jstring cFingerprint, jstring installerId, jstring packageId, jint timezoneOffset, jlong userId,
jboolean enablePushConnection, jboolean hasNetwork, jint networkType) {
jboolean enablePushConnection, jboolean hasNetwork, jint networkType, jint performanceClass) {
const char *deviceModelStr = env->GetStringUTFChars(deviceModel, 0);
const char *systemVersionStr = env->GetStringUTFChars(systemVersion, 0);
const char *appVersionStr = env->GetStringUTFChars(appVersion, 0);
@ -459,7 +459,7 @@ void init(JNIEnv *env, jclass c, jint instanceNum, jint version, jint layer, jin
std::string(cFingerprintStr),
std::string(installerIdStr), std::string(packageIdStr), timezoneOffset,
userId, true, enablePushConnection,
hasNetwork, networkType);
hasNetwork, networkType, performanceClass);
if (deviceModelStr != 0) {
env->ReleaseStringUTFChars(deviceModel, deviceModelStr);
@ -524,7 +524,7 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
{"native_setProxySettings", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void *) setProxySettings},
{"native_getConnectionState", "(I)I", (void *) getConnectionState},
{"native_setUserId", "(IJ)V", (void *) setUserId},
{"native_init", "(IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJZZI)V", (void *) init},
{"native_init", "(IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJZZII)V", (void *) init},
{"native_setLangCode", "(ILjava/lang/String;)V", (void *) setLangCode},
{"native_setRegId", "(ILjava/lang/String;)V", (void *) setRegId},
{"native_setSystemLangCode", "(ILjava/lang/String;)V", (void *) setSystemLangCode},

View File

@ -290,11 +290,13 @@ int initRecorder(const char *path, opus_int32 sampleRate) {
rate = sampleRate;
if (!path) {
LOGE("path is null");
return 0;
}
_fileOs = fopen(path, "wb");
_fileOs = fopen(path, "w");
if (!_fileOs) {
LOGE("error cannot open file: %s", path);
return 0;
}

View File

@ -790,8 +790,14 @@ int readCallback(void *opaque, uint8_t *buf, int buf_size) {
if (attached) {
javaVm->DetachCurrentThread();
}
if (buf_size == 0) {
return AVERROR_EOF;
}
int ret = (int) read(info->fd, buf, (size_t) buf_size);
return ret ? ret : AVERROR_EOF;
if (ret <= 0) {
return AVERROR_EOF;
}
return ret;
}
}
}

View File

@ -111,7 +111,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCtrDecryption(JNIEnv *en
(*env)->ReleaseByteArrayElements(env, iv, ivBuff, JNI_ABORT);
}
JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCtrDecryptionByteArray(JNIEnv *env, jclass class, jbyteArray buffer, jbyteArray key, jbyteArray iv, jint offset, jint length, jint fileOffset) {
JNIEXPORT void Java_org_telegram_messenger_Utilities_aesCtrDecryptionByteArray(JNIEnv *env, jclass class, jbyteArray buffer, jbyteArray key, jbyteArray iv, jint offset, jlong length, jint fileOffset) {
unsigned char *bufferBuff = (unsigned char *) (*env)->GetByteArrayElements(env, buffer, NULL);
unsigned char *keyBuff = (unsigned char *) (*env)->GetByteArrayElements(env, key, NULL);
unsigned char *ivBuff = (unsigned char *) (*env)->GetByteArrayElements(env, iv, NULL);

View File

@ -1083,6 +1083,9 @@ static opus_int64 op_predict_link_start(const OpusSeekRecord *_sr,int _nsr,
offset2=_sr[srj].offset;
/*For once, we can subtract with impunity.*/
den=gp2-gp1;
if (den == 0) {
return -1;
}
ipart=gp2/den;
num=offset2-offset1;
OP_ASSERT(num>0);

View File

@ -196,19 +196,19 @@ void TL_config::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &
notify_default_delay_ms = stream->readInt32(&error);
push_chat_period_ms = stream->readInt32(&error);
push_chat_limit = stream->readInt32(&error);
saved_gifs_limit = stream->readInt32(&error);
// saved_gifs_limit = stream->readInt32(&error);
edit_time_limit = stream->readInt32(&error);
revoke_time_limit = stream->readInt32(&error);
revoke_pm_time_limit = stream->readInt32(&error);
rating_e_decay = stream->readInt32(&error);
stickers_recent_limit = stream->readInt32(&error);
stickers_faved_limit = stream->readInt32(&error);
// stickers_faved_limit = stream->readInt32(&error);
channels_read_media_period = stream->readInt32(&error);
if ((flags & 1) != 0) {
tmp_sessions = stream->readInt32(&error);
}
pinned_dialogs_count_max = stream->readInt32(&error);
pinned_infolder_count_max = stream->readInt32(&error);
// pinned_dialogs_count_max = stream->readInt32(&error);
// pinned_infolder_count_max = stream->readInt32(&error);
call_receive_timeout_ms = stream->readInt32(&error);
call_ring_timeout_ms = stream->readInt32(&error);
call_connect_timeout_ms = stream->readInt32(&error);
@ -244,6 +244,9 @@ void TL_config::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &
if ((flags & 32768) != 0) {
reactions_default = std::unique_ptr<Reaction>(Reaction::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
}
if ((flags & 65536) != 0) {
autologin_token = stream->readString(&error);
}
}
void TL_config::serializeToStream(NativeByteBuffer *stream) {
@ -271,19 +274,19 @@ void TL_config::serializeToStream(NativeByteBuffer *stream) {
stream->writeInt32(notify_default_delay_ms);
stream->writeInt32(push_chat_period_ms);
stream->writeInt32(push_chat_limit);
stream->writeInt32(saved_gifs_limit);
// stream->writeInt32(saved_gifs_limit);
stream->writeInt32(edit_time_limit);
stream->writeInt32(revoke_time_limit);
stream->writeInt32(revoke_pm_time_limit);
stream->writeInt32(rating_e_decay);
stream->writeInt32(stickers_recent_limit);
stream->writeInt32(stickers_faved_limit);
// stream->writeInt32(stickers_faved_limit);
stream->writeInt32(channels_read_media_period);
if ((flags & 1) != 0) {
stream->writeInt32(tmp_sessions);
}
stream->writeInt32(pinned_dialogs_count_max);
stream->writeInt32(pinned_infolder_count_max);
// stream->writeInt32(pinned_dialogs_count_max);
// stream->writeInt32(pinned_infolder_count_max);
stream->writeInt32(call_receive_timeout_ms);
stream->writeInt32(call_ring_timeout_ms);
stream->writeInt32(call_connect_timeout_ms);
@ -319,6 +322,9 @@ void TL_config::serializeToStream(NativeByteBuffer *stream) {
if ((flags & 32768) != 0 && reactions_default != nullptr) {
reactions_default->serializeToStream(stream);
}
if ((flags & 65536) != 0) {
stream->writeString(autologin_token);
}
}
TLObject *TL_help_getConfig::deserializeResponse(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {
@ -1057,7 +1063,7 @@ auth_Authorization *auth_Authorization::TLdeserialize(NativeByteBuffer *stream,
case 0x44747e9a:
result = new TL_auth_authorizationSignUpRequired();
break;
case 0x33fb7bb8:
case 0x2ea2c0d4:
result = new TL_auth_authorization();
break;
default:
@ -1086,9 +1092,15 @@ void TL_auth_authorizationSignUpRequired::serializeToStream(NativeByteBuffer *st
void TL_auth_authorization::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) {
flags = stream->readInt32(&error);
if ((flags & 2) != 0) {
otherwise_relogin_days = stream->readInt32(&error);
}
if ((flags & 1) != 0) {
tmp_sessions = stream->readInt32(&error);
}
if ((flags & 4) != 0) {
future_auth_token = std::unique_ptr<ByteArray>(stream->readByteArray(&error));
}
user = std::unique_ptr<User>(User::TLdeserialize(stream, stream->readUint32(&error), instanceNum, error));
}

View File

@ -105,7 +105,7 @@ public:
class TL_config : public TLObject {
public:
static const uint32_t constructor = 0x232566ac;
static const uint32_t constructor = 0xcc1a241e;
int32_t flags;
int32_t date;
@ -125,17 +125,17 @@ public:
int32_t notify_default_delay_ms;
int32_t push_chat_period_ms;
int32_t push_chat_limit;
int32_t saved_gifs_limit;
// int32_t saved_gifs_limit;
int32_t edit_time_limit;
int32_t revoke_time_limit;
int32_t revoke_pm_time_limit;
int32_t rating_e_decay;
int32_t stickers_recent_limit;
int32_t stickers_faved_limit;
// int32_t stickers_faved_limit;
int32_t channels_read_media_period;
int32_t tmp_sessions;
int32_t pinned_dialogs_count_max;
int32_t pinned_infolder_count_max;
// int32_t pinned_dialogs_count_max;
// int32_t pinned_infolder_count_max;
int32_t call_receive_timeout_ms;
int32_t call_ring_timeout_ms;
int32_t call_connect_timeout_ms;
@ -153,6 +153,7 @@ public:
int32_t lang_pack_version;
int32_t base_lang_pack_version;
std::unique_ptr<Reaction> reactions_default;
std::string autologin_token;
static TL_config *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
@ -705,10 +706,12 @@ public:
class TL_auth_authorization : public auth_Authorization {
public:
static const uint32_t constructor = 0x33fb7bb8;
static const uint32_t constructor = 0x2ea2c0d4;
int32_t flags;
int32_t tmp_sessions;
int32_t otherwise_relogin_days;
std::unique_ptr<ByteArray> future_auth_token;
std::unique_ptr<User> user;
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);

View File

@ -885,14 +885,22 @@ void ConnectionsManager::onConnectionDataReceived(Connection *connection, Native
}
deserializingDatacenter = datacenter;
TLObject *object = TLdeserialize(request, messageLength, data);
bool error = false;
uint32_t constructor = data->readUint32(&error);
TLObject *object = request->deserializeResponse(data, constructor, instanceNum, error);
if (object != nullptr && error) {
delete object;
object = nullptr;
}
if (object != nullptr) {
if (datacenter->isHandshaking(connection->isMediaConnection)) {
if (LOGS_ENABLED) DEBUG_E("process handshake");
datacenter->processHandshakeResponse(connection->isMediaConnection, object, messageId);
} else {
processServerResponse(object, messageId, 0, 0, connection, 0, 0);
connection->addProcessedMessageId(messageId);
if (LOGS_ENABLED) DEBUG_E("connection(%p) received incorrect unencrypted message type", connection);
connection->reconnect();
return;
}
lastProtocolUsefullData = true;
connection->setHasUsefullData();
@ -1764,6 +1772,13 @@ int32_t ConnectionsManager::sendRequestInternal(TLObject *object, onCompleteFunc
auto request = new Request(instanceNum, lastRequestToken++, connetionType, flags, datacenterId, onComplete, onQuickAck, nullptr);
request->rawRequest = object;
request->rpcRequest = wrapInLayer(object, getDatacenterWithId(datacenterId), request);
auto cancelledIterator = tokensToBeCancelled.find(request->requestToken);
if (cancelledIterator != tokensToBeCancelled.end()) {
if (LOGS_ENABLED) DEBUG_D("(3) request is cancelled before sending, token %d", request->requestToken);
tokensToBeCancelled.erase(cancelledIterator);
delete request;
return request->requestToken;
}
requestsQueue.push_back(std::unique_ptr<Request>(request));
if (immediate) {
processRequestQueue(0, 0);
@ -1789,6 +1804,12 @@ int32_t ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onCompl
auto request = new Request(instanceNum, requestToken, connetionType, flags, datacenterId, onComplete, onQuickAck, nullptr);
request->rawRequest = object;
request->rpcRequest = wrapInLayer(object, getDatacenterWithId(datacenterId), request);
auto cancelledIterator = tokensToBeCancelled.find(request->requestToken);
if (cancelledIterator != tokensToBeCancelled.end()) {
if (LOGS_ENABLED) DEBUG_D("(1) request is cancelled before sending, token %d", requestToken);
tokensToBeCancelled.erase(cancelledIterator);
delete request;
}
requestsQueue.push_back(std::unique_ptr<Request>(request));
if (immediate) {
processRequestQueue(0, 0);
@ -1830,6 +1851,13 @@ void ConnectionsManager::sendRequest(TLObject *object, onCompleteFunc onComplete
request->ptr3 = ptr3;
request->rpcRequest = wrapInLayer(object, getDatacenterWithId(datacenterId), request);
if (LOGS_ENABLED) DEBUG_D("send request wrapped %p - %s", request->rpcRequest.get(), typeid(*(request->rpcRequest.get())).name());
auto cancelledIterator = tokensToBeCancelled.find(request->requestToken);
if (cancelledIterator != tokensToBeCancelled.end()) {
if (LOGS_ENABLED) DEBUG_D("(2) request is cancelled before sending, token %d", requestToken);
tokensToBeCancelled.erase(cancelledIterator);
delete request;
return;
}
requestsQueue.push_back(std::unique_ptr<Request>(request));
if (immediate) {
processRequestQueue(0, 0);
@ -1924,6 +1952,10 @@ void ConnectionsManager::removeRequestFromGuid(int32_t requestToken) {
}
bool ConnectionsManager::cancelRequestInternal(int32_t token, int64_t messageId, bool notifyServer, bool removeFromClass) {
if (!tokensToBeCancelled.empty() && (connectionState != ConnectionStateWaitingForNetwork || tokensToBeCancelled.size() > 5000)) {
tokensToBeCancelled.clear();
}
for (auto iter = requestsQueue.begin(); iter != requestsQueue.end(); iter++) {
Request *request = iter->get();
if ((token != 0 && request->requestToken == token) || (messageId != 0 && request->respondsToMessageId(messageId))) {
@ -1954,6 +1986,12 @@ bool ConnectionsManager::cancelRequestInternal(int32_t token, int64_t messageId,
return true;
}
}
if (token != 0 && connectionState == ConnectionStateWaitingForNetwork) {
if (LOGS_ENABLED) DEBUG_D("request is tried to be cancelled, but it does not even exist, token %d", token);
tokensToBeCancelled.insert(token);
}
return false;
}
@ -2829,6 +2867,16 @@ std::unique_ptr<TLObject> ConnectionsManager::wrapInLayer(TLObject *object, Data
objectValue->key = "tz_offset";
objectValue->value = std::unique_ptr<JSONValue>(jsonNumber);
if (currentPerformanceClass != -1) {
objectValue = new TL_jsonObjectValue();
jsonObject->value.push_back(std::unique_ptr<TL_jsonObjectValue>(objectValue));
auto jsonNumber = new TL_jsonNumber();
jsonNumber->value = currentPerformanceClass + 1;
objectValue->key = "perf_cat";
objectValue->value = std::unique_ptr<JSONValue>(jsonNumber);
}
request->flags |= 2;
if (!proxyAddress.empty() && !proxySecret.empty()) {
@ -3274,7 +3322,7 @@ void ConnectionsManager::applyDnsConfig(NativeByteBuffer *buffer, std::string ph
});
}
void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerpting, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType) {
void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerpting, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType, int32_t performanceClass) {
currentVersion = version;
currentLayer = layer;
currentApiId = apiId;
@ -3294,6 +3342,7 @@ void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, st
pushConnectionEnabled = enablePushConnection;
currentNetworkType = networkType;
networkAvailable = hasNetwork;
currentPerformanceClass = performanceClass;
if (isPaused) {
lastPauseTime = getCurrentTimeMonotonicMillis();
}

View File

@ -15,6 +15,7 @@
#include <sys/epoll.h>
#include <map>
#include <atomic>
#include <unordered_set>
#include "Defines.h"
#ifdef ANDROID
@ -63,7 +64,7 @@ public:
void pauseNetwork();
void setNetworkAvailable(bool value, int32_t type, bool slow);
void setIpStrategy(uint8_t value);
void init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerprint, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType);
void init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerprint, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType, int32_t performanceClass);
void setProxySettings(std::string address, uint16_t port, std::string username, std::string password, std::string secret);
void setLangCode(std::string langCode);
void setRegId(std::string regId);
@ -205,6 +206,7 @@ private:
requestsList requestsQueue;
requestsList runningRequests;
std::vector<uint32_t> requestingSaltsForDc;
std::unordered_set<int32_t> tokensToBeCancelled;
int32_t lastPingId = 0;
int64_t lastInvokeAfterMessageId = 0;
@ -227,6 +229,7 @@ private:
int64_t currentUserId = 0;
bool registeredForInternalPush = false;
bool pushConnectionEnabled = true;
int32_t currentPerformanceClass = -1;
std::map<uint32_t, std::vector<std::unique_ptr<NetworkMessage>>> genericMessagesToDatacenters;
std::map<uint32_t, std::vector<std::unique_ptr<NetworkMessage>>> genericMediaMessagesToDatacenters;

View File

@ -27,7 +27,7 @@
#define DOWNLOAD_CONNECTIONS_COUNT 2
#define UPLOAD_CONNECTIONS_COUNT 4
#define CONNECTION_BACKGROUND_KEEP_TIME 10000
#define MAX_ACCOUNT_COUNT 16
//#define MAX_ACCOUNT_COUNT 16
#define USE_DELEGATE_HOST_RESOLVE
#define USE_IPV4_ONLY 0

View File

@ -0,0 +1,104 @@
# Copyright (c) 2012, 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.
# ndk-build module definition for the Google Breakpad client library
#
# To use this file, do the following:
#
# 1/ Include this file from your own Android.mk, either directly
# or with through the NDK's import-module function.
#
# 2/ Use the client static library in your project with:
#
# LOCAL_STATIC_LIBRARIES += breakpad_client
#
# 3/ In your source code, include "src/client/linux/exception_handler.h"
# and use the Linux instructions to use it.
#
# This module works with either the STLport or GNU libstdc++, but you need
# to select one in your Application.mk
#
# The top Google Breakpad directory.
# We assume this Android.mk to be under 'android/google_breakpad'
LOCAL_PATH := $(call my-dir)/
# Defube the client library module, as a simple static library that
# exports the right include path / linker flags to its users.
include $(CLEAR_VARS)
LOCAL_MODULE := breakpad_client
LOCAL_CPP_EXTENSION := .cc
# Breakpad uses inline ARM assembly that requires the library
# to be built in ARM mode. Otherwise, the build will fail with
# cryptic assembler messages like:
# Compile++ thumb : google_breakpad_client <= crash_generation_client.cc
# /tmp/cc8aMSoD.s: Assembler messages:
# /tmp/cc8aMSoD.s:132: Error: invalid immediate: 288 is out of range
# /tmp/cc8aMSoD.s:244: Error: invalid immediate: 296 is out of range
LOCAL_ARM_MODE := arm
# List of client source files, directly taken from Makefile.am
LOCAL_SRC_FILES := \
src/client/linux/crash_generation/crash_generation_client.cc \
src/client/linux/handler/exception_handler.cc \
src/client/linux/handler/minidump_descriptor.cc \
src/client/linux/log/log.cc \
src/client/linux/dump_writer_common/thread_info.cc \
src/client/linux/dump_writer_common/seccomp_unwinder.cc \
src/client/linux/dump_writer_common/ucontext_reader.cc \
src/client/linux/microdump_writer/microdump_writer.cc \
src/client/linux/minidump_writer/linux_dumper.cc \
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
src/client/linux/minidump_writer/minidump_writer.cc \
src/client/minidump_file_writer.cc \
src/common/android/breakpad_getcontext.S \
src/common/convert_UTF.c \
src/common/md5.cc \
src/common/string_conversion.cc \
src/common/linux/elfutils.cc \
src/common/linux/file_id.cc \
src/common/linux/guid_creator.cc \
src/common/linux/linux_libc_support.cc \
src/common/linux/memory_mapped_file.cc \
src/common/linux/safe_readlink.cc
LOCAL_C_INCLUDES := $(LOCAL_PATH)/src/common/android/include \
$(LOCAL_PATH)/src
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_EXPORT_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)
# Done.

View File

@ -0,0 +1,57 @@
// Copyright (c) 2009, 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 BREAKPAD_GOOGLETEST_INCLUDES_H__
#define BREAKPAD_GOOGLETEST_INCLUDES_H__
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/include/gmock/gmock.h"
// If AddressSanitizer is used, NULL pointer dereferences generate SIGILL
// (illegal instruction) instead of SIGSEGV (segmentation fault). Also,
// the number of memory regions differs, so there is no point in running
// this test if AddressSanitizer is used.
//
// Ideally we'd use this attribute to disable ASAN on a per-func basis,
// but this doesn't seem to actually work, and it's changed names over
// time. So just stick with disabling the actual tests.
// http://crbug.com/304575
//#define NO_ASAN __attribute__((no_sanitize_address))
#if defined(__clang__) && defined(__has_feature)
// Have to keep this check sep from above as newer gcc will barf on it.
# if __has_feature(address_sanitizer)
# define ADDRESS_SANITIZER
# endif
#elif defined(__GNUC__) && defined(__SANITIZE_ADDRESS__)
# define ADDRESS_SANITIZER
#else
# undef ADDRESS_SANITIZER
#endif
#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__

View File

@ -0,0 +1,53 @@
// Copyright (c) 2010 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 CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
namespace google_breakpad {
class CrashGenerationServer;
class ClientInfo {
public:
ClientInfo(pid_t pid, CrashGenerationServer* crash_server)
: crash_server_(crash_server),
pid_(pid) {}
CrashGenerationServer* crash_server() const { return crash_server_; }
pid_t pid() const { return pid_; }
private:
CrashGenerationServer* crash_server_;
pid_t pid_;
};
}
#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_

View File

@ -0,0 +1,105 @@
// Copyright (c) 2010 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 "client/linux/crash_generation/crash_generation_client.h"
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <algorithm>
#include "common/linux/eintr_wrapper.h"
#include "common/linux/ignore_ret.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
namespace {
class CrashGenerationClientImpl : public CrashGenerationClient {
public:
explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {}
virtual ~CrashGenerationClientImpl() {}
virtual bool RequestDump(const void* blob, size_t blob_size) {
int fds[2];
if (sys_pipe(fds) < 0)
return false;
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
struct kernel_iovec iov;
iov.iov_base = const_cast<void*>(blob);
iov.iov_len = blob_size;
struct kernel_msghdr msg = { 0 };
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char cmsg[kControlMsgSize] = "";
msg.msg_control = cmsg;
msg.msg_controllen = sizeof(cmsg);
struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
hdr->cmsg_level = SOL_SOCKET;
hdr->cmsg_type = SCM_RIGHTS;
hdr->cmsg_len = CMSG_LEN(sizeof(int));
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
*p = fds[1];
ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
sys_close(fds[1]);
if (ret < 0) {
sys_close(fds[0]);
return false;
}
// Wait for an ACK from the server.
char b;
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
sys_close(fds[0]);
return true;
}
private:
int server_fd_;
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClientImpl);
};
} // namespace
// static
CrashGenerationClient* CrashGenerationClient::TryCreate(int server_fd) {
if (server_fd < 0)
return NULL;
return new CrashGenerationClientImpl(server_fd);
}
} // namespace google_breakpad

View File

@ -0,0 +1,65 @@
// Copyright (c) 2010 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 CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#include "common/basictypes.h"
#include <stddef.h>
namespace google_breakpad {
// CrashGenerationClient is an interface for implementing out-of-process crash
// dumping. The default implementation, accessed via the TryCreate() factory,
// works in conjunction with the CrashGenerationServer to generate a minidump
// via a remote process.
class CrashGenerationClient {
public:
CrashGenerationClient() {}
virtual ~CrashGenerationClient() {}
// Request the crash server to generate a dump. |blob| is an opaque
// CrashContext pointer from exception_handler.h.
// Returns true if the dump was successful; false otherwise.
virtual bool RequestDump(const void* blob, size_t blob_size) = 0;
// Returns a new CrashGenerationClient if |server_fd| is valid and
// connects to a CrashGenerationServer. Otherwise, return NULL.
// The returned CrashGenerationClient* is owned by the caller of
// this function.
static CrashGenerationClient* TryCreate(int server_fd);
private:
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_

View File

@ -0,0 +1,330 @@
// Copyright (c) 2010 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 <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include "client/linux/crash_generation/crash_generation_server.h"
#include "client/linux/crash_generation/client_info.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/guid_creator.h"
#include "common/linux/safe_readlink.h"
static const char kCommandQuit = 'x';
namespace google_breakpad {
CrashGenerationServer::CrashGenerationServer(
const int listen_fd,
OnClientDumpRequestCallback dump_callback,
void* dump_context,
OnClientExitingCallback exit_callback,
void* exit_context,
bool generate_dumps,
const string* dump_path) :
server_fd_(listen_fd),
dump_callback_(dump_callback),
dump_context_(dump_context),
exit_callback_(exit_callback),
exit_context_(exit_context),
generate_dumps_(generate_dumps),
started_(false)
{
if (dump_path)
dump_dir_ = *dump_path;
else
dump_dir_ = "/tmp";
}
CrashGenerationServer::~CrashGenerationServer()
{
if (started_)
Stop();
}
bool
CrashGenerationServer::Start()
{
if (started_ || 0 > server_fd_)
return false;
int control_pipe[2];
if (pipe(control_pipe))
return false;
if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
return false;
if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
return false;
if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
return false;
control_pipe_in_ = control_pipe[0];
control_pipe_out_ = control_pipe[1];
if (pthread_create(&thread_, NULL,
ThreadMain, reinterpret_cast<void*>(this)))
return false;
started_ = true;
return true;
}
void
CrashGenerationServer::Stop()
{
assert(pthread_self() != thread_);
if (!started_)
return;
HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
void* dummy;
pthread_join(thread_, &dummy);
started_ = false;
}
//static
bool
CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
{
int fds[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
return false;
static const int on = 1;
// Enable passcred on the server end of the socket
if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
return false;
if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
return false;
if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
return false;
*client_fd = fds[0];
*server_fd = fds[1];
return true;
}
// The following methods/functions execute on the server thread
void
CrashGenerationServer::Run()
{
struct pollfd pollfds[2];
memset(&pollfds, 0, sizeof(pollfds));
pollfds[0].fd = server_fd_;
pollfds[0].events = POLLIN;
pollfds[1].fd = control_pipe_in_;
pollfds[1].events = POLLIN;
while (true) {
// infinite timeout
int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
if (-1 == nevents) {
if (EINTR == errno) {
continue;
} else {
return;
}
}
if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
return;
if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
return;
}
}
bool
CrashGenerationServer::ClientEvent(short revents)
{
if (POLLHUP & revents)
return false;
assert(POLLIN & revents);
// A process has crashed and has signaled us by writing a datagram
// to the death signal socket. The datagram contains the crash context needed
// for writing the minidump as well as a file descriptor and a credentials
// block so that they can't lie about their pid.
// The length of the control message:
static const unsigned kControlMsgSize =
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
// The length of the regular payload:
static const unsigned kCrashContextSize =
sizeof(google_breakpad::ExceptionHandler::CrashContext);
struct msghdr msg = {0};
struct iovec iov[1];
char crash_context[kCrashContextSize];
char control[kControlMsgSize];
const ssize_t expected_msg_size = sizeof(crash_context);
iov[0].iov_base = crash_context;
iov[0].iov_len = sizeof(crash_context);
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
msg.msg_control = control;
msg.msg_controllen = kControlMsgSize;
const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
if (msg_size != expected_msg_size)
return true;
if (msg.msg_controllen != kControlMsgSize ||
msg.msg_flags & ~MSG_TRUNC)
return true;
// Walk the control payload and extract the file descriptor and validated pid.
pid_t crashing_pid = -1;
int signal_fd = -1;
for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
hdr = CMSG_NXTHDR(&msg, hdr)) {
if (hdr->cmsg_level != SOL_SOCKET)
continue;
if (hdr->cmsg_type == SCM_RIGHTS) {
const unsigned len = hdr->cmsg_len -
(((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
assert(len % sizeof(int) == 0u);
const unsigned num_fds = len / sizeof(int);
if (num_fds > 1 || num_fds == 0) {
// A nasty process could try and send us too many descriptors and
// force a leak.
for (unsigned i = 0; i < num_fds; ++i)
close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
return true;
} else {
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
}
} else if (hdr->cmsg_type == SCM_CREDENTIALS) {
const struct ucred *cred =
reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
crashing_pid = cred->pid;
}
}
if (crashing_pid == -1 || signal_fd == -1) {
if (signal_fd)
close(signal_fd);
return true;
}
string minidump_filename;
if (!MakeMinidumpFilename(minidump_filename))
return true;
if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
crashing_pid, crash_context,
kCrashContextSize)) {
close(signal_fd);
return true;
}
if (dump_callback_) {
ClientInfo info(crashing_pid, this);
dump_callback_(dump_context_, &info, &minidump_filename);
}
// Send the done signal to the process: it can exit now.
// (Closing this will make the child's sys_read unblock and return 0.)
close(signal_fd);
return true;
}
bool
CrashGenerationServer::ControlEvent(short revents)
{
if (POLLHUP & revents)
return false;
assert(POLLIN & revents);
char command;
if (read(control_pipe_in_, &command, 1))
return false;
switch (command) {
case kCommandQuit:
return false;
default:
assert(0);
}
return true;
}
bool
CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
{
GUID guid;
char guidString[kGUIDStringLength+1];
if (!(CreateGUID(&guid)
&& GUIDToString(&guid, guidString, sizeof(guidString))))
return false;
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
outFilename = path;
return true;
}
// static
void*
CrashGenerationServer::ThreadMain(void *arg)
{
reinterpret_cast<CrashGenerationServer*>(arg)->Run();
return NULL;
}
} // namespace google_breakpad

View File

@ -0,0 +1,135 @@
// Copyright (c) 2010 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 CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#include <pthread.h>
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
class ClientInfo;
class CrashGenerationServer {
public:
// WARNING: callbacks may be invoked on a different thread
// than that which creates the CrashGenerationServer. They must
// be thread safe.
typedef void (*OnClientDumpRequestCallback)(void* context,
const ClientInfo* client_info,
const string* file_path);
typedef void (*OnClientExitingCallback)(void* context,
const ClientInfo* client_info);
// Create an instance with the given parameters.
//
// Parameter listen_fd: The server fd created by CreateReportChannel().
// Parameter dump_callback: Callback for a client crash dump request.
// Parameter dump_context: Context for client crash dump request callback.
// Parameter exit_callback: Callback for client process exit.
// Parameter exit_context: Context for client exit callback.
// Parameter generate_dumps: Whether to automatically generate dumps.
// Client code of this class might want to generate dumps explicitly
// in the crash dump request callback. In that case, false can be
// passed for this parameter.
// Parameter dump_path: Path for generating dumps; required only if true is
// passed for generateDumps parameter; NULL can be passed otherwise.
CrashGenerationServer(const int listen_fd,
OnClientDumpRequestCallback dump_callback,
void* dump_context,
OnClientExitingCallback exit_callback,
void* exit_context,
bool generate_dumps,
const string* dump_path);
~CrashGenerationServer();
// Perform initialization steps needed to start listening to clients.
//
// Return true if initialization is successful; false otherwise.
bool Start();
// Stop the server.
void Stop();
// Create a "channel" that can be used by clients to report crashes
// to a CrashGenerationServer. |*server_fd| should be passed to
// this class's constructor, and |*client_fd| should be passed to
// the ExceptionHandler constructor in the client process.
static bool CreateReportChannel(int* server_fd, int* client_fd);
private:
// Run the server's event loop
void Run();
// Invoked when an child process (client) event occurs
// Returning true => "keep running", false => "exit loop"
bool ClientEvent(short revents);
// Invoked when the controlling thread (main) event occurs
// Returning true => "keep running", false => "exit loop"
bool ControlEvent(short revents);
// Return a unique filename at which a minidump can be written
bool MakeMinidumpFilename(string& outFilename);
// Trampoline to |Run()|
static void* ThreadMain(void* arg);
int server_fd_;
OnClientDumpRequestCallback dump_callback_;
void* dump_context_;
OnClientExitingCallback exit_callback_;
void* exit_context_;
bool generate_dumps_;
string dump_dir_;
bool started_;
pthread_t thread_;
int control_pipe_in_;
int control_pipe_out_;
// disable these
CrashGenerationServer(const CrashGenerationServer&);
CrashGenerationServer& operator=(const CrashGenerationServer&);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_

View File

@ -0,0 +1,3 @@
MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so
PUBLIC 400 0 __kernel_vsyscall
STACK WIN 4 400 100 1 1 0 0 0 0 0 1

View File

@ -0,0 +1,3 @@
MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so
PUBLIC 400 0 __kernel_vsyscall
STACK WIN 4 400 200 3 3 0 0 0 0 0 1

View File

@ -0,0 +1,61 @@
// Copyright (c) 2014, 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 CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
#define CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
#include <limits.h>
#include <list>
#include <stdint.h>
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
// One of these is produced for each mapping in the process (i.e. line in
// /proc/$x/maps).
struct MappingInfo {
uintptr_t start_addr;
size_t size;
size_t offset; // offset into the backed file.
bool exec; // true if the mapping has the execute bit set.
char name[NAME_MAX];
};
struct MappingEntry {
MappingInfo first;
uint8_t second[sizeof(MDGUID)];
};
// A list of <MappingInfo, GUID>
typedef std::list<MappingEntry> MappingList;
} // namespace google_breakpad
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_

View File

@ -0,0 +1,53 @@
// Copyright (c) 2014, 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 CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
#define CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
#if defined(__i386__)
typedef MDRawContextX86 RawContextCPU;
#elif defined(__x86_64)
typedef MDRawContextAMD64 RawContextCPU;
#elif defined(__ARM_EABI__)
typedef MDRawContextARM RawContextCPU;
#elif defined(__aarch64__)
typedef MDRawContextARM64 RawContextCPU;
#elif defined(__mips__)
typedef MDRawContextMIPS RawContextCPU;
#else
#error "This code has not been ported to your platform yet."
#endif
} // namespace google_breakpad
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H

View File

@ -0,0 +1,154 @@
// Copyright (c) 2014, 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 "client/linux/dump_writer_common/seccomp_unwinder.h"
#include <string.h>
#include "google_breakpad/common/minidump_format.h"
#include "common/linux/linux_libc_support.h"
namespace google_breakpad {
void SeccompUnwinder::PopSeccompStackFrame(RawContextCPU* cpu,
const MDRawThread& thread,
uint8_t* stack_copy) {
#if defined(__x86_64)
uint64_t bp = cpu->rbp;
uint64_t top = thread.stack.start_of_memory_range;
for (int i = 4; i--; ) {
if (bp < top ||
bp > thread.stack.start_of_memory_range +
thread.stack.memory.data_size - sizeof(bp) ||
bp & 1) {
break;
}
uint64_t old_top = top;
top = bp;
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
my_memcpy(&bp, bp_addr, sizeof(bp));
if (bp == 0xDEADBEEFDEADBEEFull) {
struct {
uint64_t r15;
uint64_t r14;
uint64_t r13;
uint64_t r12;
uint64_t r11;
uint64_t r10;
uint64_t r9;
uint64_t r8;
uint64_t rdi;
uint64_t rsi;
uint64_t rdx;
uint64_t rcx;
uint64_t rbx;
uint64_t deadbeef;
uint64_t rbp;
uint64_t fakeret;
uint64_t ret;
/* char redzone[128]; */
} seccomp_stackframe;
if (top - offsetof(__typeof__(seccomp_stackframe), deadbeef) < old_top ||
top - offsetof(__typeof__(seccomp_stackframe), deadbeef) +
sizeof(seccomp_stackframe) >
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
break;
}
my_memcpy(&seccomp_stackframe,
bp_addr - offsetof(__typeof__(seccomp_stackframe), deadbeef),
sizeof(seccomp_stackframe));
cpu->rbx = seccomp_stackframe.rbx;
cpu->rcx = seccomp_stackframe.rcx;
cpu->rdx = seccomp_stackframe.rdx;
cpu->rsi = seccomp_stackframe.rsi;
cpu->rdi = seccomp_stackframe.rdi;
cpu->rbp = seccomp_stackframe.rbp;
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
cpu->r8 = seccomp_stackframe.r8;
cpu->r9 = seccomp_stackframe.r9;
cpu->r10 = seccomp_stackframe.r10;
cpu->r11 = seccomp_stackframe.r11;
cpu->r12 = seccomp_stackframe.r12;
cpu->r13 = seccomp_stackframe.r13;
cpu->r14 = seccomp_stackframe.r14;
cpu->r15 = seccomp_stackframe.r15;
cpu->rip = seccomp_stackframe.fakeret;
return;
}
}
#elif defined(__i386__)
uint32_t bp = cpu->ebp;
uint32_t top = thread.stack.start_of_memory_range;
for (int i = 4; i--; ) {
if (bp < top ||
bp > thread.stack.start_of_memory_range +
thread.stack.memory.data_size - sizeof(bp) ||
bp & 1) {
break;
}
uint32_t old_top = top;
top = bp;
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
my_memcpy(&bp, bp_addr, sizeof(bp));
if (bp == 0xDEADBEEFu) {
struct {
uint32_t edi;
uint32_t esi;
uint32_t edx;
uint32_t ecx;
uint32_t ebx;
uint32_t deadbeef;
uint32_t ebp;
uint32_t fakeret;
uint32_t ret;
} seccomp_stackframe;
if (top - offsetof(__typeof__(seccomp_stackframe), deadbeef) < old_top ||
top - offsetof(__typeof__(seccomp_stackframe), deadbeef) +
sizeof(seccomp_stackframe) >
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
break;
}
my_memcpy(&seccomp_stackframe,
bp_addr - offsetof(__typeof__(seccomp_stackframe), deadbeef),
sizeof(seccomp_stackframe));
cpu->ebx = seccomp_stackframe.ebx;
cpu->ecx = seccomp_stackframe.ecx;
cpu->edx = seccomp_stackframe.edx;
cpu->esi = seccomp_stackframe.esi;
cpu->edi = seccomp_stackframe.edi;
cpu->ebp = seccomp_stackframe.ebp;
cpu->esp = top + 4*sizeof(void*);
cpu->eip = seccomp_stackframe.fakeret;
return;
}
}
#endif
}
} // namespace google_breakpad

View File

@ -0,0 +1,50 @@
// Copyright (c) 2014, 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 CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
#define CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
#include "client/linux/dump_writer_common/raw_context_cpu.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
struct SeccompUnwinder {
// Check if the top of the stack is part of a system call that has been
// redirected by the seccomp sandbox. If so, try to pop the stack frames
// all the way back to the point where the interception happened.
static void PopSeccompStackFrame(RawContextCPU* cpu,
const MDRawThread& thread,
uint8_t* stack_copy);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H

View File

@ -0,0 +1,299 @@
// Copyright (c) 2014, 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 "client/linux/dump_writer_common/thread_info.h"
#include <string.h>
#include <assert.h>
#include "common/linux/linux_libc_support.h"
#include "google_breakpad/common/minidump_format.h"
namespace {
#if defined(__i386__)
// Write a uint16_t to memory
// out: memory location to write to
// v: value to write.
void U16(void* out, uint16_t v) {
my_memcpy(out, &v, sizeof(v));
}
// Write a uint32_t to memory
// out: memory location to write to
// v: value to write.
void U32(void* out, uint32_t v) {
my_memcpy(out, &v, sizeof(v));
}
#endif
}
namespace google_breakpad {
#if defined(__i386__)
uintptr_t ThreadInfo::GetInstructionPointer() const {
return regs.eip;
}
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
out->context_flags = MD_CONTEXT_X86_ALL;
out->dr0 = dregs[0];
out->dr1 = dregs[1];
out->dr2 = dregs[2];
out->dr3 = dregs[3];
// 4 and 5 deliberatly omitted because they aren't included in the minidump
// format.
out->dr6 = dregs[6];
out->dr7 = dregs[7];
out->gs = regs.xgs;
out->fs = regs.xfs;
out->es = regs.xes;
out->ds = regs.xds;
out->edi = regs.edi;
out->esi = regs.esi;
out->ebx = regs.ebx;
out->edx = regs.edx;
out->ecx = regs.ecx;
out->eax = regs.eax;
out->ebp = regs.ebp;
out->eip = regs.eip;
out->cs = regs.xcs;
out->eflags = regs.eflags;
out->esp = regs.esp;
out->ss = regs.xss;
out->float_save.control_word = fpregs.cwd;
out->float_save.status_word = fpregs.swd;
out->float_save.tag_word = fpregs.twd;
out->float_save.error_offset = fpregs.fip;
out->float_save.error_selector = fpregs.fcs;
out->float_save.data_offset = fpregs.foo;
out->float_save.data_selector = fpregs.fos;
// 8 registers * 10 bytes per register.
my_memcpy(out->float_save.register_area, fpregs.st_space, 10 * 8);
// This matches the Intel fpsave format.
U16(out->extended_registers + 0, fpregs.cwd);
U16(out->extended_registers + 2, fpregs.swd);
U16(out->extended_registers + 4, fpregs.twd);
U16(out->extended_registers + 6, fpxregs.fop);
U32(out->extended_registers + 8, fpxregs.fip);
U16(out->extended_registers + 12, fpxregs.fcs);
U32(out->extended_registers + 16, fpregs.foo);
U16(out->extended_registers + 20, fpregs.fos);
U32(out->extended_registers + 24, fpxregs.mxcsr);
my_memcpy(out->extended_registers + 32, &fpxregs.st_space, 128);
my_memcpy(out->extended_registers + 160, &fpxregs.xmm_space, 128);
}
#elif defined(__x86_64)
uintptr_t ThreadInfo::GetInstructionPointer() const {
return regs.rip;
}
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
out->context_flags = MD_CONTEXT_AMD64_FULL |
MD_CONTEXT_AMD64_SEGMENTS;
out->cs = regs.cs;
out->ds = regs.ds;
out->es = regs.es;
out->fs = regs.fs;
out->gs = regs.gs;
out->ss = regs.ss;
out->eflags = regs.eflags;
out->dr0 = dregs[0];
out->dr1 = dregs[1];
out->dr2 = dregs[2];
out->dr3 = dregs[3];
// 4 and 5 deliberatly omitted because they aren't included in the minidump
// format.
out->dr6 = dregs[6];
out->dr7 = dregs[7];
out->rax = regs.rax;
out->rcx = regs.rcx;
out->rdx = regs.rdx;
out->rbx = regs.rbx;
out->rsp = regs.rsp;
out->rbp = regs.rbp;
out->rsi = regs.rsi;
out->rdi = regs.rdi;
out->r8 = regs.r8;
out->r9 = regs.r9;
out->r10 = regs.r10;
out->r11 = regs.r11;
out->r12 = regs.r12;
out->r13 = regs.r13;
out->r14 = regs.r14;
out->r15 = regs.r15;
out->rip = regs.rip;
out->flt_save.control_word = fpregs.cwd;
out->flt_save.status_word = fpregs.swd;
out->flt_save.tag_word = fpregs.ftw;
out->flt_save.error_opcode = fpregs.fop;
out->flt_save.error_offset = fpregs.rip;
out->flt_save.error_selector = 0; // We don't have this.
out->flt_save.data_offset = fpregs.rdp;
out->flt_save.data_selector = 0; // We don't have this.
out->flt_save.mx_csr = fpregs.mxcsr;
out->flt_save.mx_csr_mask = fpregs.mxcr_mask;
my_memcpy(&out->flt_save.float_registers, &fpregs.st_space, 8 * 16);
my_memcpy(&out->flt_save.xmm_registers, &fpregs.xmm_space, 16 * 16);
}
#elif defined(__ARM_EABI__)
uintptr_t ThreadInfo::GetInstructionPointer() const {
return regs.uregs[15];
}
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
out->context_flags = MD_CONTEXT_ARM_FULL;
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
out->iregs[i] = regs.uregs[i];
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
out->cpsr = 0;
#if !defined(__ANDROID__)
out->float_save.fpscr = fpregs.fpsr |
(static_cast<uint64_t>(fpregs.fpcr) << 32);
// TODO: sort this out, actually collect floating point registers
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
#endif
}
#elif defined(__aarch64__)
uintptr_t ThreadInfo::GetInstructionPointer() const {
return regs.pc;
}
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
out->context_flags = MD_CONTEXT_ARM64_FULL;
out->cpsr = static_cast<uint32_t>(regs.pstate);
for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
out->iregs[i] = regs.regs[i];
out->iregs[MD_CONTEXT_ARM64_REG_SP] = regs.sp;
out->iregs[MD_CONTEXT_ARM64_REG_PC] = regs.pc;
out->float_save.fpsr = fpregs.fpsr;
out->float_save.fpcr = fpregs.fpcr;
my_memcpy(&out->float_save.regs, &fpregs.vregs,
MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
}
#elif defined(__mips__)
uintptr_t ThreadInfo::GetInstructionPointer() const {
return mcontext.pc;
}
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
out->context_flags = MD_CONTEXT_MIPS_FULL;
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
out->iregs[i] = mcontext.gregs[i];
out->mdhi = mcontext.mdhi;
out->mdlo = mcontext.mdlo;
out->dsp_control = mcontext.dsp;
out->hi[0] = mcontext.hi1;
out->lo[0] = mcontext.lo1;
out->hi[1] = mcontext.hi2;
out->lo[1] = mcontext.lo2;
out->hi[2] = mcontext.hi3;
out->lo[2] = mcontext.lo3;
out->epc = mcontext.pc;
out->badvaddr = 0; // Not stored in mcontext
out->status = 0; // Not stored in mcontext
out->cause = 0; // Not stored in mcontext
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
out->float_save.regs[i] = mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs;
out->float_save.fpcsr = mcontext.fpc_csr;
#if _MIPS_SIM == _ABIO32
out->float_save.fir = mcontext.fpc_eir;
#endif
}
#endif // __mips__
void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) {
assert(gp_regs || size);
#if defined(__mips__)
if (gp_regs)
*gp_regs = mcontext.gregs;
if (size)
*size = sizeof(mcontext.gregs);
#else
if (gp_regs)
*gp_regs = &regs;
if (size)
*size = sizeof(regs);
#endif
}
void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) {
assert(fp_regs || size);
#if defined(__mips__)
if (fp_regs)
*fp_regs = &mcontext.fpregs;
if (size)
*size = sizeof(mcontext.fpregs);
#else
if (fp_regs)
*fp_regs = &fpregs;
if (size)
*size = sizeof(fpregs);
#endif
}
} // namespace google_breakpad

View File

@ -0,0 +1,91 @@
// Copyright (c) 2014, 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 CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
#define CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
#include <sys/ucontext.h>
#include <sys/user.h>
#include "client/linux/dump_writer_common/raw_context_cpu.h"
#include "common/memory.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
#if defined(__i386) || defined(__x86_64)
typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t;
#endif
// We produce one of these structures for each thread in the crashed process.
struct ThreadInfo {
pid_t tgid; // thread group id
pid_t ppid; // parent process
uintptr_t stack_pointer; // thread stack pointer
#if defined(__i386) || defined(__x86_64)
user_regs_struct regs;
user_fpregs_struct fpregs;
static const unsigned kNumDebugRegisters = 8;
debugreg_t dregs[8];
#if defined(__i386)
user_fpxregs_struct fpxregs;
#endif // defined(__i386)
#elif defined(__ARM_EABI__)
// Mimicking how strace does this(see syscall.c, search for GETREGS)
struct user_regs regs;
struct user_fpregs fpregs;
#elif defined(__aarch64__)
// Use the structures defined in <asm/ptrace.h>
struct user_pt_regs regs;
struct user_fpsimd_state fpregs;
#elif defined(__mips__)
// Use the structure defined in <sys/ucontext.h>.
mcontext_t mcontext;
#endif
// Returns the instruction pointer (platform-dependent impl.).
uintptr_t GetInstructionPointer() const;
// Fills a RawContextCPU using the context in the ThreadInfo object.
void FillCPUContext(RawContextCPU* out) const;
// Returns the pointer and size of general purpose register area.
void GetGeneralPurposeRegisters(void** gp_regs, size_t* size);
// Returns the pointer and size of float point register area.
void GetFloatingPointRegisters(void** fp_regs, size_t* size);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_

View File

@ -0,0 +1,253 @@
// Copyright (c) 2014, 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 "client/linux/dump_writer_common/ucontext_reader.h"
#include "common/linux/linux_libc_support.h"
#include "google_breakpad/common/minidump_format.h"
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.
#if defined(__i386__)
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
return uc->uc_mcontext.gregs[REG_ESP];
}
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
return uc->uc_mcontext.gregs[REG_EIP];
}
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
const struct _libc_fpstate* fp) {
const greg_t* regs = uc->uc_mcontext.gregs;
out->context_flags = MD_CONTEXT_X86_FULL |
MD_CONTEXT_X86_FLOATING_POINT;
out->gs = regs[REG_GS];
out->fs = regs[REG_FS];
out->es = regs[REG_ES];
out->ds = regs[REG_DS];
out->edi = regs[REG_EDI];
out->esi = regs[REG_ESI];
out->ebx = regs[REG_EBX];
out->edx = regs[REG_EDX];
out->ecx = regs[REG_ECX];
out->eax = regs[REG_EAX];
out->ebp = regs[REG_EBP];
out->eip = regs[REG_EIP];
out->cs = regs[REG_CS];
out->eflags = regs[REG_EFL];
out->esp = regs[REG_UESP];
out->ss = regs[REG_SS];
out->float_save.control_word = fp->cw;
out->float_save.status_word = fp->sw;
out->float_save.tag_word = fp->tag;
out->float_save.error_offset = fp->ipoff;
out->float_save.error_selector = fp->cssel;
out->float_save.data_offset = fp->dataoff;
out->float_save.data_selector = fp->datasel;
// 8 registers * 10 bytes per register.
my_memcpy(out->float_save.register_area, fp->_st, 10 * 8);
}
#elif defined(__x86_64)
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
return uc->uc_mcontext.gregs[REG_RSP];
}
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
return uc->uc_mcontext.gregs[REG_RIP];
}
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
const struct _libc_fpstate* fpregs) {
const greg_t* regs = uc->uc_mcontext.gregs;
out->context_flags = MD_CONTEXT_AMD64_FULL;
out->cs = regs[REG_CSGSFS] & 0xffff;
out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
out->eflags = regs[REG_EFL];
out->rax = regs[REG_RAX];
out->rcx = regs[REG_RCX];
out->rdx = regs[REG_RDX];
out->rbx = regs[REG_RBX];
out->rsp = regs[REG_RSP];
out->rbp = regs[REG_RBP];
out->rsi = regs[REG_RSI];
out->rdi = regs[REG_RDI];
out->r8 = regs[REG_R8];
out->r9 = regs[REG_R9];
out->r10 = regs[REG_R10];
out->r11 = regs[REG_R11];
out->r12 = regs[REG_R12];
out->r13 = regs[REG_R13];
out->r14 = regs[REG_R14];
out->r15 = regs[REG_R15];
out->rip = regs[REG_RIP];
out->flt_save.control_word = fpregs->cwd;
out->flt_save.status_word = fpregs->swd;
out->flt_save.tag_word = fpregs->ftw;
out->flt_save.error_opcode = fpregs->fop;
out->flt_save.error_offset = fpregs->rip;
out->flt_save.data_offset = fpregs->rdp;
out->flt_save.error_selector = 0; // We don't have this.
out->flt_save.data_selector = 0; // We don't have this.
out->flt_save.mx_csr = fpregs->mxcsr;
out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
my_memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
my_memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
}
#elif defined(__ARM_EABI__)
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
return uc->uc_mcontext.arm_sp;
}
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
return uc->uc_mcontext.arm_pc;
}
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
out->context_flags = MD_CONTEXT_ARM_FULL;
out->iregs[0] = uc->uc_mcontext.arm_r0;
out->iregs[1] = uc->uc_mcontext.arm_r1;
out->iregs[2] = uc->uc_mcontext.arm_r2;
out->iregs[3] = uc->uc_mcontext.arm_r3;
out->iregs[4] = uc->uc_mcontext.arm_r4;
out->iregs[5] = uc->uc_mcontext.arm_r5;
out->iregs[6] = uc->uc_mcontext.arm_r6;
out->iregs[7] = uc->uc_mcontext.arm_r7;
out->iregs[8] = uc->uc_mcontext.arm_r8;
out->iregs[9] = uc->uc_mcontext.arm_r9;
out->iregs[10] = uc->uc_mcontext.arm_r10;
out->iregs[11] = uc->uc_mcontext.arm_fp;
out->iregs[12] = uc->uc_mcontext.arm_ip;
out->iregs[13] = uc->uc_mcontext.arm_sp;
out->iregs[14] = uc->uc_mcontext.arm_lr;
out->iregs[15] = uc->uc_mcontext.arm_pc;
out->cpsr = uc->uc_mcontext.arm_cpsr;
// TODO: fix this after fixing ExceptionHandler
out->float_save.fpscr = 0;
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
}
#elif defined(__aarch64__)
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
return uc->uc_mcontext.sp;
}
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
return uc->uc_mcontext.pc;
}
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
const struct fpsimd_context* fpregs) {
out->context_flags = MD_CONTEXT_ARM64_FULL;
out->cpsr = static_cast<uint32_t>(uc->uc_mcontext.pstate);
for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
out->iregs[i] = uc->uc_mcontext.regs[i];
out->iregs[MD_CONTEXT_ARM64_REG_SP] = uc->uc_mcontext.sp;
out->iregs[MD_CONTEXT_ARM64_REG_PC] = uc->uc_mcontext.pc;
out->float_save.fpsr = fpregs->fpsr;
out->float_save.fpcr = fpregs->fpcr;
my_memcpy(&out->float_save.regs, &fpregs->vregs,
MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
}
#elif defined(__mips__)
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
return uc->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP];
}
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
return uc->uc_mcontext.pc;
}
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
out->context_flags = MD_CONTEXT_MIPS_FULL;
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
out->iregs[i] = uc->uc_mcontext.gregs[i];
out->mdhi = uc->uc_mcontext.mdhi;
out->mdlo = uc->uc_mcontext.mdlo;
out->hi[0] = uc->uc_mcontext.hi1;
out->hi[1] = uc->uc_mcontext.hi2;
out->hi[2] = uc->uc_mcontext.hi3;
out->lo[0] = uc->uc_mcontext.lo1;
out->lo[1] = uc->uc_mcontext.lo2;
out->lo[2] = uc->uc_mcontext.lo3;
out->dsp_control = uc->uc_mcontext.dsp;
out->epc = uc->uc_mcontext.pc;
out->badvaddr = 0; // Not reported in signal context.
out->status = 0; // Not reported in signal context.
out->cause = 0; // Not reported in signal context.
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i];
out->float_save.fpcsr = uc->uc_mcontext.fpc_csr;
#if _MIPS_SIM == _ABIO32
out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused.
#endif
}
#endif
} // namespace google_breakpad

View File

@ -0,0 +1,64 @@
// Copyright (c) 2014, 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 CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
#define CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
#include <sys/ucontext.h>
#include <sys/user.h>
#include "client/linux/dump_writer_common/raw_context_cpu.h"
#include "common/memory.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
// Wraps platform-dependent implementations of accessors to ucontext structs.
struct UContextReader {
static uintptr_t GetStackPointer(const struct ucontext* uc);
static uintptr_t GetInstructionPointer(const struct ucontext* uc);
// Juggle a arch-specific ucontext into a minidump format
// out: the minidump structure
// info: the collection of register structures.
#if defined(__i386__) || defined(__x86_64)
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
const struct _libc_fpstate* fp);
#elif defined(__aarch64__)
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
const struct fpsimd_context* fpregs);
#else
static void FillCPUContext(RawContextCPU *out, const ucontext *uc);
#endif
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H

View File

@ -0,0 +1,743 @@
// Copyright (c) 2010 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.
// The ExceptionHandler object installs signal handlers for a number of
// signals. We rely on the signal handler running on the thread which crashed
// in order to identify it. This is true of the synchronous signals (SEGV etc),
// but not true of ABRT. Thus, if you send ABRT to yourself in a program which
// uses ExceptionHandler, you need to use tgkill to direct it to the current
// thread.
//
// The signal flow looks like this:
//
// SignalHandler (uses a global stack of ExceptionHandler objects to find
// | one to handle the signal. If the first rejects it, try
// | the second etc...)
// V
// HandleSignal ----------------------------| (clones a new process which
// | | shares an address space with
// (wait for cloned | the crashed process. This
// process) | allows us to ptrace the crashed
// | | process)
// V V
// (set signal handler to ThreadEntry (static function to bounce
// SIG_DFL and rethrow, | back into the object)
// killing the crashed |
// process) V
// DoDump (writes minidump)
// |
// V
// sys_exit
//
// This code is a little fragmented. Different functions of the ExceptionHandler
// class run in a number of different contexts. Some of them run in a normal
// context and are easy to code, others run in a compromised context and the
// restrictions at the top of minidump_writer.cc apply: no libc and use the
// alternative malloc. Each function should have comment above it detailing the
// context which it runs in.
#include "client/linux/handler/exception_handler.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <ucontext.h>
#include <algorithm>
#include <utility>
#include <vector>
#include "common/basictypes.h"
#include "common/linux/linux_libc_support.h"
#include "common/memory.h"
#include "client/linux/log/log.h"
#include "client/linux/microdump_writer/microdump_writer.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "third_party/lss/linux_syscall_support.h"
#if defined(__ANDROID__)
#include "linux/sched.h"
#endif
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER 0x59616d61
#endif
namespace google_breakpad {
namespace {
// The list of signals which we consider to be crashes. The default action for
// all these signals must be Core (see man 7 signal) because we rethrow the
// signal after handling it and expect that it'll be fatal.
const int kExceptionSignals[] = {
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS
};
const int kNumHandledSignals =
sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
struct sigaction old_handlers[kNumHandledSignals];
bool handlers_installed = false;
// InstallAlternateStackLocked will store the newly installed stack in new_stack
// and (if it exists) the previously installed stack in old_stack.
stack_t old_stack;
stack_t new_stack;
bool stack_installed = false;
// Create an alternative stack to run the signal handlers on. This is done since
// the signal might have been caused by a stack overflow.
// Runs before crashing: normal context.
void InstallAlternateStackLocked() {
if (stack_installed)
return;
memset(&old_stack, 0, sizeof(old_stack));
memset(&new_stack, 0, sizeof(new_stack));
// SIGSTKSZ may be too small to prevent the signal handlers from overrunning
// the alternative stack. Ensure that the size of the alternative stack is
// large enough.
static const unsigned kSigStackSize = std::max(16384, SIGSTKSZ);
// Only set an alternative stack if there isn't already one, or if the current
// one is too small.
if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp ||
old_stack.ss_size < kSigStackSize) {
new_stack.ss_sp = calloc(1, kSigStackSize);
new_stack.ss_size = kSigStackSize;
if (sys_sigaltstack(&new_stack, NULL) == -1) {
free(new_stack.ss_sp);
return;
}
stack_installed = true;
}
}
// Runs before crashing: normal context.
void RestoreAlternateStackLocked() {
if (!stack_installed)
return;
stack_t current_stack;
if (sys_sigaltstack(NULL, &current_stack) == -1)
return;
// Only restore the old_stack if the current alternative stack is the one
// installed by the call to InstallAlternateStackLocked.
if (current_stack.ss_sp == new_stack.ss_sp) {
if (old_stack.ss_sp) {
if (sys_sigaltstack(&old_stack, NULL) == -1)
return;
} else {
stack_t disable_stack;
disable_stack.ss_flags = SS_DISABLE;
if (sys_sigaltstack(&disable_stack, NULL) == -1)
return;
}
}
free(new_stack.ss_sp);
stack_installed = false;
}
void InstallDefaultHandler(int sig) {
#if defined(__ANDROID__)
// Android L+ expose signal and sigaction symbols that override the system
// ones. There is a bug in these functions where a request to set the handler
// to SIG_DFL is ignored. In that case, an infinite loop is entered as the
// signal is repeatedly sent to breakpad's signal handler.
// To work around this, directly call the system's sigaction.
struct kernel_sigaction sa;
memset(&sa, 0, sizeof(sa));
sys_sigemptyset(&sa.sa_mask);
sa.sa_handler_ = SIG_DFL;
sa.sa_flags = SA_RESTART;
sys_rt_sigaction(sig, &sa, NULL, sizeof(kernel_sigset_t));
#else
signal(sig, SIG_DFL);
#endif
}
// The global exception handler stack. This is needed because there may exist
// multiple ExceptionHandler instances in a process. Each will have itself
// registered in this stack.
std::vector<ExceptionHandler*>* g_handler_stack_ = NULL;
pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
} // namespace
// Runs before crashing: normal context.
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
bool install_handler,
const int server_fd)
: filter_(filter),
callback_(callback),
callback_context_(callback_context),
minidump_descriptor_(descriptor),
crash_handler_(NULL) {
if (server_fd >= 0)
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
!minidump_descriptor_.IsMicrodumpOnConsole())
minidump_descriptor_.UpdatePath();
pthread_mutex_lock(&g_handler_stack_mutex_);
if (!g_handler_stack_)
g_handler_stack_ = new std::vector<ExceptionHandler*>;
if (install_handler) {
InstallAlternateStackLocked();
InstallHandlersLocked();
}
g_handler_stack_->push_back(this);
pthread_mutex_unlock(&g_handler_stack_mutex_);
}
// Runs before crashing: normal context.
ExceptionHandler::~ExceptionHandler() {
pthread_mutex_lock(&g_handler_stack_mutex_);
std::vector<ExceptionHandler*>::iterator handler =
std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this);
g_handler_stack_->erase(handler);
if (g_handler_stack_->empty()) {
delete g_handler_stack_;
g_handler_stack_ = NULL;
RestoreAlternateStackLocked();
RestoreHandlersLocked();
}
pthread_mutex_unlock(&g_handler_stack_mutex_);
}
// Runs before crashing: normal context.
// static
bool ExceptionHandler::InstallHandlersLocked() {
if (handlers_installed)
return false;
// Fail if unable to store all the old handlers.
for (int i = 0; i < kNumHandledSignals; ++i) {
if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1)
return false;
}
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
// Mask all exception signals when we're handling one of them.
for (int i = 0; i < kNumHandledSignals; ++i)
sigaddset(&sa.sa_mask, kExceptionSignals[i]);
sa.sa_sigaction = SignalHandler;
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
for (int i = 0; i < kNumHandledSignals; ++i) {
if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) {
// At this point it is impractical to back out changes, and so failure to
// install a signal is intentionally ignored.
}
}
handlers_installed = true;
return true;
}
// This function runs in a compromised context: see the top of the file.
// Runs on the crashing thread.
// static
void ExceptionHandler::RestoreHandlersLocked() {
if (!handlers_installed)
return;
for (int i = 0; i < kNumHandledSignals; ++i) {
if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
InstallDefaultHandler(kExceptionSignals[i]);
}
}
handlers_installed = false;
}
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
// crash_handler_ = callback;
// }
// This function runs in a compromised context: see the top of the file.
// Runs on the crashing thread.
// static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// All the exception signals are blocked at this point.
pthread_mutex_lock(&g_handler_stack_mutex_);
// Sometimes, Breakpad runs inside a process where some other buggy code
// saves and restores signal handlers temporarily with 'signal'
// instead of 'sigaction'. This loses the SA_SIGINFO flag associated
// with this function. As a consequence, the values of 'info' and 'uc'
// become totally bogus, generally inducing a crash.
//
// The following code tries to detect this case. When it does, it
// resets the signal handlers with sigaction + SA_SIGINFO and returns.
// This forces the signal to be thrown again, but this time the kernel
// will call the function with the right arguments.
struct sigaction cur_handler;
if (sigaction(sig, NULL, &cur_handler) == 0 &&
(cur_handler.sa_flags & SA_SIGINFO) == 0) {
// Reset signal handler with the right flags.
sigemptyset(&cur_handler.sa_mask);
sigaddset(&cur_handler.sa_mask, sig);
cur_handler.sa_sigaction = SignalHandler;
cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO;
if (sigaction(sig, &cur_handler, NULL) == -1) {
// When resetting the handler fails, try to reset the
// default one to avoid an infinite loop here.
InstallDefaultHandler(sig);
}
pthread_mutex_unlock(&g_handler_stack_mutex_);
return;
}
bool handled = false;
for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) {
handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc);
}
// Upon returning from this signal handler, sig will become unmasked and then
// it will be retriggered. If one of the ExceptionHandlers handled it
// successfully, restore the default handler. Otherwise, restore the
// previously installed handler. Then, when the signal is retriggered, it will
// be delivered to the appropriate handler.
if (handled) {
InstallDefaultHandler(sig);
} else {
RestoreHandlersLocked();
}
pthread_mutex_unlock(&g_handler_stack_mutex_);
// info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise).
if (info->si_code <= 0 || sig == SIGABRT) {
// This signal was triggered by somebody sending us the signal with kill().
// 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 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.
_exit(1);
}
} else {
// This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV).
// No need to reissue the signal. It will automatically trigger again,
// when we return from the signal handler.
}
}
struct ThreadArgument {
pid_t pid; // the crashing process
const MinidumpDescriptor* minidump_descriptor;
ExceptionHandler* handler;
const void* context; // a CrashContext structure
size_t context_size;
};
// This is the entry function for the cloned process. We are in a compromised
// context here: see the top of the file.
// static
int ExceptionHandler::ThreadEntry(void *arg) {
const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
// Block here until the crashing process unblocks us when
// we're allowed to use ptrace
thread_arg->handler->WaitForContinueSignal();
return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
thread_arg->context_size) == false;
}
// 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) {
if (filter_ && !filter_(callback_context_))
return false;
// Allow ourselves to be dumped if the signal is trusted.
bool signal_trusted = info->si_code > 0;
bool signal_pid_trusted = info->si_code == SI_USER ||
info->si_code == SI_TKILL;
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
}
CrashContext context;
// Fill in all the holes in the struct to make Valgrind happy.
memset(&context, 0, sizeof(context));
memcpy(&context.siginfo, info, sizeof(siginfo_t));
memcpy(&context.context, uc, sizeof(struct ucontext));
#if defined(__aarch64__)
struct ucontext *uc_ptr = (struct ucontext*)uc;
struct fpsimd_context *fp_ptr =
(struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved;
if (fp_ptr->head.magic == FPSIMD_MAGIC) {
memcpy(&context.float_state, fp_ptr, sizeof(context.float_state));
}
#elif !defined(__ARM_EABI__) && !defined(__mips__)
// FP state is not part of user ABI on ARM Linux.
// In case of MIPS Linux FP state is already part of struct ucontext
// and 'float_state' is not a member of CrashContext.
struct ucontext *uc_ptr = (struct ucontext*)uc;
if (uc_ptr->uc_mcontext.fpregs) {
memcpy(&context.float_state,
uc_ptr->uc_mcontext.fpregs,
sizeof(context.float_state));
}
#endif
context.tid = syscall(__NR_gettid);
if (crash_handler_ != NULL) {
if (crash_handler_(&context, sizeof(context), callback_context_)) {
return true;
}
}
return GenerateDump(&context);
}
// This is a public interface to HandleSignal that allows the client to
// generate a crash dump. This function may run in a compromised context.
bool ExceptionHandler::SimulateSignalDelivery(int sig) {
siginfo_t siginfo = {};
// Mimic a trusted signal to allow tracing the process (see
// ExceptionHandler::HandleSignal().
siginfo.si_code = SI_USER;
siginfo.si_pid = getpid();
struct ucontext context;
getcontext(&context);
return HandleSignal(sig, &siginfo, &context);
}
// This function may run in a compromised context: see the top of the file.
bool ExceptionHandler::GenerateDump(CrashContext *context) {
if (IsOutOfProcess())
return crash_generation_client_->RequestDump(context, sizeof(*context));
// Allocating too much stack isn't a problem, and better to err on the side
// of caution than smash it into random locations.
static const unsigned kChildStackSize = 16000;
PageAllocator allocator;
uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize));
if (!stack)
return false;
// clone() needs the top-most address. (scrub just to be safe)
stack += kChildStackSize;
my_memset(stack - 16, 0, 16);
ThreadArgument thread_arg;
thread_arg.handler = this;
thread_arg.minidump_descriptor = &minidump_descriptor_;
thread_arg.pid = getpid();
thread_arg.context = context;
thread_arg.context_size = sizeof(*context);
// We need to explicitly enable ptrace of parent processes on some
// kernels, but we need to know the PID of the cloned process before we
// can do this. Create a pipe here which we can use to block the
// cloned process after creating it, until we have explicitly enabled ptrace
if (sys_pipe(fdes) == -1) {
// Creating the pipe failed. We'll log an error but carry on anyway,
// as we'll probably still get a useful crash report. All that will happen
// is the write() and read() calls will fail with EBADF
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump "
"sys_pipe failed:";
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
// Ensure fdes[0] and fdes[1] are invalid file descriptors.
fdes[0] = fdes[1] = -1;
}
const pid_t child = sys_clone(
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
&thread_arg, NULL, NULL, NULL);
if (child == -1) {
sys_close(fdes[0]);
sys_close(fdes[1]);
return false;
}
// Allow the child to ptrace us
sys_prctl(PR_SET_PTRACER, child, 0, 0, 0);
SendContinueSignalToChild();
int status;
const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL));
sys_close(fdes[0]);
sys_close(fdes[1]);
if (r == -1) {
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
if (callback_)
success = callback_(minidump_descriptor_, callback_context_, success);
return success;
}
// This function runs in a compromised context: see the top of the file.
void ExceptionHandler::SendContinueSignalToChild() {
static const char okToContinueMessage = 'a';
int r;
r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char)));
if (r == -1) {
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild "
"sys_write failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
}
// This function runs in a compromised context: see the top of the file.
// Runs on the cloned process.
void ExceptionHandler::WaitForContinueSignal() {
int r;
char receivedMessage;
r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char)));
if (r == -1) {
static const char msg[] = "ExceptionHandler::WaitForContinueSignal "
"sys_read failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
}
// This function runs in a compromised context: see the top of the file.
// Runs on the cloned process.
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
size_t context_size) {
if (minidump_descriptor_.IsMicrodumpOnConsole()) {
return google_breakpad::WriteMicrodump(
crashing_process,
context,
context_size,
mapping_list_,
minidump_descriptor_.microdump_build_fingerprint(),
minidump_descriptor_.microdump_product_info());
}
if (minidump_descriptor_.IsFD()) {
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
minidump_descriptor_.size_limit(),
crashing_process,
context,
context_size,
mapping_list_,
app_memory_list_);
}
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
minidump_descriptor_.size_limit(),
crashing_process,
context,
context_size,
mapping_list_,
app_memory_list_);
}
// static
bool ExceptionHandler::WriteMinidump(const string& dump_path,
MinidumpCallback callback,
void* callback_context) {
MinidumpDescriptor descriptor(dump_path);
ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1);
return eh.WriteMinidump();
}
// In order to making using EBP to calculate the desired value for ESP
// a valid operation, ensure that this function is compiled with a
// frame pointer using the following attribute. This attribute
// is supported on GCC but not on clang.
#if defined(__i386__) && defined(__GNUC__) && !defined(__clang__)
__attribute__((optimize("no-omit-frame-pointer")))
#endif
bool ExceptionHandler::WriteMinidump() {
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
!minidump_descriptor_.IsMicrodumpOnConsole()) {
// Update the path of the minidump so that this can be called multiple times
// and new files are created for each minidump. This is done before the
// generation happens, as clients may want to access the MinidumpDescriptor
// after this call to find the exact path to the minidump file.
minidump_descriptor_.UpdatePath();
} else if (minidump_descriptor_.IsFD()) {
// Reposition the FD to its beginning and resize it to get rid of the
// previous minidump info.
lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
ignore_result(ftruncate(minidump_descriptor_.fd(), 0));
}
// Allow this process to be dumped.
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
CrashContext context;
int getcontext_result = getcontext(&context.context);
if (getcontext_result)
return false;
#if defined(__i386__)
// In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved
// from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer
// and it only makes sense when running in kernel mode with a different stack
// pointer. When WriteMiniDump is called during normal processing REG_UESP is
// zero which leads to bad minidump files.
if (!context.context.uc_mcontext.gregs[REG_UESP]) {
// If REG_UESP is set to REG_ESP then that includes the stack space for the
// CrashContext object in this function, which is about 128 KB. Since the
// Linux dumper only records 32 KB of stack this would mean that nothing
// useful would be recorded. A better option is to set REG_UESP to REG_EBP,
// perhaps with a small negative offset in case there is any code that
// objects to them being equal.
context.context.uc_mcontext.gregs[REG_UESP] =
context.context.uc_mcontext.gregs[REG_EBP] - 16;
// The stack saving is based off of REG_ESP so it must be set to match the
// new REG_UESP.
context.context.uc_mcontext.gregs[REG_ESP] =
context.context.uc_mcontext.gregs[REG_UESP];
}
#endif
#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__)
// FPU state is not part of ARM EABI ucontext_t.
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
sizeof(context.float_state));
#endif
context.tid = sys_gettid();
// Add an exception stream to the minidump for better reporting.
memset(&context.siginfo, 0, sizeof(context.siginfo));
context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
#if defined(__i386__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]);
#elif defined(__x86_64__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]);
#elif defined(__arm__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
#elif defined(__aarch64__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
#elif defined(__mips__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
#else
#error "This code has not been ported to your platform yet."
#endif
return GenerateDump(&context);
}
void ExceptionHandler::AddMappingInfo(const string& name,
const uint8_t identifier[sizeof(MDGUID)],
uintptr_t start_address,
size_t mapping_size,
size_t file_offset) {
MappingInfo info;
info.start_addr = start_address;
info.size = mapping_size;
info.offset = file_offset;
strncpy(info.name, name.c_str(), sizeof(info.name) - 1);
info.name[sizeof(info.name) - 1] = '\0';
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, identifier, sizeof(MDGUID));
mapping_list_.push_back(mapping);
}
void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) {
AppMemoryList::iterator iter =
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
if (iter != app_memory_list_.end()) {
// Don't allow registering the same pointer twice.
return;
}
AppMemory app_memory;
app_memory.ptr = ptr;
app_memory.length = length;
app_memory_list_.push_back(app_memory);
}
void ExceptionHandler::UnregisterAppMemory(void* ptr) {
AppMemoryList::iterator iter =
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
if (iter != app_memory_list_.end()) {
app_memory_list_.erase(iter);
}
}
// static
bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
pid_t child_blamed_thread,
const string& dump_path,
MinidumpCallback callback,
void* callback_context) {
// This function is not run in a compromised context.
MinidumpDescriptor descriptor(dump_path);
descriptor.UpdatePath();
if (!google_breakpad::WriteMinidump(descriptor.path(),
child,
child_blamed_thread))
return false;
return callback ? callback(descriptor, callback_context, true) : true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,278 @@
// Copyright (c) 2010 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 CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/ucontext.h>
#include <string>
#include "client/linux/crash_generation/crash_generation_client.h"
#include "client/linux/handler/minidump_descriptor.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
// ExceptionHandler
//
// ExceptionHandler can write a minidump file when an exception occurs,
// or when WriteMinidump() is called explicitly by your program.
//
// To have the exception handler write minidumps when an uncaught exception
// (crash) occurs, you should create an instance early in the execution
// of your program, and keep it around for the entire time you want to
// have crash handling active (typically, until shutdown).
// (NOTE): There should be only be one this kind of exception handler
// object per process.
//
// If you want to write minidumps without installing the exception handler,
// you can create an ExceptionHandler with install_handler set to false,
// then call WriteMinidump. You can also use this technique if you want to
// use different minidump callbacks for different call sites.
//
// In either case, a callback function is called when a minidump is written,
// which receives the full path or file descriptor of the minidump. The
// caller can collect and write additional application state to that minidump,
// and launch an external crash-reporting application.
//
// Caller should try to make the callbacks as crash-friendly as possible,
// it should avoid use heap memory allocation as much as possible.
class ExceptionHandler {
public:
// A callback function to run before Breakpad performs any substantial
// processing of an exception. A FilterCallback is called before writing
// a minidump. |context| is the parameter supplied by the user as
// callback_context when the handler was created.
//
// If a FilterCallback returns true, Breakpad will continue processing,
// attempting to write a minidump. If a FilterCallback returns false,
// Breakpad will immediately report the exception as unhandled without
// writing a minidump, allowing another handler the opportunity to handle it.
typedef bool (*FilterCallback)(void *context);
// A callback function to run after the minidump has been written.
// |descriptor| contains the file descriptor or file path containing the
// minidump. |context| is the parameter supplied by the user as
// callback_context when the handler was created. |succeeded| indicates
// whether a minidump file was successfully written.
//
// If an exception occurred and the callback returns true, Breakpad will
// treat the exception as fully-handled, suppressing any other handlers from
// being notified of the exception. If the callback returns false, Breakpad
// will treat the exception as unhandled, and allow another handler to handle
// it. If there are no other handlers, Breakpad will report the exception to
// the system as unhandled, allowing a debugger or native crash dialog the
// opportunity to handle the exception. Most callback implementations
// should normally return the value of |succeeded|, or when they wish to
// not report an exception of handled, false. Callbacks will rarely want to
// return true directly (unless |succeeded| is true).
typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor,
void* context,
bool succeeded);
// In certain cases, a user may wish to handle the generation of the minidump
// themselves. In this case, they can install a handler callback which is
// called when a crash has occurred. If this function returns true, no other
// processing of occurs and the process will shortly be crashed. If this
// returns false, the normal processing continues.
typedef bool (*HandlerCallback)(const void* crash_context,
size_t crash_context_size,
void* context);
// Creates a new ExceptionHandler instance to handle writing minidumps.
// Before writing a minidump, the optional |filter| callback will be called.
// Its return value determines whether or not Breakpad should write a
// minidump. The minidump content will be written to the file path or file
// descriptor from |descriptor|, and the optional |callback| is called after
// writing the dump file, as described above.
// If install_handler is true, then a minidump will be written whenever
// an unhandled exception occurs. If it is false, minidumps will only
// be written when WriteMinidump is called.
// If |server_fd| is valid, the minidump is generated out-of-process. If it
// is -1, in-process generation will always be used.
ExceptionHandler(const MinidumpDescriptor& descriptor,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
bool install_handler,
const int server_fd);
~ExceptionHandler();
const MinidumpDescriptor& minidump_descriptor() const {
return minidump_descriptor_;
}
void set_minidump_descriptor(const MinidumpDescriptor& descriptor) {
minidump_descriptor_ = descriptor;
}
void set_crash_handler(HandlerCallback callback) {
crash_handler_ = callback;
}
void set_crash_generation_client(CrashGenerationClient* client) {
crash_generation_client_.reset(client);
}
// Writes a minidump immediately. This can be used to capture the execution
// state independently of a crash.
// Returns true on success.
// If the ExceptionHandler has been created with a path, a new file is
// generated for each minidump. The file path can be retrieved in the
// MinidumpDescriptor passed to the MinidumpCallback or by accessing the
// MinidumpDescriptor directly from the ExceptionHandler (with
// minidump_descriptor()).
// If the ExceptionHandler has been created with a file descriptor, the file
// descriptor is repositioned to its beginning and the previous generated
// minidump is overwritten.
// Note that this method is not supposed to be called from a compromised
// context as it uses the heap.
bool WriteMinidump();
// Convenience form of WriteMinidump which does not require an
// ExceptionHandler instance.
static bool WriteMinidump(const string& dump_path,
MinidumpCallback callback,
void* callback_context);
// Write a minidump of |child| immediately. This can be used to
// capture the execution state of |child| independently of a crash.
// Pass a meaningful |child_blamed_thread| to make that thread in
// the child process the one from which a crash signature is
// extracted.
//
// WARNING: the return of this function *must* happen before
// the code that will eventually reap |child| executes.
// Otherwise there's a pernicious race condition in which |child|
// exits, is reaped, another process created with its pid, then that
// new process dumped.
static bool WriteMinidumpForChild(pid_t child,
pid_t child_blamed_thread,
const string& dump_path,
MinidumpCallback callback,
void* callback_context);
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
// blob. It shouldn't be needed in any user code.
struct CrashContext {
siginfo_t siginfo;
pid_t tid; // the crashing thread.
struct ucontext 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.
fpstate_t float_state;
#endif
};
// Returns whether out-of-process dump generation is used or not.
bool IsOutOfProcess() const {
return crash_generation_client_.get() != NULL;
}
// Add information about a memory mapping. This can be used if
// a custom library loader is used that maps things in a way
// that the linux dumper can't handle by reading the maps file.
void AddMappingInfo(const string& name,
const uint8_t identifier[sizeof(MDGUID)],
uintptr_t start_address,
size_t mapping_size,
size_t file_offset);
// Register a block of memory of length bytes starting at address ptr
// to be copied to the minidump when a crash happens.
void RegisterAppMemory(void* ptr, size_t length);
// Unregister a block of memory that was registered with RegisterAppMemory.
void UnregisterAppMemory(void* ptr);
// Force signal handling for the specified signal.
bool SimulateSignalDelivery(int sig);
// Report a crash signal from an SA_SIGINFO signal handler.
bool HandleSignal(int sig, siginfo_t* info, void* uc);
private:
// Save the old signal handlers and install new ones.
static bool InstallHandlersLocked();
// Restore the old signal handlers.
static void RestoreHandlersLocked();
void PreresolveSymbols();
bool GenerateDump(CrashContext *context);
void SendContinueSignalToChild();
void WaitForContinueSignal();
static void SignalHandler(int sig, siginfo_t* info, void* uc);
static int ThreadEntry(void* arg);
bool DoDump(pid_t crashing_process, const void* context,
size_t context_size);
const FilterCallback filter_;
const MinidumpCallback callback_;
void* const callback_context_;
scoped_ptr<CrashGenerationClient> crash_generation_client_;
MinidumpDescriptor minidump_descriptor_;
// Must be volatile. The compiler is unaware of the code which runs in
// the signal handler which reads this variable. Without volatile the
// compiler is free to optimise away writes to this variable which it
// believes are never read.
volatile HandlerCallback crash_handler_;
// We need to explicitly enable ptrace of parent processes on some
// kernels, but we need to know the PID of the cloned process before we
// 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];
// Callers can add extra info about mappings for cases where the
// dumper code cannot extract enough information from /proc/<pid>/maps.
MappingList mapping_list_;
// Callers can request additional memory regions to be included in
// the dump.
AppMemoryList app_memory_list_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
// Copyright (c) 2012 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 <stdio.h>
#include "client/linux/handler/minidump_descriptor.h"
#include "common/linux/guid_creator.h"
namespace google_breakpad {
//static
const MinidumpDescriptor::MicrodumpOnConsole
MinidumpDescriptor::kMicrodumpOnConsole = {};
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
: mode_(descriptor.mode_),
fd_(descriptor.fd_),
directory_(descriptor.directory_),
c_path_(NULL),
size_limit_(descriptor.size_limit_),
microdump_build_fingerprint_(descriptor.microdump_build_fingerprint_),
microdump_product_info_(descriptor.microdump_product_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
// can cause problems in compromised environments.
assert(descriptor.path_.empty());
}
MinidumpDescriptor& MinidumpDescriptor::operator=(
const MinidumpDescriptor& descriptor) {
assert(descriptor.path_.empty());
mode_ = descriptor.mode_;
fd_ = descriptor.fd_;
directory_ = descriptor.directory_;
path_.clear();
if (c_path_) {
// This descriptor already had a path set, so generate a new one.
c_path_ = NULL;
UpdatePath();
}
size_limit_ = descriptor.size_limit_;
microdump_build_fingerprint_ = descriptor.microdump_build_fingerprint_;
microdump_product_info_ = descriptor.microdump_product_info_;
return *this;
}
void MinidumpDescriptor::UpdatePath() {
assert(mode_ == kWriteMinidumpToFile && !directory_.empty());
GUID guid;
char guid_str[kGUIDStringLength + 1];
if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) {
assert(false);
}
path_.clear();
path_ = directory_ + "/" + guid_str + ".dmp";
c_path_ = path_.c_str();
}
void MinidumpDescriptor::SetMicrodumpBuildFingerprint(
const char* build_fingerprint) {
assert(mode_ == kWriteMicrodumpToConsole);
microdump_build_fingerprint_ = build_fingerprint;
}
void MinidumpDescriptor::SetMicrodumpProductInfo(const char* product_info) {
assert(mode_ == kWriteMicrodumpToConsole);
microdump_product_info_ = product_info;
}
} // namespace google_breakpad

View File

@ -0,0 +1,161 @@
// Copyright (c) 2012 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 CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
#include <assert.h>
#include <sys/types.h>
#include <string>
#include "common/using_std_string.h"
// This class describes how a crash dump should be generated, either:
// - Writing a full minidump to a file in a given directory (the actual path,
// inside the directory, is determined by this class).
// - Writing a full minidump to a given fd.
// - Writing a reduced microdump to the console (logcat on Android).
namespace google_breakpad {
class MinidumpDescriptor {
public:
struct MicrodumpOnConsole {};
static const MicrodumpOnConsole kMicrodumpOnConsole;
MinidumpDescriptor() : mode_(kUninitialized),
fd_(-1),
size_limit_(-1),
microdump_build_fingerprint_(NULL),
microdump_product_info_(NULL) {}
explicit MinidumpDescriptor(const string& directory)
: mode_(kWriteMinidumpToFile),
fd_(-1),
directory_(directory),
c_path_(NULL),
size_limit_(-1),
microdump_build_fingerprint_(NULL),
microdump_product_info_(NULL) {
assert(!directory.empty());
}
explicit MinidumpDescriptor(int fd)
: mode_(kWriteMinidumpToFd),
fd_(fd),
c_path_(NULL),
size_limit_(-1),
microdump_build_fingerprint_(NULL),
microdump_product_info_(NULL) {
assert(fd != -1);
}
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
: mode_(kWriteMicrodumpToConsole),
fd_(-1),
size_limit_(-1),
microdump_build_fingerprint_(NULL),
microdump_product_info_(NULL) {}
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
static MinidumpDescriptor getMicrodumpDescriptor();
bool IsFD() const { return mode_ == kWriteMinidumpToFd; }
int fd() const { return fd_; }
string directory() const { return directory_; }
const char* path() const { return c_path_; }
bool IsMicrodumpOnConsole() const {
return mode_ == kWriteMicrodumpToConsole;
}
// Updates the path so it is unique.
// Should be called from a normal context: this methods uses the heap.
void UpdatePath();
off_t size_limit() const { return size_limit_; }
void set_size_limit(off_t limit) { size_limit_ = limit; }
// TODO(primiano): make this and product info (below) just part of the
// microdump ctor once it is rolled stably into Chrome. ETA: June 2015.
void SetMicrodumpBuildFingerprint(const char* build_fingerprint);
const char* microdump_build_fingerprint() const {
return microdump_build_fingerprint_;
}
void SetMicrodumpProductInfo(const char* product_info);
const char* microdump_product_info() const {
return microdump_product_info_;
}
private:
enum DumpMode {
kUninitialized = 0,
kWriteMinidumpToFile,
kWriteMinidumpToFd,
kWriteMicrodumpToConsole
};
// Specifies the dump mode (see DumpMode).
DumpMode mode_;
// The file descriptor where the minidump is generated.
int fd_;
// The directory where the minidump should be generated.
string directory_;
// The full path to the generated minidump.
string path_;
// The C string of |path_|. Precomputed so it can be access from a compromised
// context.
const char* c_path_;
off_t size_limit_;
// The product name/version and build fingerprint that should be appended to
// the dump (microdump only). Microdumps don't have the ability of appending
// extra metadata after the dump is generated (as opposite to minidumps
// MIME fields), therefore the product details must be provided upfront.
// The string pointers are supposed to be valid through all the lifetime of
// the process (read: the caller has to guarantee that they are stored in
// global static storage).
const char* microdump_build_fingerprint_;
const char* microdump_product_info_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_

View File

@ -0,0 +1,48 @@
// Copyright (c) 2012 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 "client/linux/log/log.h"
#if defined(__ANDROID__)
#include <android/log.h>
#else
#include "third_party/lss/linux_syscall_support.h"
#endif
namespace logger {
int write(const char* buf, size_t nbytes) {
#if defined(__ANDROID__)
return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf);
#else
return sys_write(2, buf, nbytes);
#endif
}
} // namespace logger

View File

@ -0,0 +1,41 @@
// Copyright (c) 2012, 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 CLIENT_LINUX_LOG_LOG_H_
#define CLIENT_LINUX_LOG_LOG_H_
#include <stddef.h>
namespace logger {
int write(const char* buf, size_t nbytes);
} // namespace logger
#endif // CLIENT_LINUX_LOG_LOG_H_

View File

@ -0,0 +1,423 @@
// Copyright (c) 2014, 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.
// This translation unit generates microdumps into the console (logcat on
// Android). See crbug.com/410294 for more info and design docs.
#include "client/linux/microdump_writer/microdump_writer.h"
#include <sys/utsname.h>
#include "client/linux/dump_writer_common/seccomp_unwinder.h"
#include "client/linux/dump_writer_common/thread_info.h"
#include "client/linux/dump_writer_common/ucontext_reader.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/log/log.h"
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include "common/linux/linux_libc_support.h"
namespace {
using google_breakpad::ExceptionHandler;
using google_breakpad::LinuxDumper;
using google_breakpad::LinuxPtraceDumper;
using google_breakpad::MappingInfo;
using google_breakpad::MappingList;
using google_breakpad::RawContextCPU;
using google_breakpad::SeccompUnwinder;
using google_breakpad::ThreadInfo;
using google_breakpad::UContextReader;
const size_t kLineBufferSize = 2048;
class MicrodumpWriter {
public:
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
const MappingList& mappings,
const char* build_fingerprint,
const char* product_info,
LinuxDumper* dumper)
: ucontext_(context ? &context->context : NULL),
#if !defined(__ARM_EABI__) && !defined(__mips__)
float_state_(context ? &context->float_state : NULL),
#endif
dumper_(dumper),
mapping_list_(mappings),
build_fingerprint_(build_fingerprint),
product_info_(product_info),
log_line_(NULL) {
log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
if (log_line_)
log_line_[0] = '\0'; // Clear out the log line buffer.
}
~MicrodumpWriter() { dumper_->ThreadsResume(); }
bool Init() {
// In the exceptional case where the system was out of memory and there
// wasn't even room to allocate the line buffer, bail out. There is nothing
// useful we can possibly achieve without the ability to Log. At least let's
// try to not crash.
if (!dumper_->Init() || !log_line_)
return false;
return dumper_->ThreadsSuspend();
}
bool Dump() {
bool success;
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
DumpProductInformation();
DumpOSInformation();
success = DumpCrashingThread();
if (success)
success = DumpMappings();
LogLine("-----END BREAKPAD MICRODUMP-----");
dumper_->ThreadsResume();
return success;
}
private:
// Writes one line to the system log.
void LogLine(const char* msg) {
logger::write(msg, my_strlen(msg));
#if !defined(__ANDROID__)
logger::write("\n", 1); // Android logger appends the \n. Linux's doesn't.
#endif
}
// Stages the given string in the current line buffer.
void LogAppend(const char* str) {
my_strlcat(log_line_, str, kLineBufferSize);
}
// As above (required to take precedence over template specialization below).
void LogAppend(char* str) {
LogAppend(const_cast<const char*>(str));
}
// Stages the hex repr. of the given int type in the current line buffer.
template<typename T>
void LogAppend(T value) {
// Make enough room to hex encode the largest int type + NUL.
static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F'};
char hexstr[sizeof(T) * 2 + 1];
for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4)
hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F];
hexstr[sizeof(T) * 2] = '\0';
LogAppend(hexstr);
}
// Stages the buffer content hex-encoded in the current line buffer.
void LogAppend(const void* buf, size_t length) {
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf);
for (size_t i = 0; i < length; ++i, ++ptr)
LogAppend(*ptr);
}
// Writes out the current line buffer on the system log.
void LogCommitLine() {
LogLine(log_line_);
my_strlcpy(log_line_, "", kLineBufferSize);
}
void DumpProductInformation() {
LogAppend("V ");
if (product_info_) {
LogAppend(product_info_);
} else {
LogAppend("UNKNOWN:0.0.0.0");
}
LogCommitLine();
}
void DumpOSInformation() {
const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
#if defined(__ANDROID__)
const char kOSId[] = "A";
#else
const char kOSId[] = "L";
#endif
// We cannot depend on uts.machine. On multiarch devices it always returns the
// primary arch, not the one that match the executable being run.
#if defined(__aarch64__)
const char kArch[] = "arm64";
#elif defined(__ARMEL__)
const char kArch[] = "arm";
#elif defined(__x86_64__)
const char kArch[] = "x86_64";
#elif defined(__i386__)
const char kArch[] = "x86";
#elif defined(__mips__)
const char kArch[] = "mips";
#else
#error "This code has not been ported to your platform yet"
#endif
LogAppend("O ");
LogAppend(kOSId);
LogAppend(" ");
LogAppend(kArch);
LogAppend(" ");
LogAppend(n_cpus);
LogAppend(" ");
// If the client has attached a build fingerprint to the MinidumpDescriptor
// use that one. Otherwise try to get some basic info from uname().
if (build_fingerprint_) {
LogAppend(build_fingerprint_);
} else {
struct utsname uts;
if (uname(&uts) == 0) {
LogAppend(uts.machine);
LogAppend(" ");
LogAppend(uts.release);
LogAppend(" ");
LogAppend(uts.version);
} else {
LogAppend("no build fingerprint available");
}
}
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;
}
LogAppend("S 0 ");
LogAppend(stack_pointer);
LogAppend(" ");
LogAppend(reinterpret_cast<uintptr_t>(stack));
LogAppend(" ");
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;
stack_off += STACK_DUMP_CHUNK_SIZE) {
LogAppend("S ");
LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
LogAppend(" ");
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));
#if !defined(__ARM_EABI__) && !defined(__mips__)
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
#else
UContextReader::FillCPUContext(&cpu, ucontext_);
#endif
if (stack_copy)
SeccompUnwinder::PopSeccompStackFrame(&cpu, thread, stack_copy);
DumpCPUState(&cpu);
}
return true;
}
void DumpCPUState(RawContextCPU* cpu) {
LogAppend("C ");
LogAppend(cpu, sizeof(*cpu));
LogCommitLine();
}
// If there is caller-provided information about this mapping
// in the mapping_list_ list, return true. Otherwise, return false.
bool HaveMappingInfo(const MappingInfo& mapping) {
for (MappingList::const_iterator iter = mapping_list_.begin();
iter != mapping_list_.end();
++iter) {
// Ignore any mappings that are wholly contained within
// mappings in the mapping_info_ list.
if (mapping.start_addr >= iter->first.start_addr &&
(mapping.start_addr + mapping.size) <=
(iter->first.start_addr + iter->first.size)) {
return true;
}
}
return false;
}
// Dump information about the provided |mapping|. If |identifier| is non-NULL,
// use it instead of calculating a file ID from the mapping.
void DumpModule(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
const uint8_t* identifier) {
MDGUID module_identifier;
if (identifier) {
// GUID was provided by caller.
my_memcpy(&module_identifier, identifier, sizeof(MDGUID));
} else {
dumper_->ElfFileIdentifierForMapping(
mapping,
member,
mapping_id,
reinterpret_cast<uint8_t*>(&module_identifier));
}
char file_name[NAME_MAX];
char file_path[NAME_MAX];
LinuxDumper::GetMappingEffectiveNameAndPath(
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
LogAppend("M ");
LogAppend(static_cast<uintptr_t>(mapping.start_addr));
LogAppend(" ");
LogAppend(mapping.offset);
LogAppend(" ");
LogAppend(mapping.size);
LogAppend(" ");
LogAppend(module_identifier.data1);
LogAppend(module_identifier.data2);
LogAppend(module_identifier.data3);
LogAppend(module_identifier.data4[0]);
LogAppend(module_identifier.data4[1]);
LogAppend(module_identifier.data4[2]);
LogAppend(module_identifier.data4[3]);
LogAppend(module_identifier.data4[4]);
LogAppend(module_identifier.data4[5]);
LogAppend(module_identifier.data4[6]);
LogAppend(module_identifier.data4[7]);
LogAppend("0 "); // Age is always 0 on Linux.
LogAppend(file_name);
LogCommitLine();
}
// Write information about the mappings in effect.
bool DumpMappings() {
// First write all the mappings from the dumper
for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
const MappingInfo& mapping = *dumper_->mappings()[i];
if (mapping.name[0] == 0 || // only want modules with filenames.
!mapping.exec || // only want executable mappings.
mapping.size < 4096 || // too small to get a signature for.
HaveMappingInfo(mapping)) {
continue;
}
DumpModule(mapping, true, i, NULL);
}
// Next write all the mappings provided by the caller
for (MappingList::const_iterator iter = mapping_list_.begin();
iter != mapping_list_.end();
++iter) {
DumpModule(iter->first, false, 0, iter->second);
}
return true;
}
void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
const struct ucontext* const ucontext_;
#if !defined(__ARM_EABI__) && !defined(__mips__)
const google_breakpad::fpstate_t* const float_state_;
#endif
LinuxDumper* dumper_;
const MappingList& mapping_list_;
const char* const build_fingerprint_;
const char* const product_info_;
char* log_line_;
};
} // namespace
namespace google_breakpad {
bool WriteMicrodump(pid_t crashing_process,
const void* blob,
size_t blob_size,
const MappingList& mappings,
const char* build_fingerprint,
const char* product_info) {
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.set_crash_thread(context->tid);
}
MicrodumpWriter writer(context, mappings, build_fingerprint, product_info,
&dumper);
if (!writer.Init())
return false;
return writer.Dump();
}
} // namespace google_breakpad

View File

@ -0,0 +1,64 @@
// Copyright (c) 2014, 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 CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
#include <stdint.h>
#include <sys/types.h>
#include "client/linux/dump_writer_common/mapping_info.h"
namespace google_breakpad {
// Writes a microdump (a reduced dump containing only the state of the crashing
// thread) on the console (logcat on Android). These functions do not malloc nor
// use libc functions which may. Thus, it can be used in contexts where the
// state of the heap may be corrupt.
// Args:
// crashing_process: the pid of the crashing process. This must be trusted.
// blob: a blob of data from the crashing process. See exception_handler.h
// blob_size: the length of |blob| in bytes.
// mappings: a list of additional mappings provided by the application.
// build_fingerprint: a (optional) C string which determines the OS
// build fingerprint (e.g., aosp/occam/mako:5.1.1/LMY47W/1234:eng/dev-keys).
// product_info: a (optional) C string which determines the product name and
// version (e.g., WebView:42.0.2311.136).
//
// Returns true iff successful.
bool WriteMicrodump(pid_t crashing_process,
const void* blob,
size_t blob_size,
const MappingList& mappings,
const char* build_fingerprint,
const char* product_info);
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_

View File

@ -0,0 +1,164 @@
// Copyright (c) 2014 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 <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/microdump_writer/microdump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/ignore_ret.h"
#include "common/scoped_ptr.h"
#include "common/tests/auto_tempdir.h"
#include "common/using_std_string.h"
using namespace google_breakpad;
namespace {
typedef testing::Test MicrodumpWriterTest;
void CrashAndGetMicrodump(
const MappingList& mappings,
const char* build_fingerprint,
const char* product_info,
scoped_array<char>* buf) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
AutoTempDir temp_dir;
string stderr_file = temp_dir.path() + "/stderr.log";
int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ASSERT_NE(-1, err_fd);
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);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
// Set a non-zero tid to avoid tripping asserts.
context.tid = child;
// Redirect temporarily stderr to the stderr.log file.
int save_err = dup(STDERR_FILENO);
ASSERT_NE(-1, save_err);
ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
build_fingerprint, product_info));
// Revert stderr back to the console.
dup2(save_err, STDERR_FILENO);
close(save_err);
// 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);
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-----"));
}
TEST(MicrodumpWriterTest, BasicWithMappings) {
// Push some extra mapping to check the MappingList logic.
const uint32_t memory_size = sysconf(_SC_PAGESIZE);
const char* kMemoryName = "libfoo.so";
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
MappingInfo info;
info.start_addr = memory_size;
info.size = memory_size;
info.offset = 42;
strcpy(info.name, kMemoryName);
MappingList mappings;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
scoped_array<char> buf;
CrashAndGetMicrodump(mappings, NULL, NULL, &buf);
#ifdef __LP64__
ASSERT_NE(static_cast<char*>(0), strstr(
buf.get(), "M 0000000000001000 000000000000002A 0000000000001000 "
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
#else
ASSERT_NE(static_cast<char*>(0), strstr(
buf.get(), "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"));
}
// Ensure that the product info and build fingerprint metadata show up in the
// final microdump if present.
TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
const char kProductInfo[] = "MockProduct:42.0.2311.99";
const char kBuildFingerprint[] =
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
scoped_array<char> buf;
MappingList no_mappings;
CrashAndGetMicrodump(no_mappings, kBuildFingerprint, kProductInfo, &buf);
ASSERT_NE(static_cast<char*>(0), strstr(buf.get(), kBuildFingerprint));
ASSERT_NE(static_cast<char*>(0), strstr(buf.get(), kProductInfo));
}
} // namespace

View File

@ -0,0 +1,144 @@
// Copyright (c) 2013, 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 CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// Helper class used to model a set of CPUs, as read from sysfs
// files like /sys/devices/system/cpu/present
// See See http://www.kernel.org/doc/Documentation/cputopology.txt
class CpuSet {
public:
// The maximum number of supported CPUs.
static const size_t kMaxCpus = 1024;
CpuSet() {
my_memset(mask_, 0, sizeof(mask_));
}
// Parse a sysfs file to extract the corresponding CPU set.
bool ParseSysFile(int fd) {
char buffer[512];
int ret = sys_read(fd, buffer, sizeof(buffer)-1);
if (ret < 0)
return false;
buffer[ret] = '\0';
// Expected format: comma-separated list of items, where each
// item can be a decimal integer, or two decimal integers separated
// by a dash.
// E.g.:
// 0
// 0,1,2,3
// 0-3
// 1,10-23
const char* p = buffer;
const char* p_end = p + ret;
while (p < p_end) {
// Skip leading space, if any
while (p < p_end && my_isspace(*p))
p++;
// Find start and size of current item.
const char* item = p;
size_t item_len = static_cast<size_t>(p_end - p);
const char* item_next =
static_cast<const char*>(my_memchr(p, ',', item_len));
if (item_next != NULL) {
p = item_next + 1;
item_len = static_cast<size_t>(item_next - item);
} else {
p = p_end;
item_next = p_end;
}
// Ignore trailing spaces.
while (item_next > item && my_isspace(item_next[-1]))
item_next--;
// skip empty items.
if (item_next == item)
continue;
// read first decimal value.
uintptr_t start = 0;
const char* next = my_read_decimal_ptr(&start, item);
uintptr_t end = start;
if (*next == '-')
my_read_decimal_ptr(&end, next+1);
while (start <= end)
SetBit(start++);
}
return true;
}
// Intersect this CPU set with another one.
void IntersectWith(const CpuSet& other) {
for (size_t nn = 0; nn < kMaskWordCount; ++nn)
mask_[nn] &= other.mask_[nn];
}
// Return the number of CPUs in this set.
int GetCount() {
int result = 0;
for (size_t nn = 0; nn < kMaskWordCount; ++nn) {
result += __builtin_popcount(mask_[nn]);
}
return result;
}
private:
void SetBit(uintptr_t index) {
size_t nn = static_cast<size_t>(index);
if (nn < kMaxCpus)
mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits));
}
typedef uint32_t MaskWordType;
static const size_t kMaskWordBits = 8*sizeof(MaskWordType);
static const size_t kMaskWordCount =
(kMaxCpus + kMaskWordBits - 1) / kMaskWordBits;
MaskWordType mask_[kMaskWordCount];
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_

View File

@ -0,0 +1,164 @@
// Copyright (c) 2013, 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 <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/cpu_set.h"
#include "common/linux/tests/auto_testfile.h"
using namespace google_breakpad;
namespace {
typedef testing::Test CpuSetTest;
// Helper class to write test text file to a temporary file and return
// its file descriptor.
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("cpu_set", text) {
}
};
}
TEST(CpuSetTest, EmptyCount) {
CpuSet set;
ASSERT_EQ(0, set.GetCount());
}
TEST(CpuSetTest, OneCpu) {
ScopedTestFile file("10");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(1, set.GetCount());
}
TEST(CpuSetTest, OneCpuTerminated) {
ScopedTestFile file("10\n");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(1, set.GetCount());
}
TEST(CpuSetTest, TwoCpusWithComma) {
ScopedTestFile file("1,10");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(2, set.GetCount());
}
TEST(CpuSetTest, TwoCpusWithRange) {
ScopedTestFile file("1-2");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(2, set.GetCount());
}
TEST(CpuSetTest, TenCpusWithRange) {
ScopedTestFile file("9-18");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(10, set.GetCount());
}
TEST(CpuSetTest, MultiItems) {
ScopedTestFile file("0, 2-4, 128");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(5, set.GetCount());
}
TEST(CpuSetTest, IntersectWith) {
ScopedTestFile file1("9-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(11, set1.GetCount());
ScopedTestFile file2("16-24");
ASSERT_TRUE(file2.IsOk());
CpuSet set2;
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
ASSERT_EQ(9, set2.GetCount());
set1.IntersectWith(set2);
ASSERT_EQ(4, set1.GetCount());
ASSERT_EQ(9, set2.GetCount());
}
TEST(CpuSetTest, SelfIntersection) {
ScopedTestFile file1("9-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(11, set1.GetCount());
set1.IntersectWith(set1);
ASSERT_EQ(11, set1.GetCount());
}
TEST(CpuSetTest, EmptyIntersection) {
ScopedTestFile file1("0-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(20, set1.GetCount());
ScopedTestFile file2("20-39");
ASSERT_TRUE(file2.IsOk());
CpuSet set2;
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
ASSERT_EQ(20, set2.GetCount());
set1.IntersectWith(set2);
ASSERT_EQ(0, set1.GetCount());
ASSERT_EQ(20, set2.GetCount());
}

View File

@ -0,0 +1,106 @@
// Copyright (c) 2009, 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 CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
#include <stdint.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// A class for enumerating a directory without using diropen/readdir or other
// functions which may allocate memory.
class DirectoryReader {
public:
DirectoryReader(int fd)
: fd_(fd),
buf_used_(0) {
}
// Return the next entry from the directory
// name: (output) the NUL terminated entry name
//
// Returns true iff successful (false on EOF).
//
// After calling this, one must call |PopEntry| otherwise you'll get the same
// entry over and over.
bool GetNextEntry(const char** name) {
struct kernel_dirent* const dent =
reinterpret_cast<kernel_dirent*>(buf_);
if (buf_used_ == 0) {
// need to read more entries.
const int n = sys_getdents(fd_, dent, sizeof(buf_));
if (n < 0) {
return false;
} else if (n == 0) {
hit_eof_ = true;
} else {
buf_used_ += n;
}
}
if (buf_used_ == 0 && hit_eof_)
return false;
assert(buf_used_ > 0);
*name = dent->d_name;
return true;
}
void PopEntry() {
if (!buf_used_)
return;
const struct kernel_dirent* const dent =
reinterpret_cast<kernel_dirent*>(buf_);
buf_used_ -= dent->d_reclen;
my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
}
private:
const int fd_;
bool hit_eof_;
unsigned buf_used_;
uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1];
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_

View File

@ -0,0 +1,78 @@
// Copyright (c) 2009, 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 <set>
#include <string>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include "client/linux/minidump_writer/directory_reader.h"
#include "common/using_std_string.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
namespace {
typedef testing::Test DirectoryReaderTest;
}
TEST(DirectoryReaderTest, CompareResults) {
std::set<string> dent_set;
DIR *const dir = opendir("/proc/self");
ASSERT_TRUE(dir != NULL);
struct dirent* dent;
while ((dent = readdir(dir)))
dent_set.insert(dent->d_name);
closedir(dir);
const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY);
ASSERT_GE(fd, 0);
DirectoryReader dir_reader(fd);
unsigned seen = 0;
const char* name;
while (dir_reader.GetNextEntry(&name)) {
ASSERT_TRUE(dent_set.find(name) != dent_set.end());
seen++;
dir_reader.PopEntry();
}
ASSERT_TRUE(dent_set.find("status") != dent_set.end());
ASSERT_TRUE(dent_set.find("stat") != dent_set.end());
ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end());
ASSERT_EQ(dent_set.size(), seen);
close(fd);
}

View File

@ -0,0 +1,131 @@
// Copyright (c) 2009, 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 CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// A class for reading a file, line by line, without using fopen/fgets or other
// functions which may allocate memory.
class LineReader {
public:
LineReader(int fd)
: fd_(fd),
hit_eof_(false),
buf_used_(0) {
}
// The maximum length of a line.
static const size_t kMaxLineLen = 512;
// Return the next line from the file.
// line: (output) a pointer to the start of the line. The line is NUL
// terminated.
// len: (output) the length of the line (not inc the NUL byte)
//
// Returns true iff successful (false on EOF).
//
// One must call |PopLine| after this function, otherwise you'll continue to
// get the same line over and over.
bool GetNextLine(const char **line, unsigned *len) {
for (;;) {
if (buf_used_ == 0 && hit_eof_)
return false;
for (unsigned i = 0; i < buf_used_; ++i) {
if (buf_[i] == '\n' || buf_[i] == 0) {
buf_[i] = 0;
*len = i;
*line = buf_;
return true;
}
}
if (buf_used_ == sizeof(buf_)) {
// we scanned the whole buffer and didn't find an end-of-line marker.
// This line is too long to process.
return false;
}
// We didn't find any end-of-line terminators in the buffer. However, if
// this is the last line in the file it might not have one:
if (hit_eof_) {
assert(buf_used_);
// There's room for the NUL because of the buf_used_ == sizeof(buf_)
// check above.
buf_[buf_used_] = 0;
*len = buf_used_;
buf_used_ += 1; // since we appended the NUL.
*line = buf_;
return true;
}
// Otherwise, we should pull in more data from the file
const ssize_t n = sys_read(fd_, buf_ + buf_used_,
sizeof(buf_) - buf_used_);
if (n < 0) {
return false;
} else if (n == 0) {
hit_eof_ = true;
} else {
buf_used_ += n;
}
// At this point, we have either set the hit_eof_ flag, or we have more
// data to process...
}
}
void PopLine(unsigned len) {
// len doesn't include the NUL byte at the end.
assert(buf_used_ >= len + 1);
buf_used_ -= len + 1;
my_memmove(buf_, buf_ + len + 1, buf_used_);
}
private:
const int fd_;
bool hit_eof_;
unsigned buf_used_;
char buf_[kMaxLineLen];
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_

View File

@ -0,0 +1,169 @@
// Copyright (c) 2009, 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 <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include "client/linux/minidump_writer/line_reader.h"
#include "breakpad_googletest_includes.h"
#include "common/linux/tests/auto_testfile.h"
using namespace google_breakpad;
namespace {
typedef testing::Test LineReaderTest;
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("line_reader", text) {
}
ScopedTestFile(const char* text, size_t text_len)
: AutoTestFile("line_reader", text, text_len) {
}
};
}
TEST(LineReaderTest, EmptyFile) {
ScopedTestFile file("");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, OneLineTerminated) {
ScopedTestFile file("a\n");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned int len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned int)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, OneLine) {
ScopedTestFile file("a");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, TwoLinesTerminated) {
ScopedTestFile file("a\nb\n");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('b', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, TwoLines) {
ScopedTestFile file("a\nb");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('b', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, MaxLength) {
char l[LineReader::kMaxLineLen-1];
memset(l, 'a', sizeof(l));
ScopedTestFile file(l, sizeof(l));
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(sizeof(l), len);
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
ASSERT_EQ('\0', line[len]);
}
TEST(LineReaderTest, TooLong) {
// Note: this writes kMaxLineLen 'a' chars in the test file.
char l[LineReader::kMaxLineLen];
memset(l, 'a', sizeof(l));
ScopedTestFile file(l, sizeof(l));
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}

View File

@ -0,0 +1,257 @@
// Copyright (c) 2012, 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.
// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
// See linux_core_dumper.h for details.
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include <asm/ptrace.h>
#include <assert.h>
#include <elf.h>
#include <stdio.h>
#include <string.h>
#include <sys/procfs.h>
#if defined(__mips__) && defined(__ANDROID__)
// To get register definitions.
#include <asm/reg.h>
#endif
#include "common/linux/linux_libc_support.h"
namespace google_breakpad {
LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
const char* core_path,
const char* procfs_path)
: LinuxDumper(pid),
core_path_(core_path),
procfs_path_(procfs_path),
thread_infos_(&allocator_, 8) {
assert(core_path_);
}
bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
const char* node) const {
if (!path || !node)
return false;
size_t node_len = my_strlen(node);
if (node_len == 0)
return false;
size_t procfs_path_len = my_strlen(procfs_path_);
size_t total_length = procfs_path_len + 1 + node_len;
if (total_length >= NAME_MAX)
return false;
memcpy(path, procfs_path_, procfs_path_len);
path[procfs_path_len] = '/';
memcpy(path + procfs_path_len + 1, node, node_len);
path[total_length] = '\0';
return true;
}
bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
const void* src, size_t length) {
ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
// TODO(benchan): Investigate whether the data to be copied could span
// across multiple segments in the core dump file. ElfCoreDump::CopyData
// and this method do not handle that case yet.
if (!core_.CopyData(dest, virtual_address, length)) {
// If the data segment is not found in the core dump, fill the result
// with marker characters.
memset(dest, 0xab, length);
return false;
}
return true;
}
bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (index >= thread_infos_.size())
return false;
*info = thread_infos_[index];
const uint8_t* stack_pointer;
#if defined(__i386)
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
#elif defined(__x86_64)
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
#elif defined(__ARM_EABI__)
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
#elif defined(__aarch64__)
memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
#elif defined(__mips__)
stack_pointer =
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
#else
#error "This code hasn't been ported to your platform yet."
#endif
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
return true;
}
bool LinuxCoreDumper::IsPostMortem() const {
return true;
}
bool LinuxCoreDumper::ThreadsSuspend() {
return true;
}
bool LinuxCoreDumper::ThreadsResume() {
return true;
}
bool LinuxCoreDumper::EnumerateThreads() {
if (!mapped_core_file_.Map(core_path_, 0)) {
fprintf(stderr, "Could not map core dump file into memory\n");
return false;
}
core_.SetContent(mapped_core_file_.content());
if (!core_.IsValid()) {
fprintf(stderr, "Invalid core dump file\n");
return false;
}
ElfCoreDump::Note note = core_.GetFirstNote();
if (!note.IsValid()) {
fprintf(stderr, "PT_NOTE section not found\n");
return false;
}
bool first_thread = true;
do {
ElfCoreDump::Word type = note.GetType();
MemoryRange name = note.GetName();
MemoryRange description = note.GetDescription();
if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
fprintf(stderr, "Could not found a valid PT_NOTE.\n");
return false;
}
// Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
// ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
// Thread Name Type
// -------------------------------------------------------------------
// 1st thread CORE NT_PRSTATUS
// process-wide CORE NT_PRPSINFO
// process-wide CORE NT_AUXV
// 1st thread CORE NT_FPREGSET
// 1st thread LINUX NT_PRXFPREG
// 1st thread LINUX NT_386_TLS
//
// 2nd thread CORE NT_PRSTATUS
// 2nd thread CORE NT_FPREGSET
// 2nd thread LINUX NT_PRXFPREG
// 2nd thread LINUX NT_386_TLS
//
// 3rd thread CORE NT_PRSTATUS
// 3rd thread CORE NT_FPREGSET
// 3rd thread LINUX NT_PRXFPREG
// 3rd thread LINUX NT_386_TLS
//
// The following code only works if notes are ordered as expected.
switch (type) {
case NT_PRSTATUS: {
if (description.length() != sizeof(elf_prstatus)) {
fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
return false;
}
const elf_prstatus* status =
reinterpret_cast<const elf_prstatus*>(description.data());
pid_t pid = status->pr_pid;
ThreadInfo info;
memset(&info, 0, sizeof(ThreadInfo));
info.tgid = status->pr_pgrp;
info.ppid = status->pr_ppid;
#if defined(__mips__)
#if defined(__ANDROID__)
for (int i = EF_R0; i <= EF_R31; i++)
info.mcontext.gregs[i - EF_R0] = status->pr_reg[i];
#else // __ANDROID__
for (int i = EF_REG0; i <= EF_REG31; i++)
info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i];
#endif // __ANDROID__
info.mcontext.mdlo = status->pr_reg[EF_LO];
info.mcontext.mdhi = status->pr_reg[EF_HI];
info.mcontext.pc = status->pr_reg[EF_CP0_EPC];
#else // __mips__
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
#endif // __mips__
if (first_thread) {
crash_thread_ = pid;
crash_signal_ = status->pr_info.si_signo;
}
first_thread = false;
threads_.push_back(pid);
thread_infos_.push_back(info);
break;
}
#if defined(__i386) || defined(__x86_64)
case NT_FPREGSET: {
if (thread_infos_.empty())
return false;
ThreadInfo* info = &thread_infos_.back();
if (description.length() != sizeof(info->fpregs)) {
fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
return false;
}
memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
break;
}
#endif
#if defined(__i386)
case NT_PRXFPREG: {
if (thread_infos_.empty())
return false;
ThreadInfo* info = &thread_infos_.back();
if (description.length() != sizeof(info->fpxregs)) {
fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
return false;
}
memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
break;
}
#endif
}
note = note.GetNextNote();
} while (note.IsValid());
return true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,123 @@
// Copyright (c) 2012, 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.
// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper
// class, which is derived from google_breakpad::LinuxDumper to extract
// information from a crashed process via its core dump and proc files.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/elf_core_dump.h"
#include "common/linux/memory_mapped_file.h"
namespace google_breakpad {
class LinuxCoreDumper : public LinuxDumper {
public:
// Constructs a dumper for extracting information of a given process
// with a process ID of |pid| via its core dump file at |core_path| and
// its proc files at |procfs_path|. If |procfs_path| is a copy of
// /proc/<pid>, it should contain the following files:
// auxv, cmdline, environ, exe, maps, status
LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path);
// Implements LinuxDumper::BuildProcPath().
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result.|node| is the final node without any slashes. Return true on
// success.
//
// As this dumper performs a post-mortem dump and makes use of a copy
// of the proc files of the crashed process, this derived method does
// not actually make use of |pid| and always returns a subpath of
// |procfs_path_| regardless of whether |pid| corresponds to the main
// process or a thread of the process, i.e. assuming both the main process
// and its threads have the following proc files with the same content:
// auxv, cmdline, environ, exe, maps, status
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
// Implements LinuxDumper::CopyFromProcess().
// Copies content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. This method extracts the content
// the core dump and fills |dest| with a sequence of marker bytes
// if the expected data is not found in the core dump. Returns true if
// the expected data is found in the core dump.
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length);
// Implements LinuxDumper::GetThreadInfoByIndex().
// Reads information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// Implements LinuxDumper::IsPostMortem().
// Always returns true to indicate that this dumper performs a
// post-mortem dump of a crashed process via a core dump file.
virtual bool IsPostMortem() const;
// Implements LinuxDumper::ThreadsSuspend().
// As the dumper performs a post-mortem dump via a core dump file,
// there is no threads to suspend. This method does nothing and
// always returns true.
virtual bool ThreadsSuspend();
// Implements LinuxDumper::ThreadsResume().
// As the dumper performs a post-mortem dump via a core dump file,
// there is no threads to resume. This method does nothing and
// always returns true.
virtual bool ThreadsResume();
protected:
// Implements LinuxDumper::EnumerateThreads().
// Enumerates all threads of the given process into |threads_|.
virtual bool EnumerateThreads();
private:
// Path of the core dump file.
const char* core_path_;
// Path of the directory containing the proc files of the given process,
// which is usually a copy of /proc/<pid>.
const char* procfs_path_;
// Memory-mapped core dump file at |core_path_|.
MemoryMappedFile mapped_core_file_;
// Content of the core dump file.
ElfCoreDump core_;
// Thread info found in the core dump file.
wasteful_vector<ThreadInfo> thread_infos_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_

View File

@ -0,0 +1,118 @@
// Copyright (c) 2012, 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.
// linux_core_dumper_unittest.cc:
// Unit tests for google_breakpad::LinuxCoreDumoer.
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include "common/linux/tests/crash_generator.h"
#include "common/using_std_string.h"
using namespace google_breakpad;
TEST(LinuxCoreDumperTest, BuildProcPath) {
const pid_t pid = getpid();
const char procfs_path[] = "/procfs_copy";
LinuxCoreDumper dumper(getpid(), "core_file", procfs_path);
char maps_path[NAME_MAX] = "";
char maps_path_expected[NAME_MAX];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"%s/maps", procfs_path);
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
EXPECT_STREQ(maps_path_expected, maps_path);
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
char long_node[NAME_MAX];
size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1;
memset(long_node, 'a', long_node_len);
long_node[long_node_len] = '\0';
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node));
}
TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
CrashGenerator crash_generator;
if (!crash_generator.HasDefaultCorePattern()) {
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to non-default core pattern\n");
return;
}
const unsigned kNumOfThreads = 3;
const unsigned kCrashThread = 1;
const int kCrashSignal = SIGABRT;
pid_t child_pid;
ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
kCrashSignal, &child_pid));
const string core_file = crash_generator.GetCoreFilePath();
const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
#if defined(__ANDROID__)
struct stat st;
if (stat(core_file.c_str(), &st) != 0) {
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is "
"skipped due to no core file being generated");
return;
}
#endif
LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
EXPECT_TRUE(dumper.Init());
EXPECT_TRUE(dumper.IsPostMortem());
// These are no-ops and should always return true.
EXPECT_TRUE(dumper.ThreadsSuspend());
EXPECT_TRUE(dumper.ThreadsResume());
// LinuxCoreDumper cannot determine the crash address and thus it always
// sets the crash address to 0.
EXPECT_EQ(0U, dumper.crash_address());
EXPECT_EQ(kCrashSignal, dumper.crash_signal());
EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
dumper.crash_thread());
EXPECT_EQ(kNumOfThreads, dumper.threads().size());
for (unsigned i = 0; i < kNumOfThreads; ++i) {
ThreadInfo info;
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info));
const void* stack;
size_t stack_len;
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer));
EXPECT_EQ(getpid(), info.ppid);
}
}

View File

@ -0,0 +1,475 @@
// Copyright (c) 2010, 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.
// linux_dumper.cc: Implement google_breakpad::LinuxDumper.
// See linux_dumper.h for details.
// This code deals with the mechanics of getting information about a crashed
// process. Since this code may run in a compromised address space, the same
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
// use the alternative allocator.
#include "client/linux/minidump_writer/linux_dumper.h"
#include <assert.h>
#include <elf.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/elfutils.h"
#include "common/linux/file_id.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/memory_mapped_file.h"
#include "common/linux/safe_readlink.h"
#include "third_party/lss/linux_syscall_support.h"
static const char kMappedFileUnsafePrefix[] = "/dev/";
static const char kDeletedSuffix[] = " (deleted)";
static const char kReservedFlags[] = " ---p";
inline static bool IsMappedFileOpenUnsafe(
const google_breakpad::MappingInfo& mapping) {
// It is unsafe to attempt to open a mapped file that lives under /dev,
// because the semantics of the open may be driver-specific so we'd risk
// hanging the crash dumper. And a file in /dev/ almost certainly has no
// ELF file identifier anyways.
return my_strncmp(mapping.name,
kMappedFileUnsafePrefix,
sizeof(kMappedFileUnsafePrefix) - 1) == 0;
}
namespace google_breakpad {
// All interesting auvx entry types are below AT_SYSINFO_EHDR
#define AT_MAX AT_SYSINFO_EHDR
LinuxDumper::LinuxDumper(pid_t pid)
: pid_(pid),
crash_address_(0),
crash_signal_(0),
crash_thread_(pid),
threads_(&allocator_, 8),
mappings_(&allocator_),
auxv_(&allocator_, AT_MAX + 1) {
// The passed-in size to the constructor (above) is only a hint.
// Must call .resize() to do actual initialization of the elements.
auxv_.resize(AT_MAX + 1);
}
LinuxDumper::~LinuxDumper() {
}
bool LinuxDumper::Init() {
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
}
bool
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
uint8_t identifier[sizeof(MDGUID)]) {
assert(!member || mapping_id < mappings_.size());
my_memset(identifier, 0, sizeof(MDGUID));
if (IsMappedFileOpenUnsafe(mapping))
return false;
// Special-case linux-gate because it's not a real file.
if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
void* linux_gate = NULL;
if (pid_ == sys_getpid()) {
linux_gate = reinterpret_cast<void*>(mapping.start_addr);
} else {
linux_gate = allocator_.Alloc(mapping.size);
CopyFromProcess(linux_gate, pid_,
reinterpret_cast<const void*>(mapping.start_addr),
mapping.size);
}
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
}
char filename[NAME_MAX];
size_t filename_len = my_strlen(mapping.name);
if (filename_len >= NAME_MAX) {
assert(false);
return false;
}
my_memcpy(filename, mapping.name, filename_len);
filename[filename_len] = '\0';
bool filename_modified = HandleDeletedFileInMapping(filename);
MemoryMappedFile mapped_file(filename, mapping.offset);
if (!mapped_file.data() || mapped_file.size() < SELFMAG)
return false;
bool success =
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
if (success && member && filename_modified) {
mappings_[mapping_id]->name[filename_len -
sizeof(kDeletedSuffix) + 1] = '\0';
}
return success;
}
namespace {
bool ElfFileSoNameFromMappedFile(
const void* elf_base, char* soname, size_t soname_size) {
if (!IsValidElf(elf_base)) {
// Not ELF
return false;
}
const void* segment_start;
size_t segment_size;
int elf_class;
if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC,
&segment_start, &segment_size, &elf_class)) {
// 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)) {
// No dynstr section
return false;
}
const ElfW(Dyn)* dynamic = static_cast<const ElfW(Dyn)*>(segment_start);
size_t dcount = segment_size / sizeof(ElfW(Dyn));
for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) {
if (dyn->d_tag == DT_SONAME) {
const char* dynstr = static_cast<const char*>(dynstr_start);
if (dyn->d_un.d_val >= dynstr_size) {
// Beyond the end of the dynstr section
return false;
}
const char* str = dynstr + dyn->d_un.d_val;
const size_t maxsize = dynstr_size - dyn->d_un.d_val;
my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size);
return true;
}
}
// Did not find SONAME
return false;
}
// Find the shared object name (SONAME) by examining the ELF information
// for |mapping|. If the SONAME is found copy it into the passed buffer
// |soname| and return true. The size of the buffer is |soname_size|.
// The SONAME will be truncated if it is too long to fit in the buffer.
bool ElfFileSoName(
const MappingInfo& mapping, char* soname, size_t soname_size) {
if (IsMappedFileOpenUnsafe(mapping)) {
// Not safe
return false;
}
char filename[NAME_MAX];
size_t filename_len = my_strlen(mapping.name);
if (filename_len >= NAME_MAX) {
assert(false);
// name too long
return false;
}
my_memcpy(filename, mapping.name, filename_len);
filename[filename_len] = '\0';
MemoryMappedFile mapped_file(filename, mapping.offset);
if (!mapped_file.data() || mapped_file.size() < SELFMAG) {
// mmap failed
return false;
}
return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size);
}
} // namespace
// static
void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
char* file_path,
size_t file_path_size,
char* file_name,
size_t file_name_size) {
my_strlcpy(file_path, mapping.name, file_path_size);
// If an executable is mapped from a non-zero offset, this is likely because
// the executable was loaded directly from inside an archive file (e.g., an
// apk on Android). We try to find the name of the shared object (SONAME) by
// looking in the file for ELF sections.
bool mapped_from_archive = false;
if (mapping.exec && mapping.offset != 0)
mapped_from_archive = ElfFileSoName(mapping, file_name, file_name_size);
if (mapped_from_archive) {
// Some tools (e.g., stackwalk) extract the basename from the pathname. In
// this case, we append the file_name to the mapped archive path as follows:
// file_name := libname.so
// file_path := /path/to/ARCHIVE.APK/libname.so
if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) {
my_strlcat(file_path, "/", file_path_size);
my_strlcat(file_path, file_name, file_path_size);
}
} else {
// Common case:
// file_path := /path/to/libname.so
// file_name := libname.so
const char* basename = my_strrchr(file_path, '/');
basename = basename == NULL ? file_path : (basename + 1);
my_strlcpy(file_name, basename, file_name_size);
}
}
bool LinuxDumper::ReadAuxv() {
char auxv_path[NAME_MAX];
if (!BuildProcPath(auxv_path, pid_, "auxv")) {
return false;
}
int fd = sys_open(auxv_path, O_RDONLY, 0);
if (fd < 0) {
return false;
}
elf_aux_entry one_aux_entry;
bool res = false;
while (sys_read(fd,
&one_aux_entry,
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
one_aux_entry.a_type != AT_NULL) {
if (one_aux_entry.a_type <= AT_MAX) {
auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val;
res = true;
}
}
sys_close(fd);
return res;
}
bool LinuxDumper::EnumerateMappings() {
char maps_path[NAME_MAX];
if (!BuildProcPath(maps_path, pid_, "maps"))
return false;
// linux_gate_loc is the beginning of the kernel's mapping of
// linux-gate.so in the process. It doesn't actually show up in the
// maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
// aux vector entry, which gives the information necessary to special
// case its entry when creating the list of mappings.
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
// information.
const void* linux_gate_loc =
reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]);
// Although the initial executable is usually the first mapping, it's not
// guaranteed (see http://crosbug.com/25355); therefore, try to use the
// actual entry point to find the mapping.
const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]);
const int fd = sys_open(maps_path, O_RDONLY, 0);
if (fd < 0)
return false;
LineReader* const line_reader = new(allocator_) LineReader(fd);
const char* line;
unsigned line_len;
while (line_reader->GetNextLine(&line, &line_len)) {
uintptr_t start_addr, end_addr, offset;
const char* i1 = my_read_hex_ptr(&start_addr, line);
if (*i1 == '-') {
const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
if (*i2 == ' ') {
bool exec = (*(i2 + 3) == 'x');
const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
if (*i3 == ' ') {
const char* name = NULL;
// Only copy name if the name is a valid path name, or if
// it's the VDSO image.
if (((name = my_strchr(line, '/')) == NULL) &&
linux_gate_loc &&
reinterpret_cast<void*>(start_addr) == linux_gate_loc) {
name = kLinuxGateLibraryName;
offset = 0;
}
// Merge adjacent mappings with the same name into one module,
// assuming they're a single library mapped by the dynamic linker
if (name && !mappings_.empty()) {
MappingInfo* module = mappings_.back();
if ((start_addr == module->start_addr + module->size) &&
(my_strlen(name) == my_strlen(module->name)) &&
(my_strncmp(name, module->name, my_strlen(name)) == 0)) {
module->size = end_addr - module->start_addr;
line_reader->PopLine(line_len);
continue;
}
}
// Also merge mappings that result from address ranges that the
// linker reserved but which a loaded library did not use. These
// appear as an anonymous private mapping with no access flags set
// and which directly follow an executable mapping.
if (!name && !mappings_.empty()) {
MappingInfo* module = mappings_.back();
if ((start_addr == module->start_addr + module->size) &&
module->exec &&
module->name[0] == '/' &&
offset == 0 && my_strncmp(i2,
kReservedFlags,
sizeof(kReservedFlags) - 1) == 0) {
module->size = end_addr - module->start_addr;
line_reader->PopLine(line_len);
continue;
}
}
MappingInfo* const module = new(allocator_) MappingInfo;
my_memset(module, 0, sizeof(MappingInfo));
module->start_addr = start_addr;
module->size = end_addr - start_addr;
module->offset = offset;
module->exec = exec;
if (name != NULL) {
const unsigned l = my_strlen(name);
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);
}
sys_close(fd);
return !mappings_.empty();
}
// Get information about the stack, given the stack pointer. We don't try to
// walk the stack since we might not have all the information needed to do
// unwind. So we just grab, up to, 32k of stack.
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
uintptr_t int_stack_pointer) {
// Move the stack pointer to the bottom of the page that it's in.
const uintptr_t page_size = getpagesize();
uint8_t* const stack_pointer =
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
// The number of bytes of stack which we try to capture.
static const ptrdiff_t kStackToCapture = 32 * 1024;
const MappingInfo* mapping = FindMapping(stack_pointer);
if (!mapping)
return false;
const ptrdiff_t offset = stack_pointer -
reinterpret_cast<uint8_t*>(mapping->start_addr);
const ptrdiff_t distance_to_end =
static_cast<ptrdiff_t>(mapping->size) - offset;
*stack_len = distance_to_end > kStackToCapture ?
kStackToCapture : distance_to_end;
*stack = stack_pointer;
return true;
}
// 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;
for (size_t i = 0; i < mappings_.size(); ++i) {
const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr);
if (addr >= start && addr - start < mappings_[i]->size)
return mappings_[i];
}
return NULL;
}
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
// Check for ' (deleted)' in |path|.
// |path| has to be at least as long as "/x (deleted)".
const size_t path_len = my_strlen(path);
if (path_len < kDeletedSuffixLen + 2)
return false;
if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix,
kDeletedSuffixLen) != 0) {
return false;
}
// Check |path| against the /proc/pid/exe 'symlink'.
char exe_link[NAME_MAX];
char new_path[NAME_MAX];
if (!BuildProcPath(exe_link, pid_, "exe"))
return false;
if (!SafeReadLink(exe_link, new_path))
return false;
if (my_strcmp(path, new_path) != 0)
return false;
// Check to see if someone actually named their executable 'foo (deleted)'.
struct kernel_stat exe_stat;
struct kernel_stat new_path_stat;
if (sys_stat(exe_link, &exe_stat) == 0 &&
sys_stat(new_path, &new_path_stat) == 0 &&
exe_stat.st_dev == new_path_stat.st_dev &&
exe_stat.st_ino == new_path_stat.st_ino) {
return false;
}
my_memcpy(path, exe_link, NAME_MAX);
return true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,189 @@
// Copyright (c) 2010, 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.
// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
// is a base class for extracting information of a crashed process. It
// was originally a complete implementation using the ptrace API, but
// has been refactored to allow derived implementations supporting both
// ptrace and core dump. A portion of the original implementation is now
// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
// details).
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
#include <elf.h>
#include <linux/limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/user.h>
#include "client/linux/dump_writer_common/mapping_info.h"
#include "client/linux/dump_writer_common/thread_info.h"
#include "common/memory.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
#if defined(__i386) || defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM == _ABIO32)
typedef Elf32_auxv_t elf_aux_entry;
#elif defined(__x86_64) || defined(__aarch64__) || \
(defined(__mips__) && _MIPS_SIM != _ABIO32)
typedef Elf64_auxv_t elf_aux_entry;
#endif
typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
// When we find the VDSO mapping in the process's address space, this
// is the name we use for it when writing it to the minidump.
// This should always be less than NAME_MAX!
const char kLinuxGateLibraryName[] = "linux-gate.so";
class LinuxDumper {
public:
explicit LinuxDumper(pid_t pid);
virtual ~LinuxDumper();
// Parse the data for |threads| and |mappings|.
virtual bool Init();
// Return true if the dumper performs a post-mortem dump.
virtual bool IsPostMortem() const = 0;
// Suspend/resume all threads in the given process.
virtual bool ThreadsSuspend() = 0;
virtual bool ThreadsResume() = 0;
// Read information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
// 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;
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.
// stack: (output) the lowest address in the memory area
// stack_len: (output) the length of the memory area
// stack_top: the current top of the stack
bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
PageAllocator* allocator() { return &allocator_; }
// Copy content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. Returns true on success.
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) = 0;
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result.|node| is the final node without any slashes. Returns true on
// success.
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
// Generate a File ID from the .text section of a mapped entry.
// If not a member, mapping_id is ignored. This method can also manipulate the
// |mapping|.name to truncate "(deleted)" from the file name if necessary.
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
uint8_t identifier[sizeof(MDGUID)]);
uintptr_t crash_address() const { return crash_address_; }
void set_crash_address(uintptr_t crash_address) {
crash_address_ = crash_address;
}
int crash_signal() const { return crash_signal_; }
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
pid_t crash_thread() const { return crash_thread_; }
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
// Extracts the effective path and file name of from |mapping|. In most cases
// the effective name/path are just the mapping's path and basename. In some
// other cases, however, a library can be mapped from an archive (e.g., when
// loading .so libs from an apk on Android) and this method is able to
// reconstruct the original file name.
static void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
char* file_path,
size_t file_path_size,
char* file_name,
size_t file_name_size);
protected:
bool ReadAuxv();
virtual bool EnumerateMappings();
virtual bool EnumerateThreads() = 0;
// For the case where a running program has been deleted, it'll show up in
// /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
// see if '/path/to/program (deleted)' matches /proc/pid/exe and return
// /proc/pid/exe in |path| so ELF identifier generation works correctly. This
// also checks to see if '/path/to/program (deleted)' exists, so it does not
// get fooled by a poorly named binary.
// For programs that don't end with ' (deleted)', this is a no-op.
// This assumes |path| is a buffer with length NAME_MAX.
// Returns true if |path| is modified.
bool HandleDeletedFileInMapping(char* path) const;
// ID of the crashed process.
const pid_t pid_;
// Virtual address at which the process crashed.
uintptr_t crash_address_;
// Signal that terminated the crashed process.
int crash_signal_;
// ID of the crashed thread.
pid_t crash_thread_;
mutable PageAllocator allocator_;
// IDs of all the threads.
wasteful_vector<pid_t> threads_;
// Info from /proc/<pid>/maps.
wasteful_vector<MappingInfo*> mappings_;
// Info from /proc/<pid>/auxv
wasteful_vector<elf_aux_val_t> auxv_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_

View File

@ -0,0 +1,94 @@
// Copyright (c) 2010, 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.
//
// Helper program for the linux_dumper class, which creates a bunch of
// threads. The first word of each thread's stack is set to the thread
// id.
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "common/scoped_ptr.h"
#include "third_party/lss/linux_syscall_support.h"
#if defined(__ARM_EABI__)
#define TID_PTR_REGISTER "r3"
#elif defined(__aarch64__)
#define TID_PTR_REGISTER "x3"
#elif defined(__i386)
#define TID_PTR_REGISTER "ecx"
#elif defined(__x86_64)
#define TID_PTR_REGISTER "rcx"
#elif defined(__mips__)
#define TID_PTR_REGISTER "$1"
#else
#error This test has not been ported to this platform.
#endif
void *thread_function(void *data) {
int pipefd = *static_cast<int *>(data);
volatile 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;
while (true)
asm volatile ("" : : "r" (thread_id_ptr));
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
fprintf(stderr,
"usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n");
return 1;
}
int pipefd = atoi(argv[1]);
int num_threads = atoi(argv[2]);
if (num_threads < 1) {
fprintf(stderr, "ERROR: number of threads is 0");
return 1;
}
google_breakpad::scoped_array<pthread_t> threads(new pthread_t[num_threads]);
pthread_attr_t thread_attributes;
pthread_attr_init(&thread_attributes);
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
for (int i = 1; i < num_threads; i++) {
pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd);
}
thread_function(&pipefd);
return 0;
}

View File

@ -0,0 +1,349 @@
// Copyright (c) 2012, 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.
// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
// See linux_ptrace_dumper.h for detals.
// This class was originally splitted from google_breakpad::LinuxDumper.
// This code deals with the mechanics of getting information about a crashed
// process. Since this code may run in a compromised address space, the same
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
// use the alternative allocator.
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include <asm/ptrace.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/uio.h>
#include <sys/wait.h>
#if defined(__i386)
#include <cpuid.h>
#endif
#include "client/linux/minidump_writer/directory_reader.h"
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
// Suspends a thread by attaching to it.
static bool SuspendThread(pid_t pid) {
// This may fail if the thread has just died or debugged.
errno = 0;
if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
errno != 0) {
return false;
}
while (sys_waitpid(pid, NULL, __WALL) < 0) {
if (errno != EINTR) {
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
return false;
}
}
#if defined(__i386) || defined(__x86_64)
// On x86, the stack pointer is NULL or -1, when executing trusted code in
// the seccomp sandbox. Not only does this cause difficulties down the line
// when trying to dump the thread's stack, it also results in the minidumps
// containing information about the trusted threads. This information is
// generally completely meaningless and just pollutes the minidumps.
// We thus test the stack pointer and exclude any threads that are part of
// the seccomp sandbox's trusted code.
user_regs_struct regs;
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1 ||
#if defined(__i386)
!regs.esp
#elif defined(__x86_64)
!regs.rsp
#endif
) {
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
return false;
}
#endif
return true;
}
// Resumes a thread by detaching from it.
static bool ResumeThread(pid_t pid) {
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
}
namespace google_breakpad {
LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid)
: LinuxDumper(pid),
threads_suspended_(false) {
}
bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
const char* node) const {
if (!path || !node || pid <= 0)
return false;
size_t node_len = my_strlen(node);
if (node_len == 0)
return false;
const unsigned pid_len = my_uint_len(pid);
const size_t total_length = 6 + pid_len + 1 + node_len;
if (total_length >= NAME_MAX)
return false;
my_memcpy(path, "/proc/", 6);
my_uitos(path + 6, pid, pid_len);
path[6 + pid_len] = '/';
my_memcpy(path + 6 + pid_len + 1, node, node_len);
path[total_length] = '\0';
return true;
}
bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
const void* src, size_t length) {
unsigned long tmp = 55;
size_t done = 0;
static const size_t word_size = sizeof(tmp);
uint8_t* const local = (uint8_t*) dest;
uint8_t* const remote = (uint8_t*) src;
while (done < length) {
const size_t l = (length - done > word_size) ? word_size : (length - done);
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
tmp = 0;
}
my_memcpy(local + done, &tmp, l);
done += l;
}
return true;
}
// 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
// available.
bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (index >= threads_.size())
return false;
pid_t tid = threads_[index];
assert(info != NULL);
char status_path[NAME_MAX];
if (!BuildProcPath(status_path, tid, "status"))
return false;
const int fd = sys_open(status_path, O_RDONLY, 0);
if (fd < 0)
return false;
LineReader* const line_reader = new(allocator_) LineReader(fd);
const char* line;
unsigned line_len;
info->ppid = info->tgid = -1;
while (line_reader->GetNextLine(&line, &line_len)) {
if (my_strncmp("Tgid:\t", line, 6) == 0) {
my_strtoui(&info->tgid, line + 6);
} else if (my_strncmp("PPid:\t", line, 6) == 0) {
my_strtoui(&info->ppid, line + 6);
}
line_reader->PopLine(line_len);
}
sys_close(fd);
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;
}
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;
}
void* fp_addr;
info->GetFloatingPointRegisters(&fp_addr, NULL);
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
return false;
}
#endif
#if defined(__i386)
#if !defined(bit_FXSAVE) // e.g. Clang
#define bit_FXSAVE bit_FXSR
#endif
// Detect if the CPU supports the FXSAVE/FXRSTOR instructions
int eax, ebx, ecx, edx;
__cpuid(1, eax, ebx, ecx, edx);
if (edx & bit_FXSAVE) {
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) {
return false;
}
} else {
memset(&info->fpxregs, 0, sizeof(info->fpxregs));
}
#endif // defined(__i386)
#if defined(__i386) || defined(__x86_64)
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
if (sys_ptrace(
PTRACE_PEEKUSER, tid,
reinterpret_cast<void*> (offsetof(struct user,
u_debugreg[0]) + i *
sizeof(debugreg_t)),
&info->dregs[i]) == -1) {
return false;
}
}
#endif
#if defined(__mips__)
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1);
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(DSP_BASE + 1), &info->mcontext.lo1);
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(DSP_BASE + 2), &info->mcontext.hi2);
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(DSP_BASE + 3), &info->mcontext.lo2);
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(DSP_BASE + 4), &info->mcontext.hi3);
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(DSP_BASE + 5), &info->mcontext.lo3);
sys_ptrace(PTRACE_PEEKUSER, tid,
reinterpret_cast<void*>(DSP_CONTROL), &info->mcontext.dsp);
#endif
const uint8_t* stack_pointer;
#if defined(__i386)
my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
#elif defined(__x86_64)
my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
#elif defined(__ARM_EABI__)
my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
#elif defined(__aarch64__)
my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
#elif defined(__mips__)
stack_pointer =
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
#else
#error "This code hasn't been ported to your platform yet."
#endif
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
return true;
}
bool LinuxPtraceDumper::IsPostMortem() const {
return false;
}
bool LinuxPtraceDumper::ThreadsSuspend() {
if (threads_suspended_)
return true;
for (size_t i = 0; i < threads_.size(); ++i) {
if (!SuspendThread(threads_[i])) {
// If the thread either disappeared before we could attach to it, or if
// it was part of the seccomp sandbox's trusted code, it is OK to
// silently drop it from the minidump.
if (i < threads_.size() - 1) {
my_memmove(&threads_[i], &threads_[i + 1],
(threads_.size() - i - 1) * sizeof(threads_[i]));
}
threads_.resize(threads_.size() - 1);
--i;
}
}
threads_suspended_ = true;
return threads_.size() > 0;
}
bool LinuxPtraceDumper::ThreadsResume() {
if (!threads_suspended_)
return false;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= ResumeThread(threads_[i]);
threads_suspended_ = false;
return good;
}
// Parse /proc/$pid/task to list all the threads of the process identified by
// pid.
bool LinuxPtraceDumper::EnumerateThreads() {
char task_path[NAME_MAX];
if (!BuildProcPath(task_path, pid_, "task"))
return false;
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
if (fd < 0)
return false;
DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
// The directory may contain duplicate entries which we filter by assuming
// that they are consecutive.
int last_tid = -1;
const char* dent_name;
while (dir_reader->GetNextEntry(&dent_name)) {
if (my_strcmp(dent_name, ".") &&
my_strcmp(dent_name, "..")) {
int tid = 0;
if (my_strtoui(&tid, dent_name) &&
last_tid != tid) {
last_tid = tid;
threads_.push_back(tid);
}
}
dir_reader->PopEntry();
}
sys_close(fd);
return true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,92 @@
// Copyright (c) 2012, 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.
// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper
// class, which is derived from google_breakpad::LinuxDumper to extract
// information from a crashed process via ptrace.
// This class was originally splitted from google_breakpad::LinuxDumper.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
#include "client/linux/minidump_writer/linux_dumper.h"
namespace google_breakpad {
class LinuxPtraceDumper : public LinuxDumper {
public:
// Constructs a dumper for extracting information of a given process
// with a process ID of |pid|.
explicit LinuxPtraceDumper(pid_t pid);
// Implements LinuxDumper::BuildProcPath().
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result. |node| is the final node without any slashes. Returns true on
// success.
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
// Implements LinuxDumper::CopyFromProcess().
// Copies content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. This method uses ptrace to extract
// the content from the target process. Always returns true.
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length);
// Implements LinuxDumper::GetThreadInfoByIndex().
// Reads information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// Implements LinuxDumper::IsPostMortem().
// Always returns false to indicate this dumper performs a dump of
// a crashed process via ptrace.
virtual bool IsPostMortem() const;
// Implements LinuxDumper::ThreadsSuspend().
// Suspends all threads in the given process. Returns true on success.
virtual bool ThreadsSuspend();
// Implements LinuxDumper::ThreadsResume().
// Resumes all threads in the given process. Returns true on success.
virtual bool ThreadsResume();
protected:
// Implements LinuxDumper::EnumerateThreads().
// Enumerates all threads of the given process into |threads_|.
virtual bool EnumerateThreads();
private:
// Set to true if all threads of the crashed process are suspended.
bool threads_suspended_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_

View File

@ -0,0 +1,463 @@
// Copyright (c) 2009, 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.
// linux_ptrace_dumper_unittest.cc:
// Unit tests for google_breakpad::LinuxPtraceDumper.
//
// This file was renamed from linux_dumper_unittest.cc and modified due
// to LinuxDumper being splitted into two classes.
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/safe_readlink.h"
#include "common/memory.h"
#include "common/using_std_string.h"
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER 0x59616d61
#endif
using namespace google_breakpad;
namespace {
typedef testing::Test LinuxPtraceDumperTest;
/* Fixture for running tests in a child process. */
class LinuxPtraceDumperChildTest : public testing::Test {
protected:
virtual void SetUp() {
child_pid_ = fork();
#ifndef __ANDROID__
prctl(PR_SET_PTRACER, child_pid_);
#endif
}
/* Gtest is calling TestBody from this class, which sets up a child
* process in which the RealTestBody virtual member is called.
* As such, TestBody is not supposed to be overridden in derived classes.
*/
virtual void TestBody() /* final */ {
if (child_pid_ == 0) {
// child process
RealTestBody();
exit(HasFatalFailure() ? kFatalFailure :
(HasNonfatalFailure() ? kNonFatalFailure : 0));
}
ASSERT_TRUE(child_pid_ > 0);
int status;
waitpid(child_pid_, &status, 0);
if (WEXITSTATUS(status) == kFatalFailure) {
GTEST_FATAL_FAILURE_("Test failed in child process");
} else if (WEXITSTATUS(status) == kNonFatalFailure) {
GTEST_NONFATAL_FAILURE_("Test failed in child process");
}
}
/* Gtest defines TestBody functions through its macros, but classes
* derived from this one need to define RealTestBody instead.
* This is achieved by defining a TestBody macro further below.
*/
virtual void RealTestBody() = 0;
private:
static const int kFatalFailure = 1;
static const int kNonFatalFailure = 2;
pid_t child_pid_;
};
} // namespace
/* Replace TestBody declarations within TEST*() with RealTestBody
* declarations */
#define TestBody RealTestBody
TEST_F(LinuxPtraceDumperChildTest, Setup) {
LinuxPtraceDumper dumper(getppid());
}
TEST_F(LinuxPtraceDumperChildTest, FindMappings) {
LinuxPtraceDumper dumper(getppid());
ASSERT_TRUE(dumper.Init());
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
ASSERT_FALSE(dumper.FindMapping(NULL));
}
TEST_F(LinuxPtraceDumperChildTest, ThreadList) {
LinuxPtraceDumper dumper(getppid());
ASSERT_TRUE(dumper.Init());
ASSERT_GE(dumper.threads().size(), (size_t)1);
bool found = false;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
if (dumper.threads()[i] == getppid()) {
ASSERT_FALSE(found);
found = true;
}
}
ASSERT_TRUE(found);
}
// Helper stack class to close a file descriptor and unmap
// a mmap'ed mapping.
class StackHelper {
public:
StackHelper()
: fd_(-1), mapping_(NULL), size_(0) {}
~StackHelper() {
if (size_)
munmap(mapping_, size_);
if (fd_ >= 0)
close(fd_);
}
void Init(int fd, char* mapping, size_t size) {
fd_ = fd;
mapping_ = mapping;
size_ = size;
}
char* mapping() const { return mapping_; }
size_t size() const { return size_; }
private:
int fd_;
char* mapping_;
size_t size_;
};
class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest {
protected:
virtual void SetUp();
string helper_path_;
size_t page_size_;
StackHelper helper_;
};
void LinuxPtraceDumperMappingsTest::SetUp() {
helper_path_ = GetHelperBinary();
if (helper_path_.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// mmap two segments out of the helper binary, one
// enclosed in the other, but with different protections.
page_size_ = sysconf(_SC_PAGESIZE);
const size_t kMappingSize = 3 * page_size_;
int fd = open(helper_path_.c_str(), O_RDONLY);
ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_
<< ", Error: " << strerror(errno);
char* mapping =
reinterpret_cast<char*>(mmap(NULL,
kMappingSize,
PROT_READ,
MAP_SHARED,
fd,
0));
ASSERT_TRUE(mapping);
// Ensure that things get cleaned up.
helper_.Init(fd, mapping, kMappingSize);
// Carve a page out of the first mapping with different permissions.
char* inside_mapping = reinterpret_cast<char*>(
mmap(mapping + 2 * page_size_,
page_size_,
PROT_NONE,
MAP_SHARED | MAP_FIXED,
fd,
// Map a different offset just to
// better test real-world conditions.
page_size_));
ASSERT_TRUE(inside_mapping);
LinuxPtraceDumperChildTest::SetUp();
}
TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) {
// Now check that LinuxPtraceDumper interpreted the mappings properly.
LinuxPtraceDumper dumper(getppid());
ASSERT_TRUE(dumper.Init());
int mapping_count = 0;
for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
const MappingInfo& mapping = *dumper.mappings()[i];
if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) {
// This mapping should encompass the entire original mapped
// range.
EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()),
mapping.start_addr);
EXPECT_EQ(this->helper_.size(), mapping.size);
EXPECT_EQ(0U, mapping.offset);
mapping_count++;
}
}
EXPECT_EQ(1, mapping_count);
}
TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) {
const pid_t pid = getppid();
LinuxPtraceDumper dumper(pid);
char maps_path[NAME_MAX] = "";
char maps_path_expected[NAME_MAX];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"/proc/%d/maps", pid);
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
EXPECT_STREQ(maps_path_expected, maps_path);
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
char long_node[NAME_MAX];
size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1;
memset(long_node, 'a', long_node_len);
long_node[long_node_len] = '\0';
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
}
#if !defined(__ARM_EABI__) && !defined(__mips__)
// Ensure that the linux-gate VDSO is included in the mapping list.
TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) {
LinuxPtraceDumper dumper(getppid());
ASSERT_TRUE(dumper.Init());
void* linux_gate_loc =
reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]);
ASSERT_TRUE(linux_gate_loc);
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
const MappingInfo* mapping;
for (unsigned i = 0; i < mappings.size(); ++i) {
mapping = mappings[i];
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
break;
}
}
EXPECT_TRUE(found_linux_gate);
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) {
LinuxPtraceDumper dumper(getppid());
ASSERT_TRUE(dumper.Init());
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
unsigned index = 0;
for (unsigned i = 0; i < mappings.size(); ++i) {
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
index = i;
break;
}
}
ASSERT_TRUE(found_linux_gate);
// Need to suspend the child so ptrace actually works.
ASSERT_TRUE(dumper.ThreadsSuspend());
uint8_t identifier[sizeof(MDGUID)];
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
true,
index,
identifier));
uint8_t empty_identifier[sizeof(MDGUID)];
memset(empty_identifier, 0, sizeof(empty_identifier));
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
EXPECT_TRUE(dumper.ThreadsResume());
}
#endif
TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
// Calculate the File ID of our binary using both
// FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
// and ensure that we get the same result from both.
char exe_name[PATH_MAX];
ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
LinuxPtraceDumper dumper(getppid());
ASSERT_TRUE(dumper.Init());
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
bool found_exe = false;
unsigned i;
for (i = 0; i < mappings.size(); ++i) {
const MappingInfo* mapping = mappings[i];
if (!strcmp(mapping->name, exe_name)) {
found_exe = true;
break;
}
}
ASSERT_TRUE(found_exe);
uint8_t identifier1[sizeof(MDGUID)];
uint8_t identifier2[sizeof(MDGUID)];
EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
identifier1));
FileID fileid(exe_name);
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
char identifier_string1[37];
char identifier_string2[37];
FileID::ConvertIdentifierToString(identifier1, identifier_string1,
37);
FileID::ConvertIdentifierToString(identifier2, identifier_string2,
37);
EXPECT_STREQ(identifier_string1, identifier_string2);
}
/* Get back to normal behavior of TEST*() macros wrt TestBody. */
#undef TestBody
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);
// Children are ready now.
LinuxPtraceDumper dumper(child_pid);
ASSERT_TRUE(dumper.Init());
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
EXPECT_TRUE(dumper.ThreadsSuspend());
ThreadInfo one_thread;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
const void* stack;
size_t stack_len;
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len,
one_thread.stack_pointer));
// In the helper program, we stored a pointer to the thread id in a
// specific register. Check that we can recover its value.
#if defined(__ARM_EABI__)
pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]);
#elif defined(__aarch64__)
pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]);
#elif defined(__i386)
pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx);
#elif defined(__x86_64)
pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx);
#elif defined(__mips__)
pid_t* process_tid_location =
reinterpret_cast<pid_t*>(one_thread.mcontext.gregs[1]);
#else
#error This test has not been ported to this platform.
#endif
pid_t one_thread_id;
dumper.CopyFromProcess(&one_thread_id,
dumper.threads()[i],
process_tid_location,
4);
EXPECT_EQ(dumper.threads()[i], one_thread_id);
}
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));
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,124 @@
// Copyright (c) 2009, 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 CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
#include <stdint.h>
#include <sys/types.h>
#include <sys/ucontext.h>
#include <unistd.h>
#include <list>
#include <utility>
#include "client/linux/minidump_writer/linux_dumper.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
class ExceptionHandler;
#if defined(__aarch64__)
typedef struct fpsimd_context fpstate_t;
#elif !defined(__ARM_EABI__) && !defined(__mips__)
typedef struct _libc_fpstate fpstate_t;
#endif
// These entries store a list of memory regions that the client wants included
// in the minidump.
struct AppMemory {
void* ptr;
size_t length;
bool operator==(const struct AppMemory& other) const {
return ptr == other.ptr;
}
bool operator==(const void* other) const {
return ptr == other;
}
};
typedef std::list<AppMemory> AppMemoryList;
// Writes a minidump to the filesystem. These functions do not malloc nor use
// libc functions which may. Thus, it can be used in contexts where the state
// of the heap may be corrupt.
// minidump_path: the path to the file to write to. This is opened O_EXCL and
// fails open fails.
// crashing_process: the pid of the crashing process. This must be trusted.
// blob: a blob of data from the crashing process. See exception_handler.h
// blob_size: the length of |blob|, in bytes
//
// Returns true iff successful.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size);
// 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);
// Alternate form of WriteMinidump() that works with processes that
// are not expected to have crashed. If |process_blamed_thread| is
// meaningful, it will be the one from which a crash signature is
// extracted. It is not expected that this function will be called
// from a compromised context, but it is safe to do so.
bool WriteMinidump(const char* minidump_path, pid_t process,
pid_t process_blamed_thread);
// These overloads also allow passing a list of known mappings and
// a list of additional memory regions to be included in the minidump.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
// 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);
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);
bool WriteMinidump(const char* filename,
const MappingList& mappings,
const AppMemoryList& appdata,
LinuxDumper* dumper);
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_

View File

@ -0,0 +1,756 @@
// Copyright (c) 2011 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 <fcntl.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unistd.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/safe_readlink.h"
#include "common/scoped_ptr.h"
#include "common/tests/auto_tempdir.h"
#include "common/tests/file_utils.h"
#include "common/using_std_string.h"
#include "google_breakpad/processor/minidump.h"
using namespace google_breakpad;
// Length of a formatted GUID string =
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
const int kGUIDStringSize = 37;
namespace {
typedef testing::Test MinidumpWriterTest;
const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest";
TEST(MinidumpWriterTest, SetupWithPath) {
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);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// Set a non-zero tid to avoid tripping asserts.
context.tid = child;
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
struct stat st;
ASSERT_EQ(0, stat(templ.c_str(), &st));
ASSERT_GT(st.st_size, 0);
close(fds[1]);
}
TEST(MinidumpWriterTest, SetupWithFD) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
// Set a non-zero tid to avoid tripping asserts.
context.tid = child;
ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
struct stat st;
ASSERT_EQ(0, stat(templ.c_str(), &st));
ASSERT_GT(st.st_size, 0);
close(fds[1]);
}
// Test that mapping info can be specified when writing a minidump,
// and that it ends up in the module list of the minidump.
TEST(MinidumpWriterTest, MappingInfo) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const uint32_t memory_size = sysconf(_SC_PAGESIZE);
const char* kMemoryName = "a fake module";
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
char module_identifier_buffer[kGUIDStringSize];
FileID::ConvertIdentifierToString(kModuleGUID,
module_identifier_buffer,
sizeof(module_identifier_buffer));
string module_identifier(module_identifier_buffer);
// Strip out dashes
size_t pos;
while ((pos = module_identifier.find('-')) != string::npos) {
module_identifier.erase(pos, 1);
}
// And append a zero, because module IDs include an "age" field
// which is always zero on Linux.
module_identifier += "0";
// Get some memory.
char* memory =
reinterpret_cast<char*>(mmap(NULL,
memory_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0));
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
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);
}
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;
// Add information about the mapped memory.
MappingInfo info;
info.start_addr = kMemoryAddress;
info.size = memory_size;
info.offset = 0;
strcpy(info.name, kMemoryName);
MappingList mappings;
AppMemoryList memory_list;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
mappings, memory_list));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
// and debug ID.
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module =
module_list->GetModuleForAddress(kMemoryAddress);
ASSERT_TRUE(module);
EXPECT_EQ(kMemoryAddress, module->base_address());
EXPECT_EQ(memory_size, module->size());
EXPECT_EQ(kMemoryName, module->code_file());
EXPECT_EQ(module_identifier, module->debug_identifier());
uint32_t len;
// These streams are expected to be there
EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
close(fds[1]);
}
// Test that mapping info can be specified, and that it overrides
// existing mappings that are wholly contained within the specified
// range.
TEST(MinidumpWriterTest, MappingInfoContained) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const int32_t memory_size = sysconf(_SC_PAGESIZE);
const char* kMemoryName = "a fake module";
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
char module_identifier_buffer[kGUIDStringSize];
FileID::ConvertIdentifierToString(kModuleGUID,
module_identifier_buffer,
sizeof(module_identifier_buffer));
string module_identifier(module_identifier_buffer);
// Strip out dashes
size_t pos;
while ((pos = module_identifier.find('-')) != string::npos) {
module_identifier.erase(pos, 1);
}
// And append a zero, because module IDs include an "age" field
// which is always zero on Linux.
module_identifier += "0";
// mmap a file
AutoTempDir temp_dir;
string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
ASSERT_NE(-1, fd);
unlink(tempfile.c_str());
// fill with zeros
google_breakpad::scoped_array<char> buffer(new char[memory_size]);
memset(buffer.get(), 0, memory_size);
ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size));
lseek(fd, 0, SEEK_SET);
char* memory =
reinterpret_cast<char*>(mmap(NULL,
memory_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
fd,
0));
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
close(fd);
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);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
context.tid = 1;
string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName;
// Add information about the mapped memory. Report it as being larger than
// it actually is.
MappingInfo info;
info.start_addr = kMemoryAddress - memory_size;
info.size = memory_size * 3;
info.offset = 0;
strcpy(info.name, kMemoryName);
MappingList mappings;
AppMemoryList memory_list;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
mappings, memory_list));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
// and debug ID.
Minidump minidump(dumpfile);
ASSERT_TRUE(minidump.Read());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module =
module_list->GetModuleForAddress(kMemoryAddress);
ASSERT_TRUE(module);
EXPECT_EQ(info.start_addr, module->base_address());
EXPECT_EQ(info.size, module->size());
EXPECT_EQ(kMemoryName, module->code_file());
EXPECT_EQ(module_identifier, module->debug_identifier());
close(fds[1]);
}
TEST(MinidumpWriterTest, DeletedBinary) {
const string kNumberOfThreadsArgument = "1";
const string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// Copy binary to a temp file.
AutoTempDir temp_dir;
string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str()))
<< "Failed to copy " << helper_path << " to " << binpath;
ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(binpath.c_str(),
binpath.c_str(),
pipe_fd_string,
kNumberOfThreadsArgument.c_str(),
NULL);
}
close(fds[1]);
// Wait for the child process to signal that it's ready.
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;
const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr);
close(fds[0]);
// Child is ready now.
// Unlink the test binary.
unlink(binpath.c_str());
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// Set a non-zero tid to avoid tripping asserts.
context.tid = child_pid;
ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
sizeof(context)));
kill(child_pid, SIGKILL);
struct stat st;
ASSERT_EQ(0, stat(templ.c_str(), &st));
ASSERT_GT(st.st_size, 0);
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
// Check that the main module filename is correct.
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module = module_list->GetMainModule();
EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
// Check that the file ID is correct.
FileID fileid(helper_path.c_str());
uint8_t identifier[sizeof(MDGUID)];
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
char identifier_string[kGUIDStringSize];
FileID::ConvertIdentifierToString(identifier,
identifier_string,
kGUIDStringSize);
string module_identifier(identifier_string);
// Strip out dashes
size_t pos;
while ((pos = module_identifier.find('-')) != string::npos) {
module_identifier.erase(pos, 1);
}
// And append a zero, because module IDs include an "age" field
// which is always zero on Linux.
module_identifier += "0";
EXPECT_EQ(module_identifier, module->debug_identifier());
}
// Test that an additional memory region can be added to the minidump.
TEST(MinidumpWriterTest, AdditionalMemory) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
// Get some heap memory.
uint8_t* memory = new uint8_t[kMemorySize];
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
// Stick some data into the memory so the contents can be verified.
for (uint32_t i = 0; i < kMemorySize; ++i) {
memory[i] = i % 255;
}
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
// This needs a valid context for minidump writing to work, but getting
// a useful one from the child is too much work, so just use one from
// the parent since the child is just a forked copy anyway.
ASSERT_EQ(0, getcontext(&context.context));
context.tid = child;
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
unlink(templ.c_str());
MappingList mappings;
AppMemoryList memory_list;
// Add the memory region to the list of memory to be included.
AppMemory app_memory;
app_memory.ptr = memory;
app_memory.length = kMemorySize;
memory_list.push_back(app_memory);
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
mappings, memory_list));
// Read the minidump. Ensure that the memory region is present
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
ASSERT_TRUE(dump_memory_list);
const MinidumpMemoryRegion* region =
dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
ASSERT_TRUE(region);
EXPECT_EQ(kMemoryAddress, region->GetBase());
EXPECT_EQ(kMemorySize, region->GetSize());
// Verify memory contents.
EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
delete[] memory;
close(fds[1]);
}
// Test that an invalid thread stack pointer still results in a minidump.
TEST(MinidumpWriterTest, InvalidStackPointer) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
// This needs a valid context for minidump writing to work, but getting
// a useful one from the child is too much work, so just use one from
// the parent since the child is just a forked copy anyway.
ASSERT_EQ(0, getcontext(&context.context));
context.tid = child;
// Fake the child's stack pointer for its crashing thread. NOTE: This must
// be an invalid memory address for the child process (stack or otherwise).
// Try 1MB below the current stack.
uintptr_t invalid_stack_pointer =
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
#if defined(__i386)
context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
#elif defined(__x86_64)
context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
#elif defined(__ARM_EABI__)
context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
#elif defined(__aarch64__)
context.context.uc_mcontext.sp = invalid_stack_pointer;
#elif defined(__mips__)
context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =
invalid_stack_pointer;
#else
# error "This code has not been ported to your platform yet."
#endif
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
// presented with an invalid stack pointer.
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
// Read the minidump. Ensure that the memory region is present
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
// TODO(ted.mielczarek,mkrebs): Enable this part of the test once
// https://breakpad.appspot.com/413002/ is committed.
#if 0
// Make sure there's a thread without a stack. NOTE: It's okay if
// GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
// region problem".
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
ASSERT_TRUE(dump_thread_list);
bool found_empty_stack = false;
for (int i = 0; i < dump_thread_list->thread_count(); i++) {
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
ASSERT_TRUE(thread->thread() != NULL);
// When the stack size is zero bytes, GetMemory() returns NULL.
if (thread->GetMemory() == NULL) {
found_empty_stack = true;
break;
}
}
// NOTE: If you fail this, first make sure that "invalid_stack_pointer"
// above is indeed set to an invalid address.
ASSERT_TRUE(found_empty_stack);
#endif
close(fds[1]);
}
// Test that limiting the size of the minidump works.
TEST(MinidumpWriterTest, MinidumpSizeLimit) {
static const int kNumberOfThreadsInHelperProgram = 40;
char number_of_threads_arg[3];
sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram);
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
// 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(),
helper_path.c_str(),
pipe_fd_string,
number_of_threads_arg,
NULL);
}
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);
// Child and its threads are ready now.
off_t normal_file_size;
int total_normal_stack_size = 0;
AutoTempDir temp_dir;
// First, write a minidump with no size limit.
{
string normal_dump = temp_dir.path() +
"/minidump-writer-unittest.dmp";
ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1,
child_pid, NULL, 0,
MappingList(), AppMemoryList()));
struct stat st;
ASSERT_EQ(0, stat(normal_dump.c_str(), &st));
ASSERT_GT(st.st_size, 0);
normal_file_size = st.st_size;
Minidump minidump(normal_dump);
ASSERT_TRUE(minidump.Read());
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
ASSERT_TRUE(dump_thread_list);
for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
ASSERT_TRUE(thread->thread() != NULL);
// When the stack size is zero bytes, GetMemory() returns NULL.
MinidumpMemoryRegion* memory = thread->GetMemory();
ASSERT_TRUE(memory != NULL);
total_normal_stack_size += memory->GetSize();
}
}
// Second, write a minidump with a size limit big enough to not trigger
// anything.
{
// Set size limit arbitrarily 1MB larger than the normal file size -- such
// that the limiting code will not kick in.
const off_t minidump_size_limit = normal_file_size + 1024*1024;
string same_dump = temp_dir.path() +
"/minidump-writer-unittest-same.dmp";
ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit,
child_pid, NULL, 0,
MappingList(), AppMemoryList()));
struct stat st;
ASSERT_EQ(0, stat(same_dump.c_str(), &st));
// Make sure limiting wasn't actually triggered. NOTE: If you fail this,
// first make sure that "minidump_size_limit" above is indeed set to a
// large enough value -- the limit-checking code in minidump_writer.cc
// does just a rough estimate.
ASSERT_EQ(normal_file_size, st.st_size);
}
// Third, write a minidump with a size limit small enough to be triggered.
{
// Set size limit to some arbitrary amount, such that the limiting code
// will kick in. The equation used to set this value was determined by
// simply reversing the size-limit logic a little bit in order to pick a
// size we know will trigger it. The definition of
// kLimitAverageThreadStackLength here was copied from class
// MinidumpWriter in minidump_writer.cc.
static const unsigned kLimitAverageThreadStackLength = 8 * 1024;
off_t minidump_size_limit = kNumberOfThreadsInHelperProgram *
kLimitAverageThreadStackLength;
// If, in reality, each of the threads' stack is *smaller* than
// kLimitAverageThreadStackLength, the normal file size could very well be
// smaller than the arbitrary limit that was just set. In that case,
// either of these numbers should trigger the size-limiting code, but we
// might as well pick the smallest.
if (normal_file_size < minidump_size_limit)
minidump_size_limit = normal_file_size;
string limit_dump = temp_dir.path() +
"/minidump-writer-unittest-limit.dmp";
ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit,
child_pid, NULL, 0,
MappingList(), AppMemoryList()));
struct stat st;
ASSERT_EQ(0, stat(limit_dump.c_str(), &st));
ASSERT_GT(st.st_size, 0);
// Make sure the file size is at least smaller than the original. If this
// fails because it's the same size, then the size-limit logic didn't kick
// in like it was supposed to.
EXPECT_LT(st.st_size, normal_file_size);
Minidump minidump(limit_dump);
ASSERT_TRUE(minidump.Read());
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
ASSERT_TRUE(dump_thread_list);
int total_limit_stack_size = 0;
for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
ASSERT_TRUE(thread->thread() != NULL);
// When the stack size is zero bytes, GetMemory() returns NULL.
MinidumpMemoryRegion* memory = thread->GetMemory();
ASSERT_TRUE(memory != NULL);
total_limit_stack_size += memory->GetSize();
}
// Make sure stack size shrunk by at least 1KB per extra thread. The
// definition of kLimitBaseThreadCount here was copied from class
// MinidumpWriter in minidump_writer.cc.
// Note: The 1KB is arbitrary, and assumes that the thread stacks are big
// enough to shrink by that much. For example, if each thread stack was
// originally only 2KB, the current size-limit logic wouldn't actually
// shrink them because that's the size to which it tries to shrink. If
// you fail this part of the test due to something like that, the test
// logic should probably be improved to account for your situation.
const unsigned kLimitBaseThreadCount = 20;
const unsigned kMinPerExtraThreadStackReduction = 1024;
const int min_expected_reduction = (kNumberOfThreadsInHelperProgram -
kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction;
EXPECT_LT(total_limit_stack_size,
total_normal_stack_size - min_expected_reduction);
}
// Kill the helper program.
kill(child_pid, SIGKILL);
}
} // namespace

View File

@ -0,0 +1,66 @@
// Copyright (c) 2011 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.
// minidump_writer_unittest_utils.cc:
// Shared routines used by unittests under client/linux/minidump_writer.
#include <limits.h>
#include <stdlib.h>
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/safe_readlink.h"
#include "common/using_std_string.h"
namespace google_breakpad {
string GetHelperBinary() {
string helper_path;
char *bindir = getenv("bindir");
if (bindir) {
helper_path = string(bindir) + "/";
} else {
// Locate helper binary next to the current binary.
char self_path[PATH_MAX];
if (!SafeReadLink("/proc/self/exe", self_path)) {
return "";
}
helper_path = string(self_path);
size_t pos = helper_path.rfind('/');
if (pos == string::npos) {
return "";
}
helper_path.erase(pos + 1);
}
helper_path += "linux_dumper_unittest_helper";
return helper_path;
}
} // namespace google_breakpad

View File

@ -0,0 +1,49 @@
// Copyright (c) 2012, 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.
// minidump_writer_unittest_utils.h:
// Shared routines used by unittests under client/linux/minidump_writer.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
// Returns the full path to linux_dumper_unittest_helper. The full path is
// discovered either by using the environment variable "bindir" or by using
// the location of the main module of the currently running process.
string GetHelperBinary();
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_

View File

@ -0,0 +1,130 @@
// Copyright (c) 2013, 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 CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// A class for reading /proc/cpuinfo without using fopen/fgets or other
// functions which may allocate memory.
class ProcCpuInfoReader {
public:
ProcCpuInfoReader(int fd)
: line_reader_(fd), pop_count_(-1) {
}
// Return the next field name, or NULL in case of EOF.
// field: (output) Pointer to zero-terminated field name.
// Returns true on success, or false on EOF or error (line too long).
bool GetNextField(const char** field) {
for (;;) {
const char* line;
unsigned line_len;
// Try to read next line.
if (pop_count_ >= 0) {
line_reader_.PopLine(pop_count_);
pop_count_ = -1;
}
if (!line_reader_.GetNextLine(&line, &line_len))
return false;
pop_count_ = static_cast<int>(line_len);
const char* line_end = line + line_len;
// Expected format: <field-name> <space>+ ':' <space> <value>
// Note that:
// - empty lines happen.
// - <field-name> can contain spaces.
// - some fields have an empty <value>
char* sep = static_cast<char*>(my_memchr(line, ':', line_len));
if (sep == NULL)
continue;
// Record the value. Skip leading space after the column to get
// its start.
const char* val = sep+1;
while (val < line_end && my_isspace(*val))
val++;
value_ = val;
value_len_ = static_cast<size_t>(line_end - val);
// Remove trailing spaces before the column to properly 0-terminate
// the field name.
while (sep > line && my_isspace(sep[-1]))
sep--;
if (sep == line)
continue;
// zero-terminate field name.
*sep = '\0';
*field = line;
return true;
}
}
// Return the field value. This must be called after a succesful
// call to GetNextField().
const char* GetValue() {
assert(value_);
return value_;
}
// Same as GetValue(), but also returns the length in characters of
// the value.
const char* GetValueAndLen(size_t* length) {
assert(value_);
*length = value_len_;
return value_;
}
private:
LineReader line_reader_;
int pop_count_;
const char* value_;
size_t value_len_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_

View File

@ -0,0 +1,199 @@
// Copyright (c) 2013, 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 <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "client/linux/minidump_writer/proc_cpuinfo_reader.h"
#include "breakpad_googletest_includes.h"
#include "common/linux/tests/auto_testfile.h"
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
namespace {
typedef testing::Test ProcCpuInfoReaderTest;
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("proc_cpuinfo_reader", text) {
}
};
}
TEST(ProcCpuInfoReaderTest, EmptyFile) {
ScopedTestFile file("");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, OneLineTerminated) {
ScopedTestFile file("foo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, OneLine) {
ScopedTestFile file("foo : bar");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
size_t value_len;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len));
ASSERT_EQ(3U, value_len);
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) {
ScopedTestFile file("foo : bar\nzoo : tut\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("zoo", field);
ASSERT_STREQ("tut", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipMalformedLine) {
ScopedTestFile file("this line should have a column\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) {
ScopedTestFile file("\n\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipEmptyField) {
ScopedTestFile file(" : bar\nzoo : tut\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("zoo", field);
ASSERT_STREQ("tut", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) {
ScopedTestFile file("foo : bar\n\n\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, FieldWithSpaces) {
ScopedTestFile file("foo bar : zoo\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo bar", field);
ASSERT_STREQ("zoo", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, EmptyValue) {
ScopedTestFile file("foo :\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
size_t value_len;
ASSERT_STREQ("", reader.GetValueAndLen(&value_len));
ASSERT_EQ(0U, value_len);
ASSERT_FALSE(reader.GetNextField(&field));
}

View File

@ -0,0 +1,104 @@
// Copyright (c) 2009, 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/linux/google_crashdump_uploader.h"
#include "third_party/linux/include/gflags/gflags.h"
#include <string>
#include <iostream>
#include "common/using_std_string.h"
DEFINE_string(crash_server, "https://clients2.google.com/cr",
"The crash server to upload minidumps to.");
DEFINE_string(product_name, "",
"The product name that the minidump corresponds to.");
DEFINE_string(product_version, "",
"The version of the product that produced the minidump.");
DEFINE_string(client_id, "",
"The client GUID");
DEFINE_string(minidump_path, "",
"The path of the minidump file.");
DEFINE_string(ptime, "",
"The process uptime in milliseconds.");
DEFINE_string(ctime, "",
"The cumulative process uptime in milliseconds.");
DEFINE_string(email, "",
"The user's email address.");
DEFINE_string(comments, "",
"Extra user comments");
DEFINE_string(proxy_host, "",
"Proxy host");
DEFINE_string(proxy_userpasswd, "",
"Proxy username/password in user:pass format.");
bool CheckForRequiredFlagsOrDie() {
string error_text = "";
if (FLAGS_product_name.empty()) {
error_text.append("\nProduct name must be specified.");
}
if (FLAGS_product_version.empty()) {
error_text.append("\nProduct version must be specified.");
}
if (FLAGS_client_id.empty()) {
error_text.append("\nClient ID must be specified.");
}
if (FLAGS_minidump_path.empty()) {
error_text.append("\nMinidump pathname must be specified.");
}
if (!error_text.empty()) {
std::cout << error_text;
return false;
}
return true;
}
int main(int argc, char *argv[]) {
google::InitGoogleLogging(argv[0]);
google::ParseCommandLineFlags(&argc, &argv, true);
if (!CheckForRequiredFlagsOrDie()) {
return 1;
}
google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name,
FLAGS_product_version,
FLAGS_client_id,
FLAGS_ptime,
FLAGS_ctime,
FLAGS_email,
FLAGS_comments,
FLAGS_minidump_path,
FLAGS_crash_server,
FLAGS_proxy_host,
FLAGS_proxy_userpasswd);
g.Upload(NULL, NULL, NULL);
}

View File

@ -0,0 +1,97 @@
// Copyright (c) 2006, 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.
// minidump_file_writer-inl.h: Minidump file writer implementation.
//
// See minidump_file_writer.h for documentation.
#ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__
#define CLIENT_MINIDUMP_FILE_WRITER_INL_H__
#include <assert.h>
#include "client/minidump_file_writer.h"
#include "google_breakpad/common/minidump_size.h"
namespace google_breakpad {
template<typename MDType>
inline bool TypedMDRVA<MDType>::Allocate() {
allocation_state_ = SINGLE_OBJECT;
return UntypedMDRVA::Allocate(minidump_size<MDType>::size());
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::Allocate(size_t additional) {
allocation_state_ = SINGLE_OBJECT;
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + additional);
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::AllocateArray(size_t count) {
assert(count);
allocation_state_ = ARRAY;
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() * count);
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(size_t count,
size_t length) {
assert(count && length);
allocation_state_ = SINGLE_OBJECT_WITH_ARRAY;
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + count * length);
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) {
assert(allocation_state_ == ARRAY);
return writer_->Copy(
static_cast<MDRVA>(position_ + index * minidump_size<MDType>::size()),
item, minidump_size<MDType>::size());
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index,
const void *src,
size_t length) {
assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY);
return writer_->Copy(
static_cast<MDRVA>(position_ + minidump_size<MDType>::size()
+ index * length),
src, length);
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::Flush() {
return writer_->Copy(position_, &data_, minidump_size<MDType>::size());
}
} // namespace google_breakpad
#endif // CLIENT_MINIDUMP_FILE_WRITER_INL_H__

View File

@ -0,0 +1,284 @@
// Copyright (c) 2006, 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.
// minidump_file_writer.cc: Minidump file writer implementation.
//
// See minidump_file_writer.h for documentation.
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "client/minidump_file_writer-inl.h"
#include "common/linux/linux_libc_support.h"
#include "common/string_conversion.h"
#if defined(__linux__) && __linux__
#include "third_party/lss/linux_syscall_support.h"
#endif
namespace google_breakpad {
const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
MinidumpFileWriter::MinidumpFileWriter()
: file_(-1),
close_file_when_destroyed_(true),
position_(0),
size_(0) {
}
MinidumpFileWriter::~MinidumpFileWriter() {
if (close_file_when_destroyed_)
Close();
}
bool MinidumpFileWriter::Open(const char *path) {
assert(file_ == -1);
#if defined(__linux__) && __linux__
file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
#else
file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
#endif
return file_ != -1;
}
void MinidumpFileWriter::SetFile(const int file) {
assert(file_ == -1);
file_ = file;
close_file_when_destroyed_ = false;
}
bool MinidumpFileWriter::Close() {
bool result = true;
if (file_ != -1) {
if (-1 == ftruncate(file_, position_)) {
return false;
}
#if defined(__linux__) && __linux__
result = (sys_close(file_) == 0);
#else
result = (close(file_) == 0);
#endif
file_ = -1;
}
return result;
}
bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
unsigned int length,
TypedMDRVA<MDString> *mdstring) {
bool result = true;
if (sizeof(wchar_t) == sizeof(uint16_t)) {
// Shortcut if wchar_t is the same size as MDString's buffer
result = mdstring->Copy(str, mdstring->get()->length);
} else {
uint16_t out[2];
int out_idx = 0;
// Copy the string character by character
while (length && result) {
UTF32ToUTF16Char(*str, out);
if (!out[0])
return false;
// Process one character at a time
--length;
++str;
// Append the one or two UTF-16 characters. The first one will be non-
// zero, but the second one may be zero, depending on the conversion from
// UTF-32.
int out_count = out[1] ? 2 : 1;
size_t out_size = sizeof(uint16_t) * out_count;
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
out_idx += out_count;
}
}
return result;
}
bool MinidumpFileWriter::CopyStringToMDString(const char *str,
unsigned int length,
TypedMDRVA<MDString> *mdstring) {
bool result = true;
uint16_t out[2];
int out_idx = 0;
// Copy the string character by character
while (length && result) {
int conversion_count = UTF8ToUTF16Char(str, length, out);
if (!conversion_count)
return false;
// Move the pointer along based on the nubmer of converted characters
length -= conversion_count;
str += conversion_count;
// Append the one or two UTF-16 characters
int out_count = out[1] ? 2 : 1;
size_t out_size = sizeof(uint16_t) * out_count;
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
out_idx += out_count;
}
return result;
}
template <typename CharType>
bool MinidumpFileWriter::WriteStringCore(const CharType *str,
unsigned int length,
MDLocationDescriptor *location) {
assert(str);
assert(location);
// Calculate the mdstring length by either limiting to |length| as passed in
// or by finding the location of the NULL character.
unsigned int mdstring_length = 0;
if (!length)
length = INT_MAX;
for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
;
// Allocate the string buffer
TypedMDRVA<MDString> mdstring(this);
if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t)))
return false;
// Set length excluding the NULL and copy the string
mdstring.get()->length =
static_cast<uint32_t>(mdstring_length * sizeof(uint16_t));
bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
// NULL terminate
if (result) {
uint16_t ch = 0;
result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
if (result)
*location = mdstring.location();
}
return result;
}
bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
MDLocationDescriptor *location) {
return WriteStringCore(str, length, location);
}
bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
MDLocationDescriptor *location) {
return WriteStringCore(str, length, location);
}
bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
MDMemoryDescriptor *output) {
assert(src);
assert(output);
UntypedMDRVA mem(this);
if (!mem.Allocate(size))
return false;
if (!mem.Copy(src, mem.size()))
return false;
output->start_of_memory_range = reinterpret_cast<uint64_t>(src);
output->memory = mem.location();
return true;
}
MDRVA MinidumpFileWriter::Allocate(size_t size) {
assert(size);
assert(file_ != -1);
size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
if (position_ + aligned_size > size_) {
size_t growth = aligned_size;
size_t minimal_growth = getpagesize();
// Ensure that the file grows by at least the size of a memory page
if (growth < minimal_growth)
growth = minimal_growth;
size_t new_size = size_ + growth;
if (ftruncate(file_, new_size) != 0)
return kInvalidMDRVA;
size_ = new_size;
}
MDRVA current_position = position_;
position_ += static_cast<MDRVA>(aligned_size);
return current_position;
}
bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
assert(src);
assert(size);
assert(file_ != -1);
// Ensure that the data will fit in the allocated space
if (static_cast<size_t>(size + position) > size_)
return false;
// Seek and write the data
#if defined(__linux__) && __linux__
if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
if (sys_write(file_, src, size) == size) {
#else
if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
if (write(file_, src, size) == size) {
#endif
return true;
}
}
return false;
}
bool UntypedMDRVA::Allocate(size_t size) {
assert(size_ == 0);
size_ = size;
position_ = writer_->Allocate(size_);
return position_ != MinidumpFileWriter::kInvalidMDRVA;
}
bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) {
assert(src);
assert(size);
assert(pos + size <= position_ + size_);
return writer_->Copy(pos, src, size);
}
} // namespace google_breakpad

View File

@ -0,0 +1,272 @@
// Copyright (c) 2006, 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.
// minidump_file_writer.h: Implements file-based minidump generation. It's
// intended to be used with the Google Breakpad open source crash handling
// project.
#ifndef CLIENT_MINIDUMP_FILE_WRITER_H__
#define CLIENT_MINIDUMP_FILE_WRITER_H__
#include <string>
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
class UntypedMDRVA;
template<typename MDType> class TypedMDRVA;
// The user of this class can Open() a file and add minidump streams, data, and
// strings using the definitions in minidump_format.h. Since this class is
// expected to be used in a situation where the current process may be
// damaged, it will not allocate heap memory.
// Sample usage:
// MinidumpFileWriter writer;
// writer.Open("/tmp/minidump.dmp");
// TypedMDRVA<MDRawHeader> header(&writer_);
// header.Allocate();
// header->get()->signature = MD_HEADER_SIGNATURE;
// :
// writer.Close();
//
// An alternative is to use SetFile and provide a file descriptor:
// MinidumpFileWriter writer;
// writer.SetFile(minidump_fd);
// TypedMDRVA<MDRawHeader> header(&writer_);
// header.Allocate();
// header->get()->signature = MD_HEADER_SIGNATURE;
// :
// writer.Close();
class MinidumpFileWriter {
public:
// Invalid MDRVA (Minidump Relative Virtual Address)
// returned on failed allocation
static const MDRVA kInvalidMDRVA;
MinidumpFileWriter();
~MinidumpFileWriter();
// Open |path| as the destination of the minidump data. If |path| already
// exists, then Open() will fail.
// Return true on success, or false on failure.
bool Open(const char *path);
// Sets the file descriptor |file| as the destination of the minidump data.
// Can be used as an alternative to Open() when a file descriptor is
// available.
// Note that |fd| is not closed when the instance of MinidumpFileWriter is
// destroyed.
void SetFile(const int file);
// Close the current file (that was either created when Open was called, or
// specified with SetFile).
// Return true on success, or false on failure.
bool Close();
// Copy the contents of |str| to a MDString and write it to the file.
// |str| is expected to be either UTF-16 or UTF-32 depending on the size
// of wchar_t.
// Maximum |length| of characters to copy from |str|, or specify 0 to use the
// entire NULL terminated string. Copying will stop at the first NULL.
// |location| the allocated location
// Return true on success, or false on failure
bool WriteString(const wchar_t *str, unsigned int length,
MDLocationDescriptor *location);
// Same as above, except with |str| as a UTF-8 string
bool WriteString(const char *str, unsigned int length,
MDLocationDescriptor *location);
// Write |size| bytes starting at |src| into the current position.
// Return true on success and set |output| to position, or false on failure
bool WriteMemory(const void *src, size_t size, MDMemoryDescriptor *output);
// Copies |size| bytes from |src| to |position|
// Return true on success, or false on failure
bool Copy(MDRVA position, const void *src, ssize_t size);
// Return the current position for writing to the minidump
inline MDRVA position() const { return position_; }
private:
friend class UntypedMDRVA;
// Allocates an area of |size| bytes.
// Returns the position of the allocation, or kInvalidMDRVA if it was
// unable to allocate the bytes.
MDRVA Allocate(size_t size);
// The file descriptor for the output file.
int file_;
// Whether |file_| should be closed when the instance is destroyed.
bool close_file_when_destroyed_;
// Current position in buffer
MDRVA position_;
// Current allocated size
size_t size_;
// Copy |length| characters from |str| to |mdstring|. These are distinct
// because the underlying MDString is a UTF-16 based string. The wchar_t
// variant may need to create a MDString that has more characters than the
// source |str|, whereas the UTF-8 variant may coalesce characters to form
// a single UTF-16 character.
bool CopyStringToMDString(const wchar_t *str, unsigned int length,
TypedMDRVA<MDString> *mdstring);
bool CopyStringToMDString(const char *str, unsigned int length,
TypedMDRVA<MDString> *mdstring);
// The common templated code for writing a string
template <typename CharType>
bool WriteStringCore(const CharType *str, unsigned int length,
MDLocationDescriptor *location);
};
// Represents an untyped allocated chunk
class UntypedMDRVA {
public:
explicit UntypedMDRVA(MinidumpFileWriter *writer)
: writer_(writer),
position_(writer->position()),
size_(0) {}
// Allocates |size| bytes. Must not call more than once.
// Return true on success, or false on failure
bool Allocate(size_t size);
// Returns the current position or kInvalidMDRVA if allocation failed
inline MDRVA position() const { return position_; }
// Number of bytes allocated
inline size_t size() const { return size_; }
// Return size and position
inline MDLocationDescriptor location() const {
MDLocationDescriptor location = { static_cast<uint32_t>(size_),
position_ };
return location;
}
// Copy |size| bytes starting at |src| into the minidump at |position|
// Return true on success, or false on failure
bool Copy(MDRVA position, const void *src, size_t size);
// Copy |size| bytes from |src| to the current position
inline bool Copy(const void *src, size_t size) {
return Copy(position_, src, size);
}
protected:
// Writer we associate with
MinidumpFileWriter *writer_;
// Position of the start of the data
MDRVA position_;
// Allocated size
size_t size_;
};
// Represents a Minidump object chunk. Additional memory can be allocated at
// the end of the object as a:
// - single allocation
// - Array of MDType objects
// - A MDType object followed by an array
template<typename MDType>
class TypedMDRVA : public UntypedMDRVA {
public:
// Constructs an unallocated MDRVA
explicit TypedMDRVA(MinidumpFileWriter *writer)
: UntypedMDRVA(writer),
data_(),
allocation_state_(UNALLOCATED) {}
inline ~TypedMDRVA() {
// Ensure that the data_ object is written out
if (allocation_state_ != ARRAY)
Flush();
}
// Address of object data_ of MDType. This is not declared const as the
// typical usage will be to access the underlying |data_| object as to
// alter its contents.
MDType *get() { return &data_; }
// Allocates minidump_size<MDType>::size() bytes.
// Must not call more than once.
// Return true on success, or false on failure
bool Allocate();
// Allocates minidump_size<MDType>::size() + |additional| bytes.
// Must not call more than once.
// Return true on success, or false on failure
bool Allocate(size_t additional);
// Allocate an array of |count| elements of MDType.
// Must not call more than once.
// Return true on success, or false on failure
bool AllocateArray(size_t count);
// Allocate an array of |count| elements of |size| after object of MDType
// Must not call more than once.
// Return true on success, or false on failure
bool AllocateObjectAndArray(size_t count, size_t size);
// Copy |item| to |index|
// Must have been allocated using AllocateArray().
// Return true on success, or false on failure
bool CopyIndex(unsigned int index, MDType *item);
// Copy |size| bytes starting at |str| to |index|
// Must have been allocated using AllocateObjectAndArray().
// Return true on success, or false on failure
bool CopyIndexAfterObject(unsigned int index, const void *src, size_t size);
// Write data_
bool Flush();
private:
enum AllocationState {
UNALLOCATED = 0,
SINGLE_OBJECT,
ARRAY,
SINGLE_OBJECT_WITH_ARRAY
};
MDType data_;
AllocationState allocation_state_;
};
} // namespace google_breakpad
#endif // CLIENT_MINIDUMP_FILE_WRITER_H__

View File

@ -0,0 +1,179 @@
// Copyright (c) 2006, 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.
// Author: waylonis@google.com (Dan Waylonis)
/*
g++ -I../ ../common/convert_UTF.c \
../common/string_conversion.cc \
minidump_file_writer.cc \
minidump_file_writer_unittest.cc \
-o minidump_file_writer_unittest
*/
#include <fcntl.h>
#include <unistd.h>
#include "minidump_file_writer-inl.h"
using google_breakpad::MinidumpFileWriter;
#define ASSERT_TRUE(cond) \
if (!(cond)) { \
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
return false; \
}
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
#define ASSERT_NE(e1, e2) ASSERT_TRUE((e1) != (e2))
struct StringStructure {
unsigned long integer_value;
MDLocationDescriptor first_string;
MDLocationDescriptor second_string;
};
struct ArrayStructure {
unsigned char char_value;
unsigned short short_value;
unsigned long long_value;
};
typedef struct {
unsigned long count;
ArrayStructure array[0];
} ObjectAndArrayStructure;
static bool WriteFile(const char *path) {
MinidumpFileWriter writer;
if (writer.Open(path)) {
// Test a single structure
google_breakpad::TypedMDRVA<StringStructure> strings(&writer);
ASSERT_TRUE(strings.Allocate());
strings.get()->integer_value = 0xBEEF;
const char *first = "First String";
ASSERT_TRUE(writer.WriteString(first, 0, &strings.get()->first_string));
const wchar_t *second = L"Second String";
ASSERT_TRUE(writer.WriteString(second, 0, &strings.get()->second_string));
// Test an array structure
google_breakpad::TypedMDRVA<ArrayStructure> array(&writer);
unsigned int count = 10;
ASSERT_TRUE(array.AllocateArray(count));
for (unsigned char i = 0; i < count; ++i) {
ArrayStructure local;
local.char_value = i;
local.short_value = i + 1;
local.long_value = i + 2;
ASSERT_TRUE(array.CopyIndex(i, &local));
}
// Test an object followed by an array
google_breakpad::TypedMDRVA<ObjectAndArrayStructure> obj_array(&writer);
ASSERT_TRUE(obj_array.AllocateObjectAndArray(count,
sizeof(ArrayStructure)));
obj_array.get()->count = count;
for (unsigned char i = 0; i < count; ++i) {
ArrayStructure local;
local.char_value = i;
local.short_value = i + 1;
local.long_value = i + 2;
ASSERT_TRUE(obj_array.CopyIndexAfterObject(i, &local, sizeof(local)));
}
}
return writer.Close();
}
static bool CompareFile(const char *path) {
unsigned long expected[] = {
#if defined(__BIG_ENDIAN__)
0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000,
0x00000018, 0x00460069, 0x00720073, 0x00740020, 0x00530074, 0x00720069,
0x006e0067, 0x00000000, 0x0000001a, 0x00530065, 0x0063006f, 0x006e0064,
0x00200053, 0x00740072, 0x0069006e, 0x00670000, 0x00000001, 0x00000002,
0x01000002, 0x00000003, 0x02000003, 0x00000004, 0x03000004, 0x00000005,
0x04000005, 0x00000006, 0x05000006, 0x00000007, 0x06000007, 0x00000008,
0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b,
0x0000000a, 0x00000001, 0x00000002, 0x01000002, 0x00000003, 0x02000003,
0x00000004, 0x03000004, 0x00000005, 0x04000005, 0x00000006, 0x05000006,
0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009,
0x0000000a, 0x0900000a, 0x0000000b, 0x00000000
#else
0x0000beef, 0x0000001e, 0x00000018, 0x00000020,
0x00000038, 0x00000000, 0x00000018, 0x00690046,
0x00730072, 0x00200074, 0x00740053, 0x00690072,
0x0067006e, 0x00000000, 0x0000001a, 0x00650053,
0x006f0063, 0x0064006e, 0x00530020, 0x00720074,
0x006e0069, 0x00000067, 0x00011e00, 0x00000002,
0x00021e01, 0x00000003, 0x00031e02, 0x00000004,
0x00041e03, 0x00000005, 0x00051e04, 0x00000006,
0x00061e05, 0x00000007, 0x00071e06, 0x00000008,
0x00081e07, 0x00000009, 0x00091e08, 0x0000000a,
0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00,
0x00000002, 0x00021c01, 0x00000003, 0x00031c02,
0x00000004, 0x00041c03, 0x00000005, 0x00051c04,
0x00000006, 0x00061c05, 0x00000007, 0x00071c06,
0x00000008, 0x00081c07, 0x00000009, 0x00091c08,
0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000,
#endif
};
size_t expected_byte_count = sizeof(expected);
int fd = open(path, O_RDONLY, 0600);
void *buffer = malloc(expected_byte_count);
ASSERT_NE(fd, -1);
ASSERT_TRUE(buffer);
ASSERT_EQ(read(fd, buffer, expected_byte_count),
static_cast<ssize_t>(expected_byte_count));
char *b1, *b2;
b1 = reinterpret_cast<char*>(buffer);
b2 = reinterpret_cast<char*>(expected);
while (*b1 == *b2) {
b1++;
b2++;
}
printf("%p\n", reinterpret_cast<void*>(b1 - (char*)buffer));
ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0);
return true;
}
static bool RunTests() {
const char *path = "/tmp/minidump_file_writer_unittest.dmp";
ASSERT_TRUE(WriteFile(path));
ASSERT_TRUE(CompareFile(path));
unlink(path);
return true;
}
extern "C" int main(int argc, const char *argv[]) {
return RunTests() ? 0 : 1;
}

View File

@ -0,0 +1,489 @@
// Copyright (c) 2012, 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.
// A minimalistic implementation of getcontext() to be used by
// Google Breakpad on Android.
#include "common/android/ucontext_constants.h"
/* int getcontext (ucontext_t *ucp) */
#if defined(__arm__)
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.type breakpad_getcontext, #function
.align 0
.fnstart
breakpad_getcontext:
/* First, save r4-r11 */
add r1, r0, #(MCONTEXT_GREGS_OFFSET + 4*4)
stm r1, {r4-r11}
/* r12 is a scratch register, don't save it */
/* Save sp and lr explicitly. */
/* - sp can't be stored with stmia in Thumb-2 */
/* - STM instructions that store sp and pc are deprecated in ARM */
str sp, [r0, #(MCONTEXT_GREGS_OFFSET + 13*4)]
str lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)]
/* Save the caller's address in 'pc' */
str lr, [r0, #(MCONTEXT_GREGS_OFFSET + 15*4)]
/* Save ucontext_t* pointer across next call */
mov r4, r0
/* Call sigprocmask(SIG_BLOCK, NULL, &(ucontext->uc_sigmask)) */
mov r0, #0 /* SIG_BLOCK */
mov r1, #0 /* NULL */
add r2, r4, #UCONTEXT_SIGMASK_OFFSET
bl sigprocmask(PLT)
/* Intentionally do not save the FPU state here. This is because on
* Linux/ARM, one should instead use ptrace(PTRACE_GETFPREGS) or
* ptrace(PTRACE_GETVFPREGS) to get it.
*
* Note that a real implementation of getcontext() would need to save
* this here to allow setcontext()/swapcontext() to work correctly.
*/
/* Restore the values of r4 and lr */
mov r0, r4
ldr lr, [r0, #(MCONTEXT_GREGS_OFFSET + 14*4)]
ldr r4, [r0, #(MCONTEXT_GREGS_OFFSET + 4*4)]
/* Return 0 */
mov r0, #0
bx lr
.fnend
.size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__aarch64__)
#define _NSIG 64
#define __NR_rt_sigprocmask 135
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.type breakpad_getcontext, #function
.align 4
.cfi_startproc
breakpad_getcontext:
/* The saved context will return to the getcontext() call point
with a return value of 0 */
str xzr, [x0, MCONTEXT_GREGS_OFFSET + 0 * REGISTER_SIZE]
stp x18, x19, [x0, MCONTEXT_GREGS_OFFSET + 18 * REGISTER_SIZE]
stp x20, x21, [x0, MCONTEXT_GREGS_OFFSET + 20 * REGISTER_SIZE]
stp x22, x23, [x0, MCONTEXT_GREGS_OFFSET + 22 * REGISTER_SIZE]
stp x24, x25, [x0, MCONTEXT_GREGS_OFFSET + 24 * REGISTER_SIZE]
stp x26, x27, [x0, MCONTEXT_GREGS_OFFSET + 26 * REGISTER_SIZE]
stp x28, x29, [x0, MCONTEXT_GREGS_OFFSET + 28 * REGISTER_SIZE]
str x30, [x0, MCONTEXT_GREGS_OFFSET + 30 * REGISTER_SIZE]
/* Place LR into the saved PC, this will ensure that when
switching to this saved context with setcontext() control
will pass back to the caller of getcontext(), we have
already arranged to return the appropriate return value in x0
above. */
str x30, [x0, MCONTEXT_PC_OFFSET]
/* Save the current SP */
mov x2, sp
str x2, [x0, MCONTEXT_SP_OFFSET]
/* Initialize the pstate. */
str xzr, [x0, MCONTEXT_PSTATE_OFFSET]
/* Figure out where to place the first context extension
block. */
add x2, x0, #MCONTEXT_EXTENSION_OFFSET
/* Write the context extension fpsimd header. */
mov w3, #(FPSIMD_MAGIC & 0xffff)
movk w3, #(FPSIMD_MAGIC >> 16), lsl #16
str w3, [x2, #FPSIMD_CONTEXT_MAGIC_OFFSET]
mov w3, #FPSIMD_CONTEXT_SIZE
str w3, [x2, #FPSIMD_CONTEXT_SIZE_OFFSET]
/* Fill in the FP SIMD context. */
add x3, x2, #(FPSIMD_CONTEXT_VREGS_OFFSET + 8 * SIMD_REGISTER_SIZE)
stp d8, d9, [x3], #(2 * SIMD_REGISTER_SIZE)
stp d10, d11, [x3], #(2 * SIMD_REGISTER_SIZE)
stp d12, d13, [x3], #(2 * SIMD_REGISTER_SIZE)
stp d14, d15, [x3], #(2 * SIMD_REGISTER_SIZE)
add x3, x2, FPSIMD_CONTEXT_FPSR_OFFSET
mrs x4, fpsr
str w4, [x3]
mrs x4, fpcr
str w4, [x3, FPSIMD_CONTEXT_FPCR_OFFSET - FPSIMD_CONTEXT_FPSR_OFFSET]
/* Write the termination context extension header. */
add x2, x2, #FPSIMD_CONTEXT_SIZE
str xzr, [x2, #FPSIMD_CONTEXT_MAGIC_OFFSET]
str xzr, [x2, #FPSIMD_CONTEXT_SIZE_OFFSET]
/* Grab the signal mask */
/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
add x2, x0, #UCONTEXT_SIGMASK_OFFSET
mov x0, #0 /* SIG_BLOCK */
mov x1, #0 /* NULL */
mov x3, #(_NSIG / 8)
mov x8, #__NR_rt_sigprocmask
svc 0
/* Return x0 for success */
mov x0, 0
ret
.cfi_endproc
.size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__i386__)
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.align 4
.type breakpad_getcontext, @function
breakpad_getcontext:
movl 4(%esp), %eax /* eax = uc */
/* Save register values */
movl %ecx, MCONTEXT_ECX_OFFSET(%eax)
movl %edx, MCONTEXT_EDX_OFFSET(%eax)
movl %ebx, MCONTEXT_EBX_OFFSET(%eax)
movl %edi, MCONTEXT_EDI_OFFSET(%eax)
movl %esi, MCONTEXT_ESI_OFFSET(%eax)
movl %ebp, MCONTEXT_EBP_OFFSET(%eax)
movl (%esp), %edx /* return address */
lea 4(%esp), %ecx /* exclude return address from stack */
mov %edx, MCONTEXT_EIP_OFFSET(%eax)
mov %ecx, MCONTEXT_ESP_OFFSET(%eax)
xorl %ecx, %ecx
movw %fs, %cx
mov %ecx, MCONTEXT_FS_OFFSET(%eax)
movl $0, MCONTEXT_EAX_OFFSET(%eax)
/* Save floating point state to fpregstate, then update
* the fpregs pointer to point to it */
leal UCONTEXT_FPREGS_MEM_OFFSET(%eax), %ecx
fnstenv (%ecx)
fldenv (%ecx)
mov %ecx, UCONTEXT_FPREGS_OFFSET(%eax)
/* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */
leal UCONTEXT_SIGMASK_OFFSET(%eax), %edx
xorl %ecx, %ecx
push %edx /* &uc->uc_sigmask */
push %ecx /* NULL */
push %ecx /* SIGBLOCK == 0 on i386 */
call sigprocmask@PLT
addl $12, %esp
movl $0, %eax
ret
.size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__mips__)
// This implementation is inspired by implementation of getcontext in glibc.
#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>
#endif
// from asm/asm.h
#if _MIPS_SIM == _ABIO32
#define ALSZ 7
#define ALMASK ~7
#define SZREG 4
#else // _MIPS_SIM != _ABIO32
#define ALSZ 15
#define ALMASK ~15
#define SZREG 8
#endif
#include <asm/unistd.h> // for __NR_rt_sigprocmask
#define _NSIG8 128 / 8
#define SIG_BLOCK 1
.text
LOCALS_NUM = 1 // save gp on stack
FRAME_SIZE = ((LOCALS_NUM * SZREG) + ALSZ) & ALMASK
GP_FRAME_OFFSET = FRAME_SIZE - (1 * SZREG)
MCONTEXT_REG_SIZE = 8
#if _MIPS_SIM == _ABIO32
NESTED (breakpad_getcontext, FRAME_SIZE, ra)
.mask 0x00000000, 0
.fmask 0x00000000, 0
.set noreorder
.cpload t9
.set reorder
move a2, sp
#define _SP a2
addiu sp, -FRAME_SIZE
.cprestore GP_FRAME_OFFSET
sw s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw fp, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sw ra, MCONTEXT_PC_OFFSET(a0)
#ifdef __mips_hard_float
s.d fs0, (20 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs1, (22 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs2, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs3, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs4, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d fs5, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
cfc1 v1, fcr31
sw v1, MCONTEXT_FPC_CSR(a0)
#endif // __mips_hard_float
/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
li a3, _NSIG8
addu a2, a0, UCONTEXT_SIGMASK_OFFSET
move a1, zero
li a0, SIG_BLOCK
li v0, __NR_rt_sigprocmask
syscall
addiu sp, FRAME_SIZE
jr ra
END (breakpad_getcontext)
#else
#ifndef NESTED
/*
* NESTED - declare nested routine entry point
*/
#define NESTED(symbol, framesize, rpc) \
.globl symbol; \
.align 2; \
.type symbol,@function; \
.ent symbol,0; \
symbol: .frame sp, framesize, rpc;
#endif
/*
* END - mark end of function
*/
#ifndef END
# define END(function) \
.end function; \
.size function,.-function
#endif
/* int getcontext (ucontext_t *ucp) */
NESTED (breakpad_getcontext, FRAME_SIZE, ra)
.mask 0x10000000, 0
.fmask 0x00000000, 0
move a2, sp
#define _SP a2
move a3, gp
#define _GP a3
daddiu sp, -FRAME_SIZE
.cpsetup $25, GP_FRAME_OFFSET, breakpad_getcontext
/* Store a magic flag. */
li v1, 1
sd v1, (0 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) /* zero */
sd s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd _GP, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd s8, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
sd ra, MCONTEXT_PC_OFFSET(a0)
#ifdef __mips_hard_float
s.d $f24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f26, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f27, (27 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
s.d $f31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
cfc1 v1, $31
sw v1, MCONTEXT_FPC_CSR(a0)
#endif /* __mips_hard_float */
/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
li a3, _NSIG8
daddu a2, a0, UCONTEXT_SIGMASK_OFFSET
move a1, zero
li a0, SIG_BLOCK
li v0, __NR_rt_sigprocmask
syscall
.cpreturn
daddiu sp, FRAME_SIZE
move v0, zero
jr ra
END (breakpad_getcontext)
#endif // _MIPS_SIM == _ABIO32
#elif defined(__x86_64__)
/* The x64 implementation of breakpad_getcontext was derived in part
from the implementation of libunwind which requires the following
notice. */
/* libunwind - a platform-independent unwind library
Copyright (C) 2008 Google, Inc
Contributed by Paul Pluzhnikov <ppluzhnikov@google.com>
Copyright (C) 2010 Konstantin Belousov <kib@freebsd.org>
This file is part of libunwind.
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 AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
.text
.global breakpad_getcontext
.hidden breakpad_getcontext
.align 4
.type breakpad_getcontext, @function
breakpad_getcontext:
.cfi_startproc
/* Callee saved: RBX, RBP, R12-R15 */
movq %r12, MCONTEXT_GREGS_R12(%rdi)
movq %r13, MCONTEXT_GREGS_R13(%rdi)
movq %r14, MCONTEXT_GREGS_R14(%rdi)
movq %r15, MCONTEXT_GREGS_R15(%rdi)
movq %rbp, MCONTEXT_GREGS_RBP(%rdi)
movq %rbx, MCONTEXT_GREGS_RBX(%rdi)
/* Save argument registers (not strictly needed, but setcontext
restores them, so don't restore garbage). */
movq %r8, MCONTEXT_GREGS_R8(%rdi)
movq %r9, MCONTEXT_GREGS_R9(%rdi)
movq %rdi, MCONTEXT_GREGS_RDI(%rdi)
movq %rsi, MCONTEXT_GREGS_RSI(%rdi)
movq %rdx, MCONTEXT_GREGS_RDX(%rdi)
movq %rax, MCONTEXT_GREGS_RAX(%rdi)
movq %rcx, MCONTEXT_GREGS_RCX(%rdi)
/* Save fp state (not needed, except for setcontext not
restoring garbage). */
leaq MCONTEXT_FPREGS_MEM(%rdi),%r8
movq %r8, MCONTEXT_FPREGS_PTR(%rdi)
fnstenv (%r8)
stmxcsr FPREGS_OFFSET_MXCSR(%r8)
leaq 8(%rsp), %rax /* exclude this call. */
movq %rax, MCONTEXT_GREGS_RSP(%rdi)
movq 0(%rsp), %rax
movq %rax, MCONTEXT_GREGS_RIP(%rdi)
/* Save signal mask: sigprocmask(SIGBLOCK, NULL, &uc->uc_sigmask) */
leaq UCONTEXT_SIGMASK_OFFSET(%rdi), %rdx // arg3
xorq %rsi, %rsi // arg2 NULL
xorq %rdi, %rdi // arg1 SIGBLOCK == 0
call sigprocmask@PLT
/* Always return 0 for success, even if sigprocmask failed. */
xorl %eax, %eax
ret
.cfi_endproc
.size breakpad_getcontext, . - breakpad_getcontext
#else
#error "This file has not been ported for your CPU!"
#endif

View File

@ -0,0 +1,186 @@
// Copyright (c) 2012, 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.
#if defined(__x86_64__)
#include <asm/sigcontext.h>
#endif
#include <sys/ucontext.h>
#include "breakpad_googletest_includes.h"
#include "common/android/ucontext_constants.h"
template <int left, int right>
struct CompileAssertEquals {
// a compilation error here indicates left and right are not equal.
char left_too_large[right - left];
// a compilation error here indicates left and right are not equal.
char right_too_large[left - right];
};
#define COMPILE_ASSERT_EQ(left, right, tag) \
CompileAssertEquals<left, right> tag;
TEST(AndroidUContext, GRegsOffset) {
#if defined(__arm__)
// There is no gregs[] array on ARM, so compare to the offset of
// first register fields, since they're stored in order.
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.arm_r0));
#elif defined(__aarch64__)
// There is no gregs[] array on ARM, so compare to the offset of
// first register fields, since they're stored in order.
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.regs[0]));
ASSERT_EQ(static_cast<size_t>(MCONTEXT_SP_OFFSET),
offsetof(ucontext_t,uc_mcontext.sp));
ASSERT_EQ(static_cast<size_t>(MCONTEXT_PC_OFFSET),
offsetof(ucontext_t,uc_mcontext.pc));
ASSERT_EQ(static_cast<size_t>(MCONTEXT_PSTATE_OFFSET),
offsetof(ucontext_t,uc_mcontext.pstate));
ASSERT_EQ(static_cast<size_t>(MCONTEXT_EXTENSION_OFFSET),
offsetof(ucontext_t,uc_mcontext.__reserved));
#elif defined(__i386__)
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs));
#define CHECK_REG(x) \
ASSERT_EQ(static_cast<size_t>(MCONTEXT_##x##_OFFSET), \
offsetof(ucontext_t,uc_mcontext.gregs[REG_##x]))
CHECK_REG(GS);
CHECK_REG(FS);
CHECK_REG(ES);
CHECK_REG(DS);
CHECK_REG(EDI);
CHECK_REG(ESI);
CHECK_REG(EBP);
CHECK_REG(ESP);
CHECK_REG(EBX);
CHECK_REG(EDX);
CHECK_REG(ECX);
CHECK_REG(EAX);
CHECK_REG(TRAPNO);
CHECK_REG(ERR);
CHECK_REG(EIP);
CHECK_REG(CS);
CHECK_REG(EFL);
CHECK_REG(UESP);
CHECK_REG(SS);
ASSERT_EQ(static_cast<size_t>(UCONTEXT_FPREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.fpregs));
ASSERT_EQ(static_cast<size_t>(UCONTEXT_FPREGS_MEM_OFFSET),
offsetof(ucontext_t,__fpregs_mem));
#elif defined(__mips__)
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs));
// PC for mips is not part of gregs.
ASSERT_EQ(static_cast<size_t>(MCONTEXT_PC_OFFSET),
offsetof(ucontext_t,uc_mcontext.pc));
ASSERT_EQ(static_cast<size_t>(MCONTEXT_FPREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.fpregs));
ASSERT_EQ(static_cast<size_t>(MCONTEXT_FPC_CSR),
offsetof(ucontext_t,uc_mcontext.fpc_csr));
#elif defined(__x86_64__)
COMPILE_ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs),
mcontext_gregs_offset);
#define CHECK_REG(x) \
COMPILE_ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_##x), \
offsetof(ucontext_t,uc_mcontext.gregs[REG_##x]), reg_##x)
CHECK_REG(R8);
CHECK_REG(R9);
CHECK_REG(R10);
CHECK_REG(R11);
CHECK_REG(R12);
CHECK_REG(R13);
CHECK_REG(R14);
CHECK_REG(R15);
CHECK_REG(RDI);
CHECK_REG(RSI);
CHECK_REG(RBP);
CHECK_REG(RBX);
CHECK_REG(RDX);
CHECK_REG(RAX);
CHECK_REG(RCX);
CHECK_REG(RSP);
CHECK_REG(RIP);
// sigcontext is an analog to mcontext_t. The layout should be the same.
COMPILE_ASSERT_EQ(offsetof(mcontext_t,fpregs),
offsetof(sigcontext,fpstate), sigcontext_fpstate);
// Check that _fpstate from asm/sigcontext.h is essentially the same
// as _libc_fpstate.
COMPILE_ASSERT_EQ(sizeof(_libc_fpstate), sizeof(_fpstate),
sigcontext_fpstate_size);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,cwd),offsetof(_fpstate,cwd),
sigcontext_fpstate_cwd);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,swd),offsetof(_fpstate,swd),
sigcontext_fpstate_swd);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,ftw),offsetof(_fpstate,twd),
sigcontext_fpstate_twd);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,fop),offsetof(_fpstate,fop),
sigcontext_fpstate_fop);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,rip),offsetof(_fpstate,rip),
sigcontext_fpstate_rip);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,rdp),offsetof(_fpstate,rdp),
sigcontext_fpstate_rdp);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,mxcsr),offsetof(_fpstate,mxcsr),
sigcontext_fpstate_mxcsr);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,mxcr_mask),
offsetof(_fpstate,mxcsr_mask),
sigcontext_fpstate_mxcsr_mask);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,_st), offsetof(_fpstate,st_space),
sigcontext_fpstate_stspace);
COMPILE_ASSERT_EQ(offsetof(_libc_fpstate,_xmm), offsetof(_fpstate,xmm_space),
sigcontext_fpstate_xmm_space);
COMPILE_ASSERT_EQ(MCONTEXT_FPREGS_PTR,
offsetof(ucontext_t,uc_mcontext.fpregs),
mcontext_fpregs_ptr);
COMPILE_ASSERT_EQ(MCONTEXT_FPREGS_MEM, offsetof(ucontext_t,__fpregs_mem),
mcontext_fpregs_mem);
COMPILE_ASSERT_EQ(FPREGS_OFFSET_MXCSR, offsetof(_libc_fpstate,mxcsr),
fpregs_offset_mxcsr);
COMPILE_ASSERT_EQ(UCONTEXT_SIGMASK_OFFSET, offsetof(ucontext_t, uc_sigmask),
ucontext_sigmask);
#else
ASSERT_EQ(static_cast<size_t>(MCONTEXT_GREGS_OFFSET),
offsetof(ucontext_t,uc_mcontext.gregs));
#endif
}
TEST(AndroidUContext, SigmakOffset) {
ASSERT_EQ(static_cast<size_t>(UCONTEXT_SIGMASK_OFFSET),
offsetof(ucontext_t,uc_sigmask));
}

View File

@ -0,0 +1,168 @@
// Copyright (c) 2012, 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 GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H
#include <stdint.h>
#include <libgen.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// The Android <elf.h> provides BSD-based definitions for the ElfXX_Nhdr
// types
// always source-compatible with the GLibc/kernel ones. To overcome this
// issue without modifying a lot of code in Breakpad, use an ugly macro
// renaming trick with #include_next
// Avoid conflict with BSD-based definition of ElfXX_Nhdr.
// Unfortunately, their field member names do not use a 'n_' prefix.
#define Elf32_Nhdr __bsd_Elf32_Nhdr
#define Elf64_Nhdr __bsd_Elf64_Nhdr
// In case they are defined by the NDK version
#define Elf32_auxv_t __bionic_Elf32_auxv_t
#define Elf64_auxv_t __bionic_Elf64_auxv_t
#define Elf32_Dyn __bionic_Elf32_Dyn
#define Elf64_Dyn __bionic_Elf64_Dyn
#include_next <elf.h>
#undef Elf32_Nhdr
#undef Elf64_Nhdr
typedef struct {
Elf32_Word n_namesz;
Elf32_Word n_descsz;
Elf32_Word n_type;
} Elf32_Nhdr;
typedef struct {
Elf64_Word n_namesz;
Elf64_Word n_descsz;
Elf64_Word n_type;
} Elf64_Nhdr;
#undef Elf32_auxv_t
#undef Elf64_auxv_t
typedef struct {
uint32_t a_type;
union {
uint32_t a_val;
} a_un;
} Elf32_auxv_t;
typedef struct {
uint64_t a_type;
union {
uint64_t a_val;
} a_un;
} Elf64_auxv_t;
#undef Elf32_Dyn
#undef Elf64_Dyn
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
typedef struct {
Elf64_Sxword d_tag;
union {
Elf64_Xword d_val;
Elf64_Addr d_ptr;
} d_un;
} Elf64_Dyn;
// __WORDSIZE is GLibc-specific and used by Google Breakpad on Linux.
#ifndef __WORDSIZE
#if defined(__i386__) || defined(__ARM_EABI__) || defined(__mips__)
#define __WORDSIZE 32
#elif defined(__x86_64__) || defined(__aarch64__)
#define __WORDSIZE 64
#else
#error "Unsupported Android CPU ABI"
#endif
#endif
// The Android headers don't always define this constant.
#ifndef EM_X86_64
#define EM_X86_64 62
#endif
#ifndef EM_PPC64
#define EM_PPC64 21
#endif
#ifndef EM_S390
#define EM_S390 22
#endif
#if !defined(AT_SYSINFO_EHDR)
#define AT_SYSINFO_EHDR 33
#endif
#if !defined(NT_PRSTATUS)
#define NT_PRSTATUS 1
#endif
#if !defined(NT_PRPSINFO)
#define NT_PRPSINFO 3
#endif
#if !defined(NT_AUXV)
#define NT_AUXV 6
#endif
#if !defined(NT_PRXFPREG)
#define NT_PRXFPREG 0x46e62b7f
#endif
#if !defined(NT_FPREGSET)
#define NT_FPREGSET 2
#endif
#if !defined(SHT_MIPS_DWARF)
#define SHT_MIPS_DWARF 0x7000001e
#endif
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ELF_H

View File

@ -0,0 +1,41 @@
// Copyright (c) 2013, 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 GOOGLE_BREAKPAD_ANDROID_INCLUDE_SGIDEFS_H
#define GOOGLE_BREAKPAD_ANDROID_INCLUDE_SGIDEFS_H
#ifdef __mips__
// Android doesn't contain sgidefs.h, but does have <asm/sgidefs.h> which
// contains what we need.
#include <asm/sgidefs.h>
#endif // __mips__
#endif // GOOGLE_BREAKPAD_ANDROID_INCLUDE_SGIDEFS_H

View File

@ -0,0 +1,100 @@
// Copyright (c) 2012, 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 GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H
#include <sys/cdefs.h>
#ifdef __BIONIC_HAVE_STAB_H
#include <stab.h>
#else
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#define _STAB_CODE_LIST \
_STAB_CODE_DEF(UNDF,0x00) \
_STAB_CODE_DEF(GSYM,0x20) \
_STAB_CODE_DEF(FNAME,0x22) \
_STAB_CODE_DEF(FUN,0x24) \
_STAB_CODE_DEF(STSYM,0x26) \
_STAB_CODE_DEF(LCSYM,0x28) \
_STAB_CODE_DEF(MAIN,0x2a) \
_STAB_CODE_DEF(PC,0x30) \
_STAB_CODE_DEF(NSYMS,0x32) \
_STAB_CODE_DEF(NOMAP,0x34) \
_STAB_CODE_DEF(OBJ,0x38) \
_STAB_CODE_DEF(OPT,0x3c) \
_STAB_CODE_DEF(RSYM,0x40) \
_STAB_CODE_DEF(M2C,0x42) \
_STAB_CODE_DEF(SLINE,0x44) \
_STAB_CODE_DEF(DSLINE,0x46) \
_STAB_CODE_DEF(BSLINE,0x48) \
_STAB_CODE_DEF(BROWS,0x48) \
_STAB_CODE_DEF(DEFD,0x4a) \
_STAB_CODE_DEF(EHDECL,0x50) \
_STAB_CODE_DEF(MOD2,0x50) \
_STAB_CODE_DEF(CATCH,0x54) \
_STAB_CODE_DEF(SSYM,0x60) \
_STAB_CODE_DEF(SO,0x64) \
_STAB_CODE_DEF(LSYM,0x80) \
_STAB_CODE_DEF(BINCL,0x82) \
_STAB_CODE_DEF(SOL,0x84) \
_STAB_CODE_DEF(PSYM,0xa0) \
_STAB_CODE_DEF(EINCL,0xa2) \
_STAB_CODE_DEF(ENTRY,0xa4) \
_STAB_CODE_DEF(LBRAC,0xc0) \
_STAB_CODE_DEF(EXCL,0xc2) \
_STAB_CODE_DEF(SCOPE,0xc4) \
_STAB_CODE_DEF(RBRAC,0xe0) \
_STAB_CODE_DEF(BCOMM,0xe2) \
_STAB_CODE_DEF(ECOMM,0xe4) \
_STAB_CODE_DEF(ECOML,0xe8) \
_STAB_CODE_DEF(NBTEXT,0xf0) \
_STAB_CODE_DEF(NBDATA,0xf2) \
_STAB_CODE_DEF(NBBSS,0xf4) \
_STAB_CODE_DEF(NBSTS,0xf6) \
_STAB_CODE_DEF(NBLCS,0xf8) \
_STAB_CODE_DEF(LENG,0xfe)
enum __stab_debug_code {
#define _STAB_CODE_DEF(x,y) N_##x = y,
_STAB_CODE_LIST
#undef _STAB_CODE_DEF
};
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // __BIONIC_HAVE_STAB_H
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_STAB_H

View File

@ -0,0 +1,124 @@
// Copyright (c) 2012, 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 GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H
#ifdef __BIONIC_HAVE_SYS_PROCFS_H
#include_next <sys/procfs.h>
#else
#include <asm/ptrace.h>
#include <sys/cdefs.h>
#if defined (__mips__)
#include <sys/types.h>
#endif
#include <sys/user.h>
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
#if defined(__x86_64__) || defined(__aarch64__)
typedef unsigned long long elf_greg_t;
#else
typedef unsigned long elf_greg_t;
#endif
#ifdef __arm__
#define ELF_NGREG (sizeof(struct user_regs) / sizeof(elf_greg_t))
#elif defined(__aarch64__)
#define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t))
#elif defined(__mips__)
#define ELF_NGREG 45
#else
#define ELF_NGREG (sizeof(struct user_regs_struct) / sizeof(elf_greg_t))
#endif
typedef elf_greg_t elf_gregset_t[ELF_NGREG];
struct elf_siginfo {
int si_signo;
int si_code;
int si_errno;
};
struct elf_prstatus {
struct elf_siginfo pr_info;
short pr_cursig;
unsigned long pr_sigpend;
unsigned long pr_sighold;
pid_t pr_pid;
pid_t pr_ppid;
pid_t pr_pgrp;
pid_t pd_sid;
struct timeval pr_utime;
struct timeval pr_stime;
struct timeval pr_cutime;
struct timeval pr_cstime;
elf_gregset_t pr_reg;
int pr_fpvalid;
};
#define ELF_PRARGSZ 80
struct elf_prpsinfo {
char pr_state;
char pr_sname;
char pr_zomb;
char pr_nice;
unsigned long pr_flags;
#ifdef __x86_64__
unsigned int pr_uid;
unsigned int pr_gid;
#elif defined(__mips__)
unsigned long pr_uid;
unsigned long pr_gid;
#else
unsigned short pr_uid;
unsigned short pr_gid;
#endif
int pr_pid;
int pr_ppid;
int pr_pgrp;
int pr_sid;
char pr_fname[16];
char pr_psargs[ELF_PRARGSZ];
};
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // __BIONIC_HAVE_SYS_PROCFS_H
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_SYS_PROCFS_H

View File

@ -0,0 +1,35 @@
// Copyright (c) 2012, 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 GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H
#include <signal.h>
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_SIGNAL_H

View File

@ -0,0 +1,56 @@
// Copyright (c) 2012, 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 GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H
#include <sys/cdefs.h>
#ifdef __BIONIC_UCONTEXT_H
#include <ucontext.h>
#else
#include <sys/ucontext.h>
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Provided by src/android/common/breakpad_getcontext.S
int breakpad_getcontext(ucontext_t* ucp);
#define getcontext(x) breakpad_getcontext(x)
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // __BIONIC_UCONTEXT_H
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_UCONTEXT_H

View File

@ -0,0 +1,76 @@
// Copyright (c) 2012, 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.
// Android doesn't provide wcscasecmp(), so provide an alternative here.
//
// Note that this header is not needed when Breakpad is compiled against
// a recent version of Googletest. It shall be considered for removal once
// src/testing/ is updated to an appropriate revision in the future.
#ifndef GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H
#define GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H
#include_next <wchar.h>
#if !defined(__aarch64__) && !defined(__x86_64__) && \
!(defined(__mips__) && _MIPS_SIM == _ABI64)
// This needs to be in an extern "C" namespace, or Googletest will not
// compile against it.
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
static wchar_t inline wcstolower(wchar_t ch) {
if (ch >= L'a' && ch <= L'A')
ch -= L'a' - L'A';
return ch;
}
static int inline wcscasecmp(const wchar_t* s1, const wchar_t* s2) {
for (;;) {
wchar_t c1 = wcstolower(*s1);
wchar_t c2 = wcstolower(*s2);
if (c1 < c2)
return -1;
if (c1 > c2)
return 1;
if (c1 == L'0')
return 0;
s1++;
s2++;
}
}
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif
#endif // GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H

View File

@ -0,0 +1,110 @@
// Copyright (c) 2012, 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.
// mkdtemp() wasn't declared in <stdlib.h> until NDK r9b due to a simple
// packaging bug (the function has always been implemented in all versions
// of the C library). This header is provided to build Breakpad with earlier
// NDK revisions (e.g. the one used by Chromium). It may be removed in the
// future once all major projects upgrade to use a more recent NDK.
//
// The reason this is inlined here is to avoid linking a new object file
// into each unit test program (i.e. keep build files simple).
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
// Using a macro renaming trick here is necessary when building against
// NDK r9b. Otherwise the compiler will complain that calls to mkdtemp()
// are ambiguous.
#define mkdtemp breakpad_mkdtemp
namespace {
char* breakpad_mkdtemp(char* path) {
if (path == NULL) {
errno = EINVAL;
return NULL;
}
// 'path' must be terminated with six 'X'
const char kSuffix[] = "XXXXXX";
const size_t kSuffixLen = strlen(kSuffix);
char* path_end = path + strlen(path);
if (static_cast<size_t>(path_end - path) < kSuffixLen ||
memcmp(path_end - kSuffixLen, kSuffix, kSuffixLen) != 0) {
errno = EINVAL;
return NULL;
}
// If 'path' contains a directory separator, check that it exists to
// avoid looping later.
char* sep = strrchr(path, '/');
if (sep != NULL) {
struct stat st;
int ret;
*sep = '\0'; // temporarily zero-terminate the dirname.
ret = stat(path, &st);
*sep = '/'; // restore full path.
if (ret < 0)
return NULL;
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
return NULL;
}
}
// Loop. On each iteration, replace the XXXXXX suffix with a random
// number.
int tries;
for (tries = 128; tries > 0; tries--) {
int random = rand() % 1000000;
snprintf(path_end - kSuffixLen, kSuffixLen + 1, "%0d", random);
if (mkdir(path, 0700) == 0)
return path; // Success
if (errno != EEXIST)
return NULL;
}
assert(errno == EEXIST);
return NULL;
}
} // namespace
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_MKDTEMP_H

View File

@ -0,0 +1,99 @@
// Copyright (c) 2012, 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.
// This contains Pthread-related functions not provided by the Android NDK
// but required by the Breakpad unit test. The functions are inlined here
// in a C++ anonymous namespace in order to keep the build files simples.
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H
#define GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H
#include <pthread.h>
namespace {
// Android doesn't provide pthread_barrier_t for now.
#ifndef PTHREAD_BARRIER_SERIAL_THREAD
// Anything except 0 will do here.
#define PTHREAD_BARRIER_SERIAL_THREAD 0x12345
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
unsigned count;
} pthread_barrier_t;
int pthread_barrier_init(pthread_barrier_t* barrier,
const void* /* barrier_attr */,
unsigned count) {
barrier->count = count;
pthread_mutex_init(&barrier->mutex, NULL);
pthread_cond_init(&barrier->cond, NULL);
return 0;
}
int pthread_barrier_wait(pthread_barrier_t* barrier) {
// Lock the mutex
pthread_mutex_lock(&barrier->mutex);
// Decrement the count. If this is the first thread to reach 0, wake up
// waiters, unlock the mutex, then return PTHREAD_BARRIER_SERIAL_THREAD.
if (--barrier->count == 0) {
// First thread to reach the barrier
pthread_cond_broadcast(&barrier->cond);
pthread_mutex_unlock(&barrier->mutex);
return PTHREAD_BARRIER_SERIAL_THREAD;
}
// Otherwise, wait for other threads until the count reaches 0, then
// return 0 to indicate this is not the first thread.
do {
pthread_cond_wait(&barrier->cond, &barrier->mutex);
} while (barrier->count > 0);
pthread_mutex_unlock(&barrier->mutex);
return 0;
}
int pthread_barrier_destroy(pthread_barrier_t *barrier) {
barrier->count = 0;
pthread_cond_destroy(&barrier->cond);
pthread_mutex_destroy(&barrier->mutex);
return 0;
}
#endif // defined(PTHREAD_BARRIER_SERIAL_THREAD)
int pthread_yield(void) {
sched_yield();
return 0;
}
} // namespace
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_TESTING_PTHREAD_FIXES_H

View File

@ -0,0 +1,144 @@
// Copyright (c) 2012, 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.
// This header can be included either from a C, C++ or Assembly file.
// Its purpose is to contain constants that must match the offsets of
// various fields in ucontext_t.
//
// They should match the definitions from
// src/common/android/include/sys/ucontext.h
//
// Used by src/common/android/breakpad_getcontext.S
// Tested by src/common/android/testing/breakpad_getcontext_unittest.cc
#ifndef GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H
#define GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H
#if defined(__arm__)
#define MCONTEXT_GREGS_OFFSET 32
#define UCONTEXT_SIGMASK_OFFSET 104
#elif defined(__aarch64__)
#define UCONTEXT_SIGMASK_OFFSET 40
#define MCONTEXT_GREGS_OFFSET 184
#define MCONTEXT_SP_OFFSET 432
#define MCONTEXT_PC_OFFSET 440
#define MCONTEXT_PSTATE_OFFSET 448
#define MCONTEXT_EXTENSION_OFFSET 464
#define FPSIMD_MAGIC 0x46508001
#define FPSIMD_CONTEXT_MAGIC_OFFSET 0
#define FPSIMD_CONTEXT_SIZE_OFFSET 4
#define FPSIMD_CONTEXT_FPSR_OFFSET 8
#define FPSIMD_CONTEXT_FPCR_OFFSET 12
#define FPSIMD_CONTEXT_VREGS_OFFSET 16
#define FPSIMD_CONTEXT_SIZE 528
#define REGISTER_SIZE 8
#define SIMD_REGISTER_SIZE 16
#elif defined(__i386__)
#define MCONTEXT_GREGS_OFFSET 20
#define MCONTEXT_GS_OFFSET (MCONTEXT_GREGS_OFFSET + 0*4)
#define MCONTEXT_FS_OFFSET (MCONTEXT_GREGS_OFFSET + 1*4)
#define MCONTEXT_ES_OFFSET (MCONTEXT_GREGS_OFFSET + 2*4)
#define MCONTEXT_DS_OFFSET (MCONTEXT_GREGS_OFFSET + 3*4)
#define MCONTEXT_EDI_OFFSET (MCONTEXT_GREGS_OFFSET + 4*4)
#define MCONTEXT_ESI_OFFSET (MCONTEXT_GREGS_OFFSET + 5*4)
#define MCONTEXT_EBP_OFFSET (MCONTEXT_GREGS_OFFSET + 6*4)
#define MCONTEXT_ESP_OFFSET (MCONTEXT_GREGS_OFFSET + 7*4)
#define MCONTEXT_EBX_OFFSET (MCONTEXT_GREGS_OFFSET + 8*4)
#define MCONTEXT_EDX_OFFSET (MCONTEXT_GREGS_OFFSET + 9*4)
#define MCONTEXT_ECX_OFFSET (MCONTEXT_GREGS_OFFSET + 10*4)
#define MCONTEXT_EAX_OFFSET (MCONTEXT_GREGS_OFFSET + 11*4)
#define MCONTEXT_TRAPNO_OFFSET (MCONTEXT_GREGS_OFFSET + 12*4)
#define MCONTEXT_ERR_OFFSET (MCONTEXT_GREGS_OFFSET + 13*4)
#define MCONTEXT_EIP_OFFSET (MCONTEXT_GREGS_OFFSET + 14*4)
#define MCONTEXT_CS_OFFSET (MCONTEXT_GREGS_OFFSET + 15*4)
#define MCONTEXT_EFL_OFFSET (MCONTEXT_GREGS_OFFSET + 16*4)
#define MCONTEXT_UESP_OFFSET (MCONTEXT_GREGS_OFFSET + 17*4)
#define MCONTEXT_SS_OFFSET (MCONTEXT_GREGS_OFFSET + 18*4)
#define UCONTEXT_SIGMASK_OFFSET 108
#define UCONTEXT_FPREGS_OFFSET 96
#define UCONTEXT_FPREGS_MEM_OFFSET 116
#elif defined(__mips__)
#if _MIPS_SIM == _ABIO32
#define MCONTEXT_PC_OFFSET 32
#define MCONTEXT_GREGS_OFFSET 40
#define MCONTEXT_FPREGS_OFFSET 296
#define MCONTEXT_FPC_CSR 556
#define UCONTEXT_SIGMASK_OFFSET 616
#else
#define MCONTEXT_GREGS_OFFSET 40
#define MCONTEXT_FPREGS_OFFSET 296
#define MCONTEXT_PC_OFFSET 616
#define MCONTEXT_FPC_CSR 624
#define UCONTEXT_SIGMASK_OFFSET 640
#endif
#elif defined(__x86_64__)
#define MCONTEXT_GREGS_OFFSET 40
#define UCONTEXT_SIGMASK_OFFSET 296
#define MCONTEXT_GREGS_R8 40
#define MCONTEXT_GREGS_R9 48
#define MCONTEXT_GREGS_R10 56
#define MCONTEXT_GREGS_R11 64
#define MCONTEXT_GREGS_R12 72
#define MCONTEXT_GREGS_R13 80
#define MCONTEXT_GREGS_R14 88
#define MCONTEXT_GREGS_R15 96
#define MCONTEXT_GREGS_RDI 104
#define MCONTEXT_GREGS_RSI 112
#define MCONTEXT_GREGS_RBP 120
#define MCONTEXT_GREGS_RBX 128
#define MCONTEXT_GREGS_RDX 136
#define MCONTEXT_GREGS_RAX 144
#define MCONTEXT_GREGS_RCX 152
#define MCONTEXT_GREGS_RSP 160
#define MCONTEXT_GREGS_RIP 168
#define MCONTEXT_FPREGS_PTR 224
#define MCONTEXT_FPREGS_MEM 304
#define FPREGS_OFFSET_MXCSR 24
#else
#error "This header has not been ported for your CPU"
#endif
#endif // GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H

View File

@ -0,0 +1,58 @@
// Copyright (c) 2011 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_BASICTYPES_H_
#define COMMON_BASICTYPES_H_
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#ifndef DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
#endif // DISALLOW_COPY_AND_ASSIGN
namespace google_breakpad {
// Used to explicitly mark the return value of a function as unused. If you are
// really sure you don't want to do anything with the return value of a function
// that has been marked with __attribute__((warn_unused_result)), wrap it with
// this. Example:
//
// scoped_ptr<MyType> my_var = ...;
// if (TakeOwnership(my_var.get()) == SUCCESS)
// ignore_result(my_var.release());
//
template<typename T>
inline void ignore_result(const T&) {
}
} // namespace google_breakpad
#endif // COMMON_BASICTYPES_H_

View File

@ -0,0 +1,265 @@
// -*- mode: c++ -*-
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// byte_cursor.h: Classes for parsing values from a buffer of bytes.
// The ByteCursor class provides a convenient interface for reading
// fixed-size integers of arbitrary endianness, being thorough about
// checking for buffer overruns.
#ifndef COMMON_BYTE_CURSOR_H_
#define COMMON_BYTE_CURSOR_H_
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
// A buffer holding a series of bytes.
struct ByteBuffer {
ByteBuffer() : start(0), end(0) { }
ByteBuffer(const uint8_t *set_start, size_t set_size)
: start(set_start), end(set_start + set_size) { }
~ByteBuffer() { };
// Equality operators. Useful in unit tests, and when we're using
// ByteBuffers to refer to regions of a larger buffer.
bool operator==(const ByteBuffer &that) const {
return start == that.start && end == that.end;
}
bool operator!=(const ByteBuffer &that) const {
return start != that.start || end != that.end;
}
// Not C++ style guide compliant, but this definitely belongs here.
size_t Size() const {
assert(start <= end);
return end - start;
}
const uint8_t *start, *end;
};
// A cursor pointing into a ByteBuffer that can parse numbers of various
// widths and representations, strings, and data blocks, advancing through
// the buffer as it goes. All ByteCursor operations check that accesses
// haven't gone beyond the end of the enclosing ByteBuffer.
class ByteCursor {
public:
// Create a cursor reading bytes from the start of BUFFER. By default, the
// cursor reads multi-byte values in little-endian form.
ByteCursor(const ByteBuffer *buffer, bool big_endian = false)
: buffer_(buffer), here_(buffer->start),
big_endian_(big_endian), complete_(true) { }
// Accessor and setter for this cursor's endianness flag.
bool big_endian() const { return big_endian_; }
void set_big_endian(bool big_endian) { big_endian_ = big_endian; }
// Accessor and setter for this cursor's current position. The setter
// returns a reference to this cursor.
const uint8_t *here() const { return here_; }
ByteCursor &set_here(const uint8_t *here) {
assert(buffer_->start <= here && here <= buffer_->end);
here_ = here;
return *this;
}
// Return the number of bytes available to read at the cursor.
size_t Available() const { return size_t(buffer_->end - here_); }
// Return true if this cursor is at the end of its buffer.
bool AtEnd() const { return Available() == 0; }
// When used as a boolean value this cursor converts to true if all
// prior reads have been completed, or false if we ran off the end
// of the buffer.
operator bool() const { return complete_; }
// Read a SIZE-byte integer at this cursor, signed if IS_SIGNED is true,
// unsigned otherwise, using the cursor's established endianness, and set
// *RESULT to the number. If we read off the end of our buffer, clear
// this cursor's complete_ flag, and store a dummy value in *RESULT.
// Return a reference to this cursor.
template<typename T>
ByteCursor &Read(size_t size, bool is_signed, T *result) {
if (CheckAvailable(size)) {
T v = 0;
if (big_endian_) {
for (size_t i = 0; i < size; i++)
v = (v << 8) + here_[i];
} else {
// This loop condition looks weird, but size_t is unsigned, so
// decrementing i after it is zero yields the largest size_t value.
for (size_t i = size - 1; i < size; i--)
v = (v << 8) + here_[i];
}
if (is_signed && size < sizeof(T)) {
size_t sign_bit = (T)1 << (size * 8 - 1);
v = (v ^ sign_bit) - sign_bit;
}
here_ += size;
*result = v;
} else {
*result = (T) 0xdeadbeef;
}
return *this;
}
// Read an integer, using the cursor's established endianness and
// *RESULT's size and signedness, and set *RESULT to the number. If we
// read off the end of our buffer, clear this cursor's complete_ flag.
// Return a reference to this cursor.
template<typename T>
ByteCursor &operator>>(T &result) {
bool T_is_signed = (T)-1 < 0;
return Read(sizeof(T), T_is_signed, &result);
}
// Copy the SIZE bytes at the cursor to BUFFER, and advance this
// cursor to the end of them. If we read off the end of our buffer,
// clear this cursor's complete_ flag, and set *POINTER to NULL.
// Return a reference to this cursor.
ByteCursor &Read(uint8_t *buffer, size_t size) {
if (CheckAvailable(size)) {
memcpy(buffer, here_, size);
here_ += size;
}
return *this;
}
// Set STR to a copy of the '\0'-terminated string at the cursor. If the
// byte buffer does not contain a terminating zero, clear this cursor's
// complete_ flag, and set STR to the empty string. Return a reference to
// this cursor.
ByteCursor &CString(string *str) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', Available()));
if (end) {
str->assign(reinterpret_cast<const char *>(here_), end - here_);
here_ = end + 1;
} else {
str->clear();
here_ = buffer_->end;
complete_ = false;
}
return *this;
}
// Like CString(STR), but extract the string from a fixed-width buffer
// LIMIT bytes long, which may or may not contain a terminating '\0'
// byte. Specifically:
//
// - If there are not LIMIT bytes available at the cursor, clear the
// cursor's complete_ flag and set STR to the empty string.
//
// - Otherwise, if the LIMIT bytes at the cursor contain any '\0'
// characters, set *STR to a copy of the bytes before the first '\0',
// and advance the cursor by LIMIT bytes.
//
// - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
// cursor by LIMIT bytes.
ByteCursor &CString(string *str, size_t limit) {
if (CheckAvailable(limit)) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', limit));
if (end)
str->assign(reinterpret_cast<const char *>(here_), end - here_);
else
str->assign(reinterpret_cast<const char *>(here_), limit);
here_ += limit;
} else {
str->clear();
}
return *this;
}
// Set *POINTER to point to the SIZE bytes at the cursor, and advance
// this cursor to the end of them. If SIZE is omitted, don't move the
// cursor. If we read off the end of our buffer, clear this cursor's
// complete_ flag, and set *POINTER to NULL. Return a reference to this
// cursor.
ByteCursor &PointTo(const uint8_t **pointer, size_t size = 0) {
if (CheckAvailable(size)) {
*pointer = here_;
here_ += size;
} else {
*pointer = NULL;
}
return *this;
}
// Skip SIZE bytes at the cursor. If doing so would advance us off
// the end of our buffer, clear this cursor's complete_ flag, and
// set *POINTER to NULL. Return a reference to this cursor.
ByteCursor &Skip(size_t size) {
if (CheckAvailable(size))
here_ += size;
return *this;
}
private:
// If there are at least SIZE bytes available to read from the buffer,
// return true. Otherwise, set here_ to the end of the buffer, set
// complete_ to false, and return false.
bool CheckAvailable(size_t size) {
if (Available() >= size) {
return true;
} else {
here_ = buffer_->end;
complete_ = false;
return false;
}
}
// The buffer we're reading bytes from.
const ByteBuffer *buffer_;
// The next byte within buffer_ that we'll read.
const uint8_t *here_;
// True if we should read numbers in big-endian form; false if we
// should read in little-endian form.
bool big_endian_;
// True if we've been able to read all we've been asked to.
bool complete_;
};
} // namespace google_breakpad
#endif // COMMON_BYTE_CURSOR_H_

View File

@ -0,0 +1,776 @@
// Copyright (c) 2010 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer
// and google_breakpad::ByteCursor.
#include <string>
#include <string.h>
#include "breakpad_googletest_includes.h"
#include "common/byte_cursor.h"
#include "common/using_std_string.h"
using google_breakpad::ByteBuffer;
using google_breakpad::ByteCursor;
TEST(Buffer, SizeOfNothing) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
EXPECT_EQ(0U, buffer.Size());
}
TEST(Buffer, SizeOfSomething) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
EXPECT_EQ(10U, buffer.Size());
}
TEST(Extent, AvailableEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_EQ(0U, cursor.Available());
}
TEST(Extent, AtEndEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor.AtEnd());
}
TEST(Extent, AsBoolEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor);
}
TEST(Extent, AvailableSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_EQ(10U, cursor.Available());
}
TEST(Extent, AtEndSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_FALSE(cursor.AtEnd());
EXPECT_TRUE(cursor.Skip(sizeof(data)).AtEnd());
}
TEST(Extent, AsBoolSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor);
EXPECT_TRUE(cursor.Skip(sizeof(data)));
EXPECT_FALSE(cursor.Skip(1));
}
TEST(Extent, Cursor) {
uint8_t data[] = { 0xf7,
0x9f, 0xbe,
0x67, 0xfb, 0xd3, 0x58,
0x6f, 0x36, 0xde, 0xd1,
0x2a, 0x2a, 0x2a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a;
uint16_t b;
uint32_t c;
uint32_t d;
uint8_t stars[3];
EXPECT_EQ(data + 0U, cursor.here());
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(data + 1U, cursor.here());
EXPECT_TRUE(cursor >> b);
EXPECT_EQ(data + 3U, cursor.here());
EXPECT_TRUE(cursor >> c);
EXPECT_EQ(data + 7U, cursor.here());
EXPECT_TRUE(cursor.Skip(4));
EXPECT_EQ(data + 11U, cursor.here());
EXPECT_TRUE(cursor.Read(stars, 3));
EXPECT_EQ(data + 14U, cursor.here());
EXPECT_FALSE(cursor >> d);
EXPECT_EQ(data + 14U, cursor.here());
}
TEST(Extent, SetOffset) {
uint8_t data[] = { 0x5c, 0x79, 0x8c, 0xd5 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a, b, c, d, e;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0x5cU, a);
EXPECT_EQ(data + 1U, cursor.here());
EXPECT_TRUE(((cursor >> b).set_here(data + 3) >> c).set_here(data + 1)
>> d >> e);
EXPECT_EQ(0x79U, b);
EXPECT_EQ(0xd5U, c);
EXPECT_EQ(0x79U, d);
EXPECT_EQ(0x8cU, e);
EXPECT_EQ(data + 3U, cursor.here());
}
TEST(BigEndian, Signed1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, true, &a)
.Read(1, true, &b)
.Read(1, true, &c)
.Read(1, true, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(-0x80, c);
EXPECT_EQ(-1, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, true, &e));
}
TEST(BigEndian, Signed2) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff,
0x80, 0x00, 0x80, 0x80, 0xff, 0xff,
0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, true, &a)
.Read(2, true, &b)
.Read(2, true, &c)
.Read(2, true, &d)
.Read(2, true, &e)
.Read(2, true, &f)
.Read(2, true, &g)
.Read(2, true, &h)
.Read(2, true, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(-0x8000, d);
EXPECT_EQ(-0x7f80, e);
EXPECT_EQ(-1, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(-0x7544, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, true, &j));
}
TEST(BigEndian, Signed4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff,
0xb6, 0xb1, 0xff, 0xef,
0x19, 0x6a, 0xca, 0x46 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, true, &a)
.Read(4, true, &b)
.Read(4, true, &c)
.Read(4, true, &d)
.Read(4, true, &e)
.Read(4, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(-0x80000000LL, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int32_t) 0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, true, &g));
}
TEST(BigEndian, Signed8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c,
0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, true, &a)
.Read(8, true, &b)
.Read(8, true, &c)
.Read(8, true, &d)
.Read(8, true, &e)
.Read(8, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffffffffffffLL, b);
EXPECT_EQ(-0x7fffffffffffffffLL - 1, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4LL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, true, &g));
}
TEST(BigEndian, Unsigned1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, false, &a)
.Read(1, false, &b)
.Read(1, false, &c)
.Read(1, false, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(0x80, c);
EXPECT_EQ(0xff, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, false, &e));
}
TEST(BigEndian, Unsigned2) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff,
0x80, 0x00, 0x80, 0x80, 0xff, 0xff,
0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int64_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, false, &a)
.Read(2, false, &b)
.Read(2, false, &c)
.Read(2, false, &d)
.Read(2, false, &e)
.Read(2, false, &f)
.Read(2, false, &g)
.Read(2, false, &h)
.Read(2, false, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(0x8000, d);
EXPECT_EQ(0x8080, e);
EXPECT_EQ(0xffff, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(0x8abc, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, false, &j));
}
TEST(BigEndian, Unsigned4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff,
0xb6, 0xb1, 0xff, 0xef,
0x19, 0x6a, 0xca, 0x46 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, false, &a)
.Read(4, false, &b)
.Read(4, false, &c)
.Read(4, false, &d)
.Read(4, false, &e)
.Read(4, false, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(0x80000000, c);
EXPECT_EQ(0xffffffff, d);
EXPECT_EQ(0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, false, &g));
}
TEST(BigEndian, Unsigned8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c,
0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
uint64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, false, &a)
.Read(8, false, &b)
.Read(8, false, &c)
.Read(8, false, &d)
.Read(8, false, &e)
.Read(8, false, &f));
EXPECT_EQ(0U, a);
EXPECT_EQ(0x7fffffffffffffffULL, b);
EXPECT_EQ(0x8000000000000000ULL, c);
EXPECT_EQ(0xffffffffffffffffULL, d);
EXPECT_EQ(0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4ULL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, false, &g));
}
TEST(LittleEndian, Signed1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, true, &a)
.Read(1, true, &b)
.Read(1, true, &c)
.Read(1, true, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(-0x80, c);
EXPECT_EQ(-1, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, true, &e));
}
TEST(LittleEndian, Signed2) {
uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f,
0x00, 0x80, 0x80, 0x80, 0xff, 0xff,
0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, false);
int32_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, true, &a)
.Read(2, true, &b)
.Read(2, true, &c)
.Read(2, true, &d)
.Read(2, true, &e)
.Read(2, true, &f)
.Read(2, true, &g)
.Read(2, true, &h)
.Read(2, true, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(-0x8000, d);
EXPECT_EQ(-0x7f80, e);
EXPECT_EQ(-1, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(-0x7544, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, true, &j));
}
TEST(LittleEndian, Signed4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff,
0xef, 0xff, 0xb1, 0xb6,
0x46, 0xca, 0x6a, 0x19 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, true, &a)
.Read(4, true, &b)
.Read(4, true, &c)
.Read(4, true, &d)
.Read(4, true, &e)
.Read(4, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(-0x80000000LL, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int32_t) 0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, true, &g));
}
TEST(LittleEndian, Signed8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93,
0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, false);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, true, &a)
.Read(8, true, &b)
.Read(8, true, &c)
.Read(8, true, &d)
.Read(8, true, &e)
.Read(8, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffffffffffffLL, b);
EXPECT_EQ(-0x7fffffffffffffffLL - 1, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4LL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, true, &g));
}
TEST(LittleEndian, Unsigned1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, false, &a)
.Read(1, false, &b)
.Read(1, false, &c)
.Read(1, false, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(0x80, c);
EXPECT_EQ(0xff, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, false, &e));
}
TEST(LittleEndian, Unsigned2) {
uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f,
0x00, 0x80, 0x80, 0x80, 0xff, 0xff,
0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, false, &a)
.Read(2, false, &b)
.Read(2, false, &c)
.Read(2, false, &d)
.Read(2, false, &e)
.Read(2, false, &f)
.Read(2, false, &g)
.Read(2, false, &h)
.Read(2, false, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(0x8000, d);
EXPECT_EQ(0x8080, e);
EXPECT_EQ(0xffff, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(0x8abc, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, false, &j));
}
TEST(LittleEndian, Unsigned4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff,
0xef, 0xff, 0xb1, 0xb6,
0x46, 0xca, 0x6a, 0x19 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, false, &a)
.Read(4, false, &b)
.Read(4, false, &c)
.Read(4, false, &d)
.Read(4, false, &e)
.Read(4, false, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(0x80000000, c);
EXPECT_EQ(0xffffffff, d);
EXPECT_EQ(0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, false, &g));
}
TEST(LittleEndian, Unsigned8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93,
0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, false, &a)
.Read(8, false, &b)
.Read(8, false, &c)
.Read(8, false, &d)
.Read(8, false, &e)
.Read(8, false, &f));
EXPECT_EQ(0U, a);
EXPECT_EQ(0x7fffffffffffffffULL, b);
EXPECT_EQ(0x8000000000000000ULL, c);
EXPECT_EQ(0xffffffffffffffffULL, d);
EXPECT_EQ(0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4ULL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, false, &g));
}
TEST(Extractor, Signed1) {
uint8_t data[] = { 0xfd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int8_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-3, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Signed2) {
uint8_t data[] = { 0x13, 0xcd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int16_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-13037, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Signed4) {
uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a;
// For some reason, G++ 4.4.1 complains:
// warning: array subscript is above array bounds
// in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
// I'm not able to see how such a reference would occur.
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-380377902, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned1) {
uint8_t data[] = { 0xfd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xfd, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned2) {
uint8_t data[] = { 0x13, 0xcd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint16_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xcd13, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned4) {
uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint32_t a;
// For some reason, G++ 4.4.1 complains:
// warning: array subscript is above array bounds
// in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
// I'm not able to see how such a reference would occur.
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xe953e4d2, a);
EXPECT_FALSE(cursor >> a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Mixed) {
uint8_t data[] = { 0x42,
0x25, 0x0b,
0x3d, 0x25, 0xed, 0x2a,
0xec, 0x16, 0x9e, 0x14, 0x61, 0x5b, 0x2c, 0xcf,
0xd8,
0x22, 0xa5,
0x3a, 0x02, 0x6a, 0xd7,
0x93, 0x2a, 0x2d, 0x8d, 0xb4, 0x95, 0xe0, 0xc6 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
uint8_t a;
uint16_t b;
uint32_t c;
uint64_t d;
int8_t e;
int16_t f;
int32_t g;
int64_t h;
int z;
EXPECT_FALSE(cursor.AtEnd());
EXPECT_TRUE(cursor >> a >> b >> c >> d >> e >> f >> g >> h);
EXPECT_EQ(0x42U, a);
EXPECT_EQ(0x250bU, b);
EXPECT_EQ(0x3d25ed2aU, c);
EXPECT_EQ(0xec169e14615b2ccfULL, d);
EXPECT_EQ(-40, e);
EXPECT_EQ(0x22a5, f);
EXPECT_EQ(0x3a026ad7, g);
EXPECT_EQ(-7842405714468937530LL, h);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor >> z);
}
TEST(Strings, Zero) {
uint8_t data[] = { 0xa6 };
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
uint8_t received[1];
received[0] = 0xc2;
EXPECT_TRUE(cursor.Read(received, 0));
EXPECT_EQ(0xc2U, received[0]);
}
TEST(Strings, Some) {
uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t received[7] = { 0xa7, 0xf7, 0x43, 0x0c, 0x27, 0xea, 0xed };
EXPECT_TRUE(cursor.Skip(2).Read(received, 5));
uint8_t expected[7] = { 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xea, 0xed };
EXPECT_TRUE(memcmp(received, expected, 7) == 0);
}
TEST(Strings, TooMuch) {
uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t received1[3];
uint8_t received2[3];
uint8_t received3[3];
EXPECT_FALSE(cursor
.Read(received1, 3)
.Read(received2, 3)
.Read(received3, 3));
uint8_t expected1[3] = { 0x5d, 0x31, 0x09 };
uint8_t expected2[3] = { 0xa6, 0x2e, 0x2c };
EXPECT_TRUE(memcmp(received1, expected1, 3) == 0);
EXPECT_TRUE(memcmp(received2, expected2, 3) == 0);
}
TEST(Strings, PointTo) {
uint8_t data[] = { 0x83, 0x80, 0xb4, 0x38, 0x00, 0x2c, 0x0a, 0x27 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
const uint8_t *received1;
const uint8_t *received2;
const uint8_t *received3;
const uint8_t *received4;
EXPECT_FALSE(cursor
.PointTo(&received1, 3)
.PointTo(&received2, 3)
.PointTo(&received3)
.PointTo(&received4, 3));
EXPECT_EQ(data + 0, received1);
EXPECT_EQ(data + 3, received2);
EXPECT_EQ(data + 6, received3);
EXPECT_EQ(NULL, received4);
}
TEST(Strings, CString) {
uint8_t data[] = "abc\0\0foo";
ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0'
ByteCursor cursor(&buffer);
string a, b, c;
EXPECT_TRUE(cursor.CString(&a).CString(&b));
EXPECT_EQ("abc", a);
EXPECT_EQ("", b);
EXPECT_FALSE(cursor.CString(&c));
EXPECT_EQ("", c);
EXPECT_TRUE(cursor.AtEnd());
}
TEST(Strings, CStringLimit) {
uint8_t data[] = "abcdef\0\0foobar";
ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0'
ByteCursor cursor(&buffer);
string a, b, c, d, e;
EXPECT_TRUE(cursor.CString(&a, 3));
EXPECT_EQ("abc", a);
EXPECT_TRUE(cursor.CString(&b, 0));
EXPECT_EQ("", b);
EXPECT_TRUE(cursor.CString(&c, 6));
EXPECT_EQ("def", c);
EXPECT_TRUE(cursor.CString(&d, 4));
EXPECT_EQ("ooba", d);
EXPECT_FALSE(cursor.CString(&e, 4));
EXPECT_EQ("", e);
EXPECT_TRUE(cursor.AtEnd());
}
// uint8_t data[] = { 0xa6, 0x54, 0xdf, 0x67, 0x51, 0x43, 0xac, 0xf1 };
// ByteBuffer buffer(data, sizeof(data));

View File

@ -0,0 +1,243 @@
# Copyright 2014 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.
{
'target_defaults': {
'target_conditions': [
['OS=="mac"', {
'defines': ['HAVE_MACH_O_NLIST_H'],
}],
['OS=="linux"', {
'defines': ['HAVE_A_OUT_H'],
}],
],
},
'targets': [
{
'target_name': 'common',
'type': 'static_library',
'sources': [
'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',
'android/include/sys/user.h',
'android/include/ucontext.h',
'android/testing/include/wchar.h',
'android/testing/mkdtemp.h',
'android/testing/pthread_fixes.h',
'android/ucontext_constants.h',
'basictypes.h',
'byte_cursor.h',
'convert_UTF.c',
'convert_UTF.h',
'dwarf/bytereader-inl.h',
'dwarf/bytereader.cc',
'dwarf/bytereader.h',
'dwarf/cfi_assembler.cc',
'dwarf/cfi_assembler.h',
'dwarf/dwarf2diehandler.cc',
'dwarf/dwarf2diehandler.h',
'dwarf/dwarf2enums.h',
'dwarf/dwarf2reader.cc',
'dwarf/dwarf2reader.h',
'dwarf/dwarf2reader_test_common.h',
'dwarf/functioninfo.cc',
'dwarf/functioninfo.h',
'dwarf/line_state_machine.h',
'dwarf/types.h',
'dwarf_cfi_to_module.cc',
'dwarf_cfi_to_module.h',
'dwarf_cu_to_module.cc',
'dwarf_cu_to_module.h',
'dwarf_line_to_module.cc',
'dwarf_line_to_module.h',
'language.cc',
'language.h',
'linux/crc32.cc',
'linux/crc32.h',
'linux/dump_symbols.cc',
'linux/dump_symbols.h',
'linux/eintr_wrapper.h',
'linux/elf_core_dump.cc',
'linux/elf_core_dump.h',
'linux/elf_gnu_compat.h',
'linux/elf_symbols_to_module.cc',
'linux/elf_symbols_to_module.h',
'linux/elfutils-inl.h',
'linux/elfutils.cc',
'linux/elfutils.h',
'linux/file_id.cc',
'linux/file_id.h',
'linux/google_crashdump_uploader.cc',
'linux/google_crashdump_uploader.h',
'linux/guid_creator.cc',
'linux/guid_creator.h',
'linux/http_upload.cc',
'linux/http_upload.h',
'linux/ignore_ret.h',
'linux/libcurl_wrapper.cc',
'linux/libcurl_wrapper.h',
'linux/linux_libc_support.cc',
'linux/linux_libc_support.h',
'linux/memory_mapped_file.cc',
'linux/memory_mapped_file.h',
'linux/safe_readlink.cc',
'linux/safe_readlink.h',
'linux/synth_elf.cc',
'linux/synth_elf.h',
'mac/arch_utilities.cc',
'mac/arch_utilities.h',
'mac/bootstrap_compat.cc',
'mac/bootstrap_compat.h',
'mac/byteswap.h',
'mac/dump_syms.h',
'mac/dump_syms.mm',
'mac/file_id.cc',
'mac/file_id.h',
'mac/GTMDefines.h',
'mac/GTMLogger.h',
'mac/GTMLogger.m',
'mac/HTTPMultipartUpload.h',
'mac/HTTPMultipartUpload.m',
'mac/MachIPC.h',
'mac/MachIPC.mm',
'mac/macho_id.cc',
'mac/macho_id.h',
'mac/macho_reader.cc',
'mac/macho_reader.h',
'mac/macho_utilities.cc',
'mac/macho_utilities.h',
'mac/macho_walker.cc',
'mac/macho_walker.h',
'mac/scoped_task_suspend-inl.h',
'mac/string_utilities.cc',
'mac/string_utilities.h',
'mac/super_fat_arch.h',
'md5.cc',
'md5.h',
'memory.h',
'memory_range.h',
'module.cc',
'module.h',
'scoped_ptr.h',
'simple_string_dictionary.cc',
'simple_string_dictionary.h',
'solaris/dump_symbols.cc',
'solaris/dump_symbols.h',
'solaris/file_id.cc',
'solaris/file_id.h',
'solaris/guid_creator.cc',
'solaris/guid_creator.h',
'solaris/message_output.h',
'stabs_reader.cc',
'stabs_reader.h',
'stabs_to_module.cc',
'stabs_to_module.h',
'string_conversion.cc',
'string_conversion.h',
'symbol_data.h',
'test_assembler.cc',
'test_assembler.h',
'unordered.h',
'using_std_string.h',
'windows/common_windows.gyp',
'windows/dia_util.cc',
'windows/dia_util.h',
'windows/guid_string.cc',
'windows/guid_string.h',
'windows/http_upload.cc',
'windows/http_upload.h',
'windows/omap.cc',
'windows/omap.h',
'windows/omap_internal.h',
'windows/pdb_source_line_writer.cc',
'windows/pdb_source_line_writer.h',
'windows/string_utils-inl.h',
'windows/string_utils.cc',
],
'include_dirs': [
'..',
],
},
{
'target_name': 'common_unittests',
'type': 'executable',
'sources': [
'android/breakpad_getcontext_unittest.cc',
'byte_cursor_unittest.cc',
'dwarf/bytereader_unittest.cc',
'dwarf/dwarf2diehandler_unittest.cc',
'dwarf/dwarf2reader_cfi_unittest.cc',
'dwarf/dwarf2reader_die_unittest.cc',
'dwarf_cfi_to_module_unittest.cc',
'dwarf_cu_to_module_unittest.cc',
'dwarf_line_to_module_unittest.cc',
'linux/dump_symbols_unittest.cc',
'linux/elf_core_dump_unittest.cc',
'linux/elf_symbols_to_module_unittest.cc',
'linux/file_id_unittest.cc',
'linux/google_crashdump_uploader_test.cc',
'linux/linux_libc_support_unittest.cc',
'linux/memory_mapped_file_unittest.cc',
'linux/safe_readlink_unittest.cc',
'linux/synth_elf_unittest.cc',
'linux/tests/auto_testfile.h',
'linux/tests/crash_generator.cc',
'linux/tests/crash_generator.h',
'mac/macho_reader_unittest.cc',
'memory_range_unittest.cc',
'memory_unittest.cc',
'module_unittest.cc',
'simple_string_dictionary_unittest.cc',
'stabs_reader_unittest.cc',
'stabs_to_module_unittest.cc',
'test_assembler_unittest.cc',
'tests/auto_tempdir.h',
'tests/file_utils.cc',
'tests/file_utils.h',
'windows/omap_unittest.cc',
],
'include_dirs': [
'..',
],
'dependencies': [
'common',
'../build/testing.gypi:gmock_main',
'../build/testing.gypi:gmock',
'../build/testing.gypi:gtest',
],
'libraries': [
'-ldl',
],
},
],
}

View File

@ -0,0 +1,554 @@
/*
* Copyright © 1991-2015 Unicode, Inc. All rights reserved.
* Distributed under the Terms of Use in
* http://www.unicode.org/copyright.html.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of the Unicode data files and any associated documentation
* (the "Data Files") or Unicode software and any associated documentation
* (the "Software") to deal in the Data Files or Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, and/or sell copies of
* the Data Files or Software, and to permit persons to whom the Data Files
* or Software are furnished to do so, provided that
* (a) this copyright and permission notice appear with all copies
* of the Data Files or Software,
* (b) this copyright and permission notice appear in associated
* documentation, and
* (c) there is clear notice in each modified Data File or in the Software
* as well as in the documentation associated with the Data File(s) or
* Software that the data or software has been modified.
*
* THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
* NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
* DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THE DATA FILES OR SOFTWARE.
*
* Except as contained in this notice, the name of a copyright holder
* shall not be used in advertising or otherwise to promote the sale,
* use or other dealings in these Data Files or Software without prior
* written authorization of the copyright holder.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Sept 2001: fixed const & error conditions per
mods suggested by S. Parent & A. Lillich.
June 2002: Tim Dodd added detection and handling of incomplete
source sequences, enhanced error detection, added casts
to eliminate compiler warnings.
July 2003: slight mods to back out aggressive FFFE detection.
Jan 2004: updated switches in from-UTF8 conversions.
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
See the header file "ConvertUTF.h" for complete documentation.
------------------------------------------------------------------------ */
#include "convert_UTF.h"
#ifdef CVTUTF_DEBUG
#include <stdio.h>
#endif
static const int halfShift = 10; /* used for shifting by 10 bits */
static const UTF32 halfBase = 0x0010000UL;
static const UTF32 halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_START (UTF32)0xD800
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
#ifndef false
#define false 0
#endif
#ifndef true
#define true 1
#endif
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
if (target >= targetEnd) {
result = targetExhausted; break;
}
ch = *source++;
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
/* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_LEGAL_UTF32) {
if (flags == strictConversion) {
result = sourceIllegal;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
--source; /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF32* target = *targetStart;
UTF32 ch, ch2;
while (source < sourceEnd) {
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
} else if (flags == strictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
if (target >= targetEnd) {
source = oldSource; /* Back up source pointer! */
result = targetExhausted; break;
}
*target++ = ch;
}
*sourceStart = source;
*targetStart = target;
#ifdef CVTUTF_DEBUG
if (result == sourceIllegal) {
fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
fflush(stderr);
}
#endif
return result;
}
/* --------------------------------------------------------------------- */
/*
* Index into the table below with the first byte of a UTF-8 sequence to
* get the number of trailing bytes that are supposed to follow it.
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
* left as-is for anyone who may want to do such conversion, which was
* allowed in earlier algorithms.
*/
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
/*
* Magic values subtracted from a buffer value during UTF8 conversion.
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
/*
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
* for *legal* UTF-8 will be 4 or fewer bytes total.
*/
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
/* --------------------------------------------------------------------- */
/* The interface converts a whole buffer to avoid function-call overhead.
* Constants have been gathered. Loops & conditionals have been removed as
* much as possible for efficiency, in favor of drop-through switches.
* (See "Note A" at the bottom of the file for equivalent code.)
* If your compiler supports it, the "isLegalUTF8" call can be turned
* into an inline function.
*/
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
UTF32 ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
} else if (flags == strictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/* Figure out how many bytes the result will require */
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
}
target += bytesToWrite;
if (target > targetEnd) {
source = oldSource; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
/*
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
* This must be called with the length pre-determined by the first byte.
* If not calling this from ConvertUTF8to*, then the length can be set by:
* length = trailingBytesForUTF8[*source]+1;
* and the sequence is illegal right away if there aren't that many bytes
* available.
* If presented with a length > 4, this returns false. The Unicode
* definition of UTF-8 goes up to 4-byte sequences.
*/
static Boolean isLegalUTF8(const UTF8 *source, int length) {
UTF8 a;
const UTF8 *srcptr = source+length;
switch (length) {
default: return false;
/* Everything else falls through when "true"... */
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
switch (*source) {
/* no fall-through in this inner switch */
case 0xE0: if (a < 0xA0) return false; break;
case 0xED: if (a > 0x9F) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false;
}
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
}
if (*source > 0xF4) return false;
return true;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return whether a UTF-8 sequence is legal or not.
* This is not used here; it's just exported.
*/
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
int length = trailingBytesForUTF8[*source]+1;
if (source+length > sourceEnd) {
return false;
}
return isLegalUTF8(source, length);
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_UTF16) {
if (flags == strictConversion) {
result = sourceIllegal;
source -= (extraBytesToRead+1); /* return to the start */
break; /* Bail out; shouldn't continue */
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
ch = *source++;
if (flags == strictConversion ) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/*
* Figure out how many bytes the result will require. Turn any
* illegally large UTF32 things (> Plane 17) into replacement chars.
*/
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
result = sourceIllegal;
}
target += bytesToWrite;
if (target > targetEnd) {
--source; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF32* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6;
case 4: ch += *source++; ch <<= 6;
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up the source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_LEGAL_UTF32) {
/*
* UTF-16 surrogate values are illegal in UTF-32, and anything
* over Plane 17 (> 0x10FFFF) is illegal.
*/
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = ch;
}
} else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
result = sourceIllegal;
*target++ = UNI_REPLACEMENT_CHAR;
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* ---------------------------------------------------------------------
Note A.
The fall-through switches in UTF-8 reading code save a
temp variable, some decrements & conditionals. The switches
are equivalent to the following loop:
{
int tmpBytesToRead = extraBytesToRead+1;
do {
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
} while (tmpBytesToRead > 0);
}
In UTF-8 writing code, the switches on "bytesToWrite" are
similarly unrolled loops.
--------------------------------------------------------------------- */

View File

@ -0,0 +1,164 @@
/*
* Copyright © 1991-2015 Unicode, Inc. All rights reserved.
* Distributed under the Terms of Use in
* http://www.unicode.org/copyright.html.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of the Unicode data files and any associated documentation
* (the "Data Files") or Unicode software and any associated documentation
* (the "Software") to deal in the Data Files or Software
* without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, and/or sell copies of
* the Data Files or Software, and to permit persons to whom the Data Files
* or Software are furnished to do so, provided that
* (a) this copyright and permission notice appear with all copies
* of the Data Files or Software,
* (b) this copyright and permission notice appear in associated
* documentation, and
* (c) there is clear notice in each modified Data File or in the Software
* as well as in the documentation associated with the Data File(s) or
* Software that the data or software has been modified.
*
* THE DATA FILES AND SOFTWARE ARE 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 OF THIRD PARTY RIGHTS.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
* NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
* DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THE DATA FILES OR SOFTWARE.
*
* Except as contained in this notice, the name of a copyright holder
* shall not be used in advertising or otherwise to promote the sale,
* use or other dealings in these Data Files or Software without prior
* written authorization of the copyright holder.
*/
#ifndef COMMON_CONVERT_UTF_H_
#define COMMON_CONVERT_UTF_H_
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Header file.
Several funtions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file.
Each of these routines takes pointers to input buffers and output
buffers. The input buffers are const.
Each routine converts the text between *sourceStart and sourceEnd,
putting the result into the buffer between *targetStart and
targetEnd. Note: the end pointers are *after* the last item: e.g.
*(sourceEnd - 1) is the last item.
The return result indicates whether the conversion was successful,
and if not, whether the problem was in the source or target buffers.
(Only the first encountered problem is indicated.)
After the conversion, *sourceStart and *targetStart are both
updated to point to the end of last text successfully converted in
the respective buffers.
Input parameters:
sourceStart - pointer to a pointer to the source buffer.
The contents of this are modified on return so that
it points at the next thing to be converted.
targetStart - similarly, pointer to pointer to the target buffer.
sourceEnd, targetEnd - respectively pointers to the ends of the
two buffers, for overflow checking only.
These conversion functions take a ConversionFlags argument. When this
flag is set to strict, both irregular sequences and isolated surrogates
will cause an error. When the flag is set to lenient, both irregular
sequences and isolated surrogates are converted.
Whether the flag is strict or lenient, all illegal sequences will cause
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
must check for illegal sequences.
When the flag is set to lenient, characters over 0x10FFFF are converted
to the replacement character; otherwise (when the flag is set to strict)
they constitute an error.
Output parameters:
The value "sourceIllegal" is returned from some routines if the input
sequence is malformed. When "sourceIllegal" is returned, the source
value will point to the illegal value that caused the problem. E.g.,
in UTF-8 when a sequence is malformed, it points to the start of the
malformed sequence.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Fixes & updates, Sept 2001.
------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------
The following 4 definitions are compiler-specific.
The C standard does not guarantee that wchar_t has at least
16 bits, so wchar_t is no less portable than unsigned short!
All should be unsigned values to avoid sign extension during
bit mask & shift operations.
------------------------------------------------------------------------ */
typedef unsigned long UTF32; /* at least 32 bits */
typedef unsigned short UTF16; /* at least 16 bits */
typedef unsigned char UTF8; /* typically 8 bits */
typedef unsigned char Boolean; /* 0 or 1 */
/* Some fundamental constants */
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
#define UNI_MAX_BMP (UTF32)0x0000FFFF
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
typedef enum {
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */
} ConversionResult;
typedef enum {
strictConversion = 0,
lenientConversion
} ConversionFlags;
/* This is for C++ and does no harm in C */
#ifdef __cplusplus
extern "C" {
#endif
ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
#ifdef __cplusplus
}
#endif
/* --------------------------------------------------------------------- */
#endif // COMMON_CONVERT_UTF_H_

View File

@ -0,0 +1,175 @@
// Copyright 2006 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 UTIL_DEBUGINFO_BYTEREADER_INL_H__
#define UTIL_DEBUGINFO_BYTEREADER_INL_H__
#include "common/dwarf/bytereader.h"
#include <assert.h>
namespace dwarf2reader {
inline uint8 ByteReader::ReadOneByte(const char* buffer) const {
return buffer[0];
}
inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(signed_buffer);
const uint16 buffer0 = buffer[0];
const uint16 buffer1 = buffer[1];
if (endian_ == ENDIANNESS_LITTLE) {
return buffer0 | buffer1 << 8;
} else {
return buffer1 | buffer0 << 8;
}
}
inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(signed_buffer);
const uint32 buffer0 = buffer[0];
const uint32 buffer1 = buffer[1];
const uint32 buffer2 = buffer[2];
const uint32 buffer3 = buffer[3];
if (endian_ == ENDIANNESS_LITTLE) {
return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24;
} else {
return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24;
}
}
inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const {
const unsigned char *buffer
= reinterpret_cast<const unsigned char *>(signed_buffer);
const uint64 buffer0 = buffer[0];
const uint64 buffer1 = buffer[1];
const uint64 buffer2 = buffer[2];
const uint64 buffer3 = buffer[3];
const uint64 buffer4 = buffer[4];
const uint64 buffer5 = buffer[5];
const uint64 buffer6 = buffer[6];
const uint64 buffer7 = buffer[7];
if (endian_ == ENDIANNESS_LITTLE) {
return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 |
buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56;
} else {
return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 |
buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56;
}
}
// Read an unsigned LEB128 number. Each byte contains 7 bits of
// information, plus one bit saying whether the number continues or
// not.
inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer,
size_t* len) const {
uint64 result = 0;
size_t num_read = 0;
unsigned int shift = 0;
unsigned char byte;
do {
byte = *buffer++;
num_read++;
result |= (static_cast<uint64>(byte & 0x7f)) << shift;
shift += 7;
} while (byte & 0x80);
*len = num_read;
return result;
}
// Read a signed LEB128 number. These are like regular LEB128
// numbers, except the last byte may have a sign bit set.
inline int64 ByteReader::ReadSignedLEB128(const char* buffer,
size_t* len) const {
int64 result = 0;
unsigned int shift = 0;
size_t num_read = 0;
unsigned char byte;
do {
byte = *buffer++;
num_read++;
result |= (static_cast<uint64>(byte & 0x7f) << shift);
shift += 7;
} while (byte & 0x80);
if ((shift < 8 * sizeof (result)) && (byte & 0x40))
result |= -((static_cast<int64>(1)) << shift);
*len = num_read;
return result;
}
inline uint64 ByteReader::ReadOffset(const char* buffer) const {
assert(this->offset_reader_);
return (this->*offset_reader_)(buffer);
}
inline uint64 ByteReader::ReadAddress(const char* buffer) const {
assert(this->address_reader_);
return (this->*address_reader_)(buffer);
}
inline void ByteReader::SetCFIDataBase(uint64 section_base,
const char *buffer_base) {
section_base_ = section_base;
buffer_base_ = buffer_base;
have_section_base_ = true;
}
inline void ByteReader::SetTextBase(uint64 text_base) {
text_base_ = text_base;
have_text_base_ = true;
}
inline void ByteReader::SetDataBase(uint64 data_base) {
data_base_ = data_base;
have_data_base_ = true;
}
inline void ByteReader::SetFunctionBase(uint64 function_base) {
function_base_ = function_base;
have_function_base_ = true;
}
inline void ByteReader::ClearFunctionBase() {
have_function_base_ = false;
}
} // namespace dwarf2reader
#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__

View File

@ -0,0 +1,245 @@
// Copyright (c) 2010 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 <assert.h>
#include <stdlib.h>
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/bytereader.h"
namespace dwarf2reader {
ByteReader::ByteReader(enum Endianness endian)
:offset_reader_(NULL), address_reader_(NULL), endian_(endian),
address_size_(0), offset_size_(0),
have_section_base_(), have_text_base_(), have_data_base_(),
have_function_base_() { }
ByteReader::~ByteReader() { }
void ByteReader::SetOffsetSize(uint8 size) {
offset_size_ = size;
assert(size == 4 || size == 8);
if (size == 4) {
this->offset_reader_ = &ByteReader::ReadFourBytes;
} else {
this->offset_reader_ = &ByteReader::ReadEightBytes;
}
}
void ByteReader::SetAddressSize(uint8 size) {
address_size_ = size;
assert(size == 4 || size == 8);
if (size == 4) {
this->address_reader_ = &ByteReader::ReadFourBytes;
} else {
this->address_reader_ = &ByteReader::ReadEightBytes;
}
}
uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
const uint64 initial_length = ReadFourBytes(start);
start += 4;
// In DWARF2/3, if the initial length is all 1 bits, then the offset
// size is 8 and we need to read the next 8 bytes for the real length.
if (initial_length == 0xffffffff) {
SetOffsetSize(8);
*len = 12;
return ReadOffset(start);
} else {
SetOffsetSize(4);
*len = 4;
}
return initial_length;
}
bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const {
if (encoding == DW_EH_PE_omit) return true;
if (encoding == DW_EH_PE_aligned) return true;
if ((encoding & 0x7) > DW_EH_PE_udata8)
return false;
if ((encoding & 0x70) > DW_EH_PE_funcrel)
return false;
return true;
}
bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
switch (encoding & 0x70) {
case DW_EH_PE_absptr: return true;
case DW_EH_PE_pcrel: return have_section_base_;
case DW_EH_PE_textrel: return have_text_base_;
case DW_EH_PE_datarel: return have_data_base_;
case DW_EH_PE_funcrel: return have_function_base_;
default: return false;
}
}
uint64 ByteReader::ReadEncodedPointer(const char *buffer,
DwarfPointerEncoding encoding,
size_t *len) const {
// UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
// see it here.
assert(encoding != DW_EH_PE_omit);
// The Linux Standards Base 4.0 does not make this clear, but the
// GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c)
// agree that aligned pointers are always absolute, machine-sized,
// machine-signed pointers.
if (encoding == DW_EH_PE_aligned) {
assert(have_section_base_);
// We don't need to align BUFFER in *our* address space. Rather, we
// need to find the next position in our buffer that would be aligned
// when the .eh_frame section the buffer contains is loaded into the
// program's memory. So align assuming that buffer_base_ gets loaded at
// address section_base_, where section_base_ itself may or may not be
// aligned.
// First, find the offset to START from the closest prior aligned
// address.
uint64 skew = section_base_ & (AddressSize() - 1);
// Now find the offset from that aligned address to buffer.
uint64 offset = skew + (buffer - buffer_base_);
// Round up to the next boundary.
uint64 aligned = (offset + AddressSize() - 1) & -AddressSize();
// Convert back to a pointer.
const char *aligned_buffer = buffer_base_ + (aligned - skew);
// Finally, store the length and actually fetch the pointer.
*len = aligned_buffer - buffer + AddressSize();
return ReadAddress(aligned_buffer);
}
// Extract the value first, ignoring whether it's a pointer or an
// offset relative to some base.
uint64 offset;
switch (encoding & 0x0f) {
case DW_EH_PE_absptr:
// DW_EH_PE_absptr is weird, as it is used as a meaningful value for
// both the high and low nybble of encoding bytes. When it appears in
// the high nybble, it means that the pointer is absolute, not an
// offset from some base address. When it appears in the low nybble,
// as here, it means that the pointer is stored as a normal
// machine-sized and machine-signed address. A low nybble of
// DW_EH_PE_absptr does not imply that the pointer is absolute; it is
// correct for us to treat the value as an offset from a base address
// if the upper nybble is not DW_EH_PE_absptr.
offset = ReadAddress(buffer);
*len = AddressSize();
break;
case DW_EH_PE_uleb128:
offset = ReadUnsignedLEB128(buffer, len);
break;
case DW_EH_PE_udata2:
offset = ReadTwoBytes(buffer);
*len = 2;
break;
case DW_EH_PE_udata4:
offset = ReadFourBytes(buffer);
*len = 4;
break;
case DW_EH_PE_udata8:
offset = ReadEightBytes(buffer);
*len = 8;
break;
case DW_EH_PE_sleb128:
offset = ReadSignedLEB128(buffer, len);
break;
case DW_EH_PE_sdata2:
offset = ReadTwoBytes(buffer);
// Sign-extend from 16 bits.
offset = (offset ^ 0x8000) - 0x8000;
*len = 2;
break;
case DW_EH_PE_sdata4:
offset = ReadFourBytes(buffer);
// Sign-extend from 32 bits.
offset = (offset ^ 0x80000000ULL) - 0x80000000ULL;
*len = 4;
break;
case DW_EH_PE_sdata8:
// No need to sign-extend; this is the full width of our type.
offset = ReadEightBytes(buffer);
*len = 8;
break;
default:
abort();
}
// Find the appropriate base address.
uint64 base;
switch (encoding & 0x70) {
case DW_EH_PE_absptr:
base = 0;
break;
case DW_EH_PE_pcrel:
assert(have_section_base_);
base = section_base_ + (buffer - buffer_base_);
break;
case DW_EH_PE_textrel:
assert(have_text_base_);
base = text_base_;
break;
case DW_EH_PE_datarel:
assert(have_data_base_);
base = data_base_;
break;
case DW_EH_PE_funcrel:
assert(have_function_base_);
base = function_base_;
break;
default:
abort();
}
uint64 pointer = base + offset;
// Remove inappropriate upper bits.
if (AddressSize() == 4)
pointer = pointer & 0xffffffff;
else
assert(AddressSize() == sizeof(uint64));
return pointer;
}
} // namespace dwarf2reader

View File

@ -0,0 +1,310 @@
// -*- mode: C++ -*-
// Copyright (c) 2010 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_DWARF_BYTEREADER_H__
#define COMMON_DWARF_BYTEREADER_H__
#include <string>
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
namespace dwarf2reader {
// We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN
// because it conflicts with a macro
enum Endianness {
ENDIANNESS_BIG,
ENDIANNESS_LITTLE
};
// A ByteReader knows how to read single- and multi-byte values of
// various endiannesses, sizes, and encodings, as used in DWARF
// debugging information and Linux C++ exception handling data.
class ByteReader {
public:
// Construct a ByteReader capable of reading one-, two-, four-, and
// eight-byte values according to ENDIANNESS, absolute machine-sized
// addresses, DWARF-style "initial length" values, signed and
// unsigned LEB128 numbers, and Linux C++ exception handling data's
// encoded pointers.
explicit ByteReader(enum Endianness endianness);
virtual ~ByteReader();
// Read a single byte from BUFFER and return it as an unsigned 8 bit
// number.
uint8 ReadOneByte(const char* buffer) const;
// Read two bytes from BUFFER and return them as an unsigned 16 bit
// number, using this ByteReader's endianness.
uint16 ReadTwoBytes(const char* buffer) const;
// Read four bytes from BUFFER and return them as an unsigned 32 bit
// number, using this ByteReader's endianness. This function returns
// a uint64 so that it is compatible with ReadAddress and
// ReadOffset. The number it returns will never be outside the range
// of an unsigned 32 bit integer.
uint64 ReadFourBytes(const char* buffer) const;
// Read eight bytes from BUFFER and return them as an unsigned 64
// bit number, using this ByteReader's endianness.
uint64 ReadEightBytes(const char* buffer) const;
// Read an unsigned LEB128 (Little Endian Base 128) number from
// BUFFER and return it as an unsigned 64 bit integer. Set LEN to
// the number of bytes read.
//
// The unsigned LEB128 representation of an integer N is a variable
// number of bytes:
//
// - If N is between 0 and 0x7f, then its unsigned LEB128
// representation is a single byte whose value is N.
//
// - Otherwise, its unsigned LEB128 representation is (N & 0x7f) |
// 0x80, followed by the unsigned LEB128 representation of N /
// 128, rounded towards negative infinity.
//
// In other words, we break VALUE into groups of seven bits, put
// them in little-endian order, and then write them as eight-bit
// bytes with the high bit on all but the last.
uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const;
// Read a signed LEB128 number from BUFFER and return it as an
// signed 64 bit integer. Set LEN to the number of bytes read.
//
// The signed LEB128 representation of an integer N is a variable
// number of bytes:
//
// - If N is between -0x40 and 0x3f, then its signed LEB128
// representation is a single byte whose value is N in two's
// complement.
//
// - Otherwise, its signed LEB128 representation is (N & 0x7f) |
// 0x80, followed by the signed LEB128 representation of N / 128,
// rounded towards negative infinity.
//
// In other words, we break VALUE into groups of seven bits, put
// them in little-endian order, and then write them as eight-bit
// bytes with the high bit on all but the last.
int64 ReadSignedLEB128(const char* buffer, size_t* len) const;
// Indicate that addresses on this architecture are SIZE bytes long. SIZE
// must be either 4 or 8. (DWARF allows addresses to be any number of
// bytes in length from 1 to 255, but we only support 32- and 64-bit
// addresses at the moment.) You must call this before using the
// ReadAddress member function.
//
// For data in a .debug_info section, or something that .debug_info
// refers to like line number or macro data, the compilation unit
// header's address_size field indicates the address size to use. Call
// frame information doesn't indicate its address size (a shortcoming of
// the spec); you must supply the appropriate size based on the
// architecture of the target machine.
void SetAddressSize(uint8 size);
// Return the current address size, in bytes. This is either 4,
// indicating 32-bit addresses, or 8, indicating 64-bit addresses.
uint8 AddressSize() const { return address_size_; }
// Read an address from BUFFER and return it as an unsigned 64 bit
// integer, respecting this ByteReader's endianness and address size. You
// must call SetAddressSize before calling this function.
uint64 ReadAddress(const char* buffer) const;
// DWARF actually defines two slightly different formats: 32-bit DWARF
// and 64-bit DWARF. This is *not* related to the size of registers or
// addresses on the target machine; it refers only to the size of section
// offsets and data lengths appearing in the DWARF data. One only needs
// 64-bit DWARF when the debugging data itself is larger than 4GiB.
// 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the
// debugging data itself is very large.
//
// DWARF information identifies itself as 32-bit or 64-bit DWARF: each
// compilation unit and call frame information entry begins with an
// "initial length" field, which, in addition to giving the length of the
// data, also indicates the size of section offsets and lengths appearing
// in that data. The ReadInitialLength member function, below, reads an
// initial length and sets the ByteReader's offset size as a side effect.
// Thus, in the normal process of reading DWARF data, the appropriate
// offset size is set automatically. So, you should only need to call
// SetOffsetSize if you are using the same ByteReader to jump from the
// midst of one block of DWARF data into another.
// Read a DWARF "initial length" field from START, and return it as
// an unsigned 64 bit integer, respecting this ByteReader's
// endianness. Set *LEN to the length of the initial length in
// bytes, either four or twelve. As a side effect, set this
// ByteReader's offset size to either 4 (if we see a 32-bit DWARF
// initial length) or 8 (if we see a 64-bit DWARF initial length).
//
// A DWARF initial length is either:
//
// - a byte count stored as an unsigned 32-bit value less than
// 0xffffff00, indicating that the data whose length is being
// measured uses the 32-bit DWARF format, or
//
// - The 32-bit value 0xffffffff, followed by a 64-bit byte count,
// indicating that the data whose length is being measured uses
// the 64-bit DWARF format.
uint64 ReadInitialLength(const char* start, size_t* len);
// Read an offset from BUFFER and return it as an unsigned 64 bit
// integer, respecting the ByteReader's endianness. In 32-bit DWARF, the
// offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes
// long. You must call ReadInitialLength or SetOffsetSize before calling
// this function; see the comments above for details.
uint64 ReadOffset(const char* buffer) const;
// Return the current offset size, in bytes.
// A return value of 4 indicates that we are reading 32-bit DWARF.
// A return value of 8 indicates that we are reading 64-bit DWARF.
uint8 OffsetSize() const { return offset_size_; }
// Indicate that section offsets and lengths are SIZE bytes long. SIZE
// must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF).
// Usually, you should not call this function yourself; instead, let a
// call to ReadInitialLength establish the data's offset size
// automatically.
void SetOffsetSize(uint8 size);
// The Linux C++ ABI uses a variant of DWARF call frame information
// for exception handling. This data is included in the program's
// address space as the ".eh_frame" section, and intepreted at
// runtime to walk the stack, find exception handlers, and run
// cleanup code. The format is mostly the same as DWARF CFI, with
// some adjustments made to provide the additional
// exception-handling data, and to make the data easier to work with
// in memory --- for example, to allow it to be placed in read-only
// memory even when describing position-independent code.
//
// In particular, exception handling data can select a number of
// different encodings for pointers that appear in the data, as
// described by the DwarfPointerEncoding enum. There are actually
// four axes(!) to the encoding:
//
// - The pointer size: pointers can be 2, 4, or 8 bytes long, or use
// the DWARF LEB128 encoding.
//
// - The pointer's signedness: pointers can be signed or unsigned.
//
// - The pointer's base address: the data stored in the exception
// handling data can be the actual address (that is, an absolute
// pointer), or relative to one of a number of different base
// addreses --- including that of the encoded pointer itself, for
// a form of "pc-relative" addressing.
//
// - The pointer may be indirect: it may be the address where the
// true pointer is stored. (This is used to refer to things via
// global offset table entries, program linkage table entries, or
// other tricks used in position-independent code.)
//
// There are also two options that fall outside that matrix
// altogether: the pointer may be omitted, or it may have padding to
// align it on an appropriate address boundary. (That last option
// may seem like it should be just another axis, but it is not.)
// Indicate that the exception handling data is loaded starting at
// SECTION_BASE, and that the start of its buffer in our own memory
// is BUFFER_BASE. This allows us to find the address that a given
// byte in our buffer would have when loaded into the program the
// data describes. We need this to resolve DW_EH_PE_pcrel pointers.
void SetCFIDataBase(uint64 section_base, const char *buffer_base);
// Indicate that the base address of the program's ".text" section
// is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers.
void SetTextBase(uint64 text_base);
// Indicate that the base address for DW_EH_PE_datarel pointers is
// DATA_BASE. The proper value depends on the ABI; it is usually the
// address of the global offset table, held in a designated register in
// position-independent code. You will need to look at the startup code
// for the target system to be sure. I tried; my eyes bled.
void SetDataBase(uint64 data_base);
// Indicate that the base address for the FDE we are processing is
// FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel
// pointers. (This encoding does not seem to be used by the GNU
// toolchain.)
void SetFunctionBase(uint64 function_base);
// Indicate that we are no longer processing any FDE, so any use of
// a DW_EH_PE_funcrel encoding is an error.
void ClearFunctionBase();
// Return true if ENCODING is a valid pointer encoding.
bool ValidEncoding(DwarfPointerEncoding encoding) const;
// Return true if we have all the information we need to read a
// pointer that uses ENCODING. This checks that the appropriate
// SetFooBase function for ENCODING has been called.
bool UsableEncoding(DwarfPointerEncoding encoding) const;
// Read an encoded pointer from BUFFER using ENCODING; return the
// absolute address it represents, and set *LEN to the pointer's
// length in bytes, including any padding for aligned pointers.
//
// This function calls 'abort' if ENCODING is invalid or refers to a
// base address this reader hasn't been given, so you should check
// with ValidEncoding and UsableEncoding first if you would rather
// die in a more helpful way.
uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding,
size_t *len) const;
private:
// Function pointer type for our address and offset readers.
typedef uint64 (ByteReader::*AddressReader)(const char*) const;
// Read an offset from BUFFER and return it as an unsigned 64 bit
// integer. DWARF2/3 define offsets as either 4 or 8 bytes,
// generally depending on the amount of DWARF2/3 info present.
// This function pointer gets set by SetOffsetSize.
AddressReader offset_reader_;
// Read an address from BUFFER and return it as an unsigned 64 bit
// integer. DWARF2/3 allow addresses to be any size from 0-255
// bytes currently. Internally we support 4 and 8 byte addresses,
// and will CHECK on anything else.
// This function pointer gets set by SetAddressSize.
AddressReader address_reader_;
Endianness endian_;
uint8 address_size_;
uint8 offset_size_;
// Base addresses for Linux C++ exception handling data's encoded pointers.
bool have_section_base_, have_text_base_, have_data_base_;
bool have_function_base_;
uint64 section_base_, text_base_, data_base_, function_base_;
const char *buffer_base_;
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_BYTEREADER_H__

View File

@ -0,0 +1,697 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/cfi_assembler.h"
#include "common/using_std_string.h"
using dwarf2reader::ByteReader;
using dwarf2reader::DwarfPointerEncoding;
using dwarf2reader::ENDIANNESS_BIG;
using dwarf2reader::ENDIANNESS_LITTLE;
using google_breakpad::CFISection;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
using testing::Test;
struct ReaderFixture {
string contents;
size_t pointer_size;
};
class Reader: public ReaderFixture, public Test { };
class ReaderDeathTest: public ReaderFixture, public Test { };
TEST_F(Reader, SimpleConstructor) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
CFISection section(kBigEndian, 4);
section
.D8(0xc0)
.D16(0xcf0d)
.D32(0x96fdd219)
.D64(0xbbf55fef0825f117ULL)
.ULEB128(0xa0927048ba8121afULL)
.LEB128(-0x4f337badf4483f83LL)
.D32(0xfec319c9);
ASSERT_TRUE(section.GetContents(&contents));
const char *data = contents.data();
EXPECT_EQ(0xc0U, reader.ReadOneByte(data));
EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1));
EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3));
EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7));
size_t leb128_size;
EXPECT_EQ(0xa0927048ba8121afULL,
reader.ReadUnsignedLEB128(data + 15, &leb128_size));
EXPECT_EQ(10U, leb128_size);
EXPECT_EQ(-0x4f337badf4483f83LL,
reader.ReadSignedLEB128(data + 25, &leb128_size));
EXPECT_EQ(10U, leb128_size);
EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35));
}
TEST_F(Reader, ValidEncodings) {
ByteReader reader(ENDIANNESS_LITTLE);
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0)));
}
TEST_F(ReaderDeathTest, DW_EH_PE_omit) {
static const char data[1] = { 42 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit,
&pointer_size),
"encoding != DW_EH_PE_omit");
}
TEST_F(Reader, DW_EH_PE_absptr4) {
static const char data[] = { 0x27, 0x57, 0xea, 0x40 };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x40ea5727U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_absptr8) {
static const char data[] = {
0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x010598c240ea5727ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_uleb128) {
static const char data[] = { 0x81, 0x84, 0x4c };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x130201U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128,
&pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata2) {
static const char data[] = { 0xf4, 0x8d };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_EQ(0xf48dU,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2,
&pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata4) {
static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(8);
EXPECT_EQ(0xa5628f8b,
reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata8Addr8) {
static const char data[] = {
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x8fed199f69047304ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata8Addr4) {
static const char data[] = {
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x69047304ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sleb128) {
static const char data[] = { 0x42, 0xff, 0xfb, 0x73 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_EQ(-0x030201U & 0xffffffff,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128,
&pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata2) {
static const char data[] = { 0xb9, 0xbf };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0xffffffffffffbfb9ULL,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2,
&pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata4) {
static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0xffffffffadc2b8f2ULL,
reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata8) {
static const char data[] = {
0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x87269b0ce0795766ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_pcrel) {
static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel
| dwarf2reader::DW_EH_PE_absptr);
reader.SetCFIDataBase(0x89951377, data);
EXPECT_EQ(0x89951377 + 3 + 0x14c8c402,
reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_textrel) {
static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
reader.SetTextBase(0xb91beaf0);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel
| dwarf2reader::DW_EH_PE_sdata2);
EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff,
reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_datarel) {
static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(8);
reader.SetDataBase(0xbef308bd25ce74f0ULL);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel
| dwarf2reader::DW_EH_PE_sleb128);
EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL,
reader.ReadEncodedPointer(data + 2, encoding, &pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_funcrel) {
static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
reader.SetFunctionBase(0x823c3520);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel
| dwarf2reader::DW_EH_PE_udata2);
EXPECT_EQ(0x823c3520 + 0xd148,
reader.ReadEncodedPointer(data + 5, encoding, &pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST(UsableBase, CFI) {
static const char data[1] = { 0x42 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetCFIDataBase(0xb31cbd20, data);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Text) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetTextBase(0xa899ccb9);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Data) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetDataBase(0xf7b10bcd);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Function) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetFunctionBase(0xc2c0ed81);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, ClearFunction) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetFunctionBase(0xc2c0ed81);
reader.ClearFunctionBase();
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
struct AlignedFixture {
AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); }
static const char data[10];
ByteReader reader;
size_t pointer_size;
};
const char AlignedFixture::data[10] = {
0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b
};
class Aligned: public AlignedFixture, public Test { };
TEST_F(Aligned, DW_EH_PE_aligned0) {
reader.SetCFIDataBase(0xb440305c, data);
EXPECT_EQ(0xfe6e93d8U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned1) {
reader.SetCFIDataBase(0xb440305d, data);
EXPECT_EQ(0xd834d51cU,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(7U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned2) {
reader.SetCFIDataBase(0xb440305e, data);
EXPECT_EQ(0x93d834d5U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(6U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned3) {
reader.SetCFIDataBase(0xb440305f, data);
EXPECT_EQ(0x6e93d834U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(5U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned11) {
reader.SetCFIDataBase(0xb4403061, data);
EXPECT_EQ(0xd834d51cU,
reader.ReadEncodedPointer(data + 1,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(6U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned30) {
reader.SetCFIDataBase(0xb4403063, data);
EXPECT_EQ(0x6e93d834U,
reader.ReadEncodedPointer(data + 1,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned23) {
reader.SetCFIDataBase(0xb4403062, data);
EXPECT_EQ(0x1cd3ac2bU,
reader.ReadEncodedPointer(data + 3,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(7U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned03) {
reader.SetCFIDataBase(0xb4403064, data);
EXPECT_EQ(0x34d51cd3U,
reader.ReadEncodedPointer(data + 3,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(5U, pointer_size);
}

View File

@ -0,0 +1,198 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_assembler.cc: Implementation of google_breakpad::CFISection class.
// See cfi_assembler.h for details.
#include "common/dwarf/cfi_assembler.h"
#include <assert.h>
#include <stdlib.h>
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
uint8_t version,
const string &augmentation,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = false;
if (dwarf64) {
D32(kDwarf64InitialLengthMarker);
D64(entry_length_->length);
entry_length_->start = Here();
D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier);
} else {
D32(entry_length_->length);
entry_length_->start = Here();
D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier);
}
D8(version);
AppendCString(augmentation);
ULEB128(code_alignment_factor);
LEB128(data_alignment_factor);
if (version == 1)
D8(return_address_register);
else
ULEB128(return_address_register);
return *this;
}
CFISection &CFISection::FDEHeader(Label cie_pointer,
uint64_t initial_location,
uint64_t address_range,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = true;
fde_start_address_ = initial_location;
if (dwarf64) {
D32(0xffffffff);
D64(entry_length_->length);
entry_length_->start = Here();
if (eh_frame_)
D64(Here() - cie_pointer);
else
D64(cie_pointer);
} else {
D32(entry_length_->length);
entry_length_->start = Here();
if (eh_frame_)
D32(Here() - cie_pointer);
else
D32(cie_pointer);
}
EncodedPointer(initial_location);
// The FDE length in an .eh_frame section uses the same encoding as the
// initial location, but ignores the base address (selected by the upper
// nybble of the encoding), as it's a length, not an address that can be
// made relative.
EncodedPointer(address_range,
DwarfPointerEncoding(pointer_encoding_ & 0x0f));
return *this;
}
CFISection &CFISection::FinishEntry() {
assert(entry_length_);
Align(address_size_, dwarf2reader::DW_CFA_nop);
entry_length_->length = Here() - entry_length_->start;
delete entry_length_;
entry_length_ = NULL;
in_fde_ = false;
return *this;
}
CFISection &CFISection::EncodedPointer(uint64_t address,
DwarfPointerEncoding encoding,
const EncodedPointerBases &bases) {
// Omitted data is extremely easy to emit.
if (encoding == dwarf2reader::DW_EH_PE_omit)
return *this;
// If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume
// that ADDRESS is the address at which the pointer is stored --- in
// other words, that bit has no effect on how we write the pointer.
encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect);
// Find the base address to which this pointer is relative. The upper
// nybble of the encoding specifies this.
uint64_t base;
switch (encoding & 0xf0) {
case dwarf2reader::DW_EH_PE_absptr: base = 0; break;
case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break;
case dwarf2reader::DW_EH_PE_textrel: base = bases.text; break;
case dwarf2reader::DW_EH_PE_datarel: base = bases.data; break;
case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break;
case dwarf2reader::DW_EH_PE_aligned: base = 0; break;
default: abort();
};
// Make ADDRESS relative. Yes, this is appropriate even for "absptr"
// values; see gcc/unwind-pe.h.
address -= base;
// Align the pointer, if required.
if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned)
Align(AddressSize());
// Append ADDRESS to this section in the appropriate form. For the
// fixed-width forms, we don't need to differentiate between signed and
// unsigned encodings, because ADDRESS has already been extended to 64
// bits before it was passed to us.
switch (encoding & 0x0f) {
case dwarf2reader::DW_EH_PE_absptr:
Address(address);
break;
case dwarf2reader::DW_EH_PE_uleb128:
ULEB128(address);
break;
case dwarf2reader::DW_EH_PE_sleb128:
LEB128(address);
break;
case dwarf2reader::DW_EH_PE_udata2:
case dwarf2reader::DW_EH_PE_sdata2:
D16(address);
break;
case dwarf2reader::DW_EH_PE_udata4:
case dwarf2reader::DW_EH_PE_sdata4:
D32(address);
break;
case dwarf2reader::DW_EH_PE_udata8:
case dwarf2reader::DW_EH_PE_sdata8:
D64(address);
break;
default:
abort();
}
return *this;
};
const uint32_t CFISection::kDwarf64InitialLengthMarker;
const uint32_t CFISection::kDwarf32CIEIdentifier;
const uint64_t CFISection::kDwarf64CIEIdentifier;
const uint32_t CFISection::kEHFrame32CIEIdentifier;
const uint64_t CFISection::kEHFrame64CIEIdentifier;
} // namespace google_breakpad

View File

@ -0,0 +1,269 @@
// -*- mode: C++ -*-
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_assembler.h: Define CFISection, a class for creating properly
// (and improperly) formatted DWARF CFI data for unit tests.
#ifndef PROCESSOR_CFI_ASSEMBLER_H_
#define PROCESSOR_CFI_ASSEMBLER_H_
#include <string>
#include "common/dwarf/dwarf2enums.h"
#include "common/test_assembler.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
using google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
class CFISection: public Section {
public:
// CFI augmentation strings beginning with 'z', defined by the
// Linux/IA-64 C++ ABI, can specify interesting encodings for
// addresses appearing in FDE headers and call frame instructions (and
// for additional fields whose presence the augmentation string
// specifies). In particular, pointers can be specified to be relative
// to various base address: the start of the .text section, the
// location holding the address itself, and so on. These allow the
// frame data to be position-independent even when they live in
// write-protected pages. These variants are specified at the
// following two URLs:
//
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
//
// CFISection leaves the production of well-formed 'z'-augmented CIEs and
// FDEs to the user, but does provide EncodedPointer, to emit
// properly-encoded addresses for a given pointer encoding.
// EncodedPointer uses an instance of this structure to find the base
// addresses it should use; you can establish a default for all encoded
// pointers appended to this section with SetEncodedPointerBases.
struct EncodedPointerBases {
EncodedPointerBases() : cfi(), text(), data() { }
// The starting address of this CFI section in memory, for
// DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data
// that has is loaded into the program's address space.
uint64_t cfi;
// The starting address of this file's .text section, for DW_EH_PE_textrel.
uint64_t text;
// The starting address of this file's .got or .eh_frame_hdr section,
// for DW_EH_PE_datarel.
uint64_t data;
};
// Create a CFISection whose endianness is ENDIANNESS, and where
// machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is
// true, use the .eh_frame format, as described by the Linux
// Standards Base Core Specification, instead of the DWARF CFI
// format.
CFISection(Endianness endianness, size_t address_size,
bool eh_frame = false)
: Section(endianness), address_size_(address_size), eh_frame_(eh_frame),
pointer_encoding_(dwarf2reader::DW_EH_PE_absptr),
encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) {
// The 'start', 'Here', and 'Mark' members of a CFISection all refer
// to section offsets.
start() = 0;
}
// Return this CFISection's address size.
size_t AddressSize() const { return address_size_; }
// Return true if this CFISection uses the .eh_frame format, or
// false if it contains ordinary DWARF CFI data.
bool ContainsEHFrame() const { return eh_frame_; }
// Use ENCODING for pointers in calls to FDEHeader and EncodedPointer.
void SetPointerEncoding(DwarfPointerEncoding encoding) {
pointer_encoding_ = encoding;
}
// Use the addresses in BASES as the base addresses for encoded
// pointers in subsequent calls to FDEHeader or EncodedPointer.
// This function makes a copy of BASES.
void SetEncodedPointerBases(const EncodedPointerBases &bases) {
encoded_pointer_bases_ = bases;
}
// Append a Common Information Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial
// length format for the CIE's initial length. Return a reference to
// this section. You should call FinishEntry after writing the last
// instruction for the CIE.
//
// Before calling this function, you will typically want to use Mark
// or Here to make a label to pass to FDEHeader that refers to this
// CIE's position in the section.
CFISection &CIEHeader(uint64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
uint8_t version = 3,
const string &augmentation = "",
bool dwarf64 = false);
// Append a Frame Description Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial
// length format for the CIE's initial length. Return a reference to
// this section. You should call FinishEntry after writing the last
// instruction for the CIE.
//
// This function doesn't support entries that are longer than
// 0xffffff00 bytes. (The "initial length" is always a 32-bit
// value.) Nor does it support .debug_frame sections longer than
// 0xffffff00 bytes.
CFISection &FDEHeader(Label cie_pointer,
uint64_t initial_location,
uint64_t address_range,
bool dwarf64 = false);
// Note the current position as the end of the last CIE or FDE we
// started, after padding with DW_CFA_nops for alignment. This
// defines the label representing the entry's length, cited in the
// entry's header. Return a reference to this section.
CFISection &FinishEntry();
// Append the contents of BLOCK as a DW_FORM_block value: an
// unsigned LEB128 length, followed by that many bytes of data.
CFISection &Block(const string &block) {
ULEB128(block.size());
Append(block);
return *this;
}
// Append ADDRESS to this section, in the appropriate size and
// endianness. Return a reference to this section.
CFISection &Address(uint64_t address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
CFISection &Address(Label address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
// Append ADDRESS to this section, using ENCODING and BASES. ENCODING
// defaults to this section's default encoding, established by
// SetPointerEncoding. BASES defaults to this section's bases, set by
// SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the
// encoding, assume that ADDRESS is where the true address is stored.
// Return a reference to this section.
//
// (C++ doesn't let me use default arguments here, because I want to
// refer to members of *this in the default argument expression.)
CFISection &EncodedPointer(uint64_t address) {
return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_);
}
CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) {
return EncodedPointer(address, encoding, encoded_pointer_bases_);
}
CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding,
const EncodedPointerBases &bases);
// Restate some member functions, to keep chaining working nicely.
CFISection &Mark(Label *label) { Section::Mark(label); return *this; }
CFISection &D8(uint8_t v) { Section::D8(v); return *this; }
CFISection &D16(uint16_t v) { Section::D16(v); return *this; }
CFISection &D16(Label v) { Section::D16(v); return *this; }
CFISection &D32(uint32_t v) { Section::D32(v); return *this; }
CFISection &D32(const Label &v) { Section::D32(v); return *this; }
CFISection &D64(uint64_t v) { Section::D64(v); return *this; }
CFISection &D64(const Label &v) { Section::D64(v); return *this; }
CFISection &LEB128(long long v) { Section::LEB128(v); return *this; }
CFISection &ULEB128(uint64_t v) { Section::ULEB128(v); return *this; }
private:
// A length value that we've appended to the section, but is not yet
// known. LENGTH is the appended value; START is a label referring
// to the start of the data whose length was cited.
struct PendingLength {
Label length;
Label start;
};
// Constants used in CFI/.eh_frame data:
// If the first four bytes of an "initial length" are this constant, then
// the data uses the 64-bit DWARF format, and the length itself is the
// subsequent eight bytes.
static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU;
// The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data.
static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0;
static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0;
static const uint32_t kEHFrame32CIEIdentifier = 0;
static const uint64_t kEHFrame64CIEIdentifier = 0;
// The size of a machine address for the data in this section.
size_t address_size_;
// If true, we are generating a Linux .eh_frame section, instead of
// a standard DWARF .debug_frame section.
bool eh_frame_;
// The encoding to use for FDE pointers.
DwarfPointerEncoding pointer_encoding_;
// The base addresses to use when emitting encoded pointers.
EncodedPointerBases encoded_pointer_bases_;
// The length value for the current entry.
//
// Oddly, this must be dynamically allocated. Labels never get new
// values; they only acquire constraints on the value they already
// have, or assert if you assign them something incompatible. So
// each header needs truly fresh Label objects to cite in their
// headers and track their positions. The alternative is explicit
// destructor invocation and a placement new. Ick.
PendingLength *entry_length_;
// True if we are currently emitting an FDE --- that is, we have
// called FDEHeader but have not yet called FinishEntry.
bool in_fde_;
// If in_fde_ is true, this is its starting address. We use this for
// emitting DW_EH_PE_funcrel pointers.
uint64_t fde_start_address_;
};
} // namespace google_breakpad
#endif // PROCESSOR_CFI_ASSEMBLER_H_

View File

@ -0,0 +1,198 @@
// Copyright (c) 2010 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
// See dwarf2diehandler.h for details.
#include <assert.h>
#include <string>
#include "common/dwarf/dwarf2diehandler.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
DIEDispatcher::~DIEDispatcher() {
while (!die_handlers_.empty()) {
HandlerStack &entry = die_handlers_.top();
if (entry.handler_ != root_handler_)
delete entry.handler_;
die_handlers_.pop();
}
}
bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version) {
return root_handler_->StartCompilationUnit(offset, address_size,
offset_size, cu_length,
dwarf_version);
}
bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag) {
// The stack entry for the parent of this DIE, if there is one.
HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top();
// Does this call indicate that we're done receiving the parent's
// attributes' values? If so, call its EndAttributes member function.
if (parent && parent->handler_ && !parent->reported_attributes_end_) {
parent->reported_attributes_end_ = true;
if (!parent->handler_->EndAttributes()) {
// Finish off this handler now. and edit *PARENT to indicate that
// we don't want to visit any of the children.
parent->handler_->Finish();
if (parent->handler_ != root_handler_)
delete parent->handler_;
parent->handler_ = NULL;
return false;
}
}
// Find a handler for this DIE.
DIEHandler *handler;
if (parent) {
if (parent->handler_)
// Ask the parent to find a handler.
handler = parent->handler_->FindChildHandler(offset, tag);
else
// No parent handler means we're not interested in any of our
// children.
handler = NULL;
} else {
// This is the root DIE. For a non-root DIE, the parent's handler
// decides whether to visit it, but the root DIE has no parent
// handler, so we have a special method on the root DIE handler
// itself to decide.
if (root_handler_->StartRootDIE(offset, tag))
handler = root_handler_;
else
handler = NULL;
}
// Push a handler stack entry for this new handler. As an
// optimization, we don't push NULL-handler entries on top of other
// NULL-handler entries; we just let the oldest such entry stand for
// the whole subtree.
if (handler || !parent || parent->handler_) {
HandlerStack entry;
entry.offset_ = offset;
entry.handler_ = handler;
entry.reported_attributes_end_ = false;
die_handlers_.push(entry);
}
return handler != NULL;
}
void DIEDispatcher::EndDIE(uint64 offset) {
assert(!die_handlers_.empty());
HandlerStack *entry = &die_handlers_.top();
if (entry->handler_) {
// This entry had better be the handler for this DIE.
assert(entry->offset_ == offset);
// If a DIE has no children, this EndDIE call indicates that we're
// done receiving its attributes' values.
if (!entry->reported_attributes_end_)
entry->handler_->EndAttributes(); // Ignore return value: no children.
entry->handler_->Finish();
if (entry->handler_ != root_handler_)
delete entry->handler_;
} else {
// If this DIE is within a tree we're ignoring, then don't pop the
// handler stack: that entry stands for the whole tree.
if (entry->offset_ != offset)
return;
}
die_handlers_.pop();
}
void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeUnsigned(attr, form, data);
}
void DIEDispatcher::ProcessAttributeSigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeSigned(attr, form, data);
}
void DIEDispatcher::ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeReference(attr, form, data);
}
void DIEDispatcher::ProcessAttributeBuffer(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeBuffer(attr, form, data, len);
}
void DIEDispatcher::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeString(attr, form, data);
}
void DIEDispatcher::ProcessAttributeSignature(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signature) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeSignature(attr, form, signature);
}
} // namespace dwarf2reader

View File

@ -0,0 +1,363 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2reader::CompilationUnit is a simple and direct parser for
// DWARF data, but its handler interface is not convenient to use. In
// particular:
//
// - CompilationUnit calls Dwarf2Handler's member functions to report
// every attribute's value, regardless of what sort of DIE it is.
// As a result, the ProcessAttributeX functions end up looking like
// this:
//
// switch (parent_die_tag) {
// case DW_TAG_x:
// switch (attribute_name) {
// case DW_AT_y:
// handle attribute y of DIE type x
// ...
// } break;
// ...
// }
//
// In C++ it's much nicer to use virtual function dispatch to find
// the right code for a given case than to switch on the DIE tag
// like this.
//
// - Processing different kinds of DIEs requires different sets of
// data: lexical block DIEs have start and end addresses, but struct
// type DIEs don't. It would be nice to be able to have separate
// handler classes for separate kinds of DIEs, each with the members
// appropriate to its role, instead of having one handler class that
// needs to hold data for every DIE type.
//
// - There should be a separate instance of the appropriate handler
// class for each DIE, instead of a single object with tables
// tracking all the dies in the compilation unit.
//
// - It's not convenient to take some action after all a DIE's
// attributes have been seen, but before visiting any of its
// children. The only indication you have that a DIE's attribute
// list is complete is that you get either a StartDIE or an EndDIE
// call.
//
// - It's not convenient to make use of the tree structure of the
// DIEs. Skipping all the children of a given die requires
// maintaining state and returning false from StartDIE until we get
// an EndDIE call with the appropriate offset.
//
// This interface tries to take care of all that. (You're shocked, I'm sure.)
//
// Using the classes here, you provide an initial handler for the root
// DIE of the compilation unit. Each handler receives its DIE's
// attributes, and provides fresh handler objects for children of
// interest, if any. The three classes are:
//
// - DIEHandler: the base class for your DIE-type-specific handler
// classes.
//
// - RootDIEHandler: derived from DIEHandler, the base class for your
// root DIE handler class.
//
// - DIEDispatcher: derived from Dwarf2Handler, an instance of this
// invokes your DIE-type-specific handler objects.
//
// In detail:
//
// - Define handler classes specialized for the DIE types you're
// interested in. These handler classes must inherit from
// DIEHandler. Thus:
//
// class My_DW_TAG_X_Handler: public DIEHandler { ... };
// class My_DW_TAG_Y_Handler: public DIEHandler { ... };
//
// DIEHandler subclasses needn't correspond exactly to single DIE
// types, as shown here; the point is that you can have several
// different classes appropriate to different kinds of DIEs.
//
// - In particular, define a handler class for the compilation
// unit's root DIE, that inherits from RootDIEHandler:
//
// class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... };
//
// RootDIEHandler inherits from DIEHandler, adding a few additional
// member functions for examining the compilation unit as a whole,
// and other quirks of rootness.
//
// - Then, create a DIEDispatcher instance, passing it an instance of
// your root DIE handler class, and use that DIEDispatcher as the
// dwarf2reader::CompilationUnit's handler:
//
// My_DW_TAG_compile_unit_Handler root_die_handler(...);
// DIEDispatcher die_dispatcher(&root_die_handler);
// CompilationUnit reader(sections, offset, bytereader, &die_dispatcher);
//
// Here, 'die_dispatcher' acts as a shim between 'reader' and the
// various DIE-specific handlers you have defined.
//
// - When you call reader.Start(), die_dispatcher behaves as follows,
// starting with your root die handler and the compilation unit's
// root DIE:
//
// - It calls the handler's ProcessAttributeX member functions for
// each of the DIE's attributes.
//
// - It calls the handler's EndAttributes member function. This
// should return true if any of the DIE's children should be
// visited, in which case:
//
// - For each of the DIE's children, die_dispatcher calls the
// DIE's handler's FindChildHandler member function. If that
// returns a pointer to a DIEHandler instance, then
// die_dispatcher uses that handler to process the child, using
// this procedure recursively. Alternatively, if
// FindChildHandler returns NULL, die_dispatcher ignores that
// child and its descendants.
//
// - When die_dispatcher has finished processing all the DIE's
// children, it invokes the handler's Finish() member function,
// and destroys the handler. (As a special case, it doesn't
// destroy the root DIE handler.)
//
// This allows the code for handling a particular kind of DIE to be
// gathered together in a single class, makes it easy to skip all the
// children or individual children of a particular DIE, and provides
// appropriate parental context for each die.
#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
#include <stack>
#include <string>
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
// A base class for handlers for specific DIE types. The series of
// calls made on a DIE handler is as follows:
//
// - for each attribute of the DIE:
// - ProcessAttributeX()
// - EndAttributes()
// - if that returned true, then for each child:
// - FindChildHandler()
// - if that returns a non-NULL pointer to a new handler:
// - recurse, with the new handler and the child die
// - Finish()
// - destruction
class DIEHandler {
public:
DIEHandler() { }
virtual ~DIEHandler() { }
// When we visit a DIE, we first use these member functions to
// report the DIE's attributes and their values. These have the
// same restrictions as the corresponding member functions of
// dwarf2reader::Dwarf2Handler.
//
// Since DWARF does not specify in what order attributes must
// appear, avoid making decisions in these functions that would be
// affected by the presence of other attributes. The EndAttributes
// function is a more appropriate place for such work, as all the
// DIE's attributes have been seen at that point.
//
// The default definitions ignore the values they are passed.
virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) { }
virtual void ProcessAttributeSigned(enum DwarfAttribute attr,
enum DwarfForm form,
int64 data) { }
virtual void ProcessAttributeReference(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) { }
virtual void ProcessAttributeBuffer(enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len) { }
virtual void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) { }
virtual void ProcessAttributeSignature(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signture) { }
// Once we have reported all the DIE's attributes' values, we call
// this member function. If it returns false, we skip all the DIE's
// children. If it returns true, we call FindChildHandler on each
// child. If that returns a handler object, we use that to visit
// the child; otherwise, we skip the child.
//
// This is a good place to make decisions that depend on more than
// one attribute. DWARF does not specify in what order attributes
// must appear, so only when the EndAttributes function is called
// does the handler have a complete picture of the DIE's attributes.
//
// The default definition elects to ignore the DIE's children.
// You'll need to override this if you override FindChildHandler,
// but at least the default behavior isn't to pass the children to
// FindChildHandler, which then ignores them all.
virtual bool EndAttributes() { return false; }
// If EndAttributes returns true to indicate that some of the DIE's
// children might be of interest, then we apply this function to
// each of the DIE's children. If it returns a handler object, then
// we use that to visit the child DIE. If it returns NULL, we skip
// that child DIE (and all its descendants).
//
// OFFSET is the offset of the child; TAG indicates what kind of DIE
// it is.
//
// The default definition skips all children.
virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag) {
return NULL;
}
// When we are done processing a DIE, we call this member function.
// This happens after the EndAttributes call, all FindChildHandler
// calls (if any), and all operations on the children themselves (if
// any). We call Finish on every handler --- even if EndAttributes
// returns false.
virtual void Finish() { };
};
// A subclass of DIEHandler, with additional kludges for handling the
// compilation unit's root die.
class RootDIEHandler: public DIEHandler {
public:
RootDIEHandler() { }
virtual ~RootDIEHandler() { }
// We pass the values reported via Dwarf2Handler::StartCompilationUnit
// to this member function, and skip the entire compilation unit if it
// returns false. So the root DIE handler is actually also
// responsible for handling the compilation unit metadata.
// The default definition always visits the compilation unit.
virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version) { return true; }
// For the root DIE handler only, we pass the offset, tag and
// attributes of the compilation unit's root DIE. This is the only
// way the root DIE handler can find the root DIE's tag. If this
// function returns true, we will visit the root DIE using the usual
// DIEHandler methods; otherwise, we skip the entire compilation
// unit.
//
// The default definition elects to visit the root DIE.
virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag) { return true; }
};
class DIEDispatcher: public Dwarf2Handler {
public:
// Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for
// the compilation unit's root die, as described for the DIEHandler
// class.
DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { }
// Destroying a DIEDispatcher destroys all active handler objects
// except the root handler.
~DIEDispatcher();
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
bool StartDIE(uint64 offset, enum DwarfTag tag);
void ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeSigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data);
void ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeBuffer(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len);
void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data);
void ProcessAttributeSignature(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signature);
void EndDIE(uint64 offset);
private:
// The type of a handler stack entry. This includes some fields
// which don't really need to be on the stack --- they could just be
// single data members of DIEDispatcher --- but putting them here
// makes it easier to see that the code is correct.
struct HandlerStack {
// The offset of the DIE for this handler stack entry.
uint64 offset_;
// The handler object interested in this DIE's attributes and
// children. If NULL, we're not interested in either.
DIEHandler *handler_;
// Have we reported the end of this DIE's attributes to the handler?
bool reported_attributes_end_;
};
// Stack of DIE attribute handlers. At StartDIE(D), the top of the
// stack is the handler of D's parent, whom we may ask for a handler
// for D itself. At EndDIE(D), the top of the stack is D's handler.
// Special cases:
//
// - Before we've seen the compilation unit's root DIE, the stack is
// empty; we'll call root_handler_'s special member functions, and
// perhaps push root_handler_ on the stack to look at the root's
// immediate children.
//
// - When we decide to ignore a subtree, we only push an entry on
// the stack for the root of the tree being ignored, rather than
// pushing lots of stack entries with handler_ set to NULL.
std::stack<HandlerStack> die_handlers_;
// The root handler. We don't push it on die_handlers_ until we
// actually get the StartDIE call for the root.
RootDIEHandler *root_handler_;
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_DWARF2DIEHANDLER_H__

View File

@ -0,0 +1,524 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
#include <string>
#include <utility>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/using_std_string.h"
using std::make_pair;
using ::testing::_;
using ::testing::ContainerEq;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::Sequence;
using ::testing::StrEq;
using dwarf2reader::DIEDispatcher;
using dwarf2reader::DIEHandler;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfTag;
using dwarf2reader::RootDIEHandler;
class MockDIEHandler: public DIEHandler {
public:
MOCK_METHOD3(ProcessAttributeUnsigned,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD3(ProcessAttributeSigned,
void(DwarfAttribute, DwarfForm, int64));
MOCK_METHOD3(ProcessAttributeReference,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD4(ProcessAttributeBuffer,
void(DwarfAttribute, DwarfForm, const char *, uint64));
MOCK_METHOD3(ProcessAttributeString,
void(DwarfAttribute, DwarfForm, const string &));
MOCK_METHOD3(ProcessAttributeSignature,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
MOCK_METHOD0(Finish, void());
};
class MockRootDIEHandler: public RootDIEHandler {
public:
MOCK_METHOD3(ProcessAttributeUnsigned,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD3(ProcessAttributeSigned,
void(DwarfAttribute, DwarfForm, int64));
MOCK_METHOD3(ProcessAttributeReference,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD4(ProcessAttributeBuffer,
void(DwarfAttribute, DwarfForm, const char *, uint64));
MOCK_METHOD3(ProcessAttributeString,
void(DwarfAttribute, DwarfForm, const string &));
MOCK_METHOD3(ProcessAttributeSignature,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
MOCK_METHOD0(Finish, void());
MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8));
MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag));
};
// If the handler elects to skip the compilation unit, the dispatcher
// should tell the reader so.
TEST(Dwarf2DIEHandler, SkipCompilationUnit) {
Sequence s;
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66))
.InSequence(s)
.WillOnce(Return(false));
EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66));
}
// If the handler elects to skip the root DIE, the dispatcher should
// tell the reader so.
TEST(Dwarf2DIEHandler, SkipRootDIE) {
Sequence s;
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02,
0xb00febffa76e2b2bLL, 0x5c))
.InSequence(s)
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
.InSequence(s)
.WillOnce(Return(false));
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL,
0xf4, 0x02,
0xb00febffa76e2b2bLL, 0x5c));
EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
(DwarfTag) 0xb4f98da6));
die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}
// If the handler elects to skip the root DIE's children, the
// dispatcher should tell the reader so --- and avoid deleting the
// root handler.
TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
{
InSequence s;
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0,
0x09f8bf0767f91675LL, 0xdb))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
.WillOnce(Return(true));
// Please don't tell me about my children.
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(false));
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL,
0x26, 0xa0,
0x09f8bf0767f91675LL, 0xdb));
EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
(DwarfTag) 0xb4f98da6));
EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL,
(DwarfTag) 0xc3a17bba));
die_dispatcher.EndDIE(0x435150ceedccda18LL);
die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}
// The dispatcher should pass attribute values through to the die
// handler accurately.
TEST(Dwarf2DIEHandler, PassAttributeValues) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca,
0xcf, 0xa8, 0x84, 0xa7, 0x18 };
string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d";
// Set expectations.
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc,
0x2ecb4dc778a80f21LL, 0x66))
.WillOnce(Return(true));
// We'll like the root DIE.
EXPECT_CALL(mock_root_handler,
StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c))
.WillOnce(Return(true));
// Expect some attribute values.
EXPECT_CALL(mock_root_handler,
ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed,
(DwarfForm) 0x424f1468,
0xa592571997facda1ULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeSigned((DwarfAttribute) 0x43694dc9,
(DwarfForm) 0xf6f78901L,
0x92602a4e3bf1f446LL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeReference((DwarfAttribute) 0x4033e8cL,
(DwarfForm) 0xf66fbe0bL,
0x50fddef44734fdecULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af,
(DwarfForm) 0xe99a539a,
buffer, sizeof(buffer)))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeString((DwarfAttribute) 0x310ed065,
(DwarfForm) 0x15762fec,
StrEq(str)))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeSignature((DwarfAttribute) 0x58790d72,
(DwarfForm) 0x4159f138,
0x94682463613e6a5fULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _))
.Times(0);
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
// Drive the dispatcher.
// Report the CU header.
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66));
// Report the root DIE.
EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL,
(DwarfTag) 0x9829445c));
// Report some attribute values.
die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x1cc0bfed,
(DwarfForm) 0x424f1468,
0xa592571997facda1ULL);
die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x43694dc9,
(DwarfForm) 0xf6f78901,
0x92602a4e3bf1f446LL);
die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x4033e8c,
(DwarfForm) 0xf66fbe0b,
0x50fddef44734fdecULL);
die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x25d7e0af,
(DwarfForm) 0xe99a539a,
buffer, sizeof(buffer));
die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x310ed065,
(DwarfForm) 0x15762fec,
str);
die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x58790d72,
(DwarfForm) 0x4159f138,
0x94682463613e6a5fULL);
// Finish the root DIE (and thus the CU).
die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
}
TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
MockRootDIEHandler mock_root_handler;
MockDIEHandler *mock_child1_handler = new(MockDIEHandler);
MockDIEHandler *mock_child3_handler = new(MockDIEHandler);
DIEDispatcher die_dispatcher(&mock_root_handler);
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
0x47dd3c764275a216LL, 0xa5))
.WillOnce(Return(true));
// Root DIE.
{
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
ProcessAttributeSigned((DwarfAttribute) 0xf779a642,
(DwarfForm) 0x2cb63027,
0x18e744661769d08fLL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
// First child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x149f644f8116fe8cLL,
(DwarfTag) 0xac2cbd8c))
.WillOnce(Return(mock_child1_handler));
{
EXPECT_CALL(*mock_child1_handler,
ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65,
(DwarfForm) 0xe4f64c41,
0x1b04e5444a55fe67LL))
.WillOnce(Return());
EXPECT_CALL(*mock_child1_handler, EndAttributes())
.WillOnce(Return(false));
// Skip first grandchild DIE and first great-grandchild DIE.
EXPECT_CALL(*mock_child1_handler, Finish())
.WillOnce(Return());
}
// Second child DIE. Root handler will decline to return a handler
// for this child.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x97412be24875de9dLL,
(DwarfTag) 0x505a068b))
.WillOnce(Return((DIEHandler *) NULL));
// Third child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x753c964c8ab538aeLL,
(DwarfTag) 0x8c22970e))
.WillOnce(Return(mock_child3_handler));
{
EXPECT_CALL(*mock_child3_handler,
ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL))
.WillOnce(Return());
EXPECT_CALL(*mock_child3_handler, EndAttributes())
.WillOnce(Return(true));
EXPECT_CALL(*mock_child3_handler, Finish())
.WillOnce(Return());
}
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
}
// Drive the dispatcher.
// Report the CU header.
EXPECT_TRUE(die_dispatcher
.StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
0x47dd3c764275a216LL, 0xa5));
// Report the root DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL,
(DwarfTag) 0xf5d60c59));
die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL,
(DwarfAttribute) 0xf779a642,
(DwarfForm) 0x2cb63027,
0x18e744661769d08fLL);
// First child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL,
(DwarfTag) 0xac2cbd8c));
die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL,
(DwarfAttribute) 0xa6fd6f65,
(DwarfForm) 0xe4f64c41,
0x1b04e5444a55fe67LL);
// First grandchild DIE. Will be skipped.
{
EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL,
(DwarfTag) 0x22f05a15));
// First great-grandchild DIE. Will be skipped without being
// mentioned to any handler.
{
EXPECT_FALSE(die_dispatcher
.StartDIE(0xb3076285d25cac25LL,
(DwarfTag) 0xcff4061b));
die_dispatcher.EndDIE(0xb3076285d25cac25LL);
}
die_dispatcher.EndDIE(0xd68de1ee0bd29419LL);
}
die_dispatcher.EndDIE(0x149f644f8116fe8cLL);
}
// Second child DIE. Root handler will decline to find a handler for it.
{
EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL,
(DwarfTag) 0x505a068b));
die_dispatcher.EndDIE(0x97412be24875de9dLL);
}
// Third child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL,
(DwarfTag) 0x8c22970e));
die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL,
(DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL);
die_dispatcher.EndDIE(0x753c964c8ab538aeLL);
}
// Finish the root DIE (and thus the CU).
die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL);
}
}
// The DIEDispatcher destructor is supposed to delete all handlers on
// the stack, except for the root.
TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
MockRootDIEHandler mock_root_handler;
MockDIEHandler *mock_child_handler = new(MockDIEHandler);
MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler);
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
0x76d392ff393ddda2LL, 0xbf))
.WillOnce(Return(true));
// Root DIE.
{
EXPECT_CALL(mock_root_handler,
StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
// Child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x058f09240c5fc8c9LL,
(DwarfTag) 0x898bf0d0))
.WillOnce(Return(mock_child_handler));
{
EXPECT_CALL(*mock_child_handler, EndAttributes())
.WillOnce(Return(true));
// Grandchild DIE.
EXPECT_CALL(*mock_child_handler,
FindChildHandler(0x32dc00c9945dc0c8LL,
(DwarfTag) 0x2802d007))
.WillOnce(Return(mock_grandchild_handler));
{
EXPECT_CALL(*mock_grandchild_handler,
ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL))
.WillOnce(Return());
// At this point, we abandon the traversal, so none of the
// usual stuff should get called.
EXPECT_CALL(*mock_grandchild_handler, EndAttributes())
.Times(0);
EXPECT_CALL(*mock_grandchild_handler, Finish())
.Times(0);
}
EXPECT_CALL(*mock_child_handler, Finish())
.Times(0);
}
EXPECT_CALL(mock_root_handler, Finish())
.Times(0);
}
}
// The dispatcher.
DIEDispatcher die_dispatcher(&mock_root_handler);
// Report the CU header.
EXPECT_TRUE(die_dispatcher
.StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
0x76d392ff393ddda2LL, 0xbf));
// Report the root DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL,
(DwarfTag) 0x98980361));
// Child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL,
(DwarfTag) 0x898bf0d0));
// Grandchild DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL,
(DwarfTag) 0x2802d007));
die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL,
(DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL);
// Stop the traversal abruptly, so that there will still be
// handlers on the stack when the dispatcher is destructed.
// No EndDIE call...
}
// No EndDIE call...
}
// No EndDIE call...
}
}

View File

@ -0,0 +1,650 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 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_DWARF_DWARF2ENUMS_H__
#define COMMON_DWARF_DWARF2ENUMS_H__
namespace dwarf2reader {
// These enums do not follow the google3 style only because they are
// known universally (specs, other implementations) by the names in
// exactly this capitalization.
// Tag names and codes.
enum DwarfTag {
DW_TAG_padding = 0x00,
DW_TAG_array_type = 0x01,
DW_TAG_class_type = 0x02,
DW_TAG_entry_point = 0x03,
DW_TAG_enumeration_type = 0x04,
DW_TAG_formal_parameter = 0x05,
DW_TAG_imported_declaration = 0x08,
DW_TAG_label = 0x0a,
DW_TAG_lexical_block = 0x0b,
DW_TAG_member = 0x0d,
DW_TAG_pointer_type = 0x0f,
DW_TAG_reference_type = 0x10,
DW_TAG_compile_unit = 0x11,
DW_TAG_string_type = 0x12,
DW_TAG_structure_type = 0x13,
DW_TAG_subroutine_type = 0x15,
DW_TAG_typedef = 0x16,
DW_TAG_union_type = 0x17,
DW_TAG_unspecified_parameters = 0x18,
DW_TAG_variant = 0x19,
DW_TAG_common_block = 0x1a,
DW_TAG_common_inclusion = 0x1b,
DW_TAG_inheritance = 0x1c,
DW_TAG_inlined_subroutine = 0x1d,
DW_TAG_module = 0x1e,
DW_TAG_ptr_to_member_type = 0x1f,
DW_TAG_set_type = 0x20,
DW_TAG_subrange_type = 0x21,
DW_TAG_with_stmt = 0x22,
DW_TAG_access_declaration = 0x23,
DW_TAG_base_type = 0x24,
DW_TAG_catch_block = 0x25,
DW_TAG_const_type = 0x26,
DW_TAG_constant = 0x27,
DW_TAG_enumerator = 0x28,
DW_TAG_file_type = 0x29,
DW_TAG_friend = 0x2a,
DW_TAG_namelist = 0x2b,
DW_TAG_namelist_item = 0x2c,
DW_TAG_packed_type = 0x2d,
DW_TAG_subprogram = 0x2e,
DW_TAG_template_type_param = 0x2f,
DW_TAG_template_value_param = 0x30,
DW_TAG_thrown_type = 0x31,
DW_TAG_try_block = 0x32,
DW_TAG_variant_part = 0x33,
DW_TAG_variable = 0x34,
DW_TAG_volatile_type = 0x35,
// DWARF 3.
DW_TAG_dwarf_procedure = 0x36,
DW_TAG_restrict_type = 0x37,
DW_TAG_interface_type = 0x38,
DW_TAG_namespace = 0x39,
DW_TAG_imported_module = 0x3a,
DW_TAG_unspecified_type = 0x3b,
DW_TAG_partial_unit = 0x3c,
DW_TAG_imported_unit = 0x3d,
// SGI/MIPS Extensions.
DW_TAG_MIPS_loop = 0x4081,
// HP extensions. See:
// ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz
DW_TAG_HP_array_descriptor = 0x4090,
// GNU extensions.
DW_TAG_format_label = 0x4101, // For FORTRAN 77 and Fortran 90.
DW_TAG_function_template = 0x4102, // For C++.
DW_TAG_class_template = 0x4103, // For C++.
DW_TAG_GNU_BINCL = 0x4104,
DW_TAG_GNU_EINCL = 0x4105,
// Extensions for UPC. See: http://upc.gwu.edu/~upc.
DW_TAG_upc_shared_type = 0x8765,
DW_TAG_upc_strict_type = 0x8766,
DW_TAG_upc_relaxed_type = 0x8767,
// PGI (STMicroelectronics) extensions. No documentation available.
DW_TAG_PGI_kanji_type = 0xA000,
DW_TAG_PGI_interface_block = 0xA020
};
enum DwarfHasChild {
DW_children_no = 0,
DW_children_yes = 1
};
// Form names and codes.
enum DwarfForm {
DW_FORM_addr = 0x01,
DW_FORM_block2 = 0x03,
DW_FORM_block4 = 0x04,
DW_FORM_data2 = 0x05,
DW_FORM_data4 = 0x06,
DW_FORM_data8 = 0x07,
DW_FORM_string = 0x08,
DW_FORM_block = 0x09,
DW_FORM_block1 = 0x0a,
DW_FORM_data1 = 0x0b,
DW_FORM_flag = 0x0c,
DW_FORM_sdata = 0x0d,
DW_FORM_strp = 0x0e,
DW_FORM_udata = 0x0f,
DW_FORM_ref_addr = 0x10,
DW_FORM_ref1 = 0x11,
DW_FORM_ref2 = 0x12,
DW_FORM_ref4 = 0x13,
DW_FORM_ref8 = 0x14,
DW_FORM_ref_udata = 0x15,
DW_FORM_indirect = 0x16,
// Added in DWARF 4:
DW_FORM_sec_offset = 0x17,
DW_FORM_exprloc = 0x18,
DW_FORM_flag_present = 0x19,
DW_FORM_ref_sig8 = 0x20
};
// Attribute names and codes
enum DwarfAttribute {
DW_AT_sibling = 0x01,
DW_AT_location = 0x02,
DW_AT_name = 0x03,
DW_AT_ordering = 0x09,
DW_AT_subscr_data = 0x0a,
DW_AT_byte_size = 0x0b,
DW_AT_bit_offset = 0x0c,
DW_AT_bit_size = 0x0d,
DW_AT_element_list = 0x0f,
DW_AT_stmt_list = 0x10,
DW_AT_low_pc = 0x11,
DW_AT_high_pc = 0x12,
DW_AT_language = 0x13,
DW_AT_member = 0x14,
DW_AT_discr = 0x15,
DW_AT_discr_value = 0x16,
DW_AT_visibility = 0x17,
DW_AT_import = 0x18,
DW_AT_string_length = 0x19,
DW_AT_common_reference = 0x1a,
DW_AT_comp_dir = 0x1b,
DW_AT_const_value = 0x1c,
DW_AT_containing_type = 0x1d,
DW_AT_default_value = 0x1e,
DW_AT_inline = 0x20,
DW_AT_is_optional = 0x21,
DW_AT_lower_bound = 0x22,
DW_AT_producer = 0x25,
DW_AT_prototyped = 0x27,
DW_AT_return_addr = 0x2a,
DW_AT_start_scope = 0x2c,
DW_AT_stride_size = 0x2e,
DW_AT_upper_bound = 0x2f,
DW_AT_abstract_origin = 0x31,
DW_AT_accessibility = 0x32,
DW_AT_address_class = 0x33,
DW_AT_artificial = 0x34,
DW_AT_base_types = 0x35,
DW_AT_calling_convention = 0x36,
DW_AT_count = 0x37,
DW_AT_data_member_location = 0x38,
DW_AT_decl_column = 0x39,
DW_AT_decl_file = 0x3a,
DW_AT_decl_line = 0x3b,
DW_AT_declaration = 0x3c,
DW_AT_discr_list = 0x3d,
DW_AT_encoding = 0x3e,
DW_AT_external = 0x3f,
DW_AT_frame_base = 0x40,
DW_AT_friend = 0x41,
DW_AT_identifier_case = 0x42,
DW_AT_macro_info = 0x43,
DW_AT_namelist_items = 0x44,
DW_AT_priority = 0x45,
DW_AT_segment = 0x46,
DW_AT_specification = 0x47,
DW_AT_static_link = 0x48,
DW_AT_type = 0x49,
DW_AT_use_location = 0x4a,
DW_AT_variable_parameter = 0x4b,
DW_AT_virtuality = 0x4c,
DW_AT_vtable_elem_location = 0x4d,
// DWARF 3 values.
DW_AT_allocated = 0x4e,
DW_AT_associated = 0x4f,
DW_AT_data_location = 0x50,
DW_AT_stride = 0x51,
DW_AT_entry_pc = 0x52,
DW_AT_use_UTF8 = 0x53,
DW_AT_extension = 0x54,
DW_AT_ranges = 0x55,
DW_AT_trampoline = 0x56,
DW_AT_call_column = 0x57,
DW_AT_call_file = 0x58,
DW_AT_call_line = 0x59,
// SGI/MIPS extensions.
DW_AT_MIPS_fde = 0x2001,
DW_AT_MIPS_loop_begin = 0x2002,
DW_AT_MIPS_tail_loop_begin = 0x2003,
DW_AT_MIPS_epilog_begin = 0x2004,
DW_AT_MIPS_loop_unroll_factor = 0x2005,
DW_AT_MIPS_software_pipeline_depth = 0x2006,
DW_AT_MIPS_linkage_name = 0x2007,
DW_AT_MIPS_stride = 0x2008,
DW_AT_MIPS_abstract_name = 0x2009,
DW_AT_MIPS_clone_origin = 0x200a,
DW_AT_MIPS_has_inlines = 0x200b,
// HP extensions.
DW_AT_HP_block_index = 0x2000,
DW_AT_HP_unmodifiable = 0x2001, // Same as DW_AT_MIPS_fde.
DW_AT_HP_actuals_stmt_list = 0x2010,
DW_AT_HP_proc_per_section = 0x2011,
DW_AT_HP_raw_data_ptr = 0x2012,
DW_AT_HP_pass_by_reference = 0x2013,
DW_AT_HP_opt_level = 0x2014,
DW_AT_HP_prof_version_id = 0x2015,
DW_AT_HP_opt_flags = 0x2016,
DW_AT_HP_cold_region_low_pc = 0x2017,
DW_AT_HP_cold_region_high_pc = 0x2018,
DW_AT_HP_all_variables_modifiable = 0x2019,
DW_AT_HP_linkage_name = 0x201a,
DW_AT_HP_prof_flags = 0x201b, // In comp unit of procs_info for -g.
// GNU extensions.
DW_AT_sf_names = 0x2101,
DW_AT_src_info = 0x2102,
DW_AT_mac_info = 0x2103,
DW_AT_src_coords = 0x2104,
DW_AT_body_begin = 0x2105,
DW_AT_body_end = 0x2106,
DW_AT_GNU_vector = 0x2107,
// VMS extensions.
DW_AT_VMS_rtnbeg_pd_address = 0x2201,
// UPC extension.
DW_AT_upc_threads_scaled = 0x3210,
// PGI (STMicroelectronics) extensions.
DW_AT_PGI_lbase = 0x3a00,
DW_AT_PGI_soffset = 0x3a01,
DW_AT_PGI_lstride = 0x3a02
};
// Line number opcodes.
enum DwarfLineNumberOps {
DW_LNS_extended_op = 0,
DW_LNS_copy = 1,
DW_LNS_advance_pc = 2,
DW_LNS_advance_line = 3,
DW_LNS_set_file = 4,
DW_LNS_set_column = 5,
DW_LNS_negate_stmt = 6,
DW_LNS_set_basic_block = 7,
DW_LNS_const_add_pc = 8,
DW_LNS_fixed_advance_pc = 9,
// DWARF 3.
DW_LNS_set_prologue_end = 10,
DW_LNS_set_epilogue_begin = 11,
DW_LNS_set_isa = 12
};
// Line number extended opcodes.
enum DwarfLineNumberExtendedOps {
DW_LNE_end_sequence = 1,
DW_LNE_set_address = 2,
DW_LNE_define_file = 3,
// HP extensions.
DW_LNE_HP_negate_is_UV_update = 0x11,
DW_LNE_HP_push_context = 0x12,
DW_LNE_HP_pop_context = 0x13,
DW_LNE_HP_set_file_line_column = 0x14,
DW_LNE_HP_set_routine_name = 0x15,
DW_LNE_HP_set_sequence = 0x16,
DW_LNE_HP_negate_post_semantics = 0x17,
DW_LNE_HP_negate_function_exit = 0x18,
DW_LNE_HP_negate_front_end_logical = 0x19,
DW_LNE_HP_define_proc = 0x20
};
// Type encoding names and codes
enum DwarfEncoding {
DW_ATE_address =0x1,
DW_ATE_boolean =0x2,
DW_ATE_complex_float =0x3,
DW_ATE_float =0x4,
DW_ATE_signed =0x5,
DW_ATE_signed_char =0x6,
DW_ATE_unsigned =0x7,
DW_ATE_unsigned_char =0x8,
// DWARF3/DWARF3f
DW_ATE_imaginary_float =0x9,
DW_ATE_packed_decimal =0xa,
DW_ATE_numeric_string =0xb,
DW_ATE_edited =0xc,
DW_ATE_signed_fixed =0xd,
DW_ATE_unsigned_fixed =0xe,
DW_ATE_decimal_float =0xf,
DW_ATE_lo_user =0x80,
DW_ATE_hi_user =0xff
};
// Location virtual machine opcodes
enum DwarfOpcode {
DW_OP_addr =0x03,
DW_OP_deref =0x06,
DW_OP_const1u =0x08,
DW_OP_const1s =0x09,
DW_OP_const2u =0x0a,
DW_OP_const2s =0x0b,
DW_OP_const4u =0x0c,
DW_OP_const4s =0x0d,
DW_OP_const8u =0x0e,
DW_OP_const8s =0x0f,
DW_OP_constu =0x10,
DW_OP_consts =0x11,
DW_OP_dup =0x12,
DW_OP_drop =0x13,
DW_OP_over =0x14,
DW_OP_pick =0x15,
DW_OP_swap =0x16,
DW_OP_rot =0x17,
DW_OP_xderef =0x18,
DW_OP_abs =0x19,
DW_OP_and =0x1a,
DW_OP_div =0x1b,
DW_OP_minus =0x1c,
DW_OP_mod =0x1d,
DW_OP_mul =0x1e,
DW_OP_neg =0x1f,
DW_OP_not =0x20,
DW_OP_or =0x21,
DW_OP_plus =0x22,
DW_OP_plus_uconst =0x23,
DW_OP_shl =0x24,
DW_OP_shr =0x25,
DW_OP_shra =0x26,
DW_OP_xor =0x27,
DW_OP_bra =0x28,
DW_OP_eq =0x29,
DW_OP_ge =0x2a,
DW_OP_gt =0x2b,
DW_OP_le =0x2c,
DW_OP_lt =0x2d,
DW_OP_ne =0x2e,
DW_OP_skip =0x2f,
DW_OP_lit0 =0x30,
DW_OP_lit1 =0x31,
DW_OP_lit2 =0x32,
DW_OP_lit3 =0x33,
DW_OP_lit4 =0x34,
DW_OP_lit5 =0x35,
DW_OP_lit6 =0x36,
DW_OP_lit7 =0x37,
DW_OP_lit8 =0x38,
DW_OP_lit9 =0x39,
DW_OP_lit10 =0x3a,
DW_OP_lit11 =0x3b,
DW_OP_lit12 =0x3c,
DW_OP_lit13 =0x3d,
DW_OP_lit14 =0x3e,
DW_OP_lit15 =0x3f,
DW_OP_lit16 =0x40,
DW_OP_lit17 =0x41,
DW_OP_lit18 =0x42,
DW_OP_lit19 =0x43,
DW_OP_lit20 =0x44,
DW_OP_lit21 =0x45,
DW_OP_lit22 =0x46,
DW_OP_lit23 =0x47,
DW_OP_lit24 =0x48,
DW_OP_lit25 =0x49,
DW_OP_lit26 =0x4a,
DW_OP_lit27 =0x4b,
DW_OP_lit28 =0x4c,
DW_OP_lit29 =0x4d,
DW_OP_lit30 =0x4e,
DW_OP_lit31 =0x4f,
DW_OP_reg0 =0x50,
DW_OP_reg1 =0x51,
DW_OP_reg2 =0x52,
DW_OP_reg3 =0x53,
DW_OP_reg4 =0x54,
DW_OP_reg5 =0x55,
DW_OP_reg6 =0x56,
DW_OP_reg7 =0x57,
DW_OP_reg8 =0x58,
DW_OP_reg9 =0x59,
DW_OP_reg10 =0x5a,
DW_OP_reg11 =0x5b,
DW_OP_reg12 =0x5c,
DW_OP_reg13 =0x5d,
DW_OP_reg14 =0x5e,
DW_OP_reg15 =0x5f,
DW_OP_reg16 =0x60,
DW_OP_reg17 =0x61,
DW_OP_reg18 =0x62,
DW_OP_reg19 =0x63,
DW_OP_reg20 =0x64,
DW_OP_reg21 =0x65,
DW_OP_reg22 =0x66,
DW_OP_reg23 =0x67,
DW_OP_reg24 =0x68,
DW_OP_reg25 =0x69,
DW_OP_reg26 =0x6a,
DW_OP_reg27 =0x6b,
DW_OP_reg28 =0x6c,
DW_OP_reg29 =0x6d,
DW_OP_reg30 =0x6e,
DW_OP_reg31 =0x6f,
DW_OP_breg0 =0x70,
DW_OP_breg1 =0x71,
DW_OP_breg2 =0x72,
DW_OP_breg3 =0x73,
DW_OP_breg4 =0x74,
DW_OP_breg5 =0x75,
DW_OP_breg6 =0x76,
DW_OP_breg7 =0x77,
DW_OP_breg8 =0x78,
DW_OP_breg9 =0x79,
DW_OP_breg10 =0x7a,
DW_OP_breg11 =0x7b,
DW_OP_breg12 =0x7c,
DW_OP_breg13 =0x7d,
DW_OP_breg14 =0x7e,
DW_OP_breg15 =0x7f,
DW_OP_breg16 =0x80,
DW_OP_breg17 =0x81,
DW_OP_breg18 =0x82,
DW_OP_breg19 =0x83,
DW_OP_breg20 =0x84,
DW_OP_breg21 =0x85,
DW_OP_breg22 =0x86,
DW_OP_breg23 =0x87,
DW_OP_breg24 =0x88,
DW_OP_breg25 =0x89,
DW_OP_breg26 =0x8a,
DW_OP_breg27 =0x8b,
DW_OP_breg28 =0x8c,
DW_OP_breg29 =0x8d,
DW_OP_breg30 =0x8e,
DW_OP_breg31 =0x8f,
DW_OP_regX =0x90,
DW_OP_fbreg =0x91,
DW_OP_bregX =0x92,
DW_OP_piece =0x93,
DW_OP_deref_size =0x94,
DW_OP_xderef_size =0x95,
DW_OP_nop =0x96,
// DWARF3/DWARF3f
DW_OP_push_object_address =0x97,
DW_OP_call2 =0x98,
DW_OP_call4 =0x99,
DW_OP_call_ref =0x9a,
DW_OP_form_tls_address =0x9b,
DW_OP_call_frame_cfa =0x9c,
DW_OP_bit_piece =0x9d,
DW_OP_lo_user =0xe0,
DW_OP_hi_user =0xff,
// GNU extensions
DW_OP_GNU_push_tls_address =0xe0
};
// Source languages. These are values for DW_AT_language.
enum DwarfLanguage
{
DW_LANG_none =0x0000,
DW_LANG_C89 =0x0001,
DW_LANG_C =0x0002,
DW_LANG_Ada83 =0x0003,
DW_LANG_C_plus_plus =0x0004,
DW_LANG_Cobol74 =0x0005,
DW_LANG_Cobol85 =0x0006,
DW_LANG_Fortran77 =0x0007,
DW_LANG_Fortran90 =0x0008,
DW_LANG_Pascal83 =0x0009,
DW_LANG_Modula2 =0x000a,
DW_LANG_Java =0x000b,
DW_LANG_C99 =0x000c,
DW_LANG_Ada95 =0x000d,
DW_LANG_Fortran95 =0x000e,
DW_LANG_PLI =0x000f,
DW_LANG_ObjC =0x0010,
DW_LANG_ObjC_plus_plus =0x0011,
DW_LANG_UPC =0x0012,
DW_LANG_D =0x0013,
// Implementation-defined language code range.
DW_LANG_lo_user = 0x8000,
DW_LANG_hi_user = 0xffff,
// Extensions.
// MIPS assembly language. The GNU toolchain uses this for all
// assembly languages, since there's no generic DW_LANG_ value for that.
// See include/dwarf2.h in the binutils, gdb, or gcc source trees.
DW_LANG_Mips_Assembler =0x8001,
DW_LANG_Upc =0x8765 // Unified Parallel C
};
// Inline codes. These are values for DW_AT_inline.
enum DwarfInline {
DW_INL_not_inlined =0x0,
DW_INL_inlined =0x1,
DW_INL_declared_not_inlined =0x2,
DW_INL_declared_inlined =0x3
};
// Call Frame Info instructions.
enum DwarfCFI
{
DW_CFA_advance_loc = 0x40,
DW_CFA_offset = 0x80,
DW_CFA_restore = 0xc0,
DW_CFA_nop = 0x00,
DW_CFA_set_loc = 0x01,
DW_CFA_advance_loc1 = 0x02,
DW_CFA_advance_loc2 = 0x03,
DW_CFA_advance_loc4 = 0x04,
DW_CFA_offset_extended = 0x05,
DW_CFA_restore_extended = 0x06,
DW_CFA_undefined = 0x07,
DW_CFA_same_value = 0x08,
DW_CFA_register = 0x09,
DW_CFA_remember_state = 0x0a,
DW_CFA_restore_state = 0x0b,
DW_CFA_def_cfa = 0x0c,
DW_CFA_def_cfa_register = 0x0d,
DW_CFA_def_cfa_offset = 0x0e,
DW_CFA_def_cfa_expression = 0x0f,
DW_CFA_expression = 0x10,
DW_CFA_offset_extended_sf = 0x11,
DW_CFA_def_cfa_sf = 0x12,
DW_CFA_def_cfa_offset_sf = 0x13,
DW_CFA_val_offset = 0x14,
DW_CFA_val_offset_sf = 0x15,
DW_CFA_val_expression = 0x16,
// Opcodes in this range are reserved for user extensions.
DW_CFA_lo_user = 0x1c,
DW_CFA_hi_user = 0x3f,
// SGI/MIPS specific.
DW_CFA_MIPS_advance_loc8 = 0x1d,
// GNU extensions.
DW_CFA_GNU_window_save = 0x2d,
DW_CFA_GNU_args_size = 0x2e,
DW_CFA_GNU_negative_offset_extended = 0x2f
};
// Exception handling 'z' augmentation letters.
enum DwarfZAugmentationCodes {
// If the CFI augmentation string begins with 'z', then the CIE and FDE
// have an augmentation data area just before the instructions, whose
// contents are determined by the subsequent augmentation letters.
DW_Z_augmentation_start = 'z',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding, and the FDE
// augmentation data includes a language-specific data area pointer,
// represented using that encoding.
DW_Z_has_LSDA = 'L',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding, followed by a pointer
// to a personality routine, represented using that encoding.
DW_Z_has_personality_routine = 'P',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding describing how the FDE's
// initial location, address range, and DW_CFA_set_loc operands are
// encoded.
DW_Z_has_FDE_address_encoding = 'R',
// If this letter is present in a 'z' augmentation string, then code
// addresses covered by FDEs that cite this CIE are signal delivery
// trampolines. Return addresses of frames in trampolines should not be
// adjusted as described in section 6.4.4 of the DWARF 3 spec.
DW_Z_is_signal_trampoline = 'S'
};
// Exception handling frame description pointer formats, as described
// by the Linux Standard Base Core Specification 4.0, section 11.5,
// DWARF Extensions.
enum DwarfPointerEncoding
{
DW_EH_PE_absptr = 0x00,
DW_EH_PE_omit = 0xff,
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_sleb128 = 0x09,
DW_EH_PE_sdata2 = 0x0A,
DW_EH_PE_sdata4 = 0x0B,
DW_EH_PE_sdata8 = 0x0C,
DW_EH_PE_pcrel = 0x10,
DW_EH_PE_textrel = 0x20,
DW_EH_PE_datarel = 0x30,
DW_EH_PE_funcrel = 0x40,
DW_EH_PE_aligned = 0x50,
// The GNU toolchain sources define this enum value as well,
// simply to help classify the lower nybble values into signed and
// unsigned groups.
DW_EH_PE_signed = 0x08,
// This is not documented in LSB 4.0, but it is used in both the
// Linux and OS X toolchains. It can be added to any other
// 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
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_DWARF2ENUMS_H__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,484 @@
// Copyright (c) 2012, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit
#include <stdlib.h>
#include <iostream>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/dwarf2reader_test_common.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
using google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using dwarf2reader::ByteReader;
using dwarf2reader::CompilationUnit;
using dwarf2reader::Dwarf2Handler;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfHasChild;
using dwarf2reader::DwarfTag;
using dwarf2reader::ENDIANNESS_BIG;
using dwarf2reader::ENDIANNESS_LITTLE;
using dwarf2reader::SectionMap;
using std::vector;
using testing::InSequence;
using testing::Pointee;
using testing::Return;
using testing::Sequence;
using testing::Test;
using testing::TestWithParam;
using testing::_;
class MockDwarf2Handler: public Dwarf2Handler {
public:
MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version));
MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag));
MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset,
DwarfAttribute attr,
enum DwarfForm form,
uint64 data));
MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data));
MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data));
MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len));
MOCK_METHOD4(ProcessAttributeString, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data));
MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset,
DwarfAttribute attr,
enum DwarfForm form,
uint64 signature));
MOCK_METHOD1(EndDIE, void(uint64 offset));
};
struct DIEFixture {
DIEFixture() {
// Fix the initial offset of the .debug_info and .debug_abbrev sections.
info.start() = 0;
abbrevs.start() = 0;
// Default expectations for the data handler.
EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0);
EXPECT_CALL(handler, StartDIE(_, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0);
EXPECT_CALL(handler, EndDIE(_)).Times(0);
}
// Return a reference to a section map whose .debug_info section refers
// to |info|, and whose .debug_abbrev section refers to |abbrevs|. This
// function returns a reference to the same SectionMap each time; new
// calls wipe out maps established by earlier calls.
const SectionMap &MakeSectionMap() {
// Copy the sections' contents into strings that will live as long as
// the map itself.
assert(info.GetContents(&info_contents));
assert(abbrevs.GetContents(&abbrevs_contents));
section_map.clear();
section_map[".debug_info"].first = info_contents.data();
section_map[".debug_info"].second = info_contents.size();
section_map[".debug_abbrev"].first = abbrevs_contents.data();
section_map[".debug_abbrev"].second = abbrevs_contents.size();
return section_map;
}
TestCompilationUnit info;
TestAbbrevTable abbrevs;
MockDwarf2Handler handler;
string abbrevs_contents, info_contents;
SectionMap section_map;
};
struct DwarfHeaderParams {
DwarfHeaderParams(Endianness endianness, size_t format_size,
int version, size_t address_size)
: endianness(endianness), format_size(format_size),
version(version), address_size(address_size) { }
Endianness endianness;
size_t format_size; // 4-byte or 8-byte DWARF offsets
int version;
size_t address_size;
};
class DwarfHeader: public DIEFixture,
public TestWithParam<DwarfHeaderParams> { };
TEST_P(DwarfHeader, Header) {
Label abbrev_table = abbrevs.Here();
abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit,
dwarf2reader::DW_children_yes)
.Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string)
.EndAbbrev()
.EndTable();
info.set_format_size(GetParam().format_size);
info.set_endianness(GetParam().endianness);
info.Header(GetParam().version, abbrev_table, GetParam().address_size)
.ULEB128(1) // DW_TAG_compile_unit, with children
.AppendCString("sam") // DW_AT_name, DW_FORM_string
.D8(0); // end of children
info.Finish();
{
InSequence s;
EXPECT_CALL(handler,
StartCompilationUnit(0, GetParam().address_size,
GetParam().format_size, _,
GetParam().version))
.WillOnce(Return(true));
EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit))
.WillOnce(Return(true));
EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name,
dwarf2reader::DW_FORM_string,
"sam"))
.WillOnce(Return());
EXPECT_CALL(handler, EndDIE(_))
.WillOnce(Return());
}
ByteReader byte_reader(GetParam().endianness == kLittleEndian ?
ENDIANNESS_LITTLE : ENDIANNESS_BIG);
CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler);
EXPECT_EQ(parser.Start(), info_contents.size());
}
INSTANTIATE_TEST_CASE_P(
HeaderVariants, DwarfHeader,
::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
DwarfHeaderParams(kLittleEndian, 4, 2, 8),
DwarfHeaderParams(kLittleEndian, 4, 3, 4),
DwarfHeaderParams(kLittleEndian, 4, 3, 8),
DwarfHeaderParams(kLittleEndian, 4, 4, 4),
DwarfHeaderParams(kLittleEndian, 4, 4, 8),
DwarfHeaderParams(kLittleEndian, 8, 2, 4),
DwarfHeaderParams(kLittleEndian, 8, 2, 8),
DwarfHeaderParams(kLittleEndian, 8, 3, 4),
DwarfHeaderParams(kLittleEndian, 8, 3, 8),
DwarfHeaderParams(kLittleEndian, 8, 4, 4),
DwarfHeaderParams(kLittleEndian, 8, 4, 8),
DwarfHeaderParams(kBigEndian, 4, 2, 4),
DwarfHeaderParams(kBigEndian, 4, 2, 8),
DwarfHeaderParams(kBigEndian, 4, 3, 4),
DwarfHeaderParams(kBigEndian, 4, 3, 8),
DwarfHeaderParams(kBigEndian, 4, 4, 4),
DwarfHeaderParams(kBigEndian, 4, 4, 8),
DwarfHeaderParams(kBigEndian, 8, 2, 4),
DwarfHeaderParams(kBigEndian, 8, 2, 8),
DwarfHeaderParams(kBigEndian, 8, 3, 4),
DwarfHeaderParams(kBigEndian, 8, 3, 8),
DwarfHeaderParams(kBigEndian, 8, 4, 4),
DwarfHeaderParams(kBigEndian, 8, 4, 8)));
struct DwarfFormsFixture: public DIEFixture {
// Start a compilation unit, as directed by |params|, containing one
// childless DIE of the given tag, with one attribute of the given name
// and form. The 'info' fixture member is left just after the abbrev
// code, waiting for the attribute value to be appended.
void StartSingleAttributeDIE(const DwarfHeaderParams &params,
DwarfTag tag, DwarfAttribute name,
DwarfForm form) {
// Create the abbreviation table.
Label abbrev_table = abbrevs.Here();
abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no)
.Attribute(name, form)
.EndAbbrev()
.EndTable();
// Create the compilation unit, up to the attribute value.
info.set_format_size(params.format_size);
info.set_endianness(params.endianness);
info.Header(params.version, abbrev_table, params.address_size)
.ULEB128(1); // abbrev code
}
// Set up handler to expect a compilation unit matching |params|,
// containing one childless DIE of the given tag, in the sequence s. Stop
// just before the expectations.
void ExpectBeginCompilationUnit(const DwarfHeaderParams &params,
DwarfTag tag, uint64 offset=0) {
EXPECT_CALL(handler,
StartCompilationUnit(offset, params.address_size,
params.format_size, _,
params.version))
.InSequence(s)
.WillOnce(Return(true));
EXPECT_CALL(handler, StartDIE(_, tag))
.InSequence(s)
.WillOnce(Return(true));
}
void ExpectEndCompilationUnit() {
EXPECT_CALL(handler, EndDIE(_))
.InSequence(s)
.WillOnce(Return());
}
void ParseCompilationUnit(const DwarfHeaderParams &params, uint64 offset=0) {
ByteReader byte_reader(params.endianness == kLittleEndian ?
ENDIANNESS_LITTLE : ENDIANNESS_BIG);
CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler);
EXPECT_EQ(offset + parser.Start(), info_contents.size());
}
// The sequence to which the fixture's methods append expectations.
Sequence s;
};
struct DwarfForms: public DwarfFormsFixture,
public TestWithParam<DwarfHeaderParams> { };
TEST_P(DwarfForms, addr) {
StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit,
dwarf2reader::DW_AT_low_pc,
dwarf2reader::DW_FORM_addr);
uint64_t value;
if (GetParam().address_size == 4) {
value = 0xc8e9ffcc;
info.D32(value);
} else {
value = 0xe942517fc2768564ULL;
info.D64(value);
}
info.Finish();
ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc,
dwarf2reader::DW_FORM_addr,
value))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, block2_empty) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
(DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2);
info.D16(0);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2,
_, 0))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, block2) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
(DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2);
unsigned char data[258];
memset(data, '*', sizeof(data));
info.D16(sizeof(data))
.Append(data, sizeof(data));
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2,
Pointee('*'), 258))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, flag_present) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2,
(DwarfAttribute) 0x359d1972,
dwarf2reader::DW_FORM_flag_present);
// DW_FORM_flag_present occupies no space in the DIE.
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2);
EXPECT_CALL(handler,
ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972,
dwarf2reader::DW_FORM_flag_present,
1))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, sec_offset) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689,
(DwarfAttribute) 0xa060bfd1,
dwarf2reader::DW_FORM_sec_offset);
uint64_t value;
if (GetParam().format_size == 4) {
value = 0xacc9c388;
info.D32(value);
} else {
value = 0xcffe5696ffe3ed0aULL;
info.D64(value);
}
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1,
dwarf2reader::DW_FORM_sec_offset,
value))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, exprloc) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb,
(DwarfAttribute) 0xba3ae5cb,
dwarf2reader::DW_FORM_exprloc);
info.ULEB128(29)
.Append(29, 173);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb,
dwarf2reader::DW_FORM_exprloc,
Pointee(173), 29))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, ref_sig8) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
(DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8);
info.D64(0xf72fa0cb6ddcf9d6ULL);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b);
EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8,
0xf72fa0cb6ddcf9d6ULL))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
// A value passed to ProcessAttributeSignature is just an absolute number,
// not an offset within the compilation unit as most of the other
// DW_FORM_ref forms are. Check that the reader doesn't try to apply any
// offset to the signature, by reading it from a compilation unit that does
// not start at the beginning of the section.
TEST_P(DwarfForms, ref_sig8_not_first) {
info.Append(98, '*');
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
(DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8);
info.D64(0xf72fa0cb6ddcf9d6ULL);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98);
EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8,
0xf72fa0cb6ddcf9d6ULL))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam(), 98);
}
// Tests for the other attribute forms could go here.
INSTANTIATE_TEST_CASE_P(
HeaderVariants, DwarfForms,
::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
DwarfHeaderParams(kLittleEndian, 4, 2, 8),
DwarfHeaderParams(kLittleEndian, 4, 3, 4),
DwarfHeaderParams(kLittleEndian, 4, 3, 8),
DwarfHeaderParams(kLittleEndian, 4, 4, 4),
DwarfHeaderParams(kLittleEndian, 4, 4, 8),
DwarfHeaderParams(kLittleEndian, 8, 2, 4),
DwarfHeaderParams(kLittleEndian, 8, 2, 8),
DwarfHeaderParams(kLittleEndian, 8, 3, 4),
DwarfHeaderParams(kLittleEndian, 8, 3, 8),
DwarfHeaderParams(kLittleEndian, 8, 4, 4),
DwarfHeaderParams(kLittleEndian, 8, 4, 8),
DwarfHeaderParams(kBigEndian, 4, 2, 4),
DwarfHeaderParams(kBigEndian, 4, 2, 8),
DwarfHeaderParams(kBigEndian, 4, 3, 4),
DwarfHeaderParams(kBigEndian, 4, 3, 8),
DwarfHeaderParams(kBigEndian, 4, 4, 4),
DwarfHeaderParams(kBigEndian, 4, 4, 8),
DwarfHeaderParams(kBigEndian, 8, 2, 4),
DwarfHeaderParams(kBigEndian, 8, 2, 8),
DwarfHeaderParams(kBigEndian, 8, 3, 4),
DwarfHeaderParams(kBigEndian, 8, 3, 8),
DwarfHeaderParams(kBigEndian, 8, 4, 4),
DwarfHeaderParams(kBigEndian, 8, 4, 8)));

View File

@ -0,0 +1,149 @@
// -*- mode: c++ -*-
// Copyright (c) 2012, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2reader_test_common.h: Define TestCompilationUnit and
// TestAbbrevTable, classes for creating properly (and improperly)
// formatted DWARF compilation unit data for unit tests.
#ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
#define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
#include "common/test_assembler.h"
#include "common/dwarf/dwarf2enums.h"
// A subclass of test_assembler::Section, specialized for constructing
// DWARF compilation units.
class TestCompilationUnit: public google_breakpad::test_assembler::Section {
public:
typedef dwarf2reader::DwarfTag DwarfTag;
typedef dwarf2reader::DwarfAttribute DwarfAttribute;
typedef dwarf2reader::DwarfForm DwarfForm;
typedef google_breakpad::test_assembler::Label Label;
// Set the section's DWARF format size (the 32-bit DWARF format or the
// 64-bit DWARF format, for lengths and section offsets --- not the
// address size) to format_size.
void set_format_size(size_t format_size) {
assert(format_size == 4 || format_size == 8);
format_size_ = format_size;
}
// Append a DWARF section offset value, of the appropriate size for this
// compilation unit.
template<typename T>
void SectionOffset(T offset) {
if (format_size_ == 4)
D32(offset);
else
D64(offset);
}
// Append a DWARF compilation unit header to the section, with the given
// DWARF version, abbrev table offset, and address size.
TestCompilationUnit &Header(int version, const Label &abbrev_offset,
size_t address_size) {
if (format_size_ == 4) {
D32(length_);
} else {
D32(0xffffffff);
D64(length_);
}
post_length_offset_ = Size();
D16(version);
SectionOffset(abbrev_offset);
D8(address_size);
return *this;
}
// Mark the end of this header's DIEs.
TestCompilationUnit &Finish() {
length_ = Size() - post_length_offset_;
return *this;
}
private:
// The DWARF format size for this compilation unit.
size_t format_size_;
// The offset of the point in the compilation unit header immediately
// after the initial length field.
uint64_t post_length_offset_;
// The length of the compilation unit, not including the initial length field.
Label length_;
};
// A subclass of test_assembler::Section specialized for constructing DWARF
// abbreviation tables.
class TestAbbrevTable: public google_breakpad::test_assembler::Section {
public:
typedef dwarf2reader::DwarfTag DwarfTag;
typedef dwarf2reader::DwarfAttribute DwarfAttribute;
typedef dwarf2reader::DwarfForm DwarfForm;
typedef dwarf2reader::DwarfHasChild DwarfHasChild;
typedef google_breakpad::test_assembler::Label Label;
// Start a new abbreviation table entry for abbreviation code |code|,
// encoding a DIE whose tag is |tag|, and which has children if and only
// if |has_children| is true.
TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) {
assert(code != 0);
ULEB128(code);
ULEB128(static_cast<unsigned>(tag));
D8(static_cast<unsigned>(has_children));
return *this;
};
// Add an attribute to the current abbreviation code whose name is |name|
// and whose form is |form|.
TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) {
ULEB128(static_cast<unsigned>(name));
ULEB128(static_cast<unsigned>(form));
return *this;
}
// Finish the current abbreviation code.
TestAbbrevTable &EndAbbrev() {
ULEB128(0);
ULEB128(0);
return *this;
}
// Finish the current abbreviation table.
TestAbbrevTable &EndTable() {
ULEB128(0);
return *this;
}
};
#endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__

View File

@ -0,0 +1,231 @@
// Copyright (c) 2010 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.
// This is a client for the dwarf2reader to extract function and line
// information from the debug info.
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <map>
#include <queue>
#include <vector>
#include "common/dwarf/functioninfo.h"
#include "common/dwarf/bytereader.h"
#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
using google_breakpad::scoped_ptr;
namespace dwarf2reader {
CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<string>* dirs,
LineMap* linemap):linemap_(linemap),
files_(files),
dirs_(dirs) {
// The dirs and files are 1 indexed, so just make sure we put
// nothing in the 0 vector.
assert(dirs->size() == 0);
assert(files->size() == 0);
dirs->push_back("");
SourceFileInfo s;
s.name = "";
s.lowpc = ULLONG_MAX;
files->push_back(s);
}
void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) {
// These should never come out of order, actually
assert(dir_num == dirs_->size());
dirs_->push_back(name);
}
void CULineInfoHandler::DefineFile(const string& name,
int32 file_num, uint32 dir_num,
uint64 mod_time, uint64 length) {
assert(dir_num >= 0);
assert(dir_num < dirs_->size());
// These should never come out of order, actually.
if (file_num == (int32)files_->size() || file_num == -1) {
string dir = dirs_->at(dir_num);
SourceFileInfo s;
s.lowpc = ULLONG_MAX;
if (dir == "") {
s.name = name;
} else {
s.name = dir + "/" + name;
}
files_->push_back(s);
} else {
fprintf(stderr, "error in DefineFile");
}
}
void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
uint32 line_num, uint32 column_num) {
if (file_num < files_->size()) {
linemap_->insert(
std::make_pair(address,
std::make_pair(files_->at(file_num).name.c_str(),
line_num)));
if (address < files_->at(file_num).lowpc) {
files_->at(file_num).lowpc = address;
}
} else {
fprintf(stderr, "error in AddLine");
}
}
bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset,
uint8 address_size,
uint8 offset_size,
uint64 cu_length,
uint8 dwarf_version) {
current_compilation_unit_offset_ = offset;
return true;
}
// For function info, we only care about subprograms and inlined
// subroutines. For line info, the DW_AT_stmt_list lives in the
// compile unit tag.
bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag) {
switch (tag) {
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine: {
current_function_info_ = new FunctionInfo;
current_function_info_->lowpc = current_function_info_->highpc = 0;
current_function_info_->name = "";
current_function_info_->line = 0;
current_function_info_->file = "";
offset_to_funcinfo_->insert(std::make_pair(offset,
current_function_info_));
};
// FALLTHROUGH
case DW_TAG_compile_unit:
return true;
default:
return false;
}
return false;
}
// Only care about the name attribute for functions
void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
if (current_function_info_) {
if (attr == DW_AT_name)
current_function_info_->name = data;
else if (attr == DW_AT_MIPS_linkage_name)
current_function_info_->mangled_name = data;
}
}
void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
if (attr == DW_AT_stmt_list) {
SectionMap::const_iterator iter = sections_.find("__debug_line");
assert(iter != sections_.end());
scoped_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
iter->second.second - data,
reader_, linehandler_));
lireader->Start();
} else if (current_function_info_) {
switch (attr) {
case DW_AT_low_pc:
current_function_info_->lowpc = data;
break;
case DW_AT_high_pc:
current_function_info_->highpc = data;
break;
case DW_AT_decl_line:
current_function_info_->line = data;
break;
case DW_AT_decl_file:
current_function_info_->file = files_->at(data).name;
break;
default:
break;
}
}
}
void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
if (current_function_info_) {
switch (attr) {
case DW_AT_specification: {
// Some functions have a "specification" attribute
// which means they were defined elsewhere. The name
// attribute is not repeated, and must be taken from
// the specification DIE. Here we'll assume that
// any DIE referenced in this manner will already have
// been seen, but that's not really required by the spec.
FunctionMap::iterator iter = offset_to_funcinfo_->find(data);
if (iter != offset_to_funcinfo_->end()) {
current_function_info_->name = iter->second->name;
current_function_info_->mangled_name = iter->second->mangled_name;
} else {
// If you hit this, this code probably needs to be rewritten.
fprintf(stderr,
"Error: DW_AT_specification was seen before the referenced "
"DIE! (Looking for DIE at offset %08llx, in DIE at "
"offset %08llx)\n", data, offset);
}
break;
}
default:
break;
}
}
}
void CUFunctionInfoHandler::EndDIE(uint64 offset) {
if (current_function_info_ && current_function_info_->lowpc)
address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc,
current_function_info_));
}
} // namespace dwarf2reader

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